├── .gitignore ├── .npmignore ├── README.md ├── config ├── mocha-loader.js ├── webpack.config.js ├── webpack.mocha.js ├── webpack.parts.js └── webpack.prod.js ├── gamee ├── dist │ ├── gamee-js.js │ ├── gamee-js.js.map │ ├── gamee-js.min.js │ └── gamee-js.min.js.map ├── libs │ ├── bullet.js │ └── shims.js ├── src │ ├── core.js │ ├── game_controllers.js │ ├── gameeAPI.js │ ├── index.js │ └── platform_bridge.js └── tests │ ├── api.test.js │ ├── dummy.test.js │ ├── framework-integrity.test.js │ └── index.js ├── jsdoc.conf.json ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # Commenting this out is preferred by some people, see 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 28 | node_modules 29 | 30 | # Users Environment Variables 31 | .lock-wscript 32 | 33 | 34 | ### Bower ### 35 | bower_components 36 | 37 | 38 | ### Linux ### 39 | *~ 40 | 41 | # KDE directory preferences 42 | .directory 43 | 44 | 45 | ### vim ### 46 | [._]*.s[a-w][a-z] 47 | [._]s[a-w][a-z] 48 | *.un~ 49 | Session.vim 50 | .netrwhist 51 | *~ 52 | 53 | ## documentation files 54 | 55 | gamee/docs 56 | 57 | ## packed samples 58 | 59 | projects-dist 60 | projects-build 61 | 62 | ## IDE 63 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | gamee/dist 2 | .idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GAMEE JS 2 | 3 | GAMEE is a social platform full of HTML5 games. Our goal is to make the games playable everywhere. You can play in the mobile apps (Android, iOS), bots (Telegram, Kik, Facebook Messenger) or directly on web. 4 | 5 | GAMEE JS is our Javascript framework for connecting HTML5 game to [GAMEE](http://www.gameeapp.com/) platform. 6 | 7 | Documentation is currently located on [Github wiki](https://github.com/gameeapp/gamee-js/wiki) 8 | 9 | ## Usage 10 | 11 | Download the minified framework located in [gamee/dist/gamee-js.min.js](https://github.com/gameeapp/gamee-js/blob/master/gamee/dist/gamee-js.min.js) and include it in your ```index.html``` file. 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | ### Usage with NPM and Webpack 18 | 19 | Install the framework with command ```npm install gamee-js```. Then use: 20 | 21 | ```javascript 22 | import { gamee } from "gamee-js" 23 | ``` 24 | 25 | in your files. 26 | 27 | ## Previous versions (and changelogs) 28 | 29 | [https://github.com/gameeapp/gamee-js/releases](https://github.com/gameeapp/gamee-js/releases) 30 | 31 | ## Contribute 32 | 33 | If you want to contribute, please feel free to use [Github issue tracker](https://github.com/gameeapp/gamee-js/issues) of this repository. 34 | 35 | ## Build framework on your own 36 | 37 | ```bash 38 | git clone git@github.com:gameeapp/gamee-js.git 39 | ``` 40 | 41 | ```bash 42 | npm install 43 | 44 | # run test with mocha in CLI 45 | npm test 46 | 47 | # HRM test with mocha in browser 48 | npm test:mocha:watch 49 | 50 | # realtime building 51 | npm watch 52 | 53 | # one time build 54 | npm b:dist 55 | ``` 56 | 57 | ## Contact 58 | 59 | If you would like to publish your HTML5 game on the Gamee platform, please contact us on hello@gameeapp.com 60 | 61 | -------------------------------------------------------------------------------- /config/mocha-loader.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const config = require('./base') 3 | 4 | config.devServer = { 5 | host: 'localhost', 6 | port: '3001' 7 | } 8 | 9 | const index = path.resolve(__dirname, '../client/__tests__/index.js') 10 | 11 | config.entry = { 12 | test: [`mocha!${index}`] 13 | } 14 | 15 | config.output.publicPath = 'http://localhost:3001/' 16 | 17 | module.exports = config -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (env) { 2 | return require(`./webpack.${env}.js`) 3 | } -------------------------------------------------------------------------------- /config/webpack.mocha.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const merge = require('webpack-merge'); 3 | 4 | const parts = require('./webpack.parts'); 5 | 6 | module.exports = merge([ 7 | parts.devServer(), 8 | parts.page({ 9 | title: 'Mocha demo', 10 | entry: { 11 | tests: path.join(__dirname, '../gamee/tests') 12 | }, 13 | }), 14 | ]); -------------------------------------------------------------------------------- /config/webpack.parts.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | exports.devServer = ({ host, port } = {}) => ({ 6 | devServer: { 7 | historyApiFallback: true, 8 | stats: 'errors-only', 9 | host, // Defaults to `localhost` 10 | port: 3001, // Defaults to 8080 11 | overlay: { 12 | errors: true, 13 | warnings: true, 14 | }, 15 | }, 16 | }); 17 | 18 | exports.page = ({ 19 | path = '', 20 | template = require.resolve( 21 | 'html-webpack-plugin/default_index.ejs' 22 | ), 23 | title, 24 | entry, 25 | chunks, 26 | } = {}) => ({ 27 | entry, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | chunks, 31 | filename: `${path && path + '/'}index.html`, 32 | template, 33 | title, 34 | }), 35 | ], 36 | }); -------------------------------------------------------------------------------- /config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | webpack = require('webpack') 3 | 4 | module.exports = { 5 | devtool: '#source-map', 6 | 7 | entry: { 8 | "gamee-js": "./gamee/src/index.js", 9 | "gamee-js.min": "./gamee/src/index.js", 10 | }, 11 | 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.js$/, 16 | exclude: /(node_modules|bower_components)/, 17 | use: { 18 | loader: 'babel-loader', 19 | options: { 20 | presets: ['env'] 21 | } 22 | } 23 | } 24 | ] 25 | }, 26 | 27 | output: { 28 | // dont set this or context will be `gamee.gamee.emitter` etc. 29 | // library: 'gamee', 30 | libraryTarget: 'umd', 31 | path: path.resolve(__dirname, '../gamee/dist'), 32 | filename: '[name].js' 33 | }, 34 | 35 | plugins: [ 36 | new webpack.LoaderOptionsPlugin({ 37 | minimize: true, 38 | debug: false 39 | }), 40 | new webpack.BannerPlugin({ 41 | banner: `@preserve build time ${new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')}` 42 | }), 43 | new webpack.optimize.UglifyJsPlugin({ 44 | include: /\.min\.js$/, 45 | sourceMap: true, 46 | beautify: false, 47 | mangle: { 48 | screw_ie8: true, 49 | keep_fnames: true, 50 | toplevel: true, 51 | except: ["OneButtonController", 52 | "TwoButtonController", 53 | "FourButtonController", 54 | "FiveButtonController", 55 | "SixButtonController", 56 | "FourArrowController", 57 | "TouchController", 58 | "JoystickController", 59 | "JoystickButtonController", 60 | "TwoArrowsTwoButtonsController", 61 | "TwoArrowsOneButtonController", 62 | "TwoActionButtonsController"] // this will make objects reserved words, so it wont be minified and its constructor is visible in IDE and debugger 63 | }, 64 | compress: { 65 | screw_ie8: true, 66 | sequences: true, 67 | properties: true, 68 | dead_code: true, 69 | drop_debugger: true, 70 | drop_console: true, 71 | conditionals: true, 72 | comparisons: true 73 | }, 74 | comments: "some" 75 | }) 76 | ], 77 | 78 | } 79 | -------------------------------------------------------------------------------- /gamee/dist/gamee-js.js: -------------------------------------------------------------------------------- 1 | /*! @preserve build time 2019-08-27 08:54:59 */ 2 | (function webpackUniversalModuleDefinition(root, factory) { 3 | if(typeof exports === 'object' && typeof module === 'object') 4 | module.exports = factory(); 5 | else if(typeof define === 'function' && define.amd) 6 | define([], factory); 7 | else { 8 | var a = factory(); 9 | for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; 10 | } 11 | })(this, function() { 12 | return /******/ (function(modules) { // webpackBootstrap 13 | /******/ // The module cache 14 | /******/ var installedModules = {}; 15 | /******/ 16 | /******/ // The require function 17 | /******/ function __webpack_require__(moduleId) { 18 | /******/ 19 | /******/ // Check if module is in cache 20 | /******/ if(installedModules[moduleId]) { 21 | /******/ return installedModules[moduleId].exports; 22 | /******/ } 23 | /******/ // Create a new module (and put it into the cache) 24 | /******/ var module = installedModules[moduleId] = { 25 | /******/ i: moduleId, 26 | /******/ l: false, 27 | /******/ exports: {} 28 | /******/ }; 29 | /******/ 30 | /******/ // Execute the module function 31 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 32 | /******/ 33 | /******/ // Flag the module as loaded 34 | /******/ module.l = true; 35 | /******/ 36 | /******/ // Return the exports of the module 37 | /******/ return module.exports; 38 | /******/ } 39 | /******/ 40 | /******/ 41 | /******/ // expose the modules object (__webpack_modules__) 42 | /******/ __webpack_require__.m = modules; 43 | /******/ 44 | /******/ // expose the module cache 45 | /******/ __webpack_require__.c = installedModules; 46 | /******/ 47 | /******/ // identity function for calling harmony imports with the correct context 48 | /******/ __webpack_require__.i = function(value) { return value; }; 49 | /******/ 50 | /******/ // define getter function for harmony exports 51 | /******/ __webpack_require__.d = function(exports, name, getter) { 52 | /******/ if(!__webpack_require__.o(exports, name)) { 53 | /******/ Object.defineProperty(exports, name, { 54 | /******/ configurable: false, 55 | /******/ enumerable: true, 56 | /******/ get: getter 57 | /******/ }); 58 | /******/ } 59 | /******/ }; 60 | /******/ 61 | /******/ // getDefaultExport function for compatibility with non-harmony modules 62 | /******/ __webpack_require__.n = function(module) { 63 | /******/ var getter = module && module.__esModule ? 64 | /******/ function getDefault() { return module['default']; } : 65 | /******/ function getModuleExports() { return module; }; 66 | /******/ __webpack_require__.d(getter, 'a', getter); 67 | /******/ return getter; 68 | /******/ }; 69 | /******/ 70 | /******/ // Object.prototype.hasOwnProperty.call 71 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 72 | /******/ 73 | /******/ // __webpack_public_path__ 74 | /******/ __webpack_require__.p = ""; 75 | /******/ 76 | /******/ // Load entry module and return exports 77 | /******/ return __webpack_require__(__webpack_require__.s = 6); 78 | /******/ }) 79 | /************************************************************************/ 80 | /******/ ([ 81 | /* 0 */ 82 | /***/ (function(module, exports, __webpack_require__) { 83 | 84 | "use strict"; 85 | 86 | 87 | Object.defineProperty(exports, "__esModule", { 88 | value: true 89 | }); 90 | exports.CustomEmitter = CustomEmitter; 91 | exports.wrapKeyEvent = wrapKeyEvent; 92 | /** 93 | * @class CustomEvent 94 | */ 95 | (function shimCustomEvent() { 96 | try { 97 | var ce = new window.CustomEvent('test'); 98 | ce.preventDefault(); 99 | if (ce.defaultPrevented !== true) { 100 | // IE has problems with .preventDefault() on custom events 101 | // http://stackoverflow.com/questions/23349191 102 | throw new Error('Could not prevent default'); 103 | } 104 | } catch (e) { 105 | var CustomEvent = function CustomEvent(event, params) { 106 | var evt, origPrevent; 107 | params = params || { 108 | bubbles: false, 109 | cancelable: false, 110 | detail: undefined 111 | }; 112 | 113 | evt = document.createEvent("CustomEvent"); 114 | evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); 115 | origPrevent = evt.preventDefault; 116 | evt.preventDefault = function () { 117 | origPrevent.call(this); 118 | try { 119 | Object.defineProperty(this, 'defaultPrevented', { 120 | get: function get() { 121 | return true; 122 | } 123 | }); 124 | } catch (e) { 125 | this.defaultPrevented = true; 126 | } 127 | }; 128 | return evt; 129 | }; 130 | 131 | CustomEvent.prototype = window.Event.prototype; 132 | window.CustomEvent = CustomEvent; // expose definition to window 133 | } 134 | })(); 135 | 136 | //addEventListener polyfill 1.0 / Eirik Backer / MIT Licence 137 | (function (win, doc) { 138 | if (win.addEventListener) return; //No need to polyfill 139 | 140 | function docHijack(p) { 141 | var old = doc[p];doc[p] = function (v) { 142 | return addListen(old(v)); 143 | }; 144 | } 145 | function addEvent(on, fn, self) { 146 | return (self = this).attachEvent('on' + on, function (e) { 147 | e = e || win.event; 148 | e.preventDefault = e.preventDefault || function () { 149 | e.returnValue = false; 150 | }; 151 | e.stopPropagation = e.stopPropagation || function () { 152 | e.cancelBubble = true; 153 | }; 154 | fn.call(self, e); 155 | }); 156 | } 157 | function addListen(obj, i) { 158 | i = obj.length; 159 | if (i) { 160 | while (i--) { 161 | obj[i].addEventListener = addEvent; 162 | } 163 | } else { 164 | obj.addEventListener = addEvent; 165 | } 166 | return obj; 167 | } 168 | 169 | addListen([doc, win]); 170 | if ('Element' in win) win.Element.prototype.addEventListener = addEvent; //IE8 171 | else { 172 | //IE < 8 173 | doc.attachEvent('onreadystatechange', function () { 174 | addListen(doc.all); 175 | }); //Make sure we also init at domReady 176 | docHijack('getElementsByTagName'); 177 | docHijack('getElementById'); 178 | docHijack('createElement'); 179 | addListen(doc.all); 180 | } 181 | })(window, document); 182 | 183 | // naomik event emiter http://stackoverflow.com/a/24216547/1866147 184 | // usage: 185 | // function Example() { 186 | // CustomEmitter.call(this); 187 | // } 188 | 189 | // // run it 190 | // var e = new Example(); 191 | 192 | // e.addEventListener("something", function (event) { 193 | // console.log(event) 194 | // }); 195 | 196 | // e.dispatchEvent(new Event("something")); 197 | function CustomEmitter() { 198 | var eventTarget = document.createDocumentFragment(); 199 | 200 | function delegate(method) { 201 | this[method] = eventTarget[method].bind(eventTarget); 202 | } 203 | 204 | ["addEventListener", "dispatchEvent", "removeEventListener"].forEach(delegate, this); 205 | } 206 | 207 | /** ### wrapKeyEvent 208 | * 209 | * Handle old IE event differences for key events 210 | * 211 | * @param {Function} fn callback 212 | */ 213 | function wrapKeyEvent(fn) { 214 | return function (ev) { 215 | if (!ev || !ev.keyCode) { 216 | if (!ev) { 217 | ev = window.event; 218 | } 219 | 220 | if (ev.which) { 221 | ev.keyCode = ev.which; 222 | } 223 | } 224 | 225 | return fn(ev); 226 | }; 227 | } 228 | 229 | /***/ }), 230 | /* 1 */ 231 | /***/ (function(module, exports, __webpack_require__) { 232 | 233 | "use strict"; 234 | /* WEBPACK VAR INJECTION */(function(global) { 235 | 236 | Object.defineProperty(exports, "__esModule", { 237 | value: true 238 | }); 239 | exports.validateDataType = exports.DataTypeException = exports.core = undefined; 240 | 241 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 242 | 243 | var _game_controllers = __webpack_require__(5); 244 | 245 | var controllers = _interopRequireWildcard(_game_controllers); 246 | 247 | var _shims = __webpack_require__(0); 248 | 249 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 250 | 251 | // unlock audio 252 | // overrides native AudioContext & webkitAudioContext 253 | (function () { 254 | // this works as a constructor 255 | var overloadedAudioContext = function overloadedAudioContext(type) { 256 | var ctx = new type(); 257 | 258 | // add audio resume to function on touchstart 259 | if (ctx.state === 'suspended') { 260 | 261 | var resume = function resume() { 262 | 263 | // Check if hack is necessary. Only occurs in iOS6+ devices 264 | // and only when you first boot the iPhone, or play a audio/video 265 | // with a different sample rate 266 | if (/(iPhone|iPad)/i.test(navigator.userAgent)) { 267 | var buffer = ctx.createBuffer(1, 1, 44100); 268 | var dummy = ctx.createBufferSource(); 269 | dummy.buffer = buffer; 270 | dummy.connect(ctx.destination); 271 | dummy.start(0); 272 | dummy.disconnect(); 273 | } 274 | 275 | ctx.resume(); 276 | setTimeout(function () { 277 | if (ctx.state === 'running') { 278 | document.body.removeEventListener(['touchcancel', 'touchend', 'touchenter', 'touchleave', 'touchmove', 'touchstart', 'mouseenter', 'mouseover', 'mousemove', 'mousedown', 'mouseup'].join(" "), resume, false); 279 | } 280 | }, 0); 281 | }; 282 | 283 | // only touchend will work, but hey, we tried... 284 | // https://github.com/WebAudio/web-audio-api/issues/836 285 | // https://www.chromestatus.com/feature/6406908126691328 286 | document.body.addEventListener(['touchcancel', 'touchend', 'touchenter', 'touchleave', 'touchmove', 'touchstart', 'mouseenter', 'mouseover', 'mousemove', 'mousedown', 'mouseup'].join(" "), resume, false); 287 | } 288 | // allowed in JS to return different type of the object in the constructor 289 | return ctx; 290 | }; 291 | 292 | try { 293 | if (typeof window.AudioContext !== 'undefined') { 294 | window.AudioContext = overloadedAudioContext.bind(null, window.AudioContext); 295 | } else if (typeof webkitAudioContext !== 'undefined') { 296 | window.webkitAudioContext = overloadedAudioContext.bind(null, window.webkitAudioContext); 297 | } 298 | } catch (e) { 299 | // throw error in async part 300 | setTimeout(function () { 301 | throw e; 302 | }, 0); 303 | } 304 | })(); 305 | 306 | /** 307 | * @class core 308 | */ 309 | var core = exports.core = function () { 310 | 311 | // # Gamee.js 312 | // 313 | // This file defines and expose a public API for games to communicate 314 | // with Gamee*. 315 | // 316 | // Also it handles some requirements when Gamee is run in an desktop 317 | // environment. 318 | // 319 | // \* _later in the document Gamee will be referred as GameeApp to not 320 | // be mistaken for word game_ 321 | // 322 | // ** _GameeWebApp will refer to Gamee which is running in a desktop 323 | // browser_ 324 | 325 | /** an empty function */ 326 | var noop = function noop() {}; 327 | 328 | var cache = {}; 329 | 330 | /** internal variables/constants (uppercase) coupled inside separate object for potential easy referencing */ 331 | var internals = { 332 | VERSION: "2.4.0", // version of the gamee library 333 | CAPABILITIES: ["ghostMode", "saveState", "replay", "socialData", "rewardedAds", "coins", "logEvents", "playerData", "share", "gems"], // supported capabilities 334 | variant: 0, // for automating communication with server 335 | soundUnlocked: false, 336 | onReady: noop, // for intercepting real onReady because of behind the scenes variant handling 337 | onGameStart: noop // for intercepting real onGameStart because of unlocking sound 338 | }; 339 | 340 | /** ## gamee 341 | * 342 | * GameeApp interface for games. It is exposed as a `gamee` global 343 | * object and games should only use its public methods and 344 | * properties to communicate with the GameeApp. 345 | * 346 | * _There is also [$gameeNative](gamee_native.js.html) global object 347 | * which handles internal parts of the communication._ 348 | */ 349 | var core = {}; 350 | 351 | // 352 | // ## Signaling game state 353 | // 354 | // The game should signal the GameeApp its status (playing/game-over) 355 | // and current score. 356 | // 357 | 358 | /** ### gamee.gameeInit 359 | * 360 | * Must be called first before any other gamee calls 361 | * returns controller object the same way requestController did previously 362 | * ctrlType/ctrlOpts - requested control type + options 363 | * capabilities -> array of strings representing supported features: 364 | * after the initialization onReady is invoked and after that game can use the api 365 | */ 366 | core.gameeInit = function (ctrlType, ctrlOpts, capabilities, cb) { 367 | var silentMode = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; 368 | 369 | // let's validate the array here, so that all backends can benefit from it 370 | var allOk = true, 371 | cap = {}; 372 | if (capabilities !== undefined && Array.isArray(capabilities)) { 373 | for (var i = 0; i < capabilities.length; i++) { 374 | if (typeof capabilities[i] !== "string" || internals.CAPABILITIES.indexOf(capabilities[i]) === -1) allOk = false; 375 | cap[capabilities[i]] = true; 376 | } 377 | } else allOk = false; 378 | 379 | if (!allOk) throw "Capabilities array passed to gameeInit is void, malformed or unsupported capabilites requested."; 380 | // TODO remove 381 | // gameeNative.gameeInit(core, internals.VERSION, ctrlType, allOk ? capabilities : []); 382 | 383 | this.native.createRequest("init", { 384 | version: internals.VERSION, 385 | controller: ctrlType, 386 | capabilities: cap 387 | }, function (responseData) { 388 | // remember capabilities of the game 389 | cache.capabilities = cap; 390 | // 391 | // // Mute gamee-js console output 392 | // cache.silentMode = silentMode; 393 | 394 | // might fail if controller of this type doesnt exist 395 | var error = null; 396 | try { 397 | if (this.native.platform === "web") { 398 | responseData.controller = core.controller.requestController(ctrlType, { enableKeyboard: true }); 399 | this._bindKeyboardTriggers(responseData.controller); 400 | } else { 401 | responseData.controller = core.controller.requestController(ctrlType, {}); 402 | } 403 | } catch (err) { 404 | error = err; 405 | } 406 | 407 | cb(error, responseData); 408 | }.bind(this)); 409 | // TODO remove 410 | // return core.controller.requestController(ctrlType, ctrlOpts); 411 | }; 412 | 413 | core._bindKeyboardTriggers = function (controller) { 414 | global.addEventListener('message', function (ev) { 415 | switch (ev.data[0]) { 416 | case 'button_button_down': 417 | controller.trigger("keydown", { button: "button" }); 418 | break; 419 | 420 | case 'button_button_up': 421 | controller.trigger("keyup", { button: "button" }); 422 | break; 423 | 424 | case 'button_left_up': 425 | controller.trigger("keyup", { button: "left" }); 426 | break; 427 | 428 | case 'button_left_down': 429 | controller.trigger("keydown", { button: "left" }); 430 | break; 431 | 432 | case 'button_right_down': 433 | controller.trigger("keydown", { button: "right" }); 434 | break; 435 | 436 | case 'button_right_up': 437 | controller.trigger("keyup", { button: "right" }); 438 | break; 439 | 440 | case 'button_up_down': 441 | controller.trigger("keydown", { button: "up" }); 442 | break; 443 | 444 | case 'button_up_up': 445 | controller.trigger("keyup", { button: "up" }); 446 | break; 447 | 448 | case 'button_down_down': 449 | controller.trigger("keydown", { button: "down" }); 450 | break; 451 | 452 | case 'button_down_up': 453 | controller.trigger("keyup", { button: "down" }); 454 | break; 455 | 456 | case 'button_a_down': 457 | controller.trigger("keydown", { button: "A" }); 458 | break; 459 | 460 | case 'button_a_up': 461 | controller.trigger("keyup", { button: "A" }); 462 | break; 463 | 464 | case 'button_b_down': 465 | controller.trigger("keydown", { button: "B" }); 466 | break; 467 | 468 | case 'button_b_up': 469 | controller.trigger("keyup", { button: "B" }); 470 | break; 471 | } 472 | }); 473 | }; 474 | 475 | /** ### gamee.gameLoadingProgress 476 | * 477 | * Indicates how much content is already loaded in %. 478 | */ 479 | core.gameLoadingProgress = function () { 480 | var percentageSoFar = 0; 481 | 482 | return function (percentage) { 483 | if (typeof percentage !== "number" || percentage < 0 || percentage > 100) throw "Percentage passed to gameLoadingProgress out of bounds or not a number.";else if (percentage > percentageSoFar) { 484 | percentageSoFar = percentage; 485 | this.native.createRequest("gameLoadingProgress", { percentage: percentage }); 486 | } 487 | }; 488 | }(); 489 | 490 | /** ### gamee.gameReady 491 | * 492 | * Notifies platform game can accept start command. 493 | */ 494 | core.gameReady = function () { 495 | this.native.createRequest("gameReady"); 496 | }; 497 | 498 | /** ### gamee.gameStart 499 | * 500 | * Indicates that game is ready to be started (even after restart). 501 | */ 502 | // core.gameStart = function () { 503 | // gameeNative.gameLoadingProgress(100); // FB requires this 504 | // gameeNative.gameStart(gamee); 505 | // }; 506 | 507 | /** ### gamee.updateScore 508 | * 509 | * sends score to UI 510 | */ 511 | core.updateScore = function (score, opt_ghostSign) { 512 | if (typeof score !== "number") throw "Score passed to updateScore is not a number."; 513 | var data = { 514 | score: parseInt(score, 10) 515 | }; 516 | if (opt_ghostSign) { 517 | data.ghostSign = true; 518 | } 519 | this.native.createRequest("updateScore", data); 520 | // core.native.createRequest(method, requestData, callback); 521 | }; 522 | 523 | /** ### gamee.gameOver 524 | * 525 | * Indicates the game has ended, the game is waiting for subsequent onGameStart. 526 | * Data has the same format as data received in onReady callback. 527 | * Data must be string = responsibility for turning data structure into string is left to the game! 528 | */ 529 | core.gameOver = function (opt_replayData, opt_saveState, opt_hideOverlay) { 530 | opt_hideOverlay = opt_hideOverlay !== undefined ? opt_hideOverlay : false; 531 | // var allOk = ((data !== undefined) && (typeof data === "string")) || (data === undefined); 532 | // if (!allOk) console.error("Data provided to gameOver function must be string."); 533 | // gameeNative.gameOver(gamee, internals.variant, allOk ? data : ""); 534 | var requestData = {}; 535 | if (opt_replayData) { 536 | if (!opt_replayData.hasOwnProperty("variant")) { 537 | opt_replayData.variant = ""; 538 | } 539 | if (!opt_replayData.hasOwnProperty("data")) { 540 | throw "Replay data must have `data` property"; 541 | } 542 | requestData.replayData = opt_replayData; 543 | } 544 | requestData.hideOverlay = opt_hideOverlay; 545 | 546 | if (opt_saveState) { 547 | requestData.state = opt_saveState; 548 | } 549 | 550 | core.native.createRequest("gameOver", requestData); 551 | }; 552 | 553 | /** ### gamee.gameSave 554 | * 555 | * Player has requested saving current game's state 556 | * data must be string = responsibility for turning data structure into string is left to game! 557 | * share must be expression evaluating to either true or false; it indicates, whether the game progress should be shared on feed 558 | */ 559 | core.gameSave = function (data, share) { 560 | 561 | if (!cache.capabilities.saveState) throw "Save State not supported, you must add the capability on gamee.Init"; 562 | 563 | core.native.createRequest("saveState", { state: data, share: share }); 564 | }; 565 | 566 | core.requestSocial = function (cb, numberOfPlayers) { 567 | 568 | if (!cache.capabilities.socialData) throw "Social Data not supported, you must add the capability on gamee.Init"; 569 | 570 | this.native.createRequest("requestSocial", numberOfPlayers, function (responseData) { 571 | cb(null, responseData); 572 | }); 573 | }; 574 | 575 | core.logEvent = function (eventName, eventValue) { 576 | 577 | if (!cache.capabilities.logEvents) throw "Log Events not supported, you must add the capability on gamee.Init"; 578 | 579 | //var valuesToLogString = JSON.stringify(eventValue) 580 | 581 | this.native.createRequest("logEvent", { eventName: eventName, eventValue: eventValue }, function (error) { 582 | if (error) { 583 | throw error; 584 | } 585 | }); 586 | }; 587 | 588 | core.requestBattleData = function (cb) { 589 | this.native.createRequest("requestBattleData", undefined, function (responseData) { 590 | cb(null, responseData); 591 | }); 592 | }; 593 | 594 | core.requestPlayerReplay = function (userID, cb) { 595 | 596 | if (!cache.capabilities.replay) throw "Replays not supported, you must add the capability on gamee.Init"; 597 | 598 | this.native.createRequest("requestPlayerReplay", { userID: userID }, function (responseData) { 599 | cb(null, responseData); 600 | }); 601 | }; 602 | 603 | core.requestPlayerSaveState = function (userID, cb) { 604 | this.native.createRequest("requestPlayerSaveState", { userID: userID }, function (responseData) { 605 | cb(null, responseData); 606 | }); 607 | }; 608 | 609 | core.purchaseItemWithCoins = function (options, cb, oldMethod) { 610 | 611 | if (!cache.capabilities.coins) throw "Coins purchases not supported, you must add the capability on gamee.Init"; 612 | 613 | if (options) { 614 | var propertiesList = ["coinsCost", "itemName"]; 615 | propertiesList.forEach(function (property) { 616 | if (!options.hasOwnProperty(property)) throw "Purchase Options must have `" + property + "` property"; 617 | }); 618 | } 619 | 620 | if (!this.isSilentModeEnabled()) { 621 | console.log(options); 622 | } 623 | 624 | var method = "purchaseItemWithCoins"; 625 | if (oldMethod !== undefined && oldMethod === true) { 626 | method = "purchaseItem"; 627 | } 628 | this.native.createRequest(method, options, function (responseData) { 629 | cb(null, responseData); 630 | }); 631 | }; 632 | 633 | core.purchaseItemWithGems = function (options, cb) { 634 | 635 | if (!cache.capabilities.gems) throw "Gems purchases not supported, you must add the capability on gamee.Init"; 636 | 637 | if (options) { 638 | var propertiesList = ["gemsCost", "itemName"]; 639 | propertiesList.forEach(function (property) { 640 | if (!options.hasOwnProperty(property)) throw "Purchase options must have `" + property + "` property"; 641 | }); 642 | } 643 | 644 | if (!this.isSilentModeEnabled()) { 645 | console.log(options); 646 | } 647 | 648 | this.native.createRequest("purchaseItemWithGems", options, function (responseData) { 649 | cb(null, responseData); 650 | }); 651 | }; 652 | 653 | core.share = function (options, cb) { 654 | 655 | if (!cache.capabilities.share) throw "Share option not supported, you must add the capability on gamee.Init"; 656 | 657 | if (options) { 658 | var propertiesList = ["destination"]; 659 | propertiesList.forEach(function (property) { 660 | if (!options.hasOwnProperty(property)) throw "Share Options must have `" + property + "` property"; 661 | }); 662 | } 663 | 664 | if (!this.isSilentModeEnabled()) { 665 | console.log(options); 666 | } 667 | 668 | this.native.createRequest("share", options, function (responseData) { 669 | cb(null, responseData); 670 | }); 671 | }; 672 | 673 | core.loadRewardedVideo = function (cb) { 674 | 675 | if (!cache.capabilities.rewardedAds) throw "Rewarded Ads not supported, you must add the capability on gamee.Init"; 676 | 677 | this.native.createRequest("loadRewardedVideo", function (responseData) { 678 | cb(null, responseData); 679 | }); 680 | }; 681 | 682 | core.showRewardedVideo = function (cb) { 683 | 684 | if (!cache.capabilities.rewardedAds) throw "Rewarded Ads not supported, you must add the capability on gamee.Init"; 685 | 686 | this.native.createRequest("showRewardedVideo", function (responseData) { 687 | cb(null, responseData); 688 | }); 689 | }; 690 | 691 | core.requestPlayerData = function (cb, userID) { 692 | 693 | if (!cache.capabilities.playerData) throw "Player Data not supported, you must add the capability on gamee.Init"; 694 | 695 | var options = undefined; 696 | if (userID) { 697 | options = { userID: userID }; 698 | } 699 | 700 | this.native.createRequest("requestPlayerData", options, function (responseData) { 701 | cb(null, responseData); 702 | }); 703 | }; 704 | 705 | core.startSignal = function (data) { 706 | var error; 707 | 708 | if (data.replay && !cache.capabilities.replay) error = "Game doesn't support replay. "; 709 | 710 | if (data.ghostMode && !cache.capabilities.ghostMode) error = "Game doesn't support ghost Mode. "; 711 | 712 | return error; 713 | }; 714 | // 715 | // ## Private objects and methods 716 | // These are internal objects in closed scope. Good to know about them 717 | // when debugging. 718 | 719 | // 720 | // ## gamee.controller 721 | // 722 | // Namespace where the methods for controller are published. 723 | // 724 | 725 | /** 726 | * TODO transform this into instance of gamee class 727 | */ 728 | core.controller = { 729 | /** ### mainController 730 | * 731 | * Current controller. 732 | */ 733 | mainController: null, 734 | 735 | /** ### requestController 736 | * 737 | * Factory method to create a controller. It creates the controller 738 | * and signals to GameeApp which type the game requires 739 | * 740 | * You should called this method once before calling 741 | * `gamee.gameStart()`. 742 | * 743 | * @param {String} type type of controller (see [controllerTypes](#controllertypes)) 744 | * @param {Object} [opts] optional controller options 745 | * {'enableKeyboard': .., 'buttons': ...} 746 | * @param {boolean} [opts.enableKeyboard] enable the keyboard 747 | * @param {Object} [opts.buttons] remap buttons {'oldKey': 'newKey', 748 | * 'left': 'break' ..} 749 | */ 750 | requestController: function requestController(type, opts) { 751 | if (type === "FullScreen") return null; 752 | 753 | var controller = createController(type, opts); 754 | 755 | this.mainController = controller; 756 | 757 | return controller; 758 | }, 759 | 760 | /** ### additionalController 761 | * 762 | * Construct an additional controller. Sometimes games require a 763 | * different controller depending on platform (eg. touch on mobile, 764 | e but Four Buttons on desktop) 765 | * 766 | * **This is currently supported only for GameeWebApp** as a way to 767 | * have alternate keybinding. The game should request a type used 768 | * for mobile platform and then some other as *additionalController* 769 | * if alternate keybinding is needed; 770 | */ 771 | // TODO remove this function 772 | additionalController: function additionalController(type, opts) { 773 | var controller = createController(type, opts); 774 | gameeNative.additionalController(type); 775 | 776 | return controller; 777 | }, 778 | 779 | /** ### trigger 780 | * 781 | * Triggers and event for the controller 782 | * 783 | * This is called by GameeApp to trigger the *keydown*, *keyup* 784 | * events. For more info see [Controller](#controller) 785 | * 786 | * @param {String} eventName name of the event 787 | * @param {*} [data,...] data to pass for the event 788 | * 789 | */ 790 | trigger: function trigger() { 791 | var i; 792 | 793 | if (this.mainController) { 794 | this.mainController.trigger.apply(this.mainController, arguments); 795 | } else { 796 | throw new Error('No controller present'); 797 | } 798 | } 799 | }; 800 | 801 | /** ### core._keydown 802 | * 803 | * A helper function to listen for `keydown` events on window object. 804 | * 805 | * @param {Function} fn callback to handle the event 806 | */ 807 | core._keydown = function (fn) { 808 | global.addEventListener('keydown', (0, _shims.wrapKeyEvent)(fn)); 809 | }; 810 | 811 | /** ### core._keyup 812 | * 813 | * A helper function to listen for `keyup` events on window object. 814 | * 815 | * @param {Function} fn callback to handle the event 816 | */ 817 | core._keyup = function (fn) { 818 | global.addEventListener('keyup', (0, _shims.wrapKeyEvent)(fn)); 819 | }; 820 | 821 | /** ### createController 822 | * 823 | * Function to create a controller. 824 | * 825 | * *see [requestController](#requestcontroller) 826 | * 827 | * @param {String} type 828 | * @param {Object} [opts] 829 | * @returns {Controller} controller 830 | */ 831 | function createController(type, opts) { 832 | var btn, controller; 833 | 834 | if (!controllerTypes[type]) { 835 | throw new Error('Unsupported controller type, ' + type); 836 | } 837 | 838 | opts = opts || {}; 839 | 840 | controller = new controllerTypes[type](); 841 | 842 | if (opts.enableKeyboard) { 843 | controller.enableKeyboard(core); 844 | } 845 | 846 | if (opts.buttons) { 847 | for (btn in opts.buttons) { 848 | controller.remapButton(btn, opts.buttons[btn]); 849 | } 850 | } 851 | 852 | return controller; 853 | } 854 | 855 | /** ### controllerTypes 856 | * 857 | * List of controller types and their coresponding classes. 858 | * 859 | * *see [Controllers](#controllers) for more info* 860 | * @requires Controller 861 | */ 862 | var controllerTypes = { 863 | 'OneButton': controllers.OneButtonController, 864 | 'TwoButtons': controllers.TwoButtonController, 865 | 'FourButtons': controllers.FourButtonController, 866 | 'FiveButtons': controllers.FiveButtonController, 867 | 'SixButtons': controllers.SixButtonController, 868 | 'FourArrows': controllers.FourArrowController, 869 | 'Touch': controllers.TouchController, 870 | 'Joystick': controllers.JoystickController, 871 | 'JoystickWithButton': controllers.JoystickButtonController, 872 | 'TwoArrowsTwoButtons': controllers.TwoArrowsTwoButtonsController, 873 | 'TwoArrowsOneButton': controllers.TwoArrowsOneButtonController, 874 | 'TwoActionButtons': controllers.TwoActionButtonsController 875 | }; 876 | 877 | core.registerPlatform = function (platformAPI) { 878 | // platformAPI.addEventListener() 879 | // TODO ? 880 | }; 881 | 882 | /** 883 | * Is true mute all console outputs 884 | * @return {boolean} 885 | */ 886 | core.isSilentModeEnabled = function () { 887 | return cache.silentMode; 888 | }; 889 | 890 | return core; 891 | }(); 892 | 893 | var DataTypeException = exports.DataTypeException = function DataTypeException(expected, present, argument, method) { 894 | this.expected = expected; 895 | this.present = present; 896 | this.method = method; 897 | this.argument = argument; 898 | this.message = "Invalid data type in method " + this.method + ", argument " + this.argument + " is expected to be " + this.expected + ", but found " + this.present; 899 | }; 900 | 901 | var validateDataType = exports.validateDataType = function validateDataType(testedInput, expectedType, argument, originMethod) { 902 | switch (expectedType) { 903 | 904 | case "array": 905 | if (!Array.isArray(testedInput)) throw new DataTypeException(expectedType, typeof testedInput === "undefined" ? "undefined" : _typeof(testedInput), argument, originMethod); 906 | break; 907 | 908 | default: 909 | if ((typeof testedInput === "undefined" ? "undefined" : _typeof(testedInput)) !== expectedType) throw new DataTypeException(expectedType, typeof testedInput === "undefined" ? "undefined" : _typeof(testedInput), argument, originMethod); 910 | } 911 | }; 912 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7))) 913 | 914 | /***/ }), 915 | /* 2 */ 916 | /***/ (function(module, exports, __webpack_require__) { 917 | 918 | "use strict"; 919 | 920 | 921 | Object.defineProperty(exports, "__esModule", { 922 | value: true 923 | }); 924 | exports.Gamee = exports.GameeEmitter = undefined; 925 | 926 | var _core = __webpack_require__(1); 927 | 928 | var _shims = __webpack_require__(0); 929 | 930 | /** 931 | * gameeAPI module desc 932 | * @module gameeAPI 933 | */ 934 | 935 | /** 936 | * Emit events 937 | * @class GameeEmitter 938 | * @extends CustomEmitter 939 | */ 940 | var GameeEmitter = exports.GameeEmitter = function GameeEmitter() { 941 | _shims.CustomEmitter.call(this); 942 | }; 943 | 944 | /** 945 | * @class Gamee 946 | * @requires core 947 | * 948 | */ 949 | var Gamee = exports.Gamee = function Gamee(platform) { 950 | /** 951 | * @instance 952 | * 953 | * @fires gameeAPI:GameeEmitter~start 954 | * @fires gameeAPI:GameeEmitter~mute 955 | * @fires gameeAPI:GameeEmitter~unmute 956 | * @fires gameeAPI:GameeEmitter~pause 957 | * @fires gameeAPI:GameeEmitter~unpause 958 | * @fires gameeAPI:GameeEmitter~ghostHide 959 | * @fires gameeAPI:GameeEmitter~ghostShow 960 | */ 961 | this.emitter = new GameeEmitter(); 962 | this._platform = platform; 963 | }; 964 | 965 | Gamee.prototype = function () { 966 | 967 | var cbError = function cbError(err) { 968 | if (err) { 969 | throw "Error " + err.toString(); 970 | } 971 | }; 972 | 973 | return { 974 | _controller: _core.core.controller, 975 | /** 976 | * gameInit 977 | * @memberof Gamee 978 | * @param {string} controllType 979 | * @param {object} controllOpts 980 | * @param {string[]} capabilities 981 | * @param {gameInitCallback} cb 982 | * @param {boolean} silentMode 983 | */ 984 | gameInit: function gameInit(controllType, controllOpts, capabilities, cb) { 985 | var silentMode = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; 986 | 987 | (0, _core.validateDataType)(controllType, "string", "controllType", "gamee.updateScore"); 988 | (0, _core.validateDataType)(controllOpts, "object", "controllOpts", "gamee.gameInit"); 989 | (0, _core.validateDataType)(capabilities, "array", "capabilities", "gamee.gameInit"); 990 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.gameInit"); 991 | (0, _core.validateDataType)(silentMode, "boolean", "silentMode", "gamee.gameInit"); 992 | var result = _core.core.gameeInit(controllType, controllOpts, capabilities, cb, silentMode); 993 | // cb(null, result); 994 | }, 995 | 996 | /** 997 | * gameLoadingProgress 998 | * 999 | * gamee.gameLoadingProgress() 1000 | * 1001 | * @memberof Gamee 1002 | * @param {number} percentage current loading progress 1003 | * @param {Gamee~voidCallback} [opt_cb] 1004 | * 1005 | */ 1006 | gameLoadingProgress: function gameLoadingProgress(percentage, opt_cb) { 1007 | (0, _core.validateDataType)(percentage, "number", "percentage", "gamee.gameLoadingProgress"); 1008 | opt_cb = opt_cb || cbError; 1009 | (0, _core.validateDataType)(opt_cb, "function", "opt_cb", "gamee.gameLoadingProgress"); 1010 | _core.core.gameLoadingProgress(percentage); 1011 | opt_cb(null); 1012 | }, 1013 | 1014 | /** 1015 | * gameReady 1016 | * 1017 | * @memberof Gamee 1018 | * @param {Gamee~voidCallback} [opt_cb] 1019 | */ 1020 | gameReady: function gameReady(opt_cb) { 1021 | opt_cb = opt_cb || cbError; 1022 | (0, _core.validateDataType)(opt_cb, "function", "opt_cb", "gamee.gameReady"); 1023 | _core.core.gameReady(); 1024 | opt_cb(null); 1025 | }, 1026 | 1027 | /** 1028 | * gameSave 1029 | * 1030 | * NOTE: There are 2 signatures for this function 1031 | * 1032 | * gamee.gameSave(data, opt_cb) 1033 | * gamee.gameSave(data, opt_share, opt_cb) 1034 | * 1035 | * @memberof Gamee 1036 | * @param {String} data current ingame progress 1037 | * @param {Boolean} [opt_share=false] 1038 | * @param {Gamee~voidCallback} [opt_cb] 1039 | * 1040 | */ 1041 | gameSave: function gameSave(data, opt_share, opt_cb) { 1042 | var share = false, 1043 | cb; 1044 | (0, _core.validateDataType)(data, "string", "data", "gamee.gameSave"); 1045 | if (typeof opt_share === 'function') opt_cb = opt_share;else if (typeof opt_share !== "undefined") (0, _core.validateDataType)(opt_share, "boolean", "opt_share", "gamee.gameSave"); 1046 | 1047 | opt_cb = opt_cb || cbError; 1048 | (0, _core.validateDataType)(opt_cb, "function", "opt_cb", "gamee.gameSave"); 1049 | _core.core.gameSave(data, share); 1050 | opt_cb(null); 1051 | }, 1052 | 1053 | /** 1054 | * getPlatform 1055 | * 1056 | * @memberof Gamee 1057 | * @returns {string} platform type can be android | ios | web | fb 1058 | */ 1059 | getPlatform: function getPlatform() { 1060 | return this._platform; 1061 | }, 1062 | 1063 | /** 1064 | * updateScore 1065 | * 1066 | * @memberof Gamee 1067 | * @param {number} score 1068 | * @param {boolean} [opt_ghostSign=false] If true, score will be updated for ghost instead. 1069 | * @param {Gamee~voidCallback} [opt_cb] 1070 | */ 1071 | updateScore: function updateScore(score, opt_ghostSign, opt_cb) { 1072 | (0, _core.validateDataType)(score, "number", "score", "gamee.updateScore"); 1073 | if (typeof opt_ghostSign === "function") opt_cb = opt_ghostSign;else if (typeof opt_ghostSign !== "undefined") (0, _core.validateDataType)(opt_ghostSign, "boolean", "opt_ghostSign", "gamee.updateScore"); 1074 | 1075 | opt_cb = opt_cb || cbError; 1076 | (0, _core.validateDataType)(opt_cb, "function", "opt_cb", "gamee.updateScore"); 1077 | _core.core.updateScore(score, opt_ghostSign); 1078 | opt_cb(null); 1079 | }, 1080 | 1081 | /** 1082 | * gameOver 1083 | * 1084 | * @memberof Gamee 1085 | * @param {Gamee~ReplayData} [opt_replayData] 1086 | * @param {Gamee~voidCallback} [opt_cb] 1087 | * @param {Gamee~object} [opt_saveState] 1088 | * @param {Gamee~boolean} [opt_hideOverlay] 1089 | */ 1090 | gameOver: function gameOver(opt_replayData, opt_cb, opt_saveState, opt_hideOverlay) { 1091 | if (typeof opt_replayData === "function") opt_cb = opt_replayData;else if (typeof opt_replayData !== "undefined") (0, _core.validateDataType)(opt_replayData, "object", "opt_replayData", "gamee.gameOver"); 1092 | 1093 | if (typeof opt_hideOverlay !== 'undefined') { 1094 | (0, _core.validateDataType)(opt_hideOverlay, "boolean", "opt_hideOverlay", "gamee.gameOver"); 1095 | } 1096 | 1097 | opt_cb = opt_cb || cbError; 1098 | (0, _core.validateDataType)(opt_cb, "function", "opt_cb", "gamee.gameOver"); 1099 | _core.core.gameOver(opt_replayData, opt_saveState, opt_hideOverlay); 1100 | opt_cb(null); 1101 | }, 1102 | 1103 | /** 1104 | * requestSocialData 1105 | * 1106 | * @memberof Gamee 1107 | * @param {Gamee~requestSocialDataCallback} cb 1108 | * @param {number} numberOfPlayers 1109 | */ 1110 | requestSocial: function requestSocial(cb, numberOfPlayers) { 1111 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.requestSocial"); 1112 | 1113 | // functionality supposed to be removed once we do update for iOS 1114 | var data = _core.core.requestSocial(function (error, responseData) { 1115 | var modifiedResponse = !responseData.hasOwnProperty("socialData") ? { socialData: responseData } : responseData; 1116 | cb(null, modifiedResponse); 1117 | }, numberOfPlayers); 1118 | 1119 | // var data = core.requestSocial(cb); 1120 | //cb(null, data); 1121 | }, 1122 | 1123 | /** 1124 | * logEvent 1125 | * 1126 | * @memberof Gamee 1127 | * @param {string} eventName 1128 | * @param {string} eventValue 1129 | */ 1130 | logEvent: function logEvent(eventName, eventValue) { 1131 | 1132 | (0, _core.validateDataType)(eventName, "string", "eventName", "gamee.logEvent"); 1133 | 1134 | if (!eventName || eventName.length > 24) { 1135 | console.error("eventName parameter cant be null and can only contain up to 24 characters"); 1136 | return; 1137 | } 1138 | 1139 | (0, _core.validateDataType)(eventValue, "string", "eventValue", "gamee.logEvent"); 1140 | 1141 | if (!eventValue || eventValue.length > 160) { 1142 | console.error("eventValue parameter cant be null and can only contain up to 160 characters"); 1143 | return; 1144 | } 1145 | 1146 | _core.core.logEvent(eventName, eventValue); 1147 | }, 1148 | 1149 | /** 1150 | * requestBattleData 1151 | * 1152 | * @memberof Gamee 1153 | * @param {Gamee~requestBattleDataDataCallback} cb 1154 | */ 1155 | requestBattleData: function requestBattleData(cb) { 1156 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.requestBattleData"); 1157 | 1158 | _core.core.requestBattleData(cb); 1159 | }, 1160 | 1161 | /** 1162 | * requestPlayerReplay 1163 | * 1164 | * @memberof Gamee 1165 | * @param {number} userID 1166 | * @param {Gamee~requestPlayerReplayDataCallback} cb 1167 | */ 1168 | requestPlayerReplay: function requestPlayerReplay(userID, cb) { 1169 | 1170 | (0, _core.validateDataType)(userID, "number", "userID", "gamee.requestPlayerReplay"); 1171 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.requestPlayerReplay"); 1172 | 1173 | _core.core.requestPlayerReplay(userID, cb); 1174 | }, 1175 | 1176 | /** 1177 | * requestPlayerSaveState 1178 | * 1179 | * @memberof Gamee 1180 | * @param {number} userID 1181 | * @param {Gamee~requestPlayerSaveStateDataCallback} cb 1182 | */ 1183 | requestPlayerSaveState: function requestPlayerSaveState(userID, cb) { 1184 | 1185 | (0, _core.validateDataType)(userID, "number", "userID", "gamee.requestPlayerSaveState"); 1186 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.requestPlayerSaveState"); 1187 | 1188 | _core.core.requestPlayerSaveState(userID, cb); 1189 | }, 1190 | 1191 | /* 1192 | *purchaseItem 1193 | *@member of Gamee 1194 | *@param {object} purchaseDetails 1195 | *@param {Gamee~purchaseItemDataCallback} cb 1196 | */ 1197 | purchaseItem: function purchaseItem(purchaseDetails, cb) { 1198 | 1199 | (0, _core.validateDataType)(purchaseDetails, "object", "purchaseDetails", "gamee.purchaseItem"); 1200 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.purchaseItem"); 1201 | 1202 | _core.core.purchaseItemWithCoins(purchaseDetails, cb, true); 1203 | }, 1204 | 1205 | /* 1206 | *purchaseItemWithCoins 1207 | *@member of Gamee 1208 | *@param {object} purchaseDetails 1209 | *@param {Gamee~purchaseItemDataCallback} cb 1210 | */ 1211 | purchaseItemWithCoins: function purchaseItemWithCoins(purchaseDetails, cb) { 1212 | (0, _core.validateDataType)(purchaseDetails, "object", "purchaseDetails", "gamee.purchaseItemWithCoins"); 1213 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.purchaseItemWithCoins"); 1214 | 1215 | _core.core.purchaseItemWithCoins(purchaseDetails, cb); 1216 | }, 1217 | 1218 | /* 1219 | *purchaseItemWithGems 1220 | *@member of Gamee 1221 | *@param {object} purchaseDetails 1222 | *@param {Gamee~purchaseItemWithGemsDataCallback} cb 1223 | */ 1224 | purchaseItemWithGems: function purchaseItemWithGems(purchaseDetails, cb) { 1225 | 1226 | (0, _core.validateDataType)(purchaseDetails, "object", "purchaseDetails", "gamee.purchaseItemWithGems"); 1227 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.purchaseItemWithGems"); 1228 | 1229 | _core.core.purchaseItemWithGems(purchaseDetails, cb); 1230 | }, 1231 | 1232 | /*share 1233 | *@member of Gamee 1234 | *@param {object} shareDetails 1235 | *@param {Gamee~shareDataCallback} cb 1236 | */ 1237 | share: function share(shareDetails, cb) { 1238 | (0, _core.validateDataType)(shareDetails, "object", "shareDetails", "gamee.share"); 1239 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.share"); 1240 | 1241 | _core.core.share(shareDetails, cb); 1242 | }, 1243 | 1244 | /* 1245 | *loadRewardedVideo 1246 | *@member of Gamee 1247 | *@param {Gamee~loadRewardedVideo} cb 1248 | */ 1249 | loadRewardedVideo: function loadRewardedVideo(cb) { 1250 | 1251 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.loadRewardedVideo"); 1252 | _core.core.loadRewardedVideo(cb); 1253 | }, 1254 | 1255 | /* 1256 | *showRewardedVideo 1257 | *@member of Gamee 1258 | *@param{Gamee~showRewardedVideo} cb 1259 | */ 1260 | showRewardedVideo: function showRewardedVideo(cb) { 1261 | 1262 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.showRewardedVideo"); 1263 | _core.core.showRewardedVideo(cb); 1264 | }, 1265 | 1266 | /** 1267 | *requestPlayerData 1268 | *@member of Gamee 1269 | *@param{Gamee~requestPlayerData} cb 1270 | * @param {number} userID 1271 | */ 1272 | requestPlayerData: function requestPlayerData(cb, userID) { 1273 | 1274 | (0, _core.validateDataType)(cb, "function", "cb", "gamee.requestPlayerData"); 1275 | if (userID !== undefined) { 1276 | (0, _core.validateDataType)(userID, "number", "userId", "gamee.requestPlayerData"); 1277 | } 1278 | _core.core.requestPlayerData(cb, userID); 1279 | } 1280 | }; 1281 | 1282 | /** 1283 | * 1284 | * @typedef ReplayData 1285 | * @param {string} variant 1286 | * @param {string} data 1287 | */ 1288 | 1289 | /** 1290 | * This callback is displayed as part of the Requester class. 1291 | * @callback Gamee~voidCallback 1292 | * @param {string} responseCode 1293 | */ 1294 | 1295 | /** 1296 | * This callback is displayed as part of the Requester class. 1297 | * @callback Gamee~gameInitCallback 1298 | * @param {object} data 1299 | * @param {string} responseCode 1300 | */ 1301 | 1302 | /** 1303 | * This callback is displayed as part of the Requester class. 1304 | * @callback Gamee~requestSocialDataCallback 1305 | * @param {object} data 1306 | * @param {string} responseCode 1307 | */ 1308 | }(); 1309 | 1310 | /** 1311 | * Signals that game should start as normal|replay|ghost game. 1312 | * Signal means there is no overlay over the game. 1313 | * This signal is also being used for game restart. If previous 1314 | * instance of the game was running, it should be terminated without 1315 | * any additional calls and current progress should be tossed. 1316 | * @event gameeAPI:GameeEmitter~start 1317 | * @type {object} 1318 | * @property {EventDetailStart} detail - Common property of events 1319 | */ 1320 | 1321 | /** 1322 | * Data carried with start event. 1323 | * @typedef EventDetailStart 1324 | * @property {Gamee~voidCallback} callback - called after finishing task 1325 | * @property {boolean} [opt_resetState=false] - if true, game must delete current progress and saved progress 1326 | * @property {boolean} [opt_replay] - if true, game must run in replay mode 1327 | * @property {boolean} [opt_ghostMode] - if true, game must run in ghost mode 1328 | */ 1329 | 1330 | /** 1331 | * After that signal, game must silent all sounds immediately. 1332 | * Game must remain silent until unmute signal occures. 1333 | * @event gameeAPI:GameeEmitter~mute 1334 | * @type {object} 1335 | * @property {EventDetailVoid} detail - Common property of events 1336 | */ 1337 | 1338 | /** 1339 | * After unmute signal, game can play sounds again. 1340 | * @event gameeAPI:GameeEmitter~unmute 1341 | * @type {object} 1342 | * @property {EventDetailVoid} detail - Common property of events 1343 | */ 1344 | 1345 | /** 1346 | * Pause signal means there appeared overlay over the game. Player 1347 | * is unable to reach the context of the game anymore. So game should 1348 | * pause all its acctions immediately. 1349 | * @event gameeAPI:GameeEmitter~pause 1350 | * @type {object} 1351 | * @property {EventDetailVoid} detail - Common property of events 1352 | */ 1353 | 1354 | /** 1355 | * Unpause signal means there is no overlay over the game anymore. 1356 | * Game should continue with all previous actions. 1357 | * @event gameeAPI:GameeEmitter~unpause 1358 | * @type {object} 1359 | * @property {EventDetailVoid} detail - Common property of events 1360 | */ 1361 | 1362 | /** 1363 | * Signal ghostHide can appear only if game is running in ghost mode. 1364 | * Game should hide ghost behavior and look like exactly as game without 1365 | * the ghost (if this is possible). 1366 | * @event gameeAPI:GameeEmitter~ghostHide 1367 | * @type {object} 1368 | * @property {EventDetailVoid} detail - Common property of events 1369 | */ 1370 | 1371 | /** 1372 | * Signal ghostShow can appear only if game is running in ghost mode. 1373 | * Game should show ghost again if it was hidden. If ghost died or ended 1374 | * while it was hidden, game should point that out, so the player can understand 1375 | * why the ghost is not visible anymore. 1376 | * @event gameeAPI:GameeEmitter~ghostShow 1377 | * @type {object} 1378 | * @property {EventDetailVoid} detail - Common property of events 1379 | */ 1380 | 1381 | /** 1382 | * Data carried with various events. Contains only callback method. 1383 | * @typedef {object} EventDetailVoid 1384 | * @property {Gamee~voidCallback} callback - call after finishing task 1385 | */ 1386 | 1387 | /** 1388 | * @type {function} 1389 | * @param {MyEvent} e - The observable event. 1390 | * @listens gameeAPI:GameeEmitter~event:snowball 1391 | */ 1392 | 1393 | /***/ }), 1394 | /* 3 */ 1395 | /***/ (function(module, exports, __webpack_require__) { 1396 | 1397 | "use strict"; 1398 | 1399 | 1400 | Object.defineProperty(exports, "__esModule", { 1401 | value: true 1402 | }); 1403 | exports.PlatformAPI = undefined; 1404 | 1405 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 1406 | 1407 | exports.PlatformBridge = PlatformBridge; 1408 | exports.PostMessageBridge = PostMessageBridge; 1409 | exports.MobileBridge = MobileBridge; 1410 | 1411 | var _core = __webpack_require__(1); 1412 | 1413 | /** 1414 | * 1415 | * @requires core 1416 | * 1417 | * @typedef PlatformAPI 1418 | * @param {EventTarget} emitter 1419 | * @param {function} _pause 1420 | * @param {function} _resume 1421 | * @param {function} _ghostShow 1422 | * @param {function} _ghostHide 1423 | * @param {function} _mute 1424 | * @param {function} _unmute 1425 | * @param {function} _start 1426 | */ 1427 | var PlatformAPI = exports.PlatformAPI = { 1428 | emitter: null, 1429 | pause: function pause(cb) { 1430 | var event = new CustomEvent('pause', { 1431 | detail: { 1432 | callback: cb 1433 | } 1434 | }); 1435 | this.emitter.dispatchEvent(event); 1436 | }, 1437 | resume: function resume(cb) { 1438 | var event = new CustomEvent('resume', { 1439 | detail: { 1440 | callback: cb 1441 | } 1442 | }); 1443 | this.emitter.dispatchEvent(event); 1444 | }, 1445 | ghostShow: function ghostShow(cb) { 1446 | var event = new CustomEvent('ghostShow', { 1447 | detail: { 1448 | callback: cb 1449 | } 1450 | }); 1451 | this.emitter.dispatchEvent(event); 1452 | }, 1453 | ghostHide: function ghostHide(cb) { 1454 | var event = new CustomEvent('ghostHide', { 1455 | detail: { 1456 | callback: cb 1457 | } 1458 | }); 1459 | this.emitter.dispatchEvent(event); 1460 | }, 1461 | mute: function mute(cb) { 1462 | var event = new CustomEvent('mute', { 1463 | detail: { 1464 | callback: cb 1465 | } 1466 | }); 1467 | this.emitter.dispatchEvent(event); 1468 | }, 1469 | unmute: function unmute(cb) { 1470 | var event = new CustomEvent('unmute', { 1471 | detail: { 1472 | callback: cb 1473 | } 1474 | }); 1475 | this.emitter.dispatchEvent(event); 1476 | }, 1477 | start: function start(data, cb) { 1478 | var event = new CustomEvent('start', { 1479 | detail: { 1480 | callback: cb 1481 | } 1482 | }); 1483 | 1484 | var error = _core.core.startSignal(data); 1485 | if (error) { 1486 | cb(error); 1487 | return; 1488 | } 1489 | 1490 | if (data.replay) event.detail.opt_replay = true; 1491 | if (data.ghostMode) event.detail.opt_ghostMode = true; 1492 | if (data.resetState) event.detail.opt_resetState = true; 1493 | if (data.replayData) { 1494 | event.detail.replayData = data.replayData; 1495 | } 1496 | 1497 | this.emitter.dispatchEvent(event); 1498 | } 1499 | }; 1500 | 1501 | /** 1502 | * @class PlatformBridge 1503 | * 1504 | */ 1505 | function PlatformBridge() { 1506 | this.requests = {}; 1507 | this.platform = ""; 1508 | this._init(); 1509 | } 1510 | 1511 | PlatformBridge.prototype = { 1512 | instCount: 0, 1513 | _init: function _init() {}, 1514 | createRequest: function createRequest(method, opt_requestData, opt_callback) { 1515 | if (!this.validateMethod(method)) return; 1516 | if (typeof opt_requestData === 'function') { 1517 | opt_callback = opt_requestData; 1518 | opt_requestData = undefined; 1519 | } 1520 | 1521 | var messId = this.instCount++; 1522 | 1523 | if (typeof opt_callback !== 'undefined') { 1524 | this.requests[messId] = opt_callback; 1525 | } 1526 | 1527 | var preparedObject = { 1528 | request: { 1529 | method: method, 1530 | messageId: messId, 1531 | data: null 1532 | } 1533 | }; 1534 | 1535 | this.doCall(preparedObject, opt_requestData); 1536 | }, 1537 | validateMethod: function validateMethod(method) { 1538 | return method === "gameLoadingProgress" ? false : true; 1539 | }, 1540 | /** 1541 | * @abstract 1542 | */ 1543 | doCall: function doCall(preparedObject, requestData) { 1544 | throw "Not implemented"; 1545 | }, 1546 | _callback: function _callback(id, responseData) { 1547 | var cb = this.requests[id]; 1548 | delete this.requests[id]; 1549 | if (cb) cb(responseData); 1550 | }, 1551 | /** 1552 | * @abstract 1553 | */ 1554 | doResponse: function doResponse(preparedObject, responseData) { 1555 | throw "Not implemented"; 1556 | } 1557 | }; 1558 | 1559 | /** 1560 | * @class PostMessageBridge 1561 | * @requires PlatformBridge 1562 | */ 1563 | function PostMessageBridge(endpoint) { 1564 | this._gameeWin = endpoint; 1565 | PlatformBridge.call(this); 1566 | this.platform = "web"; 1567 | } 1568 | 1569 | PostMessageBridge.prototype = Object.create(PlatformBridge.prototype); 1570 | PostMessageBridge.prototype.constructor = PostMessageBridge; 1571 | 1572 | PostMessageBridge.prototype._init = function () { 1573 | 1574 | window.addEventListener('message', function (ev) { 1575 | // if(ev.origin === "source we want") 1576 | // console.log("_triggerMessage detail: " + ev.detail); 1577 | // console.log("_triggerMessage data: " + ev.data); 1578 | var data; 1579 | if (_typeof(ev.detail) === "object" && typeof ev.detail !== null) { 1580 | data = ev.detail; 1581 | } else if (_typeof(ev.data) === "object") { 1582 | data = ev.data; 1583 | } else { 1584 | // message is not from native platform 1585 | return; 1586 | } 1587 | 1588 | if (!_core.core.isSilentModeEnabled()) { 1589 | console.log(JSON.stringify(data, null, 4) + ' data'); 1590 | } 1591 | // this is request 1592 | if (data.request && data.request.method && typeof data.request.messageId !== "undefined") { 1593 | this._resolveAPICall(data.request.method, data.request.messageId, data.request.data); 1594 | } 1595 | // this is reponse 1596 | else if (data.response && typeof data.response.messageId !== "undefined") { 1597 | if (data.error) throw data.error; 1598 | this._callback(data.response.messageId, data.response.data); 1599 | } 1600 | // else this message target is not this framework 1601 | }.bind(this), false); 1602 | }; 1603 | 1604 | PostMessageBridge.prototype.doCall = function (preparedObject, requestData) { 1605 | if ((typeof requestData === 'undefined' ? 'undefined' : _typeof(requestData)) === "object") { 1606 | preparedObject.request.data = requestData || {}; 1607 | } 1608 | this._gameeWin.postMessage(preparedObject, "*"); 1609 | }; 1610 | 1611 | PostMessageBridge.prototype.doResponse = function (messageId, responseData) { 1612 | var preparedObject = { 1613 | version: this.version, 1614 | response: { 1615 | messageId: messageId 1616 | } 1617 | }; 1618 | 1619 | if (responseData) preparedObject.data = responseData; 1620 | 1621 | this._gameeWin.postMessage(preparedObject, "*"); 1622 | }; 1623 | 1624 | PostMessageBridge.prototype._resolveAPICall = function (method, messageId, opt_data) { 1625 | var cb = this.doResponse.bind(this, messageId); 1626 | 1627 | switch (method) { 1628 | case "pause": 1629 | PlatformAPI.pause(cb); 1630 | break; 1631 | case "resume": 1632 | PlatformAPI.resume(cb); 1633 | break; 1634 | case "mute": 1635 | PlatformAPI.mute(cb); 1636 | break; 1637 | case "unmute": 1638 | PlatformAPI.unmute(cb); 1639 | break; 1640 | case "ghostShow": 1641 | PlatformAPI.ghostShow(cb); 1642 | break; 1643 | case "ghostHide": 1644 | PlatformAPI.ghostHide(cb); 1645 | break; 1646 | case "start": 1647 | if (!opt_data) { 1648 | throw "Method _start missing params"; 1649 | } 1650 | PlatformAPI.start(opt_data, cb); 1651 | break; 1652 | default: 1653 | if (!_core.core.isSilentModeEnabled()) { 1654 | console.error("Unknown method call"); 1655 | } 1656 | } 1657 | }; 1658 | 1659 | /** 1660 | * @class MobileBridge 1661 | * @requires PlatformBridge 1662 | * 1663 | */ 1664 | function MobileBridge(device) { 1665 | this.device = device; 1666 | PostMessageBridge.call(this); 1667 | this.platform = "mobile"; 1668 | } 1669 | 1670 | MobileBridge.prototype = Object.create(PostMessageBridge.prototype); 1671 | MobileBridge.prototype.constructor = MobileBridge; 1672 | 1673 | MobileBridge.prototype._init = function () { 1674 | PostMessageBridge.prototype._init.call(this); 1675 | if (this.device === "ios") { 1676 | this._gameeWin = webkit.messageHandlers.callbackHandler; 1677 | } else if (this.device === "android") { 1678 | this._gameeWin = _toDevice; 1679 | } else { 1680 | throw "Unknown device used in webkit bridge"; 1681 | } 1682 | 1683 | window._triggerMessage = function (data) { 1684 | try { 1685 | data = JSON.parse(data); // message is custom message from IOS/android platform 1686 | } catch (err) { 1687 | throw "Couldn't parse message from native app: \n" + data + "\n" + err; 1688 | } 1689 | if (!_core.core.isSilentModeEnabled()) { 1690 | console.log(JSON.stringify(data, null, 4)); 1691 | } 1692 | this.dispatchEvent(new CustomEvent("message", { detail: data })); 1693 | }.bind(window); 1694 | }; 1695 | 1696 | MobileBridge.prototype.doCall = function (preparedObject, requestData) { 1697 | if ((typeof requestData === 'undefined' ? 'undefined' : _typeof(requestData)) === "object") { 1698 | preparedObject.request.data = requestData || {}; 1699 | } 1700 | 1701 | if (this.device === "android") // stringify data for android devices, but not for ios 1702 | preparedObject = JSON.stringify(preparedObject); 1703 | 1704 | this._gameeWin.postMessage(preparedObject, "*"); 1705 | }; 1706 | 1707 | /***/ }), 1708 | /* 4 */ 1709 | /***/ (function(module, exports, __webpack_require__) { 1710 | 1711 | "use strict"; 1712 | 1713 | 1714 | Object.defineProperty(exports, "__esModule", { 1715 | value: true 1716 | }); 1717 | 1718 | var BulletClass = function BulletClass() { 1719 | var _self = this, 1720 | _events = {}; 1721 | 1722 | _self.on = function (event, fn, once) { 1723 | if (arguments.length < 2 || typeof event !== "string" || typeof fn !== "function") return; 1724 | 1725 | var fnString = fn.toString(); 1726 | 1727 | // if the named event object already exists in the dictionary... 1728 | if (typeof _events[event] !== "undefined") { 1729 | // add a callback object to the named event object if one doesn't already exist. 1730 | if (typeof _events[event].callbacks[fnString] === "undefined") { 1731 | _events[event].callbacks[fnString] = { 1732 | cb: fn, 1733 | once: !!once 1734 | }; 1735 | } else if (typeof once === "boolean") { 1736 | // the function already exists, so update it's 'once' value. 1737 | _events[event].callbacks[fnString].once = once; 1738 | } 1739 | } else { 1740 | // create a new event object in the dictionary with the specified name and callback. 1741 | _events[event] = { 1742 | callbacks: {} 1743 | }; 1744 | 1745 | _events[event].callbacks[fnString] = { cb: fn, once: !!once }; 1746 | } 1747 | }; 1748 | 1749 | _self.once = function (event, fn) { 1750 | _self.on(event, fn, true); 1751 | }; 1752 | 1753 | _self.off = function (event, fn) { 1754 | if (typeof event !== "string" || typeof _events[event] === "undefined") return; 1755 | 1756 | // remove just the function, if passed as a parameter and in the dictionary. 1757 | if (typeof fn === "function") { 1758 | var fnString = fn.toString(), 1759 | fnToRemove = _events[event].callbacks[fnString]; 1760 | 1761 | if (typeof fnToRemove !== "undefined") { 1762 | // delete the callback object from the dictionary. 1763 | delete _events[event].callbacks[fnString]; 1764 | } 1765 | } else { 1766 | // delete all functions in the dictionary that are 1767 | // registered to this event by deleting the named event object. 1768 | delete _events[event]; 1769 | } 1770 | }; 1771 | 1772 | _self.trigger = function (event, data) { 1773 | if (typeof event !== "string" || typeof _events[event] === "undefined") return; 1774 | 1775 | for (var fnString in _events[event].callbacks) { 1776 | var callbackObject = _events[event].callbacks[fnString]; 1777 | 1778 | if (typeof callbackObject.cb === "function") callbackObject.cb(data); 1779 | if (typeof callbackObject.once === "boolean" && callbackObject.once === true) _self.off(event, callbackObject.cb); 1780 | } 1781 | }; 1782 | }; 1783 | 1784 | var Bullet = exports.Bullet = new BulletClass(); 1785 | 1786 | /***/ }), 1787 | /* 5 */ 1788 | /***/ (function(module, exports, __webpack_require__) { 1789 | 1790 | "use strict"; 1791 | 1792 | 1793 | Object.defineProperty(exports, "__esModule", { 1794 | value: true 1795 | }); 1796 | exports.BulletClass = undefined; 1797 | exports.Button = Button; 1798 | exports.Controller = Controller; 1799 | exports.OneButtonController = OneButtonController; 1800 | exports.TwoButtonController = TwoButtonController; 1801 | exports.TwoActionButtonsController = TwoActionButtonsController; 1802 | exports.FourButtonController = FourButtonController; 1803 | exports.FiveButtonController = FiveButtonController; 1804 | exports.SixButtonController = SixButtonController; 1805 | exports.TwoArrowsOneButtonController = TwoArrowsOneButtonController; 1806 | exports.TwoArrowsTwoButtonsController = TwoArrowsTwoButtonsController; 1807 | exports.FourArrowController = FourArrowController; 1808 | exports.TouchController = TouchController; 1809 | exports.JoystickController = JoystickController; 1810 | exports.JoystickButtonController = JoystickButtonController; 1811 | 1812 | var _bullet = __webpack_require__(4); 1813 | 1814 | /** 1815 | * @module game_controllers 1816 | */ 1817 | 1818 | /** ## Bullet 1819 | * 1820 | * [Bullet.js](https://github.com/munkychop/bullet) is used as pub/sub 1821 | * library. 1822 | * 1823 | * The controller and its buttons are instance of Bullet. 1824 | */ 1825 | var BulletClass = exports.BulletClass = _bullet.Bullet.constructor; 1826 | 1827 | /** ## Button 1828 | * 1829 | * Represenation of a controller button. It is a child of 1830 | * [Bullet](https://github.com/munkychop/bullet), so you can 1831 | * subscribe for events triggered on it. 1832 | * 1833 | * @class Button 1834 | * @param {String} key name of the button 1835 | * @param {Number} keyCode keycode for the key to represent the button 1836 | * on keyboard 1837 | */ 1838 | function Button(key, keyCode) { 1839 | var self = this; 1840 | 1841 | BulletClass.call(this); 1842 | 1843 | this._pressed = true; 1844 | 1845 | this.key = key; 1846 | this.keyCode = keyCode; 1847 | 1848 | this.on('keydown', function () { 1849 | self._pressed = true; 1850 | }); 1851 | 1852 | this.on('keyup', function () { 1853 | self._pressed = false; 1854 | }); 1855 | } 1856 | 1857 | Button.prototype = Object.create(BulletClass.constructor.prototype); 1858 | Button.constructor = Button; 1859 | 1860 | /** ### isDown 1861 | * 1862 | * Ask if the button is currently pressed. 1863 | * 1864 | * @return {Boolean} true if the button is currently pressed 1865 | */ 1866 | Button.prototype.isDown = function () { 1867 | return this._pressed; 1868 | }; 1869 | 1870 | /** ## Controller 1871 | * 1872 | * Controller has a collection of [buttons](#buttons). 1873 | * It is a child of 1874 | * [Bullet](https://github.com/munkychop/bullet), so you can 1875 | * subscribe for events triggered on it. 1876 | * 1877 | * Controllers will get all the events for its buttons so you can 1878 | * listen for them globaly from controller or individualy on every 1879 | * button. 1880 | * 1881 | * ```javascript 1882 | * controller.on('keydown', function(data) { 1883 | * console.log('button ' + data.button + ' is pressed'); 1884 | * }); 1885 | * 1886 | * controller.buttons.left.on('keydown', function() { 1887 | * console.log('button left is pressed'); 1888 | * }); 1889 | * ``` 1890 | * 1891 | * @class Controller 1892 | */ 1893 | function Controller() { 1894 | var self = this; 1895 | 1896 | BulletClass.call(this); 1897 | 1898 | // ### buttons 1899 | // 1900 | // Map of controller's [buttons](#button) by their name. 1901 | // 1902 | // ```javascript 1903 | // controller.buttons.left // Button('left', ..) 1904 | // ``` 1905 | this.buttons = {}; 1906 | 1907 | // ### buttonAlias 1908 | // 1909 | // Map of remapped buttons. 1910 | // 1911 | // *see [remapButton](#remapbutton) for more info* 1912 | // 1913 | this.buttonAlias = {}; 1914 | 1915 | // Events prefixed with *$* are private, sent from GameeApp ment 1916 | // to be handled before resended as *public (non-prefixed)* 1917 | // event. 1918 | // 1919 | // They should be not used in games as they can change in the future. 1920 | this.on('$keydown', function (data) { 1921 | if (data.button && self.buttonAlias[data.button]) { 1922 | data.button = self.buttonAlias[data.button]; 1923 | } 1924 | 1925 | self.trigger('keydown', data); 1926 | }); 1927 | 1928 | this.on('$keyup', function (data) { 1929 | if (data.button && self.buttonAlias[data.button]) { 1930 | data.button = self.buttonAlias[data.button]; 1931 | } 1932 | 1933 | self.trigger('keyup', data); 1934 | }); 1935 | 1936 | // By default GameeApp will trigger *keydown* and *keyup* events for 1937 | // the controller for every button presses/released. 1938 | // 1939 | // The controller then handles the event and triggers the event for 1940 | // the coresponding button. 1941 | // 1942 | // It expexts a `data` argument which should have a property `button` 1943 | // with the name of button. 1944 | this.on('keydown', function (data) { 1945 | if (!data.button || !self.buttons[data.button]) { 1946 | return; 1947 | } 1948 | 1949 | self.buttons[data.button].trigger('keydown'); 1950 | }); 1951 | 1952 | this.on('keyup', function (data) { 1953 | if (!data.button || !self.buttons[data.button]) { 1954 | return; 1955 | } 1956 | 1957 | self.buttons[data.button].trigger('keyup'); 1958 | }); 1959 | } 1960 | 1961 | Controller.prototype = Object.create(BulletClass.constructor.prototype); 1962 | Controller.constructor = Controller; 1963 | 1964 | /** ### addButton 1965 | * 1966 | * Add button to the controller. 1967 | * 1968 | * @param {Button} button a [Button](#button) instance 1969 | */ 1970 | Controller.prototype.addButton = function (button) { 1971 | this.buttons[button.key] = button; 1972 | }; 1973 | 1974 | /** ### enableKeyboard 1975 | * 1976 | * Enable keyboard controlls. It will attach event listeners to the 1977 | * *window* object for every button and trigger their *keydown* / 1978 | * *keyup* event for the controller. 1979 | */ 1980 | Controller.prototype.enableKeyboard = function (gamee) { 1981 | var key, 1982 | button, 1983 | keyCodes = {}, 1984 | self = this; 1985 | 1986 | for (key in this.buttons) { 1987 | button = this.buttons[key]; 1988 | 1989 | if (button.keyCode) { 1990 | keyCodes[button.keyCode] = button; 1991 | } 1992 | } 1993 | 1994 | gamee._keydown(function (ev) { 1995 | var button = keyCodes[ev.keyCode]; 1996 | 1997 | if (!button) { 1998 | return; 1999 | } 2000 | 2001 | ev.preventDefault(); 2002 | self.trigger('keydown', { button: button.key }); 2003 | }); 2004 | 2005 | gamee._keyup(function (ev) { 2006 | var button = keyCodes[ev.keyCode]; 2007 | 2008 | if (!button) { 2009 | return; 2010 | } 2011 | 2012 | ev.preventDefault(); 2013 | self.trigger('keyup', { button: button.key }); 2014 | }); 2015 | }; 2016 | 2017 | /** ### remapButton 2018 | * 2019 | * Remap the names of the controller's buttons. Controllers have their 2020 | * button names set (left, right, A, B), but sometimes in context of 2021 | * the game a different names are desired. 2022 | * 2023 | * ```javascript 2024 | * var controller = gamee.controller.requestController('TwoButtons'); 2025 | * controller.remapButton('left', 'throttle'); 2026 | * controller.remapButton('right', 'break'); 2027 | * 2028 | * controller.buttons.throttle.on('keydown', ..); 2029 | * ``` 2030 | * 2031 | * @param {String} oldName button name we want to change 2032 | * @param {String} newName new button name 2033 | */ 2034 | Controller.prototype.remapButton = function (oldName, newName) { 2035 | 2036 | // handle old code 2037 | if (newName.name) { 2038 | newName = newName.name; 2039 | } 2040 | 2041 | if (this.buttons[oldName]) { 2042 | this.buttonAlias[oldName] = newName.name; 2043 | 2044 | this.buttons[newName.name] = this.buttons[oldName]; 2045 | 2046 | delete this.buttons[oldName]; 2047 | } else { 2048 | throw Error('Button ' + oldName + ' was not found in controller'); 2049 | } 2050 | }; 2051 | 2052 | // ## Controllers 2053 | 2054 | /** ### OneButtonController 2055 | * 2056 | * Controller with only one button. 2057 | * @class OneButtonController 2058 | */ 2059 | function OneButtonController() { 2060 | Controller.call(this); 2061 | 2062 | // * __name__: 'button' 2063 | // * __key__: spacebar 2064 | this.addButton(new Button('button', 32)); 2065 | } 2066 | OneButtonController.prototype = Object.create(Controller.prototype); 2067 | OneButtonController.prototype.constructor = OneButtonController; 2068 | 2069 | /** ### TwoButtonController 2070 | * 2071 | * Controller with two buttons 2072 | * @class TwoButtonController 2073 | */ 2074 | function TwoButtonController() { 2075 | Controller.call(this); 2076 | 2077 | // * __name__: 'left' 2078 | // * __key__: left arrow 2079 | this.addButton(new Button('left', 37)); 2080 | 2081 | // * __name__: 'right' 2082 | // * __key__: righ arrow 2083 | this.addButton(new Button('right', 39)); 2084 | } 2085 | TwoButtonController.prototype = Object.create(Controller.prototype); 2086 | TwoButtonController.prototype.constructor = TwoButtonController; 2087 | 2088 | /** ### TwoActionButtonsController 2089 | * 2090 | * Controller with two action buttons (A,B) 2091 | * @class TwoActionButtonsController 2092 | */ 2093 | function TwoActionButtonsController() { 2094 | Controller.call(this); 2095 | 2096 | // * __name__: 'left' 2097 | // * __key__: left arrow 2098 | this.addButton(new Button('A', 32)); 2099 | 2100 | // * __name__: 'right' 2101 | // * __key__: righ arrow 2102 | this.addButton(new Button('B', 17)); 2103 | } 2104 | TwoActionButtonsController.prototype = Object.create(Controller.prototype); 2105 | TwoActionButtonsController.prototype.constructor = TwoActionButtonsController; 2106 | 2107 | /** ### FourButtonController 2108 | * 2109 | * Controller with four buttons 2110 | * @class FourButtonController 2111 | */ 2112 | function FourButtonController() { 2113 | Controller.call(this); 2114 | 2115 | // * __name__: 'up' 2116 | // * __key__: left arrow 2117 | this.addButton(new Button('up', 38)); 2118 | 2119 | // * __name__: 'left' 2120 | // * __key__: left arrow 2121 | this.addButton(new Button('left', 37)); 2122 | 2123 | // * __name__: 'right' 2124 | // * __key__: righ arrow 2125 | this.addButton(new Button('right', 39)); 2126 | 2127 | // * __name__: 'A' 2128 | // * __key__: spacebar 2129 | this.addButton(new Button('A', 32)); 2130 | } 2131 | FourButtonController.prototype = Object.create(Controller.prototype); 2132 | FourButtonController.prototype.constructor = FourButtonController; 2133 | 2134 | /** ### FiveButtonController 2135 | * 2136 | * Controller with five buttons 2137 | * @class FiveButtonController 2138 | */ 2139 | function FiveButtonController() { 2140 | Controller.call(this); 2141 | 2142 | // * __name__: 'up' 2143 | // * __key__: left arrow 2144 | this.addButton(new Button('up', 38)); 2145 | 2146 | // * __name__: 'left' 2147 | // * __key__: left arrow 2148 | this.addButton(new Button('left', 37)); 2149 | 2150 | // * __name__: 'right' 2151 | // * __key__: righ arrow 2152 | this.addButton(new Button('right', 39)); 2153 | 2154 | // * __name__: 'down' 2155 | // * __key__: down arrow 2156 | this.addButton(new Button('down', 40)); 2157 | 2158 | // * __name__: 'A' 2159 | // * __key__: spacebar 2160 | this.addButton(new Button('A', 32)); 2161 | } 2162 | FiveButtonController.prototype = Object.create(Controller.prototype); 2163 | FiveButtonController.prototype.constructor = FiveButtonController; 2164 | 2165 | /** ### SixButtonController 2166 | * 2167 | * Controller with six buttons 2168 | * @class SixButtonController 2169 | */ 2170 | function SixButtonController() { 2171 | Controller.call(this); 2172 | 2173 | // * __name__: 'up' 2174 | // * __key__: left arrow 2175 | this.addButton(new Button('up', 38)); 2176 | 2177 | // * __name__: 'left' 2178 | // * __key__: left arrow 2179 | this.addButton(new Button('left', 37)); 2180 | 2181 | // * __name__: 'right' 2182 | // * __key__: righ arrow 2183 | this.addButton(new Button('right', 39)); 2184 | 2185 | // * __name__: 'down' 2186 | // * __key__: down arrow 2187 | this.addButton(new Button('down', 40)); 2188 | 2189 | // * __name__: 'A' 2190 | // * __key__: spacebar 2191 | this.addButton(new Button('A', 32)); 2192 | 2193 | // * __name__: 'B' 2194 | // * __key__: ctrl 2195 | this.addButton(new Button('B', 17)); 2196 | } 2197 | SixButtonController.prototype = Object.create(Controller.prototype); 2198 | SixButtonController.prototype.constructor = SixButtonController; 2199 | 2200 | /** ### TwoArrowsOneButtonController 2201 | * 2202 | * Controller with two arrows and one action button 2203 | * @class TwoArrowsOneButtonController 2204 | */ 2205 | function TwoArrowsOneButtonController() { 2206 | Controller.call(this); 2207 | 2208 | // * __name__: 'left' 2209 | // * __key__: left arrow 2210 | this.addButton(new Button('left', 37)); 2211 | 2212 | // * __name__: 'right' 2213 | // * __key__: righ arrow 2214 | this.addButton(new Button('right', 39)); 2215 | 2216 | // * __name__: 'A' 2217 | // * __key__: spacebar 2218 | this.addButton(new Button('A', 32)); 2219 | } 2220 | TwoArrowsOneButtonController.prototype = Object.create(Controller.prototype); 2221 | TwoArrowsOneButtonController.prototype.constructor = TwoArrowsOneButtonController; 2222 | 2223 | /** ### TwoArrowsTwoButtonsController 2224 | * 2225 | * Controller with two arrows and two action buttons 2226 | * @class TwoArrowsTwoButtonsController 2227 | */ 2228 | function TwoArrowsTwoButtonsController() { 2229 | Controller.call(this); 2230 | 2231 | // * __name__: 'left' 2232 | // * __key__: left arrow 2233 | this.addButton(new Button('left', 37)); 2234 | 2235 | // * __name__: 'right' 2236 | // * __key__: righ arrow 2237 | this.addButton(new Button('right', 39)); 2238 | 2239 | // * __name__: 'A' 2240 | // * __key__: spacebar 2241 | this.addButton(new Button('A', 32)); 2242 | 2243 | // * __name__: 'B' 2244 | // * __key__: ctrl 2245 | this.addButton(new Button('B', 17)); 2246 | } 2247 | TwoArrowsTwoButtonsController.prototype = Object.create(Controller.prototype); 2248 | TwoArrowsTwoButtonsController.prototype.constructor = TwoArrowsTwoButtonsController; 2249 | 2250 | /** ### FourArrowController 2251 | * 2252 | * Controller with four arrow buttons 2253 | * @class FourArrowController 2254 | */ 2255 | function FourArrowController() { 2256 | Controller.call(this); 2257 | 2258 | // * __name__: 'up' 2259 | // * __key__: left arrow 2260 | this.addButton(new Button('up', 38)); 2261 | 2262 | // * __name__: 'left' 2263 | // * __key__: left arrow 2264 | this.addButton(new Button('left', 37)); 2265 | 2266 | // * __name__: 'right' 2267 | // * __key__: righ arrow 2268 | this.addButton(new Button('right', 39)); 2269 | 2270 | // * __name__: 'down' 2271 | // * __key__: down arrow 2272 | this.addButton(new Button('down', 40)); 2273 | } 2274 | FourArrowController.prototype = Object.create(Controller.prototype); 2275 | FourArrowController.prototype.constructor = FourArrowController; 2276 | 2277 | /** ### TouchController 2278 | * 2279 | * This controller has no buttons. Instead it has a touchpad which 2280 | * triggers *touchstart*, *touchend*, *touchmove*, *touchcancel*, 2281 | * *touchend* events (similar to 2282 | * [Touch event types](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent#Touch_event_types)) 2283 | * 2284 | * The position of the touch is in the `data.position` argument as a 2285 | * *x* and *y* with the values between [0, 0] for the left top corner 2286 | * and [1, 1] for the bottom right corner ([0.5, 0.5] is the center). 2287 | * 2288 | * ```javascript 2289 | * controller = gamee.controller.requestController('Touch'); 2290 | * 2291 | * controller.on('touchstart', function(data) { 2292 | * if (data.position.x < 0.5 && data.position.y < 0.5) { 2293 | * console.log('touch in the top left quadrant'); 2294 | * } 2295 | * }) 2296 | * ``` 2297 | * @class TouchController 2298 | */ 2299 | function TouchController() { 2300 | var self = this; 2301 | 2302 | Controller.call(this); 2303 | 2304 | this.on("$touchstart", function (data) { 2305 | self.trigger('touchstart', data); 2306 | }); 2307 | 2308 | this.on("$touchend", function (data) { 2309 | self.trigger('touchend', data); 2310 | }); 2311 | 2312 | this.on("$touchmove", function (data) { 2313 | self.trigger('touchmove', data); 2314 | }); 2315 | 2316 | this.on("$touchleave", function (data) { 2317 | self.trigger('touchleave', data); 2318 | }); 2319 | 2320 | this.on("$touchcancel", function (data) { 2321 | self.trigger('touchcancel', data); 2322 | }); 2323 | } 2324 | TouchController.prototype = Object.create(TouchController.prototype); 2325 | TouchController.prototype.constructor = TouchController; 2326 | 2327 | /** ### JoystickController 2328 | * 2329 | * JoystickController emits `change` event, after the position of the 2330 | * joystick is changed. 2331 | * 2332 | * The position of the joystick is in the property `x` and `y`. The 2333 | * position on axis is between <-1, 1> (for x -1 is max left 2334 | * position, 1 max right position). [0.0, 0.0] is the center. 2335 | * 2336 | * ```javascript 2337 | * joystick = gamee.controller.requestController('Joystick'); 2338 | * 2339 | * joystick.on('change', function() { 2340 | * new_x = joystick.x; 2341 | * nex_y = joystick.y; 2342 | * }) 2343 | * ``` 2344 | * @class JoystickController 2345 | */ 2346 | function JoystickController() { 2347 | var self = this; 2348 | 2349 | Controller.call(this); 2350 | 2351 | // x axis 2352 | this.x = 0; 2353 | // y axis 2354 | this.y = 0; 2355 | 2356 | this.on("$change", function (data) { 2357 | self.x = data.position.x; 2358 | self.y = data.position.y; 2359 | 2360 | self.trigger("change", data); 2361 | }); 2362 | } 2363 | JoystickController.prototype = Object.create(Controller.prototype); 2364 | JoystickController.prototype.constructor = JoystickController; 2365 | 2366 | /** ### JoystickButtonController 2367 | * 2368 | * JoystickButtonController is a `JoystickController` with one button. 2369 | * 2370 | * ```javascript 2371 | * joystick = gamee.controller.requestController('JoystickWithButton'); 2372 | * 2373 | * joystick.on('change', function() { 2374 | * new_x = joystick.x; 2375 | * nex_y = joystick.y; 2376 | * }) 2377 | * 2378 | * joystick.buttons.button.on('keydown', callback) 2379 | * // or simply 2380 | * joystick.on('keydown', callback) 2381 | * ``` 2382 | * @class JoystickButtonController 2383 | */ 2384 | function JoystickButtonController() { 2385 | var self = this; 2386 | 2387 | JoystickController.call(this); 2388 | 2389 | // * __name__: 'button' 2390 | // * __key__: spacebar 2391 | this.addButton(new Button('button', 32)); 2392 | } 2393 | JoystickButtonController.prototype = Object.create(JoystickController.prototype); 2394 | JoystickButtonController.prototype.constructor = JoystickButtonController; 2395 | 2396 | /***/ }), 2397 | /* 6 */ 2398 | /***/ (function(module, exports, __webpack_require__) { 2399 | 2400 | "use strict"; 2401 | 2402 | 2403 | Object.defineProperty(exports, "__esModule", { 2404 | value: true 2405 | }); 2406 | exports.gamee = undefined; 2407 | 2408 | __webpack_require__(0); 2409 | 2410 | var _gameeAPI = __webpack_require__(2); 2411 | 2412 | var _core = __webpack_require__(1); 2413 | 2414 | var _platform_bridge = __webpack_require__(3); 2415 | 2416 | /** 2417 | * Instance of gamee object with API for developers. 2418 | * Internal functions becomes private this way 2419 | * 2420 | * @requires Gamee 2421 | */ 2422 | var gamee = exports.gamee = undefined; 2423 | 2424 | /** 2425 | * Resolves what platform is being used and make instance of platform API. 2426 | * 2427 | * @requires PlatformBridge 2428 | */ 2429 | var platformBridge = function () { 2430 | 2431 | var platformBridge, 2432 | platformType = "web"; 2433 | 2434 | // Reslove Gamee enviroment 2435 | /* current user agent */ 2436 | var userAgent = navigator.userAgent.toLowerCase(); 2437 | 2438 | if (/iphone|ipod|ipad/.test(userAgent)) { 2439 | // test ios device 2440 | // user agent is use to determine current enviroment 2441 | 2442 | // Test if window with game have a parent (loading in iframe) 2443 | if (window.self !== window.top) { 2444 | platformType = "web"; 2445 | } else { 2446 | platformType = "ios"; 2447 | } 2448 | } else if (/gamee\/[0-9\.]+$/.test(userAgent)) { 2449 | // test android app 2450 | // TODO do you really test android like that? 2451 | platformType = "android"; 2452 | } else if (window.parent) { 2453 | // TODO doesnt make sence, parent always exists!! 2454 | platformType = "web"; 2455 | } else if (window.parent && window.parent.gameeSimulator) { 2456 | // TODO doesnt make sence, parent always exist? 2457 | platformType = "web"; 2458 | } 2459 | 2460 | exports.gamee = gamee = new _gameeAPI.Gamee(platformType); 2461 | 2462 | window.gamee = gamee; 2463 | 2464 | switch (platformType) { 2465 | case "web": 2466 | if (window.parent === window) { 2467 | console.error("Gamee must run in iframe on web platform"); 2468 | } 2469 | platformBridge = new _platform_bridge.PostMessageBridge(window.parent); 2470 | break; 2471 | case "ios": 2472 | platformBridge = new _platform_bridge.MobileBridge("ios"); 2473 | break; 2474 | case "android": 2475 | platformBridge = new _platform_bridge.MobileBridge("android"); 2476 | break; 2477 | default: 2478 | throw "Can't identify the platform"; 2479 | } 2480 | return platformBridge; 2481 | }(); 2482 | 2483 | _core.core.PlatformAPI = _platform_bridge.PlatformAPI; 2484 | _core.core.native = platformBridge; 2485 | 2486 | _platform_bridge.PlatformAPI.emitter = gamee.emitter; 2487 | 2488 | function loadScript(url, callback) { 2489 | // Adding the script tag to the head as suggested before 2490 | var head = document.getElementsByTagName('head')[0]; 2491 | var script = document.createElement('script'); 2492 | script.src = url; 2493 | 2494 | // Then bind the event to the callback function. 2495 | // There are several events for cross browser compatibility. 2496 | script.onreadystatechange = callback; 2497 | script.onload = callback; 2498 | 2499 | // Fire the loading 2500 | head.appendChild(script); 2501 | } 2502 | 2503 | /***/ }), 2504 | /* 7 */ 2505 | /***/ (function(module, exports) { 2506 | 2507 | var g; 2508 | 2509 | // This works in non-strict mode 2510 | g = (function() { 2511 | return this; 2512 | })(); 2513 | 2514 | try { 2515 | // This works if eval is allowed (see CSP) 2516 | g = g || Function("return this")() || (1,eval)("this"); 2517 | } catch(e) { 2518 | // This works if the window reference is available 2519 | if(typeof window === "object") 2520 | g = window; 2521 | } 2522 | 2523 | // g can still be undefined, but nothing to do about it... 2524 | // We return undefined, instead of nothing here, so it's 2525 | // easier to handle this case. if(!global) { ...} 2526 | 2527 | module.exports = g; 2528 | 2529 | 2530 | /***/ }) 2531 | /******/ ]); 2532 | }); 2533 | //# sourceMappingURL=gamee-js.js.map -------------------------------------------------------------------------------- /gamee/dist/gamee-js.min.js: -------------------------------------------------------------------------------- 1 | /*! @preserve build time 2019-08-27 08:54:59 */ 2 | !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o=e();for(var n in o)("object"==typeof exports?exports:t)[n]=o[n]}}(this,function(){return function(t){function __webpack_require__(o){if(e[o])return e[o].exports;var n=e[o]={i:o,l:!1,exports:{}};return t[o].call(n.exports,n,n.exports,__webpack_require__),n.l=!0,n.exports}var e={};return __webpack_require__.m=t,__webpack_require__.c=e,__webpack_require__.i=function(t){return t},__webpack_require__.d=function(t,e,o){__webpack_require__.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:o})},__webpack_require__.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return __webpack_require__.d(e,"a",e),e},__webpack_require__.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=6)}([function(t,e,o){"use strict";function CustomEmitter(){function delegate(e){this[e]=t[e].bind(t)}var t=document.createDocumentFragment();["addEventListener","dispatchEvent","removeEventListener"].forEach(delegate,this)}function wrapKeyEvent(t){return function(e){return e&&e.keyCode||(e||(e=window.event),e.which&&(e.keyCode=e.which)),t(e)}}Object.defineProperty(e,"__esModule",{value:!0}),e.CustomEmitter=CustomEmitter,e.wrapKeyEvent=wrapKeyEvent,function(){try{var t=new window.CustomEvent("test");if(t.preventDefault(),!0!==t.defaultPrevented)throw new Error("Could not prevent default")}catch(t){var e=function(t,e){var o,n;return e=e||{bubbles:!1,cancelable:!1,detail:void 0},o=document.createEvent("CustomEvent"),o.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n=o.preventDefault,o.preventDefault=function(){n.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch(t){this.defaultPrevented=!0}},o};e.prototype=window.Event.prototype,window.CustomEvent=e}}(),function(t,e){function docHijack(t){var o=e[t];e[t]=function(t){return addListen(o(t))}}function addEvent(e,o,n){return(n=this).attachEvent("on"+e,function(e){e=e||t.event,e.preventDefault=e.preventDefault||function(){e.returnValue=!1},e.stopPropagation=e.stopPropagation||function(){e.cancelBubble=!0},o.call(n,e)})}function addListen(t,e){if(e=t.length)for(;e--;)t[e].addEventListener=addEvent;else t.addEventListener=addEvent;return t}t.addEventListener||(addListen([e,t]),"Element"in t?t.Element.prototype.addEventListener=addEvent:(e.attachEvent("onreadystatechange",function(){addListen(e.all)}),docHijack("getElementsByTagName"),docHijack("getElementById"),docHijack("createElement"),addListen(e.all)))}(window,document)},function(t,e,o){"use strict";(function(t){Object.defineProperty(e,"__esModule",{value:!0}),e.validateDataType=e.DataTypeException=e.core=void 0;var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},r=o(5),a=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e.default=t,e}(r),i=o(0);!function(){var t=function(t){var e=new t;if("suspended"===e.state){var o=function resume(){if(/(iPhone|iPad)/i.test(navigator.userAgent)){var t=e.createBuffer(1,1,44100),o=e.createBufferSource();o.buffer=t,o.connect(e.destination),o.start(0),o.disconnect()}e.resume(),setTimeout(function(){"running"===e.state&&document.body.removeEventListener(["touchcancel","touchend","touchenter","touchleave","touchmove","touchstart","mouseenter","mouseover","mousemove","mousedown","mouseup"].join(" "),resume,!1)},0)};document.body.addEventListener(["touchcancel","touchend","touchenter","touchleave","touchmove","touchstart","mouseenter","mouseover","mousemove","mousedown","mouseup"].join(" "),o,!1)}return e};try{void 0!==window.AudioContext?window.AudioContext=t.bind(null,window.AudioContext):"undefined"!=typeof webkitAudioContext&&(window.webkitAudioContext=t.bind(null,window.webkitAudioContext))}catch(t){setTimeout(function(){throw t},0)}}();var u=(e.core=function(){function createController(t,e){var o,n;if(!u[t])throw new Error("Unsupported controller type, "+t);if(e=e||{},n=new u[t],e.enableKeyboard&&n.enableKeyboard(r),e.buttons)for(o in e.buttons)n.remapButton(o,e.buttons[o]);return n}var e=function(){},o={},n={VERSION:"2.4.0",CAPABILITIES:["ghostMode","saveState","replay","socialData","rewardedAds","coins","logEvents","playerData","share","gems"],variant:0,soundUnlocked:!1,onReady:e,onGameStart:e},r={};r.gameeInit=function(t,e,a,i){var u=(arguments.length>4&&void 0!==arguments[4]&&arguments[4],!0),s={};if(void 0!==a&&Array.isArray(a))for(var l=0;l100)throw"Percentage passed to gameLoadingProgress out of bounds or not a number.";e>t&&(t=e,this.native.createRequest("gameLoadingProgress",{percentage:e}))}}(),r.gameReady=function(){this.native.createRequest("gameReady")},r.updateScore=function(t,e){if("number"!=typeof t)throw"Score passed to updateScore is not a number.";var o={score:parseInt(t,10)};e&&(o.ghostSign=!0),this.native.createRequest("updateScore",o)},r.gameOver=function(t,e,o){o=void 0!==o&&o;var n={};if(t){if(t.hasOwnProperty("variant")||(t.variant=""),!t.hasOwnProperty("data"))throw"Replay data must have `data` property";n.replayData=t}n.hideOverlay=o,e&&(n.state=e),r.native.createRequest("gameOver",n)},r.gameSave=function(t,e){if(!o.capabilities.saveState)throw"Save State not supported, you must add the capability on gamee.Init";r.native.createRequest("saveState",{state:t,share:e})},r.requestSocial=function(t,e){if(!o.capabilities.socialData)throw"Social Data not supported, you must add the capability on gamee.Init";this.native.createRequest("requestSocial",e,function(e){t(null,e)})},r.logEvent=function(t,e){if(!o.capabilities.logEvents)throw"Log Events not supported, you must add the capability on gamee.Init";this.native.createRequest("logEvent",{eventName:t,eventValue:e},function(t){if(t)throw t})},r.requestBattleData=function(t){this.native.createRequest("requestBattleData",void 0,function(e){t(null,e)})},r.requestPlayerReplay=function(t,e){if(!o.capabilities.replay)throw"Replays not supported, you must add the capability on gamee.Init";this.native.createRequest("requestPlayerReplay",{userID:t},function(t){e(null,t)})},r.requestPlayerSaveState=function(t,e){this.native.createRequest("requestPlayerSaveState",{userID:t},function(t){e(null,t)})},r.purchaseItemWithCoins=function(t,e,n){if(!o.capabilities.coins)throw"Coins purchases not supported, you must add the capability on gamee.Init";if(t){["coinsCost","itemName"].forEach(function(e){if(!t.hasOwnProperty(e))throw"Purchase Options must have `"+e+"` property"})}this.isSilentModeEnabled();var r="purchaseItemWithCoins";void 0!==n&&!0===n&&(r="purchaseItem"),this.native.createRequest(r,t,function(t){e(null,t)})},r.purchaseItemWithGems=function(t,e){if(!o.capabilities.gems)throw"Gems purchases not supported, you must add the capability on gamee.Init";if(t){["gemsCost","itemName"].forEach(function(e){if(!t.hasOwnProperty(e))throw"Purchase options must have `"+e+"` property"})}this.isSilentModeEnabled(),this.native.createRequest("purchaseItemWithGems",t,function(t){e(null,t)})},r.share=function(t,e){if(!o.capabilities.share)throw"Share option not supported, you must add the capability on gamee.Init";if(t){["destination"].forEach(function(e){if(!t.hasOwnProperty(e))throw"Share Options must have `"+e+"` property"})}this.isSilentModeEnabled(),this.native.createRequest("share",t,function(t){e(null,t)})},r.loadRewardedVideo=function(t){if(!o.capabilities.rewardedAds)throw"Rewarded Ads not supported, you must add the capability on gamee.Init";this.native.createRequest("loadRewardedVideo",function(e){t(null,e)})},r.showRewardedVideo=function(t){if(!o.capabilities.rewardedAds)throw"Rewarded Ads not supported, you must add the capability on gamee.Init";this.native.createRequest("showRewardedVideo",function(e){t(null,e)})},r.requestPlayerData=function(t,e){if(!o.capabilities.playerData)throw"Player Data not supported, you must add the capability on gamee.Init";var n=void 0;e&&(n={userID:e}),this.native.createRequest("requestPlayerData",n,function(e){t(null,e)})},r.startSignal=function(t){var e;return t.replay&&!o.capabilities.replay&&(e="Game doesn't support replay. "),t.ghostMode&&!o.capabilities.ghostMode&&(e="Game doesn't support ghost Mode. "),e},r.controller={mainController:null,requestController:function(t,e){if("FullScreen"===t)return null;var o=createController(t,e);return this.mainController=o,o},additionalController:function(t,e){var o=createController(t,e);return gameeNative.additionalController(t),o},trigger:function(){if(!this.mainController)throw new Error("No controller present");this.mainController.trigger.apply(this.mainController,arguments)}},r._keydown=function(e){t.addEventListener("keydown",(0,i.wrapKeyEvent)(e))},r._keyup=function(e){t.addEventListener("keyup",(0,i.wrapKeyEvent)(e))};var u={OneButton:a.OneButtonController,TwoButtons:a.TwoButtonController,FourButtons:a.FourButtonController,FiveButtons:a.FiveButtonController,SixButtons:a.SixButtonController,FourArrows:a.FourArrowController,Touch:a.TouchController,Joystick:a.JoystickController,JoystickWithButton:a.JoystickButtonController,TwoArrowsTwoButtons:a.TwoArrowsTwoButtonsController,TwoArrowsOneButton:a.TwoArrowsOneButtonController,TwoActionButtons:a.TwoActionButtonsController};return r.registerPlatform=function(t){},r.isSilentModeEnabled=function(){return o.silentMode},r}(),e.DataTypeException=function(t,e,o,n){this.expected=t,this.present=e,this.method=n,this.argument=o,this.message="Invalid data type in method "+this.method+", argument "+this.argument+" is expected to be "+this.expected+", but found "+this.present});e.validateDataType=function(t,e,o,r){switch(e){case"array":if(!Array.isArray(t))throw new u(e,void 0===t?"undefined":n(t),o,r);break;default:if((void 0===t?"undefined":n(t))!==e)throw new u(e,void 0===t?"undefined":n(t),o,r)}}}).call(e,o(7))},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Gamee=e.GameeEmitter=void 0;var n=o(1),r=o(0),a=e.GameeEmitter=function(){r.CustomEmitter.call(this)};(e.Gamee=function(t){this.emitter=new a,this._platform=t}).prototype=function(){var t=function(t){if(t)throw"Error "+t.toString()};return{_controller:n.core.controller,gameInit:function(t,e,o,r){var a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];(0,n.validateDataType)(t,"string","controllType","gamee.updateScore"),(0,n.validateDataType)(e,"object","controllOpts","gamee.gameInit"),(0,n.validateDataType)(o,"array","capabilities","gamee.gameInit"),(0,n.validateDataType)(r,"function","cb","gamee.gameInit"),(0,n.validateDataType)(a,"boolean","silentMode","gamee.gameInit"),n.core.gameeInit(t,e,o,r,a)},gameLoadingProgress:function(e,o){(0,n.validateDataType)(e,"number","percentage","gamee.gameLoadingProgress"),o=o||t,(0,n.validateDataType)(o,"function","opt_cb","gamee.gameLoadingProgress"),n.core.gameLoadingProgress(e),o(null)},gameReady:function(e){e=e||t,(0,n.validateDataType)(e,"function","opt_cb","gamee.gameReady"),n.core.gameReady(),e(null)},gameSave:function(e,o,r){(0,n.validateDataType)(e,"string","data","gamee.gameSave"),"function"==typeof o?r=o:void 0!==o&&(0,n.validateDataType)(o,"boolean","opt_share","gamee.gameSave"),r=r||t,(0,n.validateDataType)(r,"function","opt_cb","gamee.gameSave"),n.core.gameSave(e,!1),r(null)},getPlatform:function(){return this._platform},updateScore:function(e,o,r){(0,n.validateDataType)(e,"number","score","gamee.updateScore"),"function"==typeof o?r=o:void 0!==o&&(0,n.validateDataType)(o,"boolean","opt_ghostSign","gamee.updateScore"),r=r||t,(0,n.validateDataType)(r,"function","opt_cb","gamee.updateScore"),n.core.updateScore(e,o),r(null)},gameOver:function(e,o,r,a){"function"==typeof e?o=e:void 0!==e&&(0,n.validateDataType)(e,"object","opt_replayData","gamee.gameOver"),void 0!==a&&(0,n.validateDataType)(a,"boolean","opt_hideOverlay","gamee.gameOver"),o=o||t,(0,n.validateDataType)(o,"function","opt_cb","gamee.gameOver"),n.core.gameOver(e,r,a),o(null)},requestSocial:function(t,e){(0,n.validateDataType)(t,"function","cb","gamee.requestSocial"),n.core.requestSocial(function(e,o){var n=o.hasOwnProperty("socialData")?o:{socialData:o};t(null,n)},e)},logEvent:function(t,e){(0,n.validateDataType)(t,"string","eventName","gamee.logEvent"),!t||t.length>24||((0,n.validateDataType)(e,"string","eventValue","gamee.logEvent"),!e||e.length>160||n.core.logEvent(t,e))},requestBattleData:function(t){(0,n.validateDataType)(t,"function","cb","gamee.requestBattleData"),n.core.requestBattleData(t)},requestPlayerReplay:function(t,e){(0,n.validateDataType)(t,"number","userID","gamee.requestPlayerReplay"),(0,n.validateDataType)(e,"function","cb","gamee.requestPlayerReplay"),n.core.requestPlayerReplay(t,e)},requestPlayerSaveState:function(t,e){(0,n.validateDataType)(t,"number","userID","gamee.requestPlayerSaveState"),(0,n.validateDataType)(e,"function","cb","gamee.requestPlayerSaveState"),n.core.requestPlayerSaveState(t,e)},purchaseItem:function(t,e){(0,n.validateDataType)(t,"object","purchaseDetails","gamee.purchaseItem"),(0,n.validateDataType)(e,"function","cb","gamee.purchaseItem"),n.core.purchaseItemWithCoins(t,e,!0)},purchaseItemWithCoins:function(t,e){(0,n.validateDataType)(t,"object","purchaseDetails","gamee.purchaseItemWithCoins"),(0,n.validateDataType)(e,"function","cb","gamee.purchaseItemWithCoins"),n.core.purchaseItemWithCoins(t,e)},purchaseItemWithGems:function(t,e){(0,n.validateDataType)(t,"object","purchaseDetails","gamee.purchaseItemWithGems"),(0,n.validateDataType)(e,"function","cb","gamee.purchaseItemWithGems"),n.core.purchaseItemWithGems(t,e)},share:function(t,e){(0,n.validateDataType)(t,"object","shareDetails","gamee.share"),(0,n.validateDataType)(e,"function","cb","gamee.share"),n.core.share(t,e)},loadRewardedVideo:function(t){(0,n.validateDataType)(t,"function","cb","gamee.loadRewardedVideo"),n.core.loadRewardedVideo(t)},showRewardedVideo:function(t){(0,n.validateDataType)(t,"function","cb","gamee.showRewardedVideo"),n.core.showRewardedVideo(t)},requestPlayerData:function(t,e){(0,n.validateDataType)(t,"function","cb","gamee.requestPlayerData"),void 0!==e&&(0,n.validateDataType)(e,"number","userId","gamee.requestPlayerData"),n.core.requestPlayerData(t,e)}}}()},function(t,e,o){"use strict";function PlatformBridge(){this.requests={},this.platform="",this._init()}function PostMessageBridge(t){this._gameeWin=t,PlatformBridge.call(this),this.platform="web"}function MobileBridge(t){this.device=t,PostMessageBridge.call(this),this.platform="mobile"}Object.defineProperty(e,"__esModule",{value:!0}),e.PlatformAPI=void 0;var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};e.PlatformBridge=PlatformBridge,e.PostMessageBridge=PostMessageBridge,e.MobileBridge=MobileBridge;var r=o(1),a=e.PlatformAPI={emitter:null,pause:function(t){var e=new CustomEvent("pause",{detail:{callback:t}});this.emitter.dispatchEvent(e)},resume:function(t){var e=new CustomEvent("resume",{detail:{callback:t}});this.emitter.dispatchEvent(e)},ghostShow:function(t){var e=new CustomEvent("ghostShow",{detail:{callback:t}});this.emitter.dispatchEvent(e)},ghostHide:function(t){var e=new CustomEvent("ghostHide",{detail:{callback:t}});this.emitter.dispatchEvent(e)},mute:function(t){var e=new CustomEvent("mute",{detail:{callback:t}});this.emitter.dispatchEvent(e)},unmute:function(t){var e=new CustomEvent("unmute",{detail:{callback:t}});this.emitter.dispatchEvent(e)},start:function(t,e){var o=new CustomEvent("start",{detail:{callback:e}}),n=r.core.startSignal(t);if(n)return void e(n);t.replay&&(o.detail.opt_replay=!0),t.ghostMode&&(o.detail.opt_ghostMode=!0),t.resetState&&(o.detail.opt_resetState=!0),t.replayData&&(o.detail.replayData=t.replayData),this.emitter.dispatchEvent(o)}};PlatformBridge.prototype={instCount:0,_init:function(){},createRequest:function(t,e,o){if(this.validateMethod(t)){"function"==typeof e&&(o=e,e=void 0);var n=this.instCount++;void 0!==o&&(this.requests[n]=o);var r={request:{method:t,messageId:n,data:null}};this.doCall(r,e)}},validateMethod:function(t){return"gameLoadingProgress"!==t},doCall:function(t,e){throw"Not implemented"},_callback:function(t,e){var o=this.requests[t];delete this.requests[t],o&&o(e)},doResponse:function(t,e){throw"Not implemented"}},PostMessageBridge.prototype=Object.create(PlatformBridge.prototype),PostMessageBridge.prototype.constructor=PostMessageBridge,PostMessageBridge.prototype._init=function(){window.addEventListener("message",function(t){var e;if("object"===n(t.detail)&&null!==typeof t.detail)e=t.detail;else{if("object"!==n(t.data))return;e=t.data}if(r.core.isSilentModeEnabled(),e.request&&e.request.method&&void 0!==e.request.messageId)this._resolveAPICall(e.request.method,e.request.messageId,e.request.data);else if(e.response&&void 0!==e.response.messageId){if(e.error)throw e.error;this._callback(e.response.messageId,e.response.data)}}.bind(this),!1)},PostMessageBridge.prototype.doCall=function(t,e){"object"===(void 0===e?"undefined":n(e))&&(t.request.data=e||{}),this._gameeWin.postMessage(t,"*")},PostMessageBridge.prototype.doResponse=function(t,e){var o={version:this.version,response:{messageId:t}};e&&(o.data=e),this._gameeWin.postMessage(o,"*")},PostMessageBridge.prototype._resolveAPICall=function(t,e,o){var n=this.doResponse.bind(this,e);switch(t){case"pause":a.pause(n);break;case"resume":a.resume(n);break;case"mute":a.mute(n);break;case"unmute":a.unmute(n);break;case"ghostShow":a.ghostShow(n);break;case"ghostHide":a.ghostHide(n);break;case"start":if(!o)throw"Method _start missing params";a.start(o,n);break;default:r.core.isSilentModeEnabled()}},MobileBridge.prototype=Object.create(PostMessageBridge.prototype),MobileBridge.prototype.constructor=MobileBridge,MobileBridge.prototype._init=function(){if(PostMessageBridge.prototype._init.call(this),"ios"===this.device)this._gameeWin=webkit.messageHandlers.callbackHandler;else{if("android"!==this.device)throw"Unknown device used in webkit bridge";this._gameeWin=_toDevice}window._triggerMessage=function(t){try{t=JSON.parse(t)}catch(e){throw"Couldn't parse message from native app: \n"+t+"\n"+e}r.core.isSilentModeEnabled(),this.dispatchEvent(new CustomEvent("message",{detail:t}))}.bind(window)},MobileBridge.prototype.doCall=function(t,e){"object"===(void 0===e?"undefined":n(e))&&(t.request.data=e||{}),"android"===this.device&&(t=JSON.stringify(t)),this._gameeWin.postMessage(t,"*")}},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){var t=this,e={};t.on=function(t,o,n){if(!(arguments.length<2||"string"!=typeof t||"function"!=typeof o)){var r=o.toString();void 0!==e[t]?void 0===e[t].callbacks[r]?e[t].callbacks[r]={cb:o,once:!!n}:"boolean"==typeof n&&(e[t].callbacks[r].once=n):(e[t]={callbacks:{}},e[t].callbacks[r]={cb:o,once:!!n})}},t.once=function(e,o){t.on(e,o,!0)},t.off=function(t,o){if("string"==typeof t&&void 0!==e[t])if("function"==typeof o){var n=o.toString(),r=e[t].callbacks[n];void 0!==r&&delete e[t].callbacks[n]}else delete e[t]},t.trigger=function(o,n){if("string"==typeof o&&void 0!==e[o])for(var r in e[o].callbacks){var a=e[o].callbacks[r];"function"==typeof a.cb&&a.cb(n),"boolean"==typeof a.once&&!0===a.once&&t.off(o,a.cb)}}};e.Bullet=new n},function(t,e,o){"use strict";function Button(t,e){var o=this;r.call(this),this._pressed=!0,this.key=t,this.keyCode=e,this.on("keydown",function(){o._pressed=!0}),this.on("keyup",function(){o._pressed=!1})}function Controller(){var t=this;r.call(this),this.buttons={},this.buttonAlias={},this.on("$keydown",function(e){e.button&&t.buttonAlias[e.button]&&(e.button=t.buttonAlias[e.button]),t.trigger("keydown",e)}),this.on("$keyup",function(e){e.button&&t.buttonAlias[e.button]&&(e.button=t.buttonAlias[e.button]),t.trigger("keyup",e)}),this.on("keydown",function(e){e.button&&t.buttons[e.button]&&t.buttons[e.button].trigger("keydown")}),this.on("keyup",function(e){e.button&&t.buttons[e.button]&&t.buttons[e.button].trigger("keyup")})}function OneButtonController(){Controller.call(this),this.addButton(new Button("button",32))}function TwoButtonController(){Controller.call(this),this.addButton(new Button("left",37)),this.addButton(new Button("right",39))}function TwoActionButtonsController(){Controller.call(this),this.addButton(new Button("A",32)),this.addButton(new Button("B",17))}function FourButtonController(){Controller.call(this),this.addButton(new Button("up",38)),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("A",32))}function FiveButtonController(){Controller.call(this),this.addButton(new Button("up",38)),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("down",40)),this.addButton(new Button("A",32))}function SixButtonController(){Controller.call(this),this.addButton(new Button("up",38)),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("down",40)),this.addButton(new Button("A",32)),this.addButton(new Button("B",17))}function TwoArrowsOneButtonController(){Controller.call(this),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("A",32))}function TwoArrowsTwoButtonsController(){Controller.call(this),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("A",32)),this.addButton(new Button("B",17))}function FourArrowController(){Controller.call(this),this.addButton(new Button("up",38)),this.addButton(new Button("left",37)),this.addButton(new Button("right",39)),this.addButton(new Button("down",40))}function TouchController(){var t=this;Controller.call(this),this.on("$touchstart",function(e){t.trigger("touchstart",e)}),this.on("$touchend",function(e){t.trigger("touchend",e)}),this.on("$touchmove",function(e){t.trigger("touchmove",e)}),this.on("$touchleave",function(e){t.trigger("touchleave",e)}),this.on("$touchcancel",function(e){t.trigger("touchcancel",e)})}function JoystickController(){var t=this;Controller.call(this),this.x=0,this.y=0,this.on("$change",function(e){t.x=e.position.x,t.y=e.position.y,t.trigger("change",e)})}function JoystickButtonController(){JoystickController.call(this),this.addButton(new Button("button",32))}Object.defineProperty(e,"__esModule",{value:!0}),e.BulletClass=void 0,e.Button=Button,e.Controller=Controller,e.OneButtonController=OneButtonController,e.TwoButtonController=TwoButtonController,e.TwoActionButtonsController=TwoActionButtonsController,e.FourButtonController=FourButtonController,e.FiveButtonController=FiveButtonController,e.SixButtonController=SixButtonController,e.TwoArrowsOneButtonController=TwoArrowsOneButtonController,e.TwoArrowsTwoButtonsController=TwoArrowsTwoButtonsController,e.FourArrowController=FourArrowController,e.TouchController=TouchController,e.JoystickController=JoystickController,e.JoystickButtonController=JoystickButtonController;var n=o(4),r=e.BulletClass=n.Bullet.constructor;Button.prototype=Object.create(r.constructor.prototype),Button.constructor=Button,Button.prototype.isDown=function(){return this._pressed},Controller.prototype=Object.create(r.constructor.prototype),Controller.constructor=Controller,Controller.prototype.addButton=function(t){this.buttons[t.key]=t},Controller.prototype.enableKeyboard=function(t){var e,o,n={},r=this;for(e in this.buttons)o=this.buttons[e],o.keyCode&&(n[o.keyCode]=o);t._keydown(function(t){var e=n[t.keyCode];e&&(t.preventDefault(),r.trigger("keydown",{button:e.key}))}),t._keyup(function(t){var e=n[t.keyCode];e&&(t.preventDefault(),r.trigger("keyup",{button:e.key}))})},Controller.prototype.remapButton=function(t,e){if(e.name&&(e=e.name),!this.buttons[t])throw Error("Button "+t+" was not found in controller");this.buttonAlias[t]=e.name,this.buttons[e.name]=this.buttons[t],delete this.buttons[t]},OneButtonController.prototype=Object.create(Controller.prototype),OneButtonController.prototype.constructor=OneButtonController,TwoButtonController.prototype=Object.create(Controller.prototype),TwoButtonController.prototype.constructor=TwoButtonController,TwoActionButtonsController.prototype=Object.create(Controller.prototype),TwoActionButtonsController.prototype.constructor=TwoActionButtonsController,FourButtonController.prototype=Object.create(Controller.prototype),FourButtonController.prototype.constructor=FourButtonController,FiveButtonController.prototype=Object.create(Controller.prototype),FiveButtonController.prototype.constructor=FiveButtonController,SixButtonController.prototype=Object.create(Controller.prototype),SixButtonController.prototype.constructor=SixButtonController,TwoArrowsOneButtonController.prototype=Object.create(Controller.prototype),TwoArrowsOneButtonController.prototype.constructor=TwoArrowsOneButtonController,TwoArrowsTwoButtonsController.prototype=Object.create(Controller.prototype),TwoArrowsTwoButtonsController.prototype.constructor=TwoArrowsTwoButtonsController,FourArrowController.prototype=Object.create(Controller.prototype),FourArrowController.prototype.constructor=FourArrowController,TouchController.prototype=Object.create(TouchController.prototype),TouchController.prototype.constructor=TouchController,JoystickController.prototype=Object.create(Controller.prototype),JoystickController.prototype.constructor=JoystickController,JoystickButtonController.prototype=Object.create(JoystickController.prototype),JoystickButtonController.prototype.constructor=JoystickButtonController},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.gamee=void 0,o(0);var n=o(2),r=o(1),a=o(3),i=e.gamee=void 0,u=function(){var t,o="web",r=navigator.userAgent.toLowerCase();switch(/iphone|ipod|ipad/.test(r)?o=window.self!==window.top?"web":"ios":/gamee\/[0-9\.]+$/.test(r)?o="android":window.parent?o="web":window.parent&&window.parent.gameeSimulator&&(o="web"),e.gamee=i=new n.Gamee(o),window.gamee=i,o){case"web":window.parent,window,t=new a.PostMessageBridge(window.parent);break;case"ios":t=new a.MobileBridge("ios");break;case"android":t=new a.MobileBridge("android");break;default:throw"Can't identify the platform"}return t}();r.core.PlatformAPI=a.PlatformAPI,r.core.native=u,a.PlatformAPI.emitter=i.emitter},function(t,e){var o;o=function(){return this}();try{o=o||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(o=window)}t.exports=o}])}); 3 | //# sourceMappingURL=gamee-js.min.js.map -------------------------------------------------------------------------------- /gamee/libs/bullet.js: -------------------------------------------------------------------------------- 1 | 2 | var BulletClass = function () { 3 | var _self = this, 4 | _events = {}; 5 | 6 | _self.on = function (event, fn, once) { 7 | if (arguments.length < 2 || 8 | typeof event !== "string" || 9 | typeof fn !== "function") return; 10 | 11 | var fnString = fn.toString(); 12 | 13 | // if the named event object already exists in the dictionary... 14 | if (typeof _events[event] !== "undefined") { 15 | // add a callback object to the named event object if one doesn't already exist. 16 | if (typeof _events[event].callbacks[fnString] === "undefined") { 17 | _events[event].callbacks[fnString] = { 18 | cb: fn, 19 | once: !!once 20 | }; 21 | } 22 | else if (typeof once === "boolean") { 23 | // the function already exists, so update it's 'once' value. 24 | _events[event].callbacks[fnString].once = once; 25 | } 26 | } 27 | else { 28 | // create a new event object in the dictionary with the specified name and callback. 29 | _events[event] = { 30 | callbacks: {} 31 | }; 32 | 33 | _events[event].callbacks[fnString] = { cb: fn, once: !!once }; 34 | } 35 | }; 36 | 37 | _self.once = function (event, fn) { 38 | _self.on(event, fn, true); 39 | }; 40 | 41 | _self.off = function (event, fn) { 42 | if (typeof event !== "string" || 43 | typeof _events[event] === "undefined") return; 44 | 45 | // remove just the function, if passed as a parameter and in the dictionary. 46 | if (typeof fn === "function") { 47 | var fnString = fn.toString(), 48 | fnToRemove = _events[event].callbacks[fnString]; 49 | 50 | if (typeof fnToRemove !== "undefined") { 51 | // delete the callback object from the dictionary. 52 | delete _events[event].callbacks[fnString]; 53 | } 54 | } 55 | else { 56 | // delete all functions in the dictionary that are 57 | // registered to this event by deleting the named event object. 58 | delete _events[event]; 59 | } 60 | }; 61 | 62 | _self.trigger = function (event, data) { 63 | if (typeof event !== "string" || 64 | typeof _events[event] === "undefined") return; 65 | 66 | for (var fnString in _events[event].callbacks) { 67 | var callbackObject = _events[event].callbacks[fnString]; 68 | 69 | if (typeof callbackObject.cb === "function") callbackObject.cb(data); 70 | if (typeof callbackObject.once === "boolean" && callbackObject.once === true) _self.off(event, callbackObject.cb); 71 | } 72 | }; 73 | 74 | }; 75 | 76 | 77 | export var Bullet = new BulletClass(); -------------------------------------------------------------------------------- /gamee/libs/shims.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class CustomEvent 3 | */ 4 | (function shimCustomEvent() { 5 | try { 6 | var ce = new window.CustomEvent('test'); 7 | ce.preventDefault(); 8 | if (ce.defaultPrevented !== true) { 9 | // IE has problems with .preventDefault() on custom events 10 | // http://stackoverflow.com/questions/23349191 11 | throw new Error('Could not prevent default'); 12 | } 13 | } catch (e) { 14 | var CustomEvent = function (event, params) { 15 | var evt, origPrevent; 16 | params = params || { 17 | bubbles: false, 18 | cancelable: false, 19 | detail: undefined 20 | }; 21 | 22 | evt = document.createEvent("CustomEvent"); 23 | evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); 24 | origPrevent = evt.preventDefault; 25 | evt.preventDefault = function () { 26 | origPrevent.call(this); 27 | try { 28 | Object.defineProperty(this, 'defaultPrevented', { 29 | get: function () { 30 | return true; 31 | } 32 | }); 33 | } catch (e) { 34 | this.defaultPrevented = true; 35 | } 36 | }; 37 | return evt; 38 | }; 39 | 40 | CustomEvent.prototype = window.Event.prototype; 41 | window.CustomEvent = CustomEvent; // expose definition to window 42 | } 43 | })(); 44 | 45 | //addEventListener polyfill 1.0 / Eirik Backer / MIT Licence 46 | (function (win, doc) { 47 | if (win.addEventListener) return; //No need to polyfill 48 | 49 | function docHijack(p) { var old = doc[p]; doc[p] = function (v) { return addListen(old(v)); }; } 50 | function addEvent(on, fn, self) { 51 | return (self = this).attachEvent('on' + on, function (e) { 52 | e = e || win.event; 53 | e.preventDefault = e.preventDefault || function () { e.returnValue = false; }; 54 | e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true; }; 55 | fn.call(self, e); 56 | }); 57 | } 58 | function addListen(obj, i) { 59 | i = obj.length; 60 | if (i) { 61 | while (i--) 62 | obj[i].addEventListener = addEvent; 63 | } else { 64 | obj.addEventListener = addEvent; 65 | } 66 | return obj; 67 | } 68 | 69 | addListen([doc, win]); 70 | if ('Element' in win) win.Element.prototype.addEventListener = addEvent; //IE8 71 | else { //IE < 8 72 | doc.attachEvent('onreadystatechange', function () { addListen(doc.all); }); //Make sure we also init at domReady 73 | docHijack('getElementsByTagName'); 74 | docHijack('getElementById'); 75 | docHijack('createElement'); 76 | addListen(doc.all); 77 | } 78 | })(window, document); 79 | 80 | // naomik event emiter http://stackoverflow.com/a/24216547/1866147 81 | // usage: 82 | // function Example() { 83 | // CustomEmitter.call(this); 84 | // } 85 | 86 | // // run it 87 | // var e = new Example(); 88 | 89 | // e.addEventListener("something", function (event) { 90 | // console.log(event) 91 | // }); 92 | 93 | // e.dispatchEvent(new Event("something")); 94 | export function CustomEmitter() { 95 | var eventTarget = document.createDocumentFragment(); 96 | 97 | function delegate(method) { 98 | this[method] = eventTarget[method].bind(eventTarget); 99 | } 100 | 101 | [ 102 | "addEventListener", 103 | "dispatchEvent", 104 | "removeEventListener" 105 | ].forEach(delegate, this); 106 | } 107 | 108 | /** ### wrapKeyEvent 109 | * 110 | * Handle old IE event differences for key events 111 | * 112 | * @param {Function} fn callback 113 | */ 114 | export function wrapKeyEvent(fn) { 115 | return function (ev) { 116 | if (!ev || !ev.keyCode) { 117 | if (!ev) { 118 | ev = window.event; 119 | } 120 | 121 | if (ev.which) { 122 | ev.keyCode = ev.which; 123 | } 124 | } 125 | 126 | return fn(ev); 127 | }; 128 | } -------------------------------------------------------------------------------- /gamee/src/core.js: -------------------------------------------------------------------------------- 1 | import * as controllers from "./game_controllers.js" 2 | import { wrapKeyEvent } from "../libs/shims.js" 3 | 4 | // unlock audio 5 | // overrides native AudioContext & webkitAudioContext 6 | (function () { 7 | // this works as a constructor 8 | var overloadedAudioContext = function (type) { 9 | var ctx = new type(); 10 | 11 | // add audio resume to function on touchstart 12 | if (ctx.state === 'suspended') { 13 | 14 | var resume = function () { 15 | 16 | // Check if hack is necessary. Only occurs in iOS6+ devices 17 | // and only when you first boot the iPhone, or play a audio/video 18 | // with a different sample rate 19 | if (/(iPhone|iPad)/i.test(navigator.userAgent)) { 20 | var buffer = ctx.createBuffer(1, 1, 44100); 21 | var dummy = ctx.createBufferSource(); 22 | dummy.buffer = buffer; 23 | dummy.connect(ctx.destination); 24 | dummy.start(0); 25 | dummy.disconnect(); 26 | } 27 | 28 | ctx.resume(); 29 | setTimeout(function () { 30 | if (ctx.state === 'running') { 31 | document.body.removeEventListener([ 32 | 'touchcancel', 33 | 'touchend', 34 | 'touchenter', 35 | 'touchleave', 36 | 'touchmove', 37 | 'touchstart', 38 | 'mouseenter', 39 | 'mouseover', 40 | 'mousemove', 41 | 'mousedown', 42 | 'mouseup' 43 | ].join(" "), resume, false); 44 | } 45 | }, 0); 46 | }; 47 | 48 | // only touchend will work, but hey, we tried... 49 | // https://github.com/WebAudio/web-audio-api/issues/836 50 | // https://www.chromestatus.com/feature/6406908126691328 51 | document.body.addEventListener([ 52 | 'touchcancel', 53 | 'touchend', 54 | 'touchenter', 55 | 'touchleave', 56 | 'touchmove', 57 | 'touchstart', 58 | 'mouseenter', 59 | 'mouseover', 60 | 'mousemove', 61 | 'mousedown', 62 | 'mouseup' 63 | ].join(" "), 64 | resume, false); 65 | } 66 | // allowed in JS to return different type of the object in the constructor 67 | return ctx 68 | }; 69 | 70 | try { 71 | if (typeof window.AudioContext !== 'undefined') { 72 | window.AudioContext = overloadedAudioContext.bind(null, window.AudioContext); 73 | } else if (typeof webkitAudioContext !== 'undefined') { 74 | window.webkitAudioContext = overloadedAudioContext.bind(null, window.webkitAudioContext); 75 | } 76 | } catch (e) { // throw error in async part 77 | setTimeout(() => { 78 | throw e; 79 | }, 0) 80 | } 81 | })(); 82 | 83 | 84 | /** 85 | * @class core 86 | */ 87 | export var core = (function () { 88 | 89 | // # Gamee.js 90 | // 91 | // This file defines and expose a public API for games to communicate 92 | // with Gamee*. 93 | // 94 | // Also it handles some requirements when Gamee is run in an desktop 95 | // environment. 96 | // 97 | // \* _later in the document Gamee will be referred as GameeApp to not 98 | // be mistaken for word game_ 99 | // 100 | // ** _GameeWebApp will refer to Gamee which is running in a desktop 101 | // browser_ 102 | 103 | /** an empty function */ 104 | var noop = function () { }; 105 | 106 | var cache = {}; 107 | 108 | /** internal variables/constants (uppercase) coupled inside separate object for potential easy referencing */ 109 | var internals = { 110 | VERSION: "2.4.0", // version of the gamee library 111 | CAPABILITIES: ["ghostMode", "saveState", "replay", "socialData","rewardedAds","coins","logEvents","playerData","share", "gems"], // supported capabilities 112 | variant: 0, // for automating communication with server 113 | soundUnlocked: false, 114 | onReady: noop, // for intercepting real onReady because of behind the scenes variant handling 115 | onGameStart: noop // for intercepting real onGameStart because of unlocking sound 116 | }; 117 | 118 | /** ## gamee 119 | * 120 | * GameeApp interface for games. It is exposed as a `gamee` global 121 | * object and games should only use its public methods and 122 | * properties to communicate with the GameeApp. 123 | * 124 | * _There is also [$gameeNative](gamee_native.js.html) global object 125 | * which handles internal parts of the communication._ 126 | */ 127 | var core = {}; 128 | 129 | // 130 | // ## Signaling game state 131 | // 132 | // The game should signal the GameeApp its status (playing/game-over) 133 | // and current score. 134 | // 135 | 136 | /** ### gamee.gameeInit 137 | * 138 | * Must be called first before any other gamee calls 139 | * returns controller object the same way requestController did previously 140 | * ctrlType/ctrlOpts - requested control type + options 141 | * capabilities -> array of strings representing supported features: 142 | * after the initialization onReady is invoked and after that game can use the api 143 | */ 144 | core.gameeInit = function (ctrlType, ctrlOpts, capabilities, cb, silentMode = false) { 145 | // let's validate the array here, so that all backends can benefit from it 146 | var allOk = true, cap = {}; 147 | if ((capabilities !== undefined) && (Array.isArray(capabilities))) { 148 | for (var i = 0; i < capabilities.length; i++) { 149 | if ((typeof capabilities[i] !== "string") || 150 | (internals.CAPABILITIES.indexOf(capabilities[i]) === -1)) allOk = false; 151 | cap[capabilities[i]] = true; 152 | } 153 | } else allOk = false; 154 | 155 | if (!allOk) 156 | throw "Capabilities array passed to gameeInit is void, malformed or unsupported capabilites requested."; 157 | // TODO remove 158 | // gameeNative.gameeInit(core, internals.VERSION, ctrlType, allOk ? capabilities : []); 159 | 160 | this.native.createRequest("init", { 161 | version: internals.VERSION, 162 | controller: ctrlType, 163 | capabilities: cap 164 | }, function (responseData) { 165 | // remember capabilities of the game 166 | cache.capabilities = cap; 167 | // 168 | // // Mute gamee-js console output 169 | // cache.silentMode = silentMode; 170 | 171 | // might fail if controller of this type doesnt exist 172 | var error = null; 173 | try { 174 | if (this.native.platform === "web") { 175 | responseData.controller = core.controller.requestController(ctrlType, { enableKeyboard: true }); 176 | this._bindKeyboardTriggers(responseData.controller); 177 | } else { 178 | responseData.controller = core.controller.requestController(ctrlType, {}); 179 | } 180 | } catch (err) { 181 | error = err; 182 | } 183 | 184 | cb(error, responseData); 185 | }.bind(this)); 186 | // TODO remove 187 | // return core.controller.requestController(ctrlType, ctrlOpts); 188 | }; 189 | 190 | core._bindKeyboardTriggers = function (controller) { 191 | global.addEventListener('message', function (ev) { 192 | switch (ev.data[0]) { 193 | case 'button_button_down': 194 | controller.trigger("keydown", { button: "button" }); 195 | break; 196 | 197 | case 'button_button_up': 198 | controller.trigger("keyup", { button: "button" }); 199 | break; 200 | 201 | case 'button_left_up': 202 | controller.trigger("keyup", { button: "left" }); 203 | break; 204 | 205 | case 'button_left_down': 206 | controller.trigger("keydown", { button: "left" }); 207 | break; 208 | 209 | case 'button_right_down': 210 | controller.trigger("keydown", { button: "right" }); 211 | break; 212 | 213 | case 'button_right_up': 214 | controller.trigger("keyup", { button: "right" }); 215 | break; 216 | 217 | case 'button_up_down': 218 | controller.trigger("keydown", { button: "up" }); 219 | break; 220 | 221 | case 'button_up_up': 222 | controller.trigger("keyup", { button: "up" }); 223 | break; 224 | 225 | case 'button_down_down': 226 | controller.trigger("keydown", { button: "down" }); 227 | break; 228 | 229 | case 'button_down_up': 230 | controller.trigger("keyup", { button: "down" }); 231 | break; 232 | 233 | case 'button_a_down': 234 | controller.trigger("keydown", { button: "A" }); 235 | break; 236 | 237 | case 'button_a_up': 238 | controller.trigger("keyup", { button: "A" }); 239 | break; 240 | 241 | case 'button_b_down': 242 | controller.trigger("keydown", { button: "B" }); 243 | break; 244 | 245 | case 'button_b_up': 246 | controller.trigger("keyup", { button: "B" }); 247 | break; 248 | } 249 | }); 250 | }; 251 | 252 | /** ### gamee.gameLoadingProgress 253 | * 254 | * Indicates how much content is already loaded in %. 255 | */ 256 | core.gameLoadingProgress = (function () { 257 | var percentageSoFar = 0; 258 | 259 | return function (percentage) { 260 | if ((typeof percentage !== "number") || (percentage < 0) || (percentage > 100)) 261 | throw "Percentage passed to gameLoadingProgress out of bounds or not a number."; 262 | else if (percentage > percentageSoFar) { 263 | percentageSoFar = percentage; 264 | this.native.createRequest("gameLoadingProgress", { percentage: percentage }); 265 | } 266 | }; 267 | })(); 268 | 269 | 270 | /** ### gamee.gameReady 271 | * 272 | * Notifies platform game can accept start command. 273 | */ 274 | core.gameReady = function () { 275 | this.native.createRequest("gameReady"); 276 | }; 277 | 278 | /** ### gamee.gameStart 279 | * 280 | * Indicates that game is ready to be started (even after restart). 281 | */ 282 | // core.gameStart = function () { 283 | // gameeNative.gameLoadingProgress(100); // FB requires this 284 | // gameeNative.gameStart(gamee); 285 | // }; 286 | 287 | /** ### gamee.updateScore 288 | * 289 | * sends score to UI 290 | */ 291 | core.updateScore = function (score, opt_ghostSign) { 292 | if (typeof score !== "number") 293 | throw "Score passed to updateScore is not a number."; 294 | var data = { 295 | score: parseInt(score, 10) 296 | }; 297 | if (opt_ghostSign) { 298 | data.ghostSign = true; 299 | } 300 | this.native.createRequest("updateScore", data); 301 | // core.native.createRequest(method, requestData, callback); 302 | }; 303 | 304 | /** ### gamee.gameOver 305 | * 306 | * Indicates the game has ended, the game is waiting for subsequent onGameStart. 307 | * Data has the same format as data received in onReady callback. 308 | * Data must be string = responsibility for turning data structure into string is left to the game! 309 | */ 310 | core.gameOver = function (opt_replayData, opt_saveState, opt_hideOverlay) { 311 | opt_hideOverlay = opt_hideOverlay !== undefined ? opt_hideOverlay : false; 312 | // var allOk = ((data !== undefined) && (typeof data === "string")) || (data === undefined); 313 | // if (!allOk) console.error("Data provided to gameOver function must be string."); 314 | // gameeNative.gameOver(gamee, internals.variant, allOk ? data : ""); 315 | var requestData = {}; 316 | if (opt_replayData) { 317 | if (!opt_replayData.hasOwnProperty("variant")) { 318 | opt_replayData.variant = ""; 319 | } 320 | if (!opt_replayData.hasOwnProperty("data")) { 321 | throw "Replay data must have `data` property"; 322 | } 323 | requestData.replayData = opt_replayData; 324 | } 325 | requestData.hideOverlay = opt_hideOverlay; 326 | 327 | if (opt_saveState) { 328 | requestData.state = opt_saveState; 329 | } 330 | 331 | core.native.createRequest("gameOver", requestData); 332 | }; 333 | 334 | /** ### gamee.gameSave 335 | * 336 | * Player has requested saving current game's state 337 | * data must be string = responsibility for turning data structure into string is left to game! 338 | * share must be expression evaluating to either true or false; it indicates, whether the game progress should be shared on feed 339 | */ 340 | core.gameSave = function (data, share) { 341 | 342 | if(!cache.capabilities.saveState) 343 | throw "Save State not supported, you must add the capability on gamee.Init"; 344 | 345 | core.native.createRequest("saveState", { state: data, share: share }); 346 | }; 347 | 348 | core.requestSocial = function (cb,numberOfPlayers) { 349 | 350 | if(!cache.capabilities.socialData) 351 | throw "Social Data not supported, you must add the capability on gamee.Init"; 352 | 353 | this.native.createRequest("requestSocial", numberOfPlayers, function (responseData) { 354 | cb(null, responseData); 355 | }); 356 | }; 357 | 358 | core.logEvent = function (eventName, eventValue) { 359 | 360 | if(!cache.capabilities.logEvents) 361 | throw "Log Events not supported, you must add the capability on gamee.Init"; 362 | 363 | //var valuesToLogString = JSON.stringify(eventValue) 364 | 365 | this.native.createRequest("logEvent", {eventName,eventValue}, function (error){ 366 | if(error){ 367 | throw error 368 | } 369 | }); 370 | 371 | }; 372 | 373 | core.requestBattleData = function (cb) { 374 | this.native.createRequest("requestBattleData", undefined, function (responseData) { 375 | cb(null, responseData); 376 | }); 377 | }; 378 | 379 | core.requestPlayerReplay = function (userID, cb) { 380 | 381 | if(!cache.capabilities.replay) 382 | throw "Replays not supported, you must add the capability on gamee.Init"; 383 | 384 | this.native.createRequest("requestPlayerReplay", {userID}, function (responseData) { 385 | cb(null, responseData); 386 | }); 387 | }; 388 | 389 | core.requestPlayerSaveState = function (userID, cb) { 390 | this.native.createRequest("requestPlayerSaveState", {userID}, function (responseData) { 391 | cb(null, responseData); 392 | }); 393 | }; 394 | 395 | core.purchaseItemWithCoins = function (options, cb, oldMethod) { 396 | 397 | if(!cache.capabilities.coins) 398 | throw "Coins purchases not supported, you must add the capability on gamee.Init"; 399 | 400 | if (options) { 401 | var propertiesList = ["coinsCost","itemName"]; 402 | propertiesList.forEach(function (property){ 403 | if(!options.hasOwnProperty(property)) 404 | throw "Purchase Options must have `"+property+"` property" 405 | }) 406 | } 407 | 408 | if (!this.isSilentModeEnabled()) { 409 | console.log(options); 410 | } 411 | 412 | var method = "purchaseItemWithCoins"; 413 | if (oldMethod !== undefined && oldMethod === true) { 414 | method = "purchaseItem"; 415 | } 416 | this.native.createRequest(method, options, function (responseData) { 417 | cb(null, responseData); 418 | }); 419 | }; 420 | 421 | core.purchaseItemWithGems = function (options, cb) { 422 | 423 | if(!cache.capabilities.gems) 424 | throw "Gems purchases not supported, you must add the capability on gamee.Init"; 425 | 426 | if (options) { 427 | var propertiesList = ["gemsCost","itemName"]; 428 | propertiesList.forEach(function (property){ 429 | if(!options.hasOwnProperty(property)) 430 | throw "Purchase options must have `"+property+"` property" 431 | }) 432 | } 433 | 434 | if (!this.isSilentModeEnabled()) { 435 | console.log(options); 436 | } 437 | 438 | this.native.createRequest("purchaseItemWithGems", options, function (responseData) { 439 | cb(null, responseData); 440 | }); 441 | }; 442 | 443 | core.share = function (options, cb) { 444 | 445 | if(!cache.capabilities.share) 446 | throw "Share option not supported, you must add the capability on gamee.Init"; 447 | 448 | if (options) { 449 | var propertiesList = ["destination"]; 450 | propertiesList.forEach(function (property){ 451 | if(!options.hasOwnProperty(property)) 452 | throw "Share Options must have `"+property+"` property"; 453 | }) 454 | } 455 | 456 | if (!this.isSilentModeEnabled()) { 457 | console.log(options); 458 | } 459 | 460 | this.native.createRequest("share",options, function (responseData) { 461 | cb(null, responseData); 462 | }); 463 | }; 464 | 465 | core.loadRewardedVideo = function (cb) { 466 | 467 | if(!cache.capabilities.rewardedAds) 468 | throw "Rewarded Ads not supported, you must add the capability on gamee.Init"; 469 | 470 | this.native.createRequest("loadRewardedVideo", function (responseData) { 471 | cb(null, responseData); 472 | }); 473 | }; 474 | 475 | core.showRewardedVideo = function (cb) { 476 | 477 | if(!cache.capabilities.rewardedAds) 478 | throw "Rewarded Ads not supported, you must add the capability on gamee.Init"; 479 | 480 | this.native.createRequest("showRewardedVideo", function (responseData) { 481 | cb(null, responseData); 482 | }); 483 | }; 484 | 485 | core.requestPlayerData = function (cb, userID) { 486 | 487 | if(!cache.capabilities.playerData) 488 | throw "Player Data not supported, you must add the capability on gamee.Init"; 489 | 490 | let options = undefined; 491 | if (userID) { 492 | options = {userID}; 493 | } 494 | 495 | this.native.createRequest("requestPlayerData", options, function (responseData) { 496 | cb(null, responseData); 497 | }); 498 | }; 499 | 500 | core.startSignal = function (data) { 501 | var error; 502 | 503 | if (data.replay && !cache.capabilities.replay) 504 | error = "Game doesn't support replay. "; 505 | 506 | if (data.ghostMode && !cache.capabilities.ghostMode) 507 | error = "Game doesn't support ghost Mode. "; 508 | 509 | return error; 510 | }; 511 | // 512 | // ## Private objects and methods 513 | // These are internal objects in closed scope. Good to know about them 514 | // when debugging. 515 | 516 | // 517 | // ## gamee.controller 518 | // 519 | // Namespace where the methods for controller are published. 520 | // 521 | 522 | /** 523 | * TODO transform this into instance of gamee class 524 | */ 525 | core.controller = { 526 | /** ### mainController 527 | * 528 | * Current controller. 529 | */ 530 | mainController: null, 531 | 532 | /** ### requestController 533 | * 534 | * Factory method to create a controller. It creates the controller 535 | * and signals to GameeApp which type the game requires 536 | * 537 | * You should called this method once before calling 538 | * `gamee.gameStart()`. 539 | * 540 | * @param {String} type type of controller (see [controllerTypes](#controllertypes)) 541 | * @param {Object} [opts] optional controller options 542 | * {'enableKeyboard': .., 'buttons': ...} 543 | * @param {boolean} [opts.enableKeyboard] enable the keyboard 544 | * @param {Object} [opts.buttons] remap buttons {'oldKey': 'newKey', 545 | * 'left': 'break' ..} 546 | */ 547 | requestController: function (type, opts) { 548 | if (type === "FullScreen") 549 | return null; 550 | 551 | var controller = createController(type, opts); 552 | 553 | this.mainController = controller; 554 | 555 | return controller; 556 | }, 557 | 558 | /** ### additionalController 559 | * 560 | * Construct an additional controller. Sometimes games require a 561 | * different controller depending on platform (eg. touch on mobile, 562 | e but Four Buttons on desktop) 563 | * 564 | * **This is currently supported only for GameeWebApp** as a way to 565 | * have alternate keybinding. The game should request a type used 566 | * for mobile platform and then some other as *additionalController* 567 | * if alternate keybinding is needed; 568 | */ 569 | // TODO remove this function 570 | additionalController: function (type, opts) { 571 | var controller = createController(type, opts); 572 | gameeNative.additionalController(type); 573 | 574 | return controller; 575 | }, 576 | 577 | /** ### trigger 578 | * 579 | * Triggers and event for the controller 580 | * 581 | * This is called by GameeApp to trigger the *keydown*, *keyup* 582 | * events. For more info see [Controller](#controller) 583 | * 584 | * @param {String} eventName name of the event 585 | * @param {*} [data,...] data to pass for the event 586 | * 587 | */ 588 | trigger: function () { 589 | var i; 590 | 591 | if (this.mainController) { 592 | this.mainController.trigger.apply(this.mainController, arguments); 593 | } else { 594 | throw new Error('No controller present'); 595 | } 596 | } 597 | }; 598 | 599 | /** ### core._keydown 600 | * 601 | * A helper function to listen for `keydown` events on window object. 602 | * 603 | * @param {Function} fn callback to handle the event 604 | */ 605 | core._keydown = function (fn) { 606 | global.addEventListener('keydown', wrapKeyEvent(fn)); 607 | }; 608 | 609 | /** ### core._keyup 610 | * 611 | * A helper function to listen for `keyup` events on window object. 612 | * 613 | * @param {Function} fn callback to handle the event 614 | */ 615 | core._keyup = function (fn) { 616 | global.addEventListener('keyup', wrapKeyEvent(fn)); 617 | }; 618 | 619 | /** ### createController 620 | * 621 | * Function to create a controller. 622 | * 623 | * *see [requestController](#requestcontroller) 624 | * 625 | * @param {String} type 626 | * @param {Object} [opts] 627 | * @returns {Controller} controller 628 | */ 629 | function createController(type, opts) { 630 | var btn, controller; 631 | 632 | if (!controllerTypes[type]) { 633 | throw new Error('Unsupported controller type, ' + type); 634 | } 635 | 636 | opts = opts || {}; 637 | 638 | controller = new controllerTypes[type](); 639 | 640 | if (opts.enableKeyboard) { 641 | controller.enableKeyboard(core); 642 | } 643 | 644 | if (opts.buttons) { 645 | for (btn in opts.buttons) { 646 | controller.remapButton(btn, opts.buttons[btn]); 647 | } 648 | } 649 | 650 | return controller; 651 | } 652 | 653 | 654 | 655 | /** ### controllerTypes 656 | * 657 | * List of controller types and their coresponding classes. 658 | * 659 | * *see [Controllers](#controllers) for more info* 660 | * @requires Controller 661 | */ 662 | var controllerTypes = { 663 | 'OneButton': controllers.OneButtonController, 664 | 'TwoButtons': controllers.TwoButtonController, 665 | 'FourButtons': controllers.FourButtonController, 666 | 'FiveButtons': controllers.FiveButtonController, 667 | 'SixButtons': controllers.SixButtonController, 668 | 'FourArrows': controllers.FourArrowController, 669 | 'Touch': controllers.TouchController, 670 | 'Joystick': controllers.JoystickController, 671 | 'JoystickWithButton': controllers.JoystickButtonController, 672 | 'TwoArrowsTwoButtons': controllers.TwoArrowsTwoButtonsController, 673 | 'TwoArrowsOneButton': controllers.TwoArrowsOneButtonController, 674 | 'TwoActionButtons': controllers.TwoActionButtonsController 675 | }; 676 | 677 | 678 | core.registerPlatform = function (platformAPI) { 679 | // platformAPI.addEventListener() 680 | // TODO ? 681 | }; 682 | 683 | /** 684 | * Is true mute all console outputs 685 | * @return {boolean} 686 | */ 687 | core.isSilentModeEnabled = function () { 688 | return cache.silentMode; 689 | }; 690 | 691 | return core; 692 | })(); 693 | 694 | export var DataTypeException = function (expected, present, argument, method) { 695 | this.expected = expected; 696 | this.present = present; 697 | this.method = method; 698 | this.argument = argument; 699 | this.message = `Invalid data type in method ${this.method}, argument ${this.argument} is expected to be ${this.expected}, but found ${this.present}`; 700 | }; 701 | 702 | export var validateDataType = function (testedInput, expectedType, argument, originMethod) { 703 | switch (expectedType) { 704 | 705 | case "array": 706 | if (!Array.isArray(testedInput)) 707 | throw new DataTypeException(expectedType, typeof testedInput, argument, originMethod); 708 | break; 709 | 710 | default: 711 | if (typeof testedInput !== expectedType) 712 | throw new DataTypeException(expectedType, typeof testedInput, argument, originMethod); 713 | } 714 | }; 715 | -------------------------------------------------------------------------------- /gamee/src/game_controllers.js: -------------------------------------------------------------------------------- 1 | import { Bullet } from "../libs/bullet.js" 2 | 3 | /** 4 | * @module game_controllers 5 | */ 6 | 7 | /** ## Bullet 8 | * 9 | * [Bullet.js](https://github.com/munkychop/bullet) is used as pub/sub 10 | * library. 11 | * 12 | * The controller and its buttons are instance of Bullet. 13 | */ 14 | export var BulletClass = Bullet.constructor; 15 | 16 | 17 | /** ## Button 18 | * 19 | * Represenation of a controller button. It is a child of 20 | * [Bullet](https://github.com/munkychop/bullet), so you can 21 | * subscribe for events triggered on it. 22 | * 23 | * @class Button 24 | * @param {String} key name of the button 25 | * @param {Number} keyCode keycode for the key to represent the button 26 | * on keyboard 27 | */ 28 | export function Button(key, keyCode) { 29 | var self = this; 30 | 31 | BulletClass.call(this); 32 | 33 | this._pressed = true; 34 | 35 | this.key = key; 36 | this.keyCode = keyCode; 37 | 38 | this.on('keydown', function () { 39 | self._pressed = true; 40 | }); 41 | 42 | this.on('keyup', function () { 43 | self._pressed = false; 44 | }); 45 | } 46 | 47 | Button.prototype = Object.create(BulletClass.constructor.prototype); 48 | Button.constructor = Button; 49 | 50 | /** ### isDown 51 | * 52 | * Ask if the button is currently pressed. 53 | * 54 | * @return {Boolean} true if the button is currently pressed 55 | */ 56 | Button.prototype.isDown = function () { 57 | return this._pressed; 58 | }; 59 | 60 | /** ## Controller 61 | * 62 | * Controller has a collection of [buttons](#buttons). 63 | * It is a child of 64 | * [Bullet](https://github.com/munkychop/bullet), so you can 65 | * subscribe for events triggered on it. 66 | * 67 | * Controllers will get all the events for its buttons so you can 68 | * listen for them globaly from controller or individualy on every 69 | * button. 70 | * 71 | * ```javascript 72 | * controller.on('keydown', function(data) { 73 | * console.log('button ' + data.button + ' is pressed'); 74 | * }); 75 | * 76 | * controller.buttons.left.on('keydown', function() { 77 | * console.log('button left is pressed'); 78 | * }); 79 | * ``` 80 | * 81 | * @class Controller 82 | */ 83 | export function Controller() { 84 | var self = this; 85 | 86 | BulletClass.call(this); 87 | 88 | // ### buttons 89 | // 90 | // Map of controller's [buttons](#button) by their name. 91 | // 92 | // ```javascript 93 | // controller.buttons.left // Button('left', ..) 94 | // ``` 95 | this.buttons = {}; 96 | 97 | // ### buttonAlias 98 | // 99 | // Map of remapped buttons. 100 | // 101 | // *see [remapButton](#remapbutton) for more info* 102 | // 103 | this.buttonAlias = {}; 104 | 105 | // Events prefixed with *$* are private, sent from GameeApp ment 106 | // to be handled before resended as *public (non-prefixed)* 107 | // event. 108 | // 109 | // They should be not used in games as they can change in the future. 110 | this.on('$keydown', function (data) { 111 | if (data.button && self.buttonAlias[data.button]) { 112 | data.button = self.buttonAlias[data.button]; 113 | } 114 | 115 | self.trigger('keydown', data); 116 | }); 117 | 118 | this.on('$keyup', function (data) { 119 | if (data.button && self.buttonAlias[data.button]) { 120 | data.button = self.buttonAlias[data.button]; 121 | } 122 | 123 | self.trigger('keyup', data); 124 | }); 125 | 126 | // By default GameeApp will trigger *keydown* and *keyup* events for 127 | // the controller for every button presses/released. 128 | // 129 | // The controller then handles the event and triggers the event for 130 | // the coresponding button. 131 | // 132 | // It expexts a `data` argument which should have a property `button` 133 | // with the name of button. 134 | this.on('keydown', function (data) { 135 | if (!data.button || !self.buttons[data.button]) { 136 | return; 137 | } 138 | 139 | self.buttons[data.button].trigger('keydown'); 140 | }); 141 | 142 | this.on('keyup', function (data) { 143 | if (!data.button || !self.buttons[data.button]) { 144 | return; 145 | } 146 | 147 | self.buttons[data.button].trigger('keyup'); 148 | }); 149 | } 150 | 151 | Controller.prototype = Object.create(BulletClass.constructor.prototype); 152 | Controller.constructor = Controller; 153 | 154 | /** ### addButton 155 | * 156 | * Add button to the controller. 157 | * 158 | * @param {Button} button a [Button](#button) instance 159 | */ 160 | Controller.prototype.addButton = function (button) { 161 | this.buttons[button.key] = button; 162 | }; 163 | 164 | /** ### enableKeyboard 165 | * 166 | * Enable keyboard controlls. It will attach event listeners to the 167 | * *window* object for every button and trigger their *keydown* / 168 | * *keyup* event for the controller. 169 | */ 170 | Controller.prototype.enableKeyboard = function (gamee) { 171 | var key, button, keyCodes = {}, self = this; 172 | 173 | for (key in this.buttons) { 174 | button = this.buttons[key]; 175 | 176 | if (button.keyCode) { 177 | keyCodes[button.keyCode] = button; 178 | } 179 | } 180 | 181 | gamee._keydown(function (ev) { 182 | var button = keyCodes[ev.keyCode]; 183 | 184 | if (!button) { 185 | return; 186 | } 187 | 188 | ev.preventDefault(); 189 | self.trigger('keydown', { button: button.key }); 190 | }); 191 | 192 | gamee._keyup(function (ev) { 193 | var button = keyCodes[ev.keyCode]; 194 | 195 | if (!button) { 196 | return; 197 | } 198 | 199 | ev.preventDefault(); 200 | self.trigger('keyup', { button: button.key }); 201 | }); 202 | }; 203 | 204 | /** ### remapButton 205 | * 206 | * Remap the names of the controller's buttons. Controllers have their 207 | * button names set (left, right, A, B), but sometimes in context of 208 | * the game a different names are desired. 209 | * 210 | * ```javascript 211 | * var controller = gamee.controller.requestController('TwoButtons'); 212 | * controller.remapButton('left', 'throttle'); 213 | * controller.remapButton('right', 'break'); 214 | * 215 | * controller.buttons.throttle.on('keydown', ..); 216 | * ``` 217 | * 218 | * @param {String} oldName button name we want to change 219 | * @param {String} newName new button name 220 | */ 221 | Controller.prototype.remapButton = function (oldName, newName) { 222 | 223 | // handle old code 224 | if (newName.name) { 225 | newName = newName.name; 226 | } 227 | 228 | if (this.buttons[oldName]) { 229 | this.buttonAlias[oldName] = newName.name; 230 | 231 | this.buttons[newName.name] = this.buttons[oldName]; 232 | 233 | delete this.buttons[oldName]; 234 | } else { 235 | throw Error('Button ' + oldName + ' was not found in controller'); 236 | } 237 | }; 238 | 239 | // ## Controllers 240 | 241 | /** ### OneButtonController 242 | * 243 | * Controller with only one button. 244 | * @class OneButtonController 245 | */ 246 | export function OneButtonController() { 247 | Controller.call(this); 248 | 249 | // * __name__: 'button' 250 | // * __key__: spacebar 251 | this.addButton(new Button('button', 32)); 252 | } 253 | OneButtonController.prototype = Object.create(Controller.prototype); 254 | OneButtonController.prototype.constructor = OneButtonController; 255 | 256 | 257 | /** ### TwoButtonController 258 | * 259 | * Controller with two buttons 260 | * @class TwoButtonController 261 | */ 262 | export function TwoButtonController() { 263 | Controller.call(this); 264 | 265 | // * __name__: 'left' 266 | // * __key__: left arrow 267 | this.addButton(new Button('left', 37)); 268 | 269 | // * __name__: 'right' 270 | // * __key__: righ arrow 271 | this.addButton(new Button('right', 39)); 272 | } 273 | TwoButtonController.prototype = Object.create(Controller.prototype); 274 | TwoButtonController.prototype.constructor = TwoButtonController; 275 | 276 | 277 | /** ### TwoActionButtonsController 278 | * 279 | * Controller with two action buttons (A,B) 280 | * @class TwoActionButtonsController 281 | */ 282 | export function TwoActionButtonsController() { 283 | Controller.call(this); 284 | 285 | // * __name__: 'left' 286 | // * __key__: left arrow 287 | this.addButton(new Button('A', 32)); 288 | 289 | // * __name__: 'right' 290 | // * __key__: righ arrow 291 | this.addButton(new Button('B', 17)); 292 | } 293 | TwoActionButtonsController.prototype = Object.create(Controller.prototype); 294 | TwoActionButtonsController.prototype.constructor = TwoActionButtonsController; 295 | 296 | 297 | /** ### FourButtonController 298 | * 299 | * Controller with four buttons 300 | * @class FourButtonController 301 | */ 302 | export function FourButtonController() { 303 | Controller.call(this); 304 | 305 | // * __name__: 'up' 306 | // * __key__: left arrow 307 | this.addButton(new Button('up', 38)); 308 | 309 | // * __name__: 'left' 310 | // * __key__: left arrow 311 | this.addButton(new Button('left', 37)); 312 | 313 | 314 | // * __name__: 'right' 315 | // * __key__: righ arrow 316 | this.addButton(new Button('right', 39)); 317 | 318 | // * __name__: 'A' 319 | // * __key__: spacebar 320 | this.addButton(new Button('A', 32)); 321 | } 322 | FourButtonController.prototype = Object.create(Controller.prototype); 323 | FourButtonController.prototype.constructor = FourButtonController; 324 | 325 | /** ### FiveButtonController 326 | * 327 | * Controller with five buttons 328 | * @class FiveButtonController 329 | */ 330 | export function FiveButtonController() { 331 | Controller.call(this); 332 | 333 | // * __name__: 'up' 334 | // * __key__: left arrow 335 | this.addButton(new Button('up', 38)); 336 | 337 | // * __name__: 'left' 338 | // * __key__: left arrow 339 | this.addButton(new Button('left', 37)); 340 | 341 | 342 | // * __name__: 'right' 343 | // * __key__: righ arrow 344 | this.addButton(new Button('right', 39)); 345 | 346 | // * __name__: 'down' 347 | // * __key__: down arrow 348 | this.addButton(new Button('down', 40)); 349 | 350 | // * __name__: 'A' 351 | // * __key__: spacebar 352 | this.addButton(new Button('A', 32)); 353 | } 354 | FiveButtonController.prototype = Object.create(Controller.prototype); 355 | FiveButtonController.prototype.constructor = FiveButtonController; 356 | 357 | /** ### SixButtonController 358 | * 359 | * Controller with six buttons 360 | * @class SixButtonController 361 | */ 362 | export function SixButtonController() { 363 | Controller.call(this); 364 | 365 | // * __name__: 'up' 366 | // * __key__: left arrow 367 | this.addButton(new Button('up', 38)); 368 | 369 | // * __name__: 'left' 370 | // * __key__: left arrow 371 | this.addButton(new Button('left', 37)); 372 | 373 | 374 | // * __name__: 'right' 375 | // * __key__: righ arrow 376 | this.addButton(new Button('right', 39)); 377 | 378 | // * __name__: 'down' 379 | // * __key__: down arrow 380 | this.addButton(new Button('down', 40)); 381 | 382 | // * __name__: 'A' 383 | // * __key__: spacebar 384 | this.addButton(new Button('A', 32)); 385 | 386 | // * __name__: 'B' 387 | // * __key__: ctrl 388 | this.addButton(new Button('B', 17)); 389 | } 390 | SixButtonController.prototype = Object.create(Controller.prototype); 391 | SixButtonController.prototype.constructor = SixButtonController; 392 | 393 | /** ### TwoArrowsOneButtonController 394 | * 395 | * Controller with two arrows and one action button 396 | * @class TwoArrowsOneButtonController 397 | */ 398 | export function TwoArrowsOneButtonController() { 399 | Controller.call(this); 400 | 401 | 402 | // * __name__: 'left' 403 | // * __key__: left arrow 404 | this.addButton(new Button('left', 37)); 405 | 406 | 407 | // * __name__: 'right' 408 | // * __key__: righ arrow 409 | this.addButton(new Button('right', 39)); 410 | 411 | 412 | // * __name__: 'A' 413 | // * __key__: spacebar 414 | this.addButton(new Button('A', 32)); 415 | 416 | } 417 | TwoArrowsOneButtonController.prototype = Object.create(Controller.prototype); 418 | TwoArrowsOneButtonController.prototype.constructor = TwoArrowsOneButtonController; 419 | 420 | /** ### TwoArrowsTwoButtonsController 421 | * 422 | * Controller with two arrows and two action buttons 423 | * @class TwoArrowsTwoButtonsController 424 | */ 425 | export function TwoArrowsTwoButtonsController() { 426 | Controller.call(this); 427 | 428 | 429 | // * __name__: 'left' 430 | // * __key__: left arrow 431 | this.addButton(new Button('left', 37)); 432 | 433 | 434 | // * __name__: 'right' 435 | // * __key__: righ arrow 436 | this.addButton(new Button('right', 39)); 437 | 438 | 439 | // * __name__: 'A' 440 | // * __key__: spacebar 441 | this.addButton(new Button('A', 32)); 442 | 443 | // * __name__: 'B' 444 | // * __key__: ctrl 445 | this.addButton(new Button('B', 17)); 446 | 447 | } 448 | TwoArrowsTwoButtonsController.prototype = Object.create(Controller.prototype); 449 | TwoArrowsTwoButtonsController.prototype.constructor = TwoArrowsTwoButtonsController; 450 | 451 | /** ### FourArrowController 452 | * 453 | * Controller with four arrow buttons 454 | * @class FourArrowController 455 | */ 456 | export function FourArrowController() { 457 | Controller.call(this); 458 | 459 | // * __name__: 'up' 460 | // * __key__: left arrow 461 | this.addButton(new Button('up', 38)); 462 | 463 | // * __name__: 'left' 464 | // * __key__: left arrow 465 | this.addButton(new Button('left', 37)); 466 | 467 | 468 | // * __name__: 'right' 469 | // * __key__: righ arrow 470 | this.addButton(new Button('right', 39)); 471 | 472 | // * __name__: 'down' 473 | // * __key__: down arrow 474 | this.addButton(new Button('down', 40)); 475 | } 476 | FourArrowController.prototype = Object.create(Controller.prototype); 477 | FourArrowController.prototype.constructor = FourArrowController; 478 | 479 | /** ### TouchController 480 | * 481 | * This controller has no buttons. Instead it has a touchpad which 482 | * triggers *touchstart*, *touchend*, *touchmove*, *touchcancel*, 483 | * *touchend* events (similar to 484 | * [Touch event types](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent#Touch_event_types)) 485 | * 486 | * The position of the touch is in the `data.position` argument as a 487 | * *x* and *y* with the values between [0, 0] for the left top corner 488 | * and [1, 1] for the bottom right corner ([0.5, 0.5] is the center). 489 | * 490 | * ```javascript 491 | * controller = gamee.controller.requestController('Touch'); 492 | * 493 | * controller.on('touchstart', function(data) { 494 | * if (data.position.x < 0.5 && data.position.y < 0.5) { 495 | * console.log('touch in the top left quadrant'); 496 | * } 497 | * }) 498 | * ``` 499 | * @class TouchController 500 | */ 501 | export function TouchController() { 502 | var self = this; 503 | 504 | Controller.call(this); 505 | 506 | this.on("$touchstart", function (data) { 507 | self.trigger('touchstart', data); 508 | }); 509 | 510 | this.on("$touchend", function (data) { 511 | self.trigger('touchend', data); 512 | }); 513 | 514 | this.on("$touchmove", function (data) { 515 | self.trigger('touchmove', data); 516 | }); 517 | 518 | this.on("$touchleave", function (data) { 519 | self.trigger('touchleave', data); 520 | }); 521 | 522 | this.on("$touchcancel", function (data) { 523 | self.trigger('touchcancel', data); 524 | }); 525 | } 526 | TouchController.prototype = Object.create(TouchController.prototype); 527 | TouchController.prototype.constructor = TouchController; 528 | 529 | /** ### JoystickController 530 | * 531 | * JoystickController emits `change` event, after the position of the 532 | * joystick is changed. 533 | * 534 | * The position of the joystick is in the property `x` and `y`. The 535 | * position on axis is between <-1, 1> (for x -1 is max left 536 | * position, 1 max right position). [0.0, 0.0] is the center. 537 | * 538 | * ```javascript 539 | * joystick = gamee.controller.requestController('Joystick'); 540 | * 541 | * joystick.on('change', function() { 542 | * new_x = joystick.x; 543 | * nex_y = joystick.y; 544 | * }) 545 | * ``` 546 | * @class JoystickController 547 | */ 548 | export function JoystickController() { 549 | var self = this; 550 | 551 | Controller.call(this); 552 | 553 | // x axis 554 | this.x = 0; 555 | // y axis 556 | this.y = 0; 557 | 558 | this.on("$change", function (data) { 559 | self.x = data.position.x; 560 | self.y = data.position.y; 561 | 562 | self.trigger("change", data); 563 | }); 564 | } 565 | JoystickController.prototype = Object.create(Controller.prototype); 566 | JoystickController.prototype.constructor = JoystickController; 567 | 568 | /** ### JoystickButtonController 569 | * 570 | * JoystickButtonController is a `JoystickController` with one button. 571 | * 572 | * ```javascript 573 | * joystick = gamee.controller.requestController('JoystickWithButton'); 574 | * 575 | * joystick.on('change', function() { 576 | * new_x = joystick.x; 577 | * nex_y = joystick.y; 578 | * }) 579 | * 580 | * joystick.buttons.button.on('keydown', callback) 581 | * // or simply 582 | * joystick.on('keydown', callback) 583 | * ``` 584 | * @class JoystickButtonController 585 | */ 586 | export function JoystickButtonController() { 587 | var self = this; 588 | 589 | JoystickController.call(this); 590 | 591 | // * __name__: 'button' 592 | // * __key__: spacebar 593 | this.addButton(new Button('button', 32)); 594 | } 595 | JoystickButtonController.prototype = Object.create(JoystickController.prototype); 596 | JoystickButtonController.prototype.constructor = JoystickButtonController; 597 | -------------------------------------------------------------------------------- /gamee/src/gameeAPI.js: -------------------------------------------------------------------------------- 1 | import { core } from "./core.js" 2 | import { CustomEmitter } from "../libs/shims.js" 3 | import { validateDataType } from "./core.js" 4 | 5 | /** 6 | * gameeAPI module desc 7 | * @module gameeAPI 8 | */ 9 | 10 | /** 11 | * Emit events 12 | * @class GameeEmitter 13 | * @extends CustomEmitter 14 | */ 15 | export var GameeEmitter = function () { 16 | CustomEmitter.call(this); 17 | }; 18 | 19 | /** 20 | * @class Gamee 21 | * @requires core 22 | * 23 | */ 24 | export var Gamee = function (platform) { 25 | /** 26 | * @instance 27 | * 28 | * @fires gameeAPI:GameeEmitter~start 29 | * @fires gameeAPI:GameeEmitter~mute 30 | * @fires gameeAPI:GameeEmitter~unmute 31 | * @fires gameeAPI:GameeEmitter~pause 32 | * @fires gameeAPI:GameeEmitter~unpause 33 | * @fires gameeAPI:GameeEmitter~ghostHide 34 | * @fires gameeAPI:GameeEmitter~ghostShow 35 | */ 36 | this.emitter = new GameeEmitter(); 37 | this._platform = platform; 38 | }; 39 | 40 | Gamee.prototype = (function () { 41 | 42 | var cbError = function (err) { 43 | if (err) { 44 | throw "Error " + err.toString(); 45 | } 46 | }; 47 | 48 | return { 49 | _controller: core.controller, 50 | /** 51 | * gameInit 52 | * @memberof Gamee 53 | * @param {string} controllType 54 | * @param {object} controllOpts 55 | * @param {string[]} capabilities 56 | * @param {gameInitCallback} cb 57 | * @param {boolean} silentMode 58 | */ 59 | gameInit: function (controllType, controllOpts, capabilities, cb, silentMode = false) { 60 | validateDataType(controllType, "string", "controllType", "gamee.updateScore"); 61 | validateDataType(controllOpts, "object", "controllOpts", "gamee.gameInit"); 62 | validateDataType(capabilities, "array", "capabilities", "gamee.gameInit"); 63 | validateDataType(cb, "function", "cb", "gamee.gameInit"); 64 | validateDataType(silentMode, "boolean", "silentMode", "gamee.gameInit"); 65 | var result = core.gameeInit(controllType, controllOpts, capabilities, cb, silentMode); 66 | // cb(null, result); 67 | }, 68 | 69 | /** 70 | * gameLoadingProgress 71 | * 72 | * gamee.gameLoadingProgress() 73 | * 74 | * @memberof Gamee 75 | * @param {number} percentage current loading progress 76 | * @param {Gamee~voidCallback} [opt_cb] 77 | * 78 | */ 79 | gameLoadingProgress: function (percentage, opt_cb) { 80 | validateDataType(percentage, "number", "percentage", "gamee.gameLoadingProgress"); 81 | opt_cb = opt_cb || cbError; 82 | validateDataType(opt_cb, "function", "opt_cb", "gamee.gameLoadingProgress"); 83 | core.gameLoadingProgress(percentage); 84 | opt_cb(null); 85 | }, 86 | 87 | /** 88 | * gameReady 89 | * 90 | * @memberof Gamee 91 | * @param {Gamee~voidCallback} [opt_cb] 92 | */ 93 | gameReady: function (opt_cb) { 94 | opt_cb = opt_cb || cbError; 95 | validateDataType(opt_cb, "function", "opt_cb", "gamee.gameReady"); 96 | core.gameReady(); 97 | opt_cb(null); 98 | }, 99 | 100 | /** 101 | * gameSave 102 | * 103 | * NOTE: There are 2 signatures for this function 104 | * 105 | * gamee.gameSave(data, opt_cb) 106 | * gamee.gameSave(data, opt_share, opt_cb) 107 | * 108 | * @memberof Gamee 109 | * @param {String} data current ingame progress 110 | * @param {Boolean} [opt_share=false] 111 | * @param {Gamee~voidCallback} [opt_cb] 112 | * 113 | */ 114 | gameSave: function (data, opt_share, opt_cb) { 115 | var share = false, cb; 116 | validateDataType(data, "string", "data", "gamee.gameSave"); 117 | if (typeof opt_share === 'function') 118 | opt_cb = opt_share; 119 | else if (typeof opt_share !== "undefined") 120 | validateDataType(opt_share, "boolean", "opt_share", "gamee.gameSave"); 121 | 122 | opt_cb = opt_cb || cbError; 123 | validateDataType(opt_cb, "function", "opt_cb", "gamee.gameSave"); 124 | core.gameSave(data, share); 125 | opt_cb(null); 126 | }, 127 | 128 | /** 129 | * getPlatform 130 | * 131 | * @memberof Gamee 132 | * @returns {string} platform type can be android | ios | web | fb 133 | */ 134 | getPlatform: function () { 135 | return this._platform; 136 | }, 137 | 138 | /** 139 | * updateScore 140 | * 141 | * @memberof Gamee 142 | * @param {number} score 143 | * @param {boolean} [opt_ghostSign=false] If true, score will be updated for ghost instead. 144 | * @param {Gamee~voidCallback} [opt_cb] 145 | */ 146 | updateScore: function (score, opt_ghostSign, opt_cb) { 147 | validateDataType(score, "number", "score", "gamee.updateScore"); 148 | if (typeof opt_ghostSign === "function") 149 | opt_cb = opt_ghostSign; 150 | else if (typeof opt_ghostSign !== "undefined") 151 | validateDataType(opt_ghostSign, "boolean", "opt_ghostSign", "gamee.updateScore"); 152 | 153 | opt_cb = opt_cb || cbError; 154 | validateDataType(opt_cb, "function", "opt_cb", "gamee.updateScore"); 155 | core.updateScore(score, opt_ghostSign); 156 | opt_cb(null); 157 | }, 158 | 159 | /** 160 | * gameOver 161 | * 162 | * @memberof Gamee 163 | * @param {Gamee~ReplayData} [opt_replayData] 164 | * @param {Gamee~voidCallback} [opt_cb] 165 | * @param {Gamee~object} [opt_saveState] 166 | * @param {Gamee~boolean} [opt_hideOverlay] 167 | */ 168 | gameOver: function (opt_replayData, opt_cb, opt_saveState, opt_hideOverlay) { 169 | if (typeof opt_replayData === "function") 170 | opt_cb = opt_replayData; 171 | else if (typeof opt_replayData !== "undefined") 172 | validateDataType(opt_replayData, "object", "opt_replayData", "gamee.gameOver"); 173 | 174 | if (typeof opt_hideOverlay !== 'undefined') { 175 | validateDataType(opt_hideOverlay, "boolean", "opt_hideOverlay", "gamee.gameOver"); 176 | } 177 | 178 | opt_cb = opt_cb || cbError; 179 | validateDataType(opt_cb, "function", "opt_cb", "gamee.gameOver"); 180 | core.gameOver(opt_replayData, opt_saveState, opt_hideOverlay); 181 | opt_cb(null); 182 | }, 183 | 184 | /** 185 | * requestSocialData 186 | * 187 | * @memberof Gamee 188 | * @param {Gamee~requestSocialDataCallback} cb 189 | * @param {number} numberOfPlayers 190 | */ 191 | requestSocial: function (cb, numberOfPlayers) { 192 | validateDataType(cb, "function", "cb", "gamee.requestSocial"); 193 | 194 | // functionality supposed to be removed once we do update for iOS 195 | var data = core.requestSocial(function (error, responseData) { 196 | var modifiedResponse = !responseData.hasOwnProperty("socialData") ? { socialData: responseData } : responseData; 197 | cb(null, modifiedResponse); 198 | }, numberOfPlayers); 199 | 200 | // var data = core.requestSocial(cb); 201 | //cb(null, data); 202 | }, 203 | 204 | /** 205 | * logEvent 206 | * 207 | * @memberof Gamee 208 | * @param {string} eventName 209 | * @param {string} eventValue 210 | */ 211 | logEvent: function (eventName, eventValue) { 212 | 213 | validateDataType(eventName,"string","eventName","gamee.logEvent"); 214 | 215 | if(!eventName || eventName.length > 24){ 216 | console.error("eventName parameter cant be null and can only contain up to 24 characters"); 217 | return 218 | } 219 | 220 | validateDataType(eventValue,"string","eventValue","gamee.logEvent"); 221 | 222 | if(!eventValue || eventValue.length > 160){ 223 | console.error("eventValue parameter cant be null and can only contain up to 160 characters"); 224 | return 225 | } 226 | 227 | core.logEvent(eventName,eventValue); 228 | }, 229 | 230 | /** 231 | * requestBattleData 232 | * 233 | * @memberof Gamee 234 | * @param {Gamee~requestBattleDataDataCallback} cb 235 | */ 236 | requestBattleData: function (cb) { 237 | validateDataType(cb, "function", "cb", "gamee.requestBattleData"); 238 | 239 | core.requestBattleData(cb); 240 | }, 241 | 242 | /** 243 | * requestPlayerReplay 244 | * 245 | * @memberof Gamee 246 | * @param {number} userID 247 | * @param {Gamee~requestPlayerReplayDataCallback} cb 248 | */ 249 | requestPlayerReplay: function (userID, cb) { 250 | 251 | validateDataType(userID, "number", "userID", "gamee.requestPlayerReplay"); 252 | validateDataType(cb, "function", "cb", "gamee.requestPlayerReplay"); 253 | 254 | core.requestPlayerReplay(userID, cb); 255 | }, 256 | 257 | /** 258 | * requestPlayerSaveState 259 | * 260 | * @memberof Gamee 261 | * @param {number} userID 262 | * @param {Gamee~requestPlayerSaveStateDataCallback} cb 263 | */ 264 | requestPlayerSaveState: function (userID, cb) { 265 | 266 | validateDataType(userID, "number", "userID", "gamee.requestPlayerSaveState"); 267 | validateDataType(cb, "function", "cb", "gamee.requestPlayerSaveState"); 268 | 269 | core.requestPlayerSaveState(userID, cb); 270 | }, 271 | 272 | /* 273 | *purchaseItem 274 | *@member of Gamee 275 | *@param {object} purchaseDetails 276 | *@param {Gamee~purchaseItemDataCallback} cb 277 | */ 278 | purchaseItem: function (purchaseDetails,cb){ 279 | 280 | validateDataType(purchaseDetails,"object","purchaseDetails","gamee.purchaseItem"); 281 | validateDataType(cb,"function","cb","gamee.purchaseItem"); 282 | 283 | core.purchaseItemWithCoins(purchaseDetails, cb, true) 284 | }, 285 | 286 | /* 287 | *purchaseItemWithCoins 288 | *@member of Gamee 289 | *@param {object} purchaseDetails 290 | *@param {Gamee~purchaseItemDataCallback} cb 291 | */ 292 | purchaseItemWithCoins: function (purchaseDetails, cb) { 293 | validateDataType(purchaseDetails,"object","purchaseDetails","gamee.purchaseItemWithCoins"); 294 | validateDataType(cb,"function","cb","gamee.purchaseItemWithCoins"); 295 | 296 | core.purchaseItemWithCoins(purchaseDetails, cb) 297 | }, 298 | 299 | /* 300 | *purchaseItemWithGems 301 | *@member of Gamee 302 | *@param {object} purchaseDetails 303 | *@param {Gamee~purchaseItemWithGemsDataCallback} cb 304 | */ 305 | purchaseItemWithGems: function (purchaseDetails,cb) { 306 | 307 | validateDataType(purchaseDetails,"object","purchaseDetails","gamee.purchaseItemWithGems"); 308 | validateDataType(cb,"function","cb","gamee.purchaseItemWithGems"); 309 | 310 | core.purchaseItemWithGems(purchaseDetails,cb) 311 | }, 312 | 313 | /*share 314 | *@member of Gamee 315 | *@param {object} shareDetails 316 | *@param {Gamee~shareDataCallback} cb 317 | */ 318 | share: function (shareDetails,cb){ 319 | validateDataType(shareDetails,"object","shareDetails","gamee.share"); 320 | validateDataType(cb,"function","cb","gamee.share"); 321 | 322 | core.share(shareDetails,cb) 323 | }, 324 | 325 | /* 326 | *loadRewardedVideo 327 | *@member of Gamee 328 | *@param {Gamee~loadRewardedVideo} cb 329 | */ 330 | loadRewardedVideo: function (cb){ 331 | 332 | validateDataType(cb,"function","cb","gamee.loadRewardedVideo"); 333 | core.loadRewardedVideo(cb) 334 | }, 335 | 336 | /* 337 | *showRewardedVideo 338 | *@member of Gamee 339 | *@param{Gamee~showRewardedVideo} cb 340 | */ 341 | showRewardedVideo: function (cb){ 342 | 343 | validateDataType(cb,"function","cb","gamee.showRewardedVideo"); 344 | core.showRewardedVideo(cb) 345 | }, 346 | 347 | /** 348 | *requestPlayerData 349 | *@member of Gamee 350 | *@param{Gamee~requestPlayerData} cb 351 | * @param {number} userID 352 | */ 353 | requestPlayerData: function (cb, userID){ 354 | 355 | validateDataType(cb,"function","cb","gamee.requestPlayerData"); 356 | if (userID !== undefined) { 357 | validateDataType(userID,"number","userId","gamee.requestPlayerData"); 358 | } 359 | core.requestPlayerData(cb, userID) 360 | }, 361 | }; 362 | 363 | /** 364 | * 365 | * @typedef ReplayData 366 | * @param {string} variant 367 | * @param {string} data 368 | */ 369 | 370 | /** 371 | * This callback is displayed as part of the Requester class. 372 | * @callback Gamee~voidCallback 373 | * @param {string} responseCode 374 | */ 375 | 376 | /** 377 | * This callback is displayed as part of the Requester class. 378 | * @callback Gamee~gameInitCallback 379 | * @param {object} data 380 | * @param {string} responseCode 381 | */ 382 | 383 | /** 384 | * This callback is displayed as part of the Requester class. 385 | * @callback Gamee~requestSocialDataCallback 386 | * @param {object} data 387 | * @param {string} responseCode 388 | */ 389 | 390 | })(); 391 | 392 | /** 393 | * Signals that game should start as normal|replay|ghost game. 394 | * Signal means there is no overlay over the game. 395 | * This signal is also being used for game restart. If previous 396 | * instance of the game was running, it should be terminated without 397 | * any additional calls and current progress should be tossed. 398 | * @event gameeAPI:GameeEmitter~start 399 | * @type {object} 400 | * @property {EventDetailStart} detail - Common property of events 401 | */ 402 | 403 | /** 404 | * Data carried with start event. 405 | * @typedef EventDetailStart 406 | * @property {Gamee~voidCallback} callback - called after finishing task 407 | * @property {boolean} [opt_resetState=false] - if true, game must delete current progress and saved progress 408 | * @property {boolean} [opt_replay] - if true, game must run in replay mode 409 | * @property {boolean} [opt_ghostMode] - if true, game must run in ghost mode 410 | */ 411 | 412 | /** 413 | * After that signal, game must silent all sounds immediately. 414 | * Game must remain silent until unmute signal occures. 415 | * @event gameeAPI:GameeEmitter~mute 416 | * @type {object} 417 | * @property {EventDetailVoid} detail - Common property of events 418 | */ 419 | 420 | /** 421 | * After unmute signal, game can play sounds again. 422 | * @event gameeAPI:GameeEmitter~unmute 423 | * @type {object} 424 | * @property {EventDetailVoid} detail - Common property of events 425 | */ 426 | 427 | /** 428 | * Pause signal means there appeared overlay over the game. Player 429 | * is unable to reach the context of the game anymore. So game should 430 | * pause all its acctions immediately. 431 | * @event gameeAPI:GameeEmitter~pause 432 | * @type {object} 433 | * @property {EventDetailVoid} detail - Common property of events 434 | */ 435 | 436 | /** 437 | * Unpause signal means there is no overlay over the game anymore. 438 | * Game should continue with all previous actions. 439 | * @event gameeAPI:GameeEmitter~unpause 440 | * @type {object} 441 | * @property {EventDetailVoid} detail - Common property of events 442 | */ 443 | 444 | /** 445 | * Signal ghostHide can appear only if game is running in ghost mode. 446 | * Game should hide ghost behavior and look like exactly as game without 447 | * the ghost (if this is possible). 448 | * @event gameeAPI:GameeEmitter~ghostHide 449 | * @type {object} 450 | * @property {EventDetailVoid} detail - Common property of events 451 | */ 452 | 453 | /** 454 | * Signal ghostShow can appear only if game is running in ghost mode. 455 | * Game should show ghost again if it was hidden. If ghost died or ended 456 | * while it was hidden, game should point that out, so the player can understand 457 | * why the ghost is not visible anymore. 458 | * @event gameeAPI:GameeEmitter~ghostShow 459 | * @type {object} 460 | * @property {EventDetailVoid} detail - Common property of events 461 | */ 462 | 463 | /** 464 | * Data carried with various events. Contains only callback method. 465 | * @typedef {object} EventDetailVoid 466 | * @property {Gamee~voidCallback} callback - call after finishing task 467 | */ 468 | 469 | /** 470 | * @type {function} 471 | * @param {MyEvent} e - The observable event. 472 | * @listens gameeAPI:GameeEmitter~event:snowball 473 | */ 474 | -------------------------------------------------------------------------------- /gamee/src/index.js: -------------------------------------------------------------------------------- 1 | import { } from "../libs/shims.js" 2 | import { Gamee } from "./gameeAPI.js" 3 | import { core } from "./core.js" 4 | import { PlatformAPI, PlatformBridge, PostMessageBridge, MobileBridge } from "./platform_bridge.js" 5 | 6 | 7 | /** 8 | * Instance of gamee object with API for developers. 9 | * Internal functions becomes private this way 10 | * 11 | * @requires Gamee 12 | */ 13 | export var gamee; 14 | 15 | /** 16 | * Resolves what platform is being used and make instance of platform API. 17 | * 18 | * @requires PlatformBridge 19 | */ 20 | var platformBridge = (function () { 21 | 22 | var platformBridge, platformType = "web"; 23 | 24 | // Reslove Gamee enviroment 25 | /* current user agent */ 26 | var userAgent = navigator.userAgent.toLowerCase(); 27 | 28 | if (/iphone|ipod|ipad/.test(userAgent)) { // test ios device 29 | // user agent is use to determine current enviroment 30 | 31 | // Test if window with game have a parent (loading in iframe) 32 | if (window.self !== window.top) { 33 | platformType = "web"; 34 | } else { 35 | platformType = "ios"; 36 | } 37 | } else if (/gamee\/[0-9\.]+$/.test(userAgent)) { // test android app 38 | // TODO do you really test android like that? 39 | platformType = "android"; 40 | } else if (window.parent) { // TODO doesnt make sence, parent always exists!! 41 | platformType = "web"; 42 | } else if (window.parent && window.parent.gameeSimulator) { // TODO doesnt make sence, parent always exist? 43 | platformType = "web"; 44 | } 45 | 46 | gamee = new Gamee(platformType); 47 | 48 | window.gamee = gamee; 49 | 50 | switch (platformType) { 51 | case "web": 52 | if (window.parent === window) { 53 | console.error("Gamee must run in iframe on web platform"); 54 | } 55 | platformBridge = new PostMessageBridge(window.parent); 56 | break; 57 | case "ios": 58 | platformBridge = new MobileBridge("ios"); 59 | break; 60 | case "android": 61 | platformBridge = new MobileBridge("android"); 62 | break; 63 | default: 64 | throw "Can't identify the platform"; 65 | } 66 | return platformBridge; 67 | })(); 68 | 69 | core.PlatformAPI = PlatformAPI; 70 | core.native = platformBridge; 71 | 72 | PlatformAPI.emitter = gamee.emitter; 73 | 74 | 75 | function loadScript (url, callback) { 76 | // Adding the script tag to the head as suggested before 77 | var head = document.getElementsByTagName('head')[0]; 78 | var script = document.createElement('script'); 79 | script.src = url; 80 | 81 | // Then bind the event to the callback function. 82 | // There are several events for cross browser compatibility. 83 | script.onreadystatechange = callback; 84 | script.onload = callback; 85 | 86 | // Fire the loading 87 | head.appendChild(script); 88 | } 89 | -------------------------------------------------------------------------------- /gamee/src/platform_bridge.js: -------------------------------------------------------------------------------- 1 | import { core } from "./core.js" 2 | 3 | /** 4 | * 5 | * @requires core 6 | * 7 | * @typedef PlatformAPI 8 | * @param {EventTarget} emitter 9 | * @param {function} _pause 10 | * @param {function} _resume 11 | * @param {function} _ghostShow 12 | * @param {function} _ghostHide 13 | * @param {function} _mute 14 | * @param {function} _unmute 15 | * @param {function} _start 16 | */ 17 | export var PlatformAPI = { 18 | emitter: null, 19 | pause: function (cb) { 20 | var event = new CustomEvent('pause', { 21 | detail: { 22 | callback: cb 23 | } 24 | }); 25 | this.emitter.dispatchEvent(event); 26 | }, 27 | resume: function (cb) { 28 | var event = new CustomEvent('resume', { 29 | detail: { 30 | callback: cb 31 | } 32 | }); 33 | this.emitter.dispatchEvent(event); 34 | }, 35 | ghostShow: function (cb) { 36 | var event = new CustomEvent('ghostShow', { 37 | detail: { 38 | callback: cb 39 | } 40 | }); 41 | this.emitter.dispatchEvent(event); 42 | }, 43 | ghostHide: function (cb) { 44 | var event = new CustomEvent('ghostHide', { 45 | detail: { 46 | callback: cb 47 | } 48 | }); 49 | this.emitter.dispatchEvent(event); 50 | }, 51 | mute: function (cb) { 52 | var event = new CustomEvent('mute', { 53 | detail: { 54 | callback: cb 55 | } 56 | }); 57 | this.emitter.dispatchEvent(event); 58 | }, 59 | unmute: function (cb) { 60 | var event = new CustomEvent('unmute', { 61 | detail: { 62 | callback: cb 63 | } 64 | }); 65 | this.emitter.dispatchEvent(event); 66 | }, 67 | start: function (data, cb) { 68 | var event = new CustomEvent('start', { 69 | detail: { 70 | callback: cb 71 | } 72 | }); 73 | 74 | var error = core.startSignal(data); 75 | if (error) { 76 | cb(error); 77 | return; 78 | } 79 | 80 | if (data.replay) 81 | event.detail.opt_replay = true; 82 | if (data.ghostMode) 83 | event.detail.opt_ghostMode = true; 84 | if (data.resetState) 85 | event.detail.opt_resetState = true; 86 | if (data.replayData){ 87 | event.detail.replayData = data.replayData 88 | } 89 | 90 | this.emitter.dispatchEvent(event); 91 | } 92 | }; 93 | 94 | 95 | /** 96 | * @class PlatformBridge 97 | * 98 | */ 99 | export function PlatformBridge() { 100 | this.requests = {}; 101 | this.platform = ""; 102 | this._init(); 103 | } 104 | 105 | PlatformBridge.prototype = { 106 | instCount: 0, 107 | _init: function () { 108 | }, 109 | createRequest: function (method, opt_requestData, opt_callback) { 110 | if (!this.validateMethod(method)) 111 | return; 112 | if (typeof opt_requestData === 'function') { 113 | opt_callback = opt_requestData; 114 | opt_requestData = undefined; 115 | } 116 | 117 | var messId = this.instCount++; 118 | 119 | if (typeof opt_callback !== 'undefined') { 120 | this.requests[messId] = opt_callback; 121 | } 122 | 123 | var preparedObject = { 124 | request: { 125 | method: method, 126 | messageId: messId, 127 | data: null 128 | } 129 | }; 130 | 131 | this.doCall(preparedObject, opt_requestData); 132 | }, 133 | validateMethod: function (method) { 134 | return method === "gameLoadingProgress" ? false : true; 135 | }, 136 | /** 137 | * @abstract 138 | */ 139 | doCall: function (preparedObject, requestData) { 140 | throw "Not implemented"; 141 | }, 142 | _callback: function (id, responseData) { 143 | var cb = this.requests[id]; 144 | delete this.requests[id]; 145 | if (cb) 146 | cb(responseData); 147 | }, 148 | /** 149 | * @abstract 150 | */ 151 | doResponse: function (preparedObject, responseData) { 152 | throw "Not implemented"; 153 | }, 154 | }; 155 | 156 | 157 | /** 158 | * @class PostMessageBridge 159 | * @requires PlatformBridge 160 | */ 161 | export function PostMessageBridge(endpoint) { 162 | this._gameeWin = endpoint; 163 | PlatformBridge.call(this); 164 | this.platform = "web"; 165 | } 166 | 167 | PostMessageBridge.prototype = Object.create(PlatformBridge.prototype); 168 | PostMessageBridge.prototype.constructor = PostMessageBridge; 169 | 170 | PostMessageBridge.prototype._init = function () { 171 | 172 | window.addEventListener('message', function (ev) { 173 | // if(ev.origin === "source we want") 174 | // console.log("_triggerMessage detail: " + ev.detail); 175 | // console.log("_triggerMessage data: " + ev.data); 176 | var data; 177 | if (typeof ev.detail === "object" && typeof ev.detail !== null) { 178 | data = ev.detail; 179 | } else if (typeof ev.data === "object") { 180 | data = ev.data; 181 | } else { 182 | // message is not from native platform 183 | return; 184 | } 185 | 186 | if (!core.isSilentModeEnabled()) { 187 | console.log(JSON.stringify(data, null, 4) + ' data'); 188 | } 189 | // this is request 190 | if (data.request && data.request.method && typeof data.request.messageId !== "undefined") { 191 | this._resolveAPICall(data.request.method, data.request.messageId, data.request.data); 192 | } 193 | // this is reponse 194 | else if (data.response && typeof data.response.messageId !== "undefined") { 195 | if (data.error) 196 | throw data.error; 197 | this._callback(data.response.messageId, data.response.data); 198 | } 199 | // else this message target is not this framework 200 | }.bind(this), false); 201 | }; 202 | 203 | 204 | PostMessageBridge.prototype.doCall = function (preparedObject, requestData) { 205 | if (typeof requestData === "object") { 206 | preparedObject.request.data = requestData || {}; 207 | } 208 | this._gameeWin.postMessage(preparedObject, "*"); 209 | }; 210 | 211 | PostMessageBridge.prototype.doResponse = function (messageId, responseData) { 212 | var preparedObject = { 213 | version: this.version, 214 | response: { 215 | messageId: messageId 216 | } 217 | }; 218 | 219 | if (responseData) 220 | preparedObject.data = responseData; 221 | 222 | this._gameeWin.postMessage(preparedObject, "*"); 223 | }; 224 | 225 | PostMessageBridge.prototype._resolveAPICall = function (method, messageId, opt_data) { 226 | var cb = this.doResponse.bind(this, messageId); 227 | 228 | switch (method) { 229 | case "pause": 230 | PlatformAPI.pause(cb); 231 | break; 232 | case "resume": 233 | PlatformAPI.resume(cb); 234 | break; 235 | case "mute": 236 | PlatformAPI.mute(cb); 237 | break; 238 | case "unmute": 239 | PlatformAPI.unmute(cb); 240 | break; 241 | case "ghostShow": 242 | PlatformAPI.ghostShow(cb); 243 | break; 244 | case "ghostHide": 245 | PlatformAPI.ghostHide(cb); 246 | break; 247 | case "start": 248 | if (!opt_data) { 249 | throw "Method _start missing params"; 250 | } 251 | PlatformAPI.start(opt_data, cb); 252 | break; 253 | default: 254 | if (!core.isSilentModeEnabled()) { 255 | console.error("Unknown method call"); 256 | } 257 | } 258 | }; 259 | 260 | 261 | /** 262 | * @class MobileBridge 263 | * @requires PlatformBridge 264 | * 265 | */ 266 | export function MobileBridge(device) { 267 | this.device = device; 268 | PostMessageBridge.call(this); 269 | this.platform = "mobile"; 270 | } 271 | 272 | MobileBridge.prototype = Object.create(PostMessageBridge.prototype); 273 | MobileBridge.prototype.constructor = MobileBridge; 274 | 275 | MobileBridge.prototype._init = function () { 276 | PostMessageBridge.prototype._init.call(this); 277 | if (this.device === "ios") { 278 | this._gameeWin = webkit.messageHandlers.callbackHandler; 279 | } else if (this.device === "android") { 280 | this._gameeWin = _toDevice; 281 | } else { 282 | throw "Unknown device used in webkit bridge"; 283 | } 284 | 285 | window._triggerMessage = function (data) { 286 | try { 287 | data = JSON.parse(data); // message is custom message from IOS/android platform 288 | } catch (err) { 289 | throw "Couldn't parse message from native app: \n" + data + "\n" + err; 290 | } 291 | if (!core.isSilentModeEnabled()) { 292 | console.log(JSON.stringify(data, null, 4)); 293 | } 294 | this.dispatchEvent(new CustomEvent("message", { detail: data })); 295 | }.bind(window); 296 | 297 | }; 298 | 299 | MobileBridge.prototype.doCall = function (preparedObject, requestData) { 300 | if (typeof requestData === "object") { 301 | preparedObject.request.data = requestData || {}; 302 | } 303 | 304 | if (this.device === "android") // stringify data for android devices, but not for ios 305 | preparedObject = JSON.stringify(preparedObject); 306 | 307 | this._gameeWin.postMessage(preparedObject, "*"); 308 | }; 309 | -------------------------------------------------------------------------------- /gamee/tests/api.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | import { gamee } from '../dist/gamee-js.min.js' 4 | 5 | describe('Framework API', () => { 6 | 7 | it('gamee.getPlatform returns platform', () => { 8 | assert.equal(gamee.getPlatform(), "web" || "ios" || "android" || "fb"); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /gamee/tests/dummy.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('Dummy test pass', () => { 4 | it('should add correctly', () => { 5 | assert.equal(2, 2); 6 | }); 7 | }); 8 | 9 | describe('Dummy test fail', () => { 10 | it('should add correctly', () => { 11 | assert.equal(3, 2); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /gamee/tests/framework-integrity.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | import { gamee } from '../dist/gamee-js.min.js' 4 | 5 | describe('Test of properties of the gamee object', () => { 6 | // gamee object 7 | it('gamee variable is an object', () => { 8 | assert.equal(typeof gamee, "object"); 9 | }); 10 | 11 | // basic properties 12 | it('gamee.gameInit is a function', () => { 13 | assert.equal(typeof gamee.gameInit, "function"); 14 | }); 15 | it('gamee.gameReady is a function', () => { 16 | assert.equal(typeof gamee.gameReady, "function"); 17 | }); 18 | it('gamee.updateScore is a function', () => { 19 | assert.equal(typeof gamee.updateScore, "function"); 20 | }); 21 | it('gamee.gameOver is a function', () => { 22 | assert.equal(typeof gamee.gameOver, "function"); 23 | }); 24 | it('gamee.emitter is an object', () => { 25 | assert.equal(typeof gamee.emitter, "object"); 26 | }); 27 | 28 | // advanced properties 29 | it('gamee.getPlatform is a function', () => { 30 | assert.equal(typeof gamee.getPlatform, "function"); 31 | }); 32 | it('gamee.gameLoadingProgress is a function', () => { 33 | assert.equal(typeof gamee.gameLoadingProgress, "function"); 34 | }); 35 | it('gamee.gameSave is a function', () => { 36 | assert.equal(typeof gamee.gameSave, "function"); 37 | }); 38 | it('gamee.requestSocial is a function', () => { 39 | assert.equal(typeof gamee.requestSocial, "function"); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /gamee/tests/index.js: -------------------------------------------------------------------------------- 1 | // Skip execution in Node 2 | if (module.hot) { 3 | const context = require.context( 4 | 'mocha-loader!./', // Process through mocha-loader 5 | false, // Skip recursive processing 6 | /\.test.js$/ // Pick only files ending with .test.js 7 | ); 8 | 9 | // Execute each test suite 10 | context.keys().forEach(context); 11 | } 12 | -------------------------------------------------------------------------------- /jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "plugins": [ 6 | "plugins/markdown" 7 | ], 8 | "private": false, 9 | "templates": { 10 | "cleverLinks": false, 11 | "monospaceLinks": true, 12 | "dateFormat": "ddd MMM Do YYYY", 13 | "outputSourceFiles": false, 14 | "outputSourcePath": false, 15 | "systemName": "GAMEE", 16 | "footer": "", 17 | "copyright": "", 18 | "linenums": true, 19 | "collapseSymbols": false, 20 | "inverseNav": true, 21 | "highlightTutorialCode": true, 22 | "default": { 23 | "outputSourceFiles": false, 24 | "includeDate": false 25 | } 26 | }, 27 | "markdown": { 28 | "parser": "gfm", 29 | "hardwrap": true 30 | }, 31 | "opts": { 32 | "readme": "README.md", 33 | "encoding": "utf8" 34 | } 35 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gamee-js", 3 | "version": "2.4.0", 4 | "description": "Gamee JS SDK", 5 | "main": "gamee/src/index.js", 6 | "scripts": { 7 | "test": "mocha tests", 8 | "test:mocha": "mocha tests", 9 | "test:mocha:watch": "webpack-dev-server --hot --config config/webpack.mocha.js", 10 | "b:dist": "webpack --env=prod --config config/webpack.config.js --progress --profile --colors", 11 | "watch": "webpack --env=prod --config config/webpack.config.js --progress --profile --colors --watch", 12 | "start": "start npm run watch && start npm run test:mocha:watch" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/gameeapp/gamee-js/issues" 16 | }, 17 | "homepage": "https://github.com/gameeapp/gamee-js", 18 | "devDependencies": { 19 | "babel-core": "^6.25.0", 20 | "babel-loader": "^7.0.0", 21 | "babel-preset-env": "^1.5.1", 22 | "banner-webpack-plugin": "^0.2.3", 23 | "html-webpack-plugin": "^2.29.0", 24 | "mocha": "^3.4.2", 25 | "mocha-loader": "^1.1.1", 26 | "webpack": "^2.7.0", 27 | "webpack-dev-server": "^2.11.3", 28 | "webpack-merge": "^4.1.0" 29 | } 30 | } 31 | --------------------------------------------------------------------------------