├── Game.rpgproject ├── .vscode └── settings.json ├── src ├── rpg_windows.js ├── core │ ├── Stage.js │ ├── pixi.js │ ├── ScreenSprite.js │ ├── native.js │ ├── Utils.js │ ├── Weather.js │ ├── tone.js │ ├── TilingSprite.js │ ├── WindowLayer.js │ ├── cache.js │ ├── misc.js │ ├── TouchInput.js │ ├── Sprite.js │ ├── Window.js │ └── Input.js ├── rpg_core.js └── windows │ └── Window_Base.js ├── .gitignore ├── js ├── main.js ├── plugins.js └── libs │ ├── iphone-inline-video.browser.js │ ├── lz-string.js │ ├── fpsmeter.js │ └── pixi-picture.js ├── README.md ├── package.json ├── index.html └── webpack.config.js /Game.rpgproject: -------------------------------------------------------------------------------- 1 | RPGMV 1.5.1 -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false 3 | } -------------------------------------------------------------------------------- /src/rpg_windows.js: -------------------------------------------------------------------------------- 1 | import { Window_Base } from './windows/Window_Base' 2 | 3 | window.Window_Base = Window_Base 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | audio 3 | data 4 | fonts 5 | icon 6 | img 7 | movies 8 | js/plugins 9 | js/libs/dragonbones 10 | build 11 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // main.js 3 | //============================================================================= 4 | 5 | PluginManager.setup($plugins); 6 | 7 | window.onload = function() { 8 | SceneManager.run(Scene_Boot); 9 | }; 10 | -------------------------------------------------------------------------------- /js/plugins.js: -------------------------------------------------------------------------------- 1 | // Generated by RPG Maker. 2 | // Do not edit this file directly. 3 | var $plugins = [ 4 | { 5 | name: 'Community_Basic', 6 | status: true, 7 | description: 'Basic plugin for manipulating important parameters.', 8 | parameters: { 9 | cacheLimit: '20', 10 | screenWidth: '816', 11 | screenHeight: '624', 12 | changeWindowWidthTo: '', 13 | changeWindowHeightTo: '', 14 | renderingMode: 'auto', 15 | alwaysDash: 'off' 16 | } 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RPG Maker MV to Modern JS 2 | RPG Maker MV refactor to modern JavaScript ES6+ 3 | 4 | It is just like the title says. I'm preparing a blog post on why am I doing this. In short I think ES6 classes are way more readable than thousands of prototype lines. Also, it's a good case study. In the future I plan on also adding Typescript. 5 | 6 | ## Install 7 | TL;DR: Make a new project in RPG Maker MV, get all files from this repo and copy over this new project folder. 8 | 9 | Step by step: 10 | * Make a new project in RPG Maker MV 11 | * In your terminal: 12 | ```bash 13 | cd 14 | git clone git@github.com:bsides/rpgmakermv-modernjs.git 15 | ``` 16 | * Copy everything from the folder `rpgmakermv-modernjs` to your project's root folder 17 | * In your terminal, run `yarn` 18 | 19 | ## Develop 20 | `yarn dev` will make it watch for changes and compile everything you need 21 | -------------------------------------------------------------------------------- /src/core/Stage.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The root object of the display tree. 4 | * 5 | * @class Stage 6 | * @constructor 7 | */ 8 | class Stage extends PIXI.Container { 9 | constructor() { 10 | super() 11 | } 12 | initialize() { 13 | // super, for god sakes 14 | PIXI.Container.call(this) 15 | 16 | // The interactive flag causes a memory leak. 17 | this.interactive = false 18 | } 19 | 20 | /** 21 | * [read-only] The array of children of the stage. 22 | * 23 | * @property children 24 | * @type Array 25 | */ 26 | 27 | /** 28 | * Adds a child to the container. 29 | * 30 | * @method addChild 31 | * @param {Object} child The child to add 32 | * @return {Object} The child that was added 33 | */ 34 | 35 | /** 36 | * Adds a child to the container at a specified index. 37 | * 38 | * @method addChildAt 39 | * @param {Object} child The child to add 40 | * @param {Number} index The index to place the child in 41 | * @return {Object} The child that was added 42 | */ 43 | 44 | /** 45 | * Removes a child from the container. 46 | * 47 | * @method removeChild 48 | * @param {Object} child The child to remove 49 | * @return {Object} The child that was removed 50 | */ 51 | 52 | /** 53 | * Removes a child from the specified index position. 54 | * 55 | * @method removeChildAt 56 | * @param {Number} index The index to get the child from 57 | * @return {Object} The child that was removed 58 | */ 59 | } 60 | 61 | export { Stage } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpgmakermv-modernjs", 3 | "version": "1.0.0", 4 | "description": "RPG Maker MV refactor to modern JavaScript ES6+", 5 | "main": "index.html", 6 | "watch": { 7 | "build": "js/core/*.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "watchpack": "webpack --progress --watch", 12 | "startpack": "webpack-dev-server --open", 13 | "buildpack": "webpack", 14 | "babel": "babel src --out-dir build --watch --source-maps", 15 | "build": "webpack --env build", 16 | "dev": "webpack --progress --colors --watch --env dev", 17 | "start": "webpack-dev-server" 18 | }, 19 | "keywords": [ 20 | "rpg maker mv", 21 | "rpg maker", 22 | "rpgmaker", 23 | "refactor", 24 | "rmmv" 25 | ], 26 | "author": "Rafael BSIDES Pereira ", 27 | "license": "MIT", 28 | "devDependencies": { 29 | "babel-cli": "^6.26.0", 30 | "babel-core": "^6.26.0", 31 | "babel-eslint": "^8.0.0", 32 | "babel-loader": "^7.1.2", 33 | "babel-plugin-add-module-exports": "^0.2.1", 34 | "babel-plugin-transform-class-properties": "^6.24.1", 35 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 36 | "babel-preset-env": "^1.6.0", 37 | "babel-preset-es2015": "^6.24.1", 38 | "case-sensitive-paths-webpack-plugin": "^2.1.1", 39 | "eslint": "^4.7.0", 40 | "eslint-loader": "^1.9.0", 41 | "exports-loader": "^0.6.4", 42 | "webpack": "^3.6.0", 43 | "webpack-dev-server": "^2.8.2", 44 | "webpack-glob-entry": "^2.1.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/core/pixi.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The point class. 4 | * 5 | * @class Point 6 | * @constructor 7 | * @param {Number} x The x coordinate 8 | * @param {Number} y The y coordinate 9 | */ 10 | class Point extends PIXI.Point { 11 | constructor(x, y) { 12 | super(x, y) 13 | } 14 | } 15 | 16 | //----------------------------------------------------------------------------- 17 | /** 18 | * The rectangle class. 19 | * 20 | * @class Rectangle 21 | * @constructor 22 | * @param {Number} x The x coordinate for the upper-left corner 23 | * @param {Number} y The y coordinate for the upper-left corner 24 | * @param {Number} width The width of the rectangle 25 | * @param {Number} height The height of the rectangle 26 | */ 27 | 28 | class Rectangle extends PIXI.Rectangle { 29 | /** 30 | * @static 31 | * @property emptyRectangle 32 | * @type Rectangle 33 | * @private 34 | */ 35 | static emptyRectangle = new Rectangle(0, 0, 0, 0) 36 | /** 37 | * The x coordinate for the upper-left corner. 38 | * 39 | * @property x 40 | * @type Number 41 | */ 42 | 43 | /** 44 | * The y coordinate for the upper-left corner. 45 | * 46 | * @property y 47 | * @type Number 48 | */ 49 | 50 | /** 51 | * The width of the rectangle. 52 | * 53 | * @property width 54 | * @type Number 55 | */ 56 | 57 | /** 58 | * The height of the rectangle. 59 | * 60 | * @property height 61 | * @type Number 62 | */ 63 | 64 | constructor(x, y, width, height) { 65 | super(x, y, width, height) 66 | } 67 | } 68 | 69 | export { Point, Rectangle } 70 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | RPG Maker MV to ModernJS 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global __dirname, require, module*/ 2 | 3 | const webpack = require('webpack') 4 | const path = require('path') 5 | const entry = require('webpack-glob-entry') 6 | const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin') 7 | 8 | let srcPath = __dirname + '/src' 9 | 10 | const config = { 11 | entry: { 12 | rpg_core: srcPath + '/rpg_core.js', 13 | rpg_windows: srcPath + '/rpg_windows.js' 14 | }, 15 | devtool: 'cheap-source-map', 16 | output: { 17 | path: __dirname + '/build', 18 | pathinfo: true, 19 | filename: '[name].js', 20 | // library: ['[name]'], 21 | // libraryExport: '[name]', 22 | // libraryTarget: 'window', 23 | libraryTarget: 'umd', 24 | umdNamedDefine: true 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.(js)$/, 30 | loader: 'babel-loader', 31 | exclude: /(node_modules|bower_components)/, 32 | options: { 33 | presets: [ 34 | [ 35 | 'env', 36 | { 37 | targets: { 38 | browser: 'current' 39 | }, 40 | modules: false 41 | } 42 | ] 43 | ], 44 | cacheDirectory: true, 45 | plugins: [ 46 | 'babel-plugin-add-module-exports', 47 | 'transform-class-properties', 48 | 'transform-object-rest-spread' 49 | ] 50 | } 51 | } 52 | ] 53 | }, 54 | resolve: { 55 | modules: [path.resolve('./node_modules'), path.resolve('./src')], 56 | extensions: ['.json', '.js'] 57 | }, 58 | devServer: { 59 | contentBase: path.join(__dirname), 60 | compress: true, 61 | port: 9000, 62 | overlay: { 63 | warnings: true, 64 | errors: true 65 | }, 66 | watchContentBase: true, 67 | watchOptions: { 68 | poll: true 69 | } 70 | }, 71 | externals: { 72 | 'nw.gui': '' 73 | }, 74 | plugins: [new webpack.NamedModulesPlugin(), new CaseSensitivePathsPlugin()] 75 | } 76 | 77 | module.exports = config 78 | -------------------------------------------------------------------------------- /src/rpg_core.js: -------------------------------------------------------------------------------- 1 | import { Bitmap } from './core/Bitmap' 2 | import { Utils, JsExtensions } from './core/Utils' 3 | import { CacheEntry, CacheMap, ImageCache, RequestQueue } from './core/cache' 4 | import './core/native' 5 | import { Point, Rectangle } from './core/pixi' 6 | import { Graphics } from './core/Graphics' 7 | import { Input } from './core/Input' 8 | import { TouchInput } from './core/TouchInput' 9 | import { Sprite } from './core/Sprite' 10 | import { TilingSprite } from './core/TilingSprite' 11 | import { Tilemap, ShaderTilemap } from './core/tilemap' 12 | import { ScreenSprite } from './core/ScreenSprite' 13 | import { Window } from './core/Window' 14 | import { WindowLayer } from './core/WindowLayer' 15 | import { Weather } from './core/Weather' 16 | import { ToneFilter, ToneSprite } from './core/tone' 17 | import { Stage } from './core/Stage' 18 | import { WebAudio, Html5Audio } from './core/audio' 19 | import { JsonEx, Decrypter, ResourceHandler } from './core/misc' 20 | 21 | //============================================================================= 22 | // rpg_core.js v1.5.1 23 | //============================================================================= 24 | 25 | window.Bitmap = Bitmap 26 | 27 | window.Utils = Utils 28 | window.JsExtensions = JsExtensions 29 | 30 | window.CacheEntry = CacheEntry 31 | window.CacheMap = CacheMap 32 | window.ImageCache = ImageCache 33 | window.RequestQueue = RequestQueue 34 | 35 | window.Point = Point 36 | window.Rectangle = Rectangle 37 | 38 | window.Graphics = Graphics 39 | 40 | window.Input = Input 41 | 42 | window.TouchInput = TouchInput 43 | 44 | window.Sprite = Sprite 45 | window.TilingSprite = TilingSprite 46 | 47 | window.Tilemap = Tilemap 48 | window.ShaderTilemap = ShaderTilemap 49 | 50 | window.ScreenSprite = ScreenSprite 51 | 52 | window.Window = Window 53 | 54 | window.WindowLayer = WindowLayer 55 | 56 | window.Weather = Weather 57 | 58 | window.ToneFilter = ToneFilter 59 | window.ToneSprite = ToneSprite 60 | 61 | window.Stage = Stage 62 | 63 | window.WebAudio = WebAudio 64 | window.Html5Audio = Html5Audio 65 | 66 | window.JsonEx = JsonEx 67 | window.Decrypter = Decrypter 68 | window.ResourceHandler = ResourceHandler 69 | -------------------------------------------------------------------------------- /src/core/ScreenSprite.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The sprite which covers the entire game screen. 4 | * 5 | * @class ScreenSprite 6 | * @constructor 7 | */ 8 | class ScreenSprite extends PIXI.Container { 9 | constructor() { 10 | super() 11 | this.initialize.apply(this, arguments) 12 | } 13 | initialize() { 14 | this._graphics = new PIXI.Graphics() 15 | this.addChild(this._graphics) 16 | this.opacity = 0 17 | 18 | this._red = -1 19 | this._green = -1 20 | this._blue = -1 21 | this._colorText = '' 22 | this.setBlack() 23 | } 24 | 25 | /** 26 | * The opacity of the sprite (0 to 255). 27 | * 28 | * @property opacity 29 | * @type Number 30 | */ 31 | get opacity() { 32 | return this.alpha * 255 33 | } 34 | set opacity(value) { 35 | this.alpha = value.clamp(0, 255) / 255 36 | } 37 | 38 | static YEPWarned = false 39 | static warnYep() { 40 | if (!ScreenSprite.YEPWarned) { 41 | console.log( 42 | 'Deprecation warning. Please update YEP_CoreEngine. ScreenSprite is not a sprite, it has graphics inside.' 43 | ) 44 | ScreenSprite.YEPWarned = true 45 | } 46 | } 47 | 48 | get anchor() { 49 | ScreenSprite.warnYep() 50 | this.scale.x = 1 51 | this.scale.y = 1 52 | return { x: 0, y: 0 } 53 | } 54 | set anchor(value) { 55 | this.alpha = value.clamp(0, 255) / 255 56 | } 57 | 58 | get blendMode() { 59 | return this._graphics.blendMode 60 | } 61 | set blendMode(value) { 62 | this._graphics.blendMode = value 63 | } 64 | 65 | /** 66 | * Sets black to the color of the screen sprite. 67 | * 68 | * @method setBlack 69 | */ 70 | setBlack() { 71 | this.setColor(0, 0, 0) 72 | } 73 | 74 | /** 75 | * Sets white to the color of the screen sprite. 76 | * 77 | * @method setWhite 78 | */ 79 | setWhite() { 80 | this.setColor(255, 255, 255) 81 | } 82 | 83 | /** 84 | * Sets the color of the screen sprite by values. 85 | * 86 | * @method setColor 87 | * @param {Number} r The red value in the range (0, 255) 88 | * @param {Number} g The green value in the range (0, 255) 89 | * @param {Number} b The blue value in the range (0, 255) 90 | */ 91 | setColor(r, g, b) { 92 | if (this._red !== r || this._green !== g || this._blue !== b) { 93 | r = Math.round(r || 0).clamp(0, 255) 94 | g = Math.round(g || 0).clamp(0, 255) 95 | b = Math.round(b || 0).clamp(0, 255) 96 | this._red = r 97 | this._green = g 98 | this._blue = b 99 | this._colorText = Utils.rgbToCssColor(r, g, b) 100 | 101 | const graphics = this._graphics 102 | graphics.clear() 103 | const intColor = (r << 16) | (g << 8) | b 104 | graphics.beginFill(intColor, 1) 105 | //whole screen with zoom. BWAHAHAHAHA 106 | graphics.drawRect( 107 | -Graphics.width * 5, 108 | -Graphics.height * 5, 109 | Graphics.width * 10, 110 | Graphics.height * 10 111 | ) 112 | } 113 | } 114 | } 115 | 116 | export { ScreenSprite } 117 | -------------------------------------------------------------------------------- /js/libs/iphone-inline-video.browser.js: -------------------------------------------------------------------------------- 1 | /*! npm.im/iphone-inline-video */ 2 | var makeVideoPlayableInline=function(){"use strict";/*! npm.im/intervalometer */ 3 | function e(e,n,r,i){function t(r){d=n(t,i),e(r-(a||r)),a=r}var d,a;return{start:function(){d||t(0)},stop:function(){r(d),d=null,a=0}}}function n(n){return e(n,requestAnimationFrame,cancelAnimationFrame)}function r(e,n,r,i){function t(n){Boolean(e[r])===Boolean(i)&&n.stopImmediatePropagation(),delete e[r]}return e.addEventListener(n,t,!1),t}function i(e,n,r,i){function t(){return r[n]}function d(e){r[n]=e}i&&d(e[n]),Object.defineProperty(e,n,{get:t,set:d})}function t(e,n,r){r.addEventListener(n,function(){return e.dispatchEvent(new Event(n))})}function d(e,n){Promise.resolve().then(function(){e.dispatchEvent(new Event(n))})}function a(e){var n=new Audio;return t(e,"play",n),t(e,"playing",n),t(e,"pause",n),n.crossOrigin=e.crossOrigin,n.src=e.src||e.currentSrc||"data:",n}function o(e,n,r){(m||0)+200=e.video.duration}function s(e){var n=this;n.video.readyState>=n.video.HAVE_FUTURE_DATA?(n.hasAudio||(n.driver.currentTime=n.video.currentTime+e*n.video.playbackRate/1e3,n.video.loop&&u(n)&&(n.driver.currentTime=0)),o(n.video,n.driver.currentTime)):n.video.networkState!==n.video.NETWORK_IDLE||n.video.buffered.length||n.video.load(),n.video.ended&&(delete n.video[g],n.video.pause(!0))}function c(){var e=this,n=e[b];return e.webkitDisplayingFullscreen?void e[E]():("data:"!==n.driver.src&&n.driver.src!==e.src&&(o(e,0,!0),n.driver.src=e.src),void(e.paused&&(n.paused=!1,e.buffered.length||e.load(),n.driver.play(),n.updater.start(),n.hasAudio||(d(e,"play"),n.video.readyState>=n.video.HAVE_ENOUGH_DATA&&d(e,"playing")))))}function v(e){var n=this,r=n[b];r.driver.pause(),r.updater.stop(),n.webkitDisplayingFullscreen&&n[T](),r.paused&&!e||(r.paused=!0,r.hasAudio||d(n,"pause"),n.ended&&(n[g]=!0,d(n,"ended")))}function p(e,r){var i=e[b]={};i.paused=!0,i.hasAudio=r,i.video=e,i.updater=n(s.bind(i)),r?i.driver=a(e):(e.addEventListener("canplay",function(){e.paused||d(e,"playing")}),i.driver={src:e.src||e.currentSrc||"data:",muted:!0,paused:!0,pause:function(){i.driver.paused=!0},play:function(){i.driver.paused=!1,u(i)&&o(e,0)},get ended(){return u(i)}}),e.addEventListener("emptied",function(){var n=!i.driver.src||"data:"===i.driver.src;i.driver.src&&i.driver.src!==e.src&&(o(e,0,!0),i.driver.src=e.src,n?i.driver.play():i.updater.stop())},!1),e.addEventListener("webkitbeginfullscreen",function(){e.paused?r&&!i.driver.buffered.length&&i.driver.load():(e.pause(),e[E]())}),r&&(e.addEventListener("webkitendfullscreen",function(){i.driver.currentTime=e.currentTime}),e.addEventListener("seeking",function(){A.indexOf(100*e.currentTime|0)<0&&(i.driver.currentTime=e.currentTime)}))}function l(e){var n=e[b];e[E]=e.play,e[T]=e.pause,e.play=c,e.pause=v,i(e,"paused",n.driver),i(e,"muted",n.driver,!0),i(e,"playbackRate",n.driver,!0),i(e,"ended",n.driver),i(e,"loop",n.driver,!0),r(e,"seeking"),r(e,"seeked"),r(e,"timeupdate",g,!1),r(e,"ended",g,!1)}function f(e,n,r){void 0===n&&(n=!0),void 0===r&&(r=!0),r&&!h||e[b]||(p(e,n),l(e),e.classList.add("IIV"),!n&&e.autoplay&&e.play(),/iPhone|iPod|iPad/.test(navigator.platform)||console.warn("iphone-inline-video is not guaranteed to work in emulated environments"))}var m,y="undefined"==typeof Symbol?function(e){return"@"+(e||"@")+Math.random()}:Symbol,h="object-fit"in document.head.style&&/iPhone|iPod/i.test(navigator.userAgent)&&!matchMedia("(-webkit-video-playable-inline)").matches,b=y(),g=y(),E=y("nativeplay"),T=y("nativepause"),A=[],k=0;return f.isWhitelisted=h,f}(); -------------------------------------------------------------------------------- /src/core/native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a number whose value is limited to the given range. 3 | * 4 | * @method Number.prototype.clamp 5 | * @param {Number} min The lower boundary 6 | * @param {Number} max The upper boundary 7 | * @return {Number} A number in the range (min, max) 8 | */ 9 | Number.prototype.clamp = function(min, max) { 10 | return Math.min(Math.max(this, min), max) 11 | } 12 | 13 | /** 14 | * Returns a modulo value which is always positive. 15 | * 16 | * @method Number.prototype.mod 17 | * @param {Number} n The divisor 18 | * @return {Number} A modulo value 19 | */ 20 | Number.prototype.mod = function(n) { 21 | return (this % n + n) % n 22 | } 23 | 24 | /** 25 | * Replaces %1, %2 and so on in the string to the arguments. 26 | * 27 | * @method String.prototype.format 28 | * @param {Any} ...args The objects to format 29 | * @return {String} A formatted string 30 | */ 31 | String.prototype.format = function() { 32 | var args = arguments 33 | return this.replace(/%([0-9]+)/g, function(s, n) { 34 | return args[Number(n) - 1] 35 | }) 36 | } 37 | 38 | /** 39 | * Makes a number string with leading zeros. 40 | * 41 | * @method String.prototype.padZero 42 | * @param {Number} length The length of the output string 43 | * @return {String} A string with leading zeros 44 | */ 45 | String.prototype.padZero = function(length) { 46 | var s = this 47 | while (s.length < length) { 48 | s = '0' + s 49 | } 50 | return s 51 | } 52 | 53 | /** 54 | * Makes a number string with leading zeros. 55 | * 56 | * @method Number.prototype.padZero 57 | * @param {Number} length The length of the output string 58 | * @return {String} A string with leading zeros 59 | */ 60 | Number.prototype.padZero = function(length) { 61 | return String(this).padZero(length) 62 | } 63 | 64 | Object.defineProperties(Array.prototype, { 65 | /** 66 | * Checks whether the two arrays are same. 67 | * 68 | * @method Array.prototype.equals 69 | * @param {Array} array The array to compare to 70 | * @return {Boolean} True if the two arrays are same 71 | */ 72 | equals: { 73 | enumerable: false, 74 | value: function(array) { 75 | if (!array || this.length !== array.length) { 76 | return false 77 | } 78 | for (var i = 0; i < this.length; i++) { 79 | if (this[i] instanceof Array && array[i] instanceof Array) { 80 | if (!this[i].equals(array[i])) { 81 | return false 82 | } 83 | } else if (this[i] !== array[i]) { 84 | return false 85 | } 86 | } 87 | return true 88 | } 89 | }, 90 | /** 91 | * Makes a shallow copy of the array. 92 | * 93 | * @method Array.prototype.clone 94 | * @return {Array} A shallow copy of the array 95 | */ 96 | clone: { 97 | enumerable: false, 98 | value: function() { 99 | return this.slice(0) 100 | } 101 | }, 102 | /** 103 | * Checks whether the array contains a given element. 104 | * 105 | * @method Array.prototype.contains 106 | * @param {Any} element The element to search for 107 | * @return {Boolean} True if the array contains a given element 108 | */ 109 | contains: { 110 | enumerable: false, 111 | value: function(element) { 112 | return this.indexOf(element) >= 0 113 | } 114 | } 115 | }) 116 | 117 | /** 118 | * Checks whether the string contains a given string. 119 | * 120 | * @method String.prototype.contains 121 | * @param {String} string The string to search for 122 | * @return {Boolean} True if the string contains a given string 123 | */ 124 | String.prototype.contains = function(string) { 125 | return this.indexOf(string) >= 0 126 | } 127 | 128 | /** 129 | * Generates a random integer in the range (0, max-1). 130 | * 131 | * @static 132 | * @method Math.randomInt 133 | * @param {Number} max The upper boundary (excluded) 134 | * @return {Number} A random integer 135 | */ 136 | Math.randomInt = function(max) { 137 | return Math.floor(max * Math.random()) 138 | } 139 | -------------------------------------------------------------------------------- /src/core/Utils.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * This is not a class, but contains some methods that will be added to the 4 | * standard Javascript objects. 5 | * 6 | * @class JsExtensions 7 | */ 8 | const JsExtensions = function JsExtensions() { 9 | throw new Error('This is not a class') 10 | } 11 | 12 | //----------------------------------------------------------------------------- 13 | /** 14 | * The static class that defines utility methods. 15 | * 16 | * @class Utils 17 | */ 18 | 19 | class Utils { 20 | /** 21 | * The name of the RPG Maker. 'MV' in the current version. 22 | * 23 | * @static 24 | * @property RPGMAKER_NAME 25 | * @type String 26 | * @final 27 | */ 28 | static RPGMAKER_NAME = 'MV' 29 | 30 | /** 31 | * The version of the RPG Maker. 32 | * 33 | * @static 34 | * @property RPGMAKER_VERSION 35 | * @type String 36 | * @final 37 | */ 38 | static RPGMAKER_VERSION = '1.5.1' 39 | 40 | /** 41 | * Checks whether the option is in the query string. 42 | * 43 | * @static 44 | * @method isOptionValid 45 | * @param {String} name The option name 46 | * @return {Boolean} True if the option is in the query string 47 | */ 48 | static isOptionValid(name) { 49 | return location.search 50 | .slice(1) 51 | .split('&') 52 | .contains(name) 53 | } 54 | 55 | /** 56 | * Checks whether the platform is NW.js. 57 | * 58 | * @static 59 | * @method isNwjs 60 | * @return {Boolean} True if the platform is NW.js 61 | */ 62 | static isNwjs() { 63 | return false 64 | return typeof require === 'function' && typeof process === 'object' 65 | } 66 | 67 | /** 68 | * Checks whether the platform is a mobile device. 69 | * 70 | * @static 71 | * @method isMobileDevice 72 | * @return {Boolean} True if the platform is a mobile device 73 | */ 74 | static isMobileDevice() { 75 | const r = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i 76 | return !!navigator.userAgent.match(r) 77 | } 78 | 79 | /** 80 | * Checks whether the browser is Mobile Safari. 81 | * 82 | * @static 83 | * @method isMobileSafari 84 | * @return {Boolean} True if the browser is Mobile Safari 85 | */ 86 | static isMobileSafari() { 87 | const agent = navigator.userAgent 88 | return !!( 89 | agent.match(/iPhone|iPad|iPod/) && 90 | agent.match(/AppleWebKit/) && 91 | !agent.match('CriOS') 92 | ) 93 | } 94 | 95 | /** 96 | * Checks whether the browser is Android Chrome. 97 | * 98 | * @static 99 | * @method isAndroidChrome 100 | * @return {Boolean} True if the browser is Android Chrome 101 | */ 102 | static isAndroidChrome() { 103 | const agent = navigator.userAgent 104 | return !!(agent.match(/Android/) && agent.match(/Chrome/)) 105 | } 106 | 107 | /** 108 | * Checks whether the browser can read files in the game folder. 109 | * 110 | * @static 111 | * @method canReadGameFiles 112 | * @return {Boolean} True if the browser can read files in the game folder 113 | */ 114 | static canReadGameFiles() { 115 | const scripts = document.getElementsByTagName('script') 116 | const lastScript = scripts[scripts.length - 1] 117 | const xhr = new XMLHttpRequest() 118 | try { 119 | xhr.open('GET', lastScript.src) 120 | xhr.overrideMimeType('text/javascript') 121 | xhr.send() 122 | return true 123 | } catch (e) { 124 | return false 125 | } 126 | } 127 | 128 | /** 129 | * Makes a CSS color string from RGB values. 130 | * 131 | * @static 132 | * @method rgbToCssColor 133 | * @param {Number} r The red value in the range (0, 255) 134 | * @param {Number} g The green value in the range (0, 255) 135 | * @param {Number} b The blue value in the range (0, 255) 136 | * @return {String} CSS color string 137 | */ 138 | static rgbToCssColor(r, g, b) { 139 | r = Math.round(r) 140 | g = Math.round(g) 141 | b = Math.round(b) 142 | return 'rgb(' + r + ',' + g + ',' + b + ')' 143 | } 144 | 145 | _id = 1 146 | static generateRuntimeId() { 147 | return this._id++ 148 | } 149 | 150 | static _supportPassiveEvent = null 151 | /** 152 | * Test this browser support passive event feature 153 | * 154 | * @static 155 | * @method isSupportPassiveEvent 156 | * @return {Boolean} this browser support passive event or not 157 | */ 158 | static isSupportPassiveEvent() { 159 | if (typeof this._supportPassiveEvent === 'boolean') { 160 | return this._supportPassiveEvent 161 | } 162 | // test support passive event 163 | // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection 164 | let passive = false 165 | const options = Object.defineProperty({}, 'passive', { 166 | get: function() { 167 | passive = true 168 | } 169 | }) 170 | window.addEventListener('test', null, options) 171 | this._supportPassiveEvent = passive 172 | return passive 173 | } 174 | } 175 | 176 | export { Utils, JsExtensions } 177 | -------------------------------------------------------------------------------- /src/core/Weather.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The weather effect which displays rain, storm, or snow. 4 | * 5 | * @class Weather 6 | * @constructor 7 | */ 8 | class Weather extends PIXI.Container { 9 | constructor() { 10 | super() 11 | this.initialize.apply(this, arguments) 12 | } 13 | 14 | initialize() { 15 | // super() 16 | //PIXI.Container.call(this) 17 | 18 | this._width = Graphics.width 19 | this._height = Graphics.height 20 | this._sprites = [] 21 | 22 | this._createBitmaps() 23 | this._createDimmer() 24 | 25 | /** 26 | * The type of the weather in ['none', 'rain', 'storm', 'snow']. 27 | * 28 | * @property type 29 | * @type String 30 | */ 31 | this.type = 'none' 32 | 33 | /** 34 | * The power of the weather in the range (0, 9). 35 | * 36 | * @property power 37 | * @type Number 38 | */ 39 | this.power = 0 40 | 41 | /** 42 | * The origin point of the weather for scrolling. 43 | * 44 | * @property origin 45 | * @type Point 46 | */ 47 | this.origin = new Point() 48 | } 49 | 50 | /** 51 | * Updates the weather for each frame. 52 | * 53 | * @method update 54 | */ 55 | update() { 56 | this._updateDimmer() 57 | this._updateAllSprites() 58 | } 59 | 60 | /** 61 | * @method _createBitmaps 62 | * @private 63 | */ 64 | _createBitmaps() { 65 | this._rainBitmap = new Bitmap(1, 60) 66 | this._rainBitmap.fillAll('white') 67 | this._stormBitmap = new Bitmap(2, 100) 68 | this._stormBitmap.fillAll('white') 69 | this._snowBitmap = new Bitmap(9, 9) 70 | this._snowBitmap.drawCircle(4, 4, 4, 'white') 71 | } 72 | 73 | /** 74 | * @method _createDimmer 75 | * @private 76 | */ 77 | _createDimmer() { 78 | this._dimmerSprite = new ScreenSprite() 79 | this._dimmerSprite.setColor(80, 80, 80) 80 | this.addChild(this._dimmerSprite) 81 | } 82 | 83 | /** 84 | * @method _updateDimmer 85 | * @private 86 | */ 87 | _updateDimmer() { 88 | this._dimmerSprite.opacity = Math.floor(this.power * 6) 89 | } 90 | 91 | /** 92 | * @method _updateAllSprites 93 | * @private 94 | */ 95 | _updateAllSprites() { 96 | var maxSprites = Math.floor(this.power * 10) 97 | while (this._sprites.length < maxSprites) { 98 | this._addSprite() 99 | } 100 | while (this._sprites.length > maxSprites) { 101 | this._removeSprite() 102 | } 103 | this._sprites.forEach(function(sprite) { 104 | this._updateSprite(sprite) 105 | sprite.x = sprite.ax - this.origin.x 106 | sprite.y = sprite.ay - this.origin.y 107 | }, this) 108 | } 109 | 110 | /** 111 | * @method _addSprite 112 | * @private 113 | */ 114 | _addSprite() { 115 | var sprite = new Sprite(this.viewport) 116 | sprite.opacity = 0 117 | this._sprites.push(sprite) 118 | this.addChild(sprite) 119 | } 120 | 121 | /** 122 | * @method _removeSprite 123 | * @private 124 | */ 125 | _removeSprite() { 126 | this.removeChild(this._sprites.pop()) 127 | } 128 | 129 | /** 130 | * @method _updateSprite 131 | * @param {Sprite} sprite 132 | * @private 133 | */ 134 | _updateSprite(sprite) { 135 | switch (this.type) { 136 | case 'rain': 137 | this._updateRainSprite(sprite) 138 | break 139 | case 'storm': 140 | this._updateStormSprite(sprite) 141 | break 142 | case 'snow': 143 | this._updateSnowSprite(sprite) 144 | break 145 | } 146 | if (sprite.opacity < 40) { 147 | this._rebornSprite(sprite) 148 | } 149 | } 150 | 151 | /** 152 | * @method _updateRainSprite 153 | * @param {Sprite} sprite 154 | * @private 155 | */ 156 | _updateRainSprite(sprite) { 157 | sprite.bitmap = this._rainBitmap 158 | sprite.rotation = Math.PI / 16 159 | sprite.ax -= 6 * Math.sin(sprite.rotation) 160 | sprite.ay += 6 * Math.cos(sprite.rotation) 161 | sprite.opacity -= 6 162 | } 163 | 164 | /** 165 | * @method _updateStormSprite 166 | * @param {Sprite} sprite 167 | * @private 168 | */ 169 | _updateStormSprite(sprite) { 170 | sprite.bitmap = this._stormBitmap 171 | sprite.rotation = Math.PI / 8 172 | sprite.ax -= 8 * Math.sin(sprite.rotation) 173 | sprite.ay += 8 * Math.cos(sprite.rotation) 174 | sprite.opacity -= 8 175 | } 176 | 177 | /** 178 | * @method _updateSnowSprite 179 | * @param {Sprite} sprite 180 | * @private 181 | */ 182 | _updateSnowSprite(sprite) { 183 | sprite.bitmap = this._snowBitmap 184 | sprite.rotation = Math.PI / 16 185 | sprite.ax -= 3 * Math.sin(sprite.rotation) 186 | sprite.ay += 3 * Math.cos(sprite.rotation) 187 | sprite.opacity -= 3 188 | } 189 | 190 | /** 191 | * @method _rebornSprite 192 | * @param {Sprite} sprite 193 | * @private 194 | */ 195 | _rebornSprite(sprite) { 196 | sprite.ax = Math.randomInt(Graphics.width + 100) - 100 + this.origin.x 197 | sprite.ay = Math.randomInt(Graphics.height + 200) - 200 + this.origin.y 198 | sprite.opacity = 160 + Math.randomInt(60) 199 | } 200 | } 201 | 202 | export { Weather } 203 | -------------------------------------------------------------------------------- /src/core/tone.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The color matrix filter for WebGL. 4 | * 5 | * @class ToneFilter 6 | * @extends PIXI.Filter 7 | * @constructor 8 | */ 9 | class ToneFilter extends PIXI.filters.ColorMatrixFilter { 10 | constructor() { 11 | super() 12 | } 13 | 14 | /** 15 | * Changes the hue. 16 | * 17 | * @method adjustHue 18 | * @param {Number} value The hue value in the range (-360, 360) 19 | */ 20 | adjustHue(value) { 21 | this.hue(value, true) 22 | } 23 | 24 | /** 25 | * Changes the saturation. 26 | * 27 | * @method adjustSaturation 28 | * @param {Number} value The saturation value in the range (-255, 255) 29 | */ 30 | adjustSaturation(value) { 31 | value = (value || 0).clamp(-255, 255) / 255 32 | this.saturate(value, true) 33 | } 34 | 35 | /** 36 | * Changes the tone. 37 | * 38 | * @method adjustTone 39 | * @param {Number} r The red strength in the range (-255, 255) 40 | * @param {Number} g The green strength in the range (-255, 255) 41 | * @param {Number} b The blue strength in the range (-255, 255) 42 | */ 43 | adjustTone(r, g, b) { 44 | r = (r || 0).clamp(-255, 255) / 255 45 | g = (g || 0).clamp(-255, 255) / 255 46 | b = (b || 0).clamp(-255, 255) / 255 47 | 48 | if (r !== 0 || g !== 0 || b !== 0) { 49 | const matrix = [ 50 | 1, 51 | 0, 52 | 0, 53 | r, 54 | 0, 55 | 0, 56 | 1, 57 | 0, 58 | g, 59 | 0, 60 | 0, 61 | 0, 62 | 1, 63 | b, 64 | 0, 65 | 0, 66 | 0, 67 | 0, 68 | 1, 69 | 0 70 | ] 71 | 72 | this._loadMatrix(matrix, true) 73 | } 74 | } 75 | } 76 | 77 | //----------------------------------------------------------------------------- 78 | /** 79 | * The sprite which changes the screen color in 2D canvas mode. 80 | * 81 | * @class ToneSprite 82 | * @constructor 83 | */ 84 | class ToneSprite extends PIXI.Container { 85 | constructor() { 86 | super() 87 | } 88 | initialize() { 89 | // PIXI.Container.call(this) 90 | this.clear() 91 | } 92 | 93 | /** 94 | * Clears the tone. 95 | * 96 | * @method reset 97 | */ 98 | clear() { 99 | this._red = 0 100 | this._green = 0 101 | this._blue = 0 102 | this._gray = 0 103 | } 104 | 105 | /** 106 | * Sets the tone. 107 | * 108 | * @method setTone 109 | * @param {Number} r The red strength in the range (-255, 255) 110 | * @param {Number} g The green strength in the range (-255, 255) 111 | * @param {Number} b The blue strength in the range (-255, 255) 112 | * @param {Number} gray The grayscale level in the range (0, 255) 113 | */ 114 | setTone(r, g, b, gray) { 115 | this._red = Math.round(r || 0).clamp(-255, 255) 116 | this._green = Math.round(g || 0).clamp(-255, 255) 117 | this._blue = Math.round(b || 0).clamp(-255, 255) 118 | this._gray = Math.round(gray || 0).clamp(0, 255) 119 | } 120 | 121 | /** 122 | * @method _renderCanvas 123 | * @param {Object} renderSession 124 | * @private 125 | */ 126 | _renderCanvas(renderer) { 127 | if (this.visible) { 128 | const context = renderer.context 129 | const t = this.worldTransform 130 | const r = renderer.resolution 131 | const width = Graphics.width 132 | const height = Graphics.height 133 | context.save() 134 | context.setTransform(t.a, t.b, t.c, t.d, t.tx * r, t.ty * r) 135 | if (Graphics.canUseSaturationBlend() && this._gray > 0) { 136 | context.globalCompositeOperation = 'saturation' 137 | context.globalAlpha = this._gray / 255 138 | context.fillStyle = '#ffffff' 139 | context.fillRect(0, 0, width, height) 140 | } 141 | context.globalAlpha = 1 142 | const r1 = Math.max(0, this._red) 143 | const g1 = Math.max(0, this._green) 144 | const b1 = Math.max(0, this._blue) 145 | if (r1 || g1 || b1) { 146 | context.globalCompositeOperation = 'lighter' 147 | context.fillStyle = Utils.rgbToCssColor(r1, g1, b1) 148 | context.fillRect(0, 0, width, height) 149 | } 150 | if (Graphics.canUseDifferenceBlend()) { 151 | const r2 = Math.max(0, -this._red) 152 | const g2 = Math.max(0, -this._green) 153 | const b2 = Math.max(0, -this._blue) 154 | if (r2 || g2 || b2) { 155 | context.globalCompositeOperation = 'difference' 156 | context.fillStyle = '#ffffff' 157 | context.fillRect(0, 0, width, height) 158 | context.globalCompositeOperation = 'lighter' 159 | context.fillStyle = Utils.rgbToCssColor(r2, g2, b2) 160 | context.fillRect(0, 0, width, height) 161 | context.globalCompositeOperation = 'difference' 162 | context.fillStyle = '#ffffff' 163 | context.fillRect(0, 0, width, height) 164 | } 165 | } 166 | context.restore() 167 | } 168 | } 169 | 170 | /** 171 | * @method _renderWebGL 172 | * @param {Object} renderSession 173 | * @private 174 | */ 175 | _renderWebGL(renderer) { 176 | // Not supported 177 | } 178 | } 179 | 180 | export { ToneFilter, ToneSprite } 181 | -------------------------------------------------------------------------------- /src/core/TilingSprite.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The sprite object for a tiling image. 4 | * 5 | * @class TilingSprite 6 | * @constructor 7 | * @param {Bitmap} bitmap The image for the tiling sprite 8 | */ 9 | 10 | class TilingSprite extends PIXI.extras.PictureTilingSprite { 11 | constructor(bitmap) { 12 | const texture = new PIXI.Texture(new PIXI.BaseTexture()) 13 | super(texture) 14 | 15 | this.initialize.apply(this, arguments) 16 | } 17 | // this is the constructor and will be removed as soon as we remove calls to initialize 18 | initialize(bitmap) { 19 | const texture = new PIXI.Texture(new PIXI.BaseTexture()) 20 | PIXI.extras.PictureTilingSprite.call(this, texture) 21 | 22 | this._bitmap = null 23 | this._width = 0 24 | this._height = 0 25 | this._frame = new Rectangle() 26 | this.spriteId = Sprite._counter++ 27 | /** 28 | * The origin point of the tiling sprite for scrolling. 29 | * 30 | * @property origin 31 | * @type Point 32 | */ 33 | this.origin = new Point() 34 | 35 | this.bitmap = bitmap 36 | } 37 | 38 | _renderCanvas_PIXI = super._renderCanvas 39 | _renderWebGL_PIXI = super._renderWebGL 40 | 41 | /** 42 | * @method _renderCanvas 43 | * @param {Object} renderer 44 | * @private 45 | */ 46 | _renderCanvas(renderer) { 47 | if (this._bitmap) { 48 | this._bitmap.touch() 49 | } 50 | if (this.texture.frame.width > 0 && this.texture.frame.height > 0) { 51 | this._renderCanvas_PIXI(renderer) 52 | } 53 | } 54 | 55 | /** 56 | * @method _renderWebGL 57 | * @param {Object} renderer 58 | * @private 59 | */ 60 | _renderWebGL(renderer) { 61 | if (this._bitmap) { 62 | this._bitmap.touch() 63 | } 64 | if (this.texture.frame.width > 0 && this.texture.frame.height > 0) { 65 | if (this._bitmap) { 66 | this._bitmap.checkDirty() 67 | } 68 | this._renderWebGL_PIXI(renderer) 69 | } 70 | } 71 | 72 | /** 73 | * The image for the tiling sprite. 74 | * 75 | * @property bitmap 76 | * @type Bitmap 77 | */ 78 | get bitmap() { 79 | return this._bitmap 80 | } 81 | set bitmap(value) { 82 | if (this._bitmap !== value) { 83 | this._bitmap = value 84 | if (this._bitmap) { 85 | this._bitmap.addLoadListener(this._onBitmapLoad.bind(this)) 86 | } else { 87 | this.texture.frame = Rectangle.emptyRectangle 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * The opacity of the tiling sprite (0 to 255). 94 | * 95 | * @property opacity 96 | * @type Number 97 | */ 98 | get opacity() { 99 | return this.alpha * 255 100 | } 101 | set opacity(value) { 102 | this.alpha = value.clamp(0, 255) / 255 103 | } 104 | 105 | /** 106 | * Updates the tiling sprite for each frame. 107 | * 108 | * @method update 109 | */ 110 | update() { 111 | this.children.forEach(function(child) { 112 | if (child.update) { 113 | child.update() 114 | } 115 | }) 116 | } 117 | 118 | /** 119 | * Sets the x, y, width, and height all at once. 120 | * 121 | * @method move 122 | * @param {Number} x The x coordinate of the tiling sprite 123 | * @param {Number} y The y coordinate of the tiling sprite 124 | * @param {Number} width The width of the tiling sprite 125 | * @param {Number} height The height of the tiling sprite 126 | */ 127 | move(x, y, width, height) { 128 | this.x = x || 0 129 | this.y = y || 0 130 | this._width = width || 0 131 | this._height = height || 0 132 | } 133 | 134 | /** 135 | * Specifies the region of the image that the tiling sprite will use. 136 | * 137 | * @method setFrame 138 | * @param {Number} x The x coordinate of the frame 139 | * @param {Number} y The y coordinate of the frame 140 | * @param {Number} width The width of the frame 141 | * @param {Number} height The height of the frame 142 | */ 143 | setFrame(x, y, width, height) { 144 | this._frame.x = x 145 | this._frame.y = y 146 | this._frame.width = width 147 | this._frame.height = height 148 | this._refresh() 149 | } 150 | 151 | /** 152 | * @method updateTransform 153 | * @private 154 | */ 155 | updateTransform() { 156 | this.tilePosition.x = Math.round(-this.origin.x) 157 | this.tilePosition.y = Math.round(-this.origin.y) 158 | this.updateTransformTS() 159 | } 160 | 161 | updateTransformTS = PIXI.extras.TilingSprite.prototype.updateTransform 162 | 163 | /** 164 | * @method _onBitmapLoad 165 | * @private 166 | */ 167 | _onBitmapLoad() { 168 | this.texture.baseTexture = this._bitmap.baseTexture 169 | this._refresh() 170 | } 171 | 172 | /** 173 | * @method _refresh 174 | * @private 175 | */ 176 | _refresh() { 177 | const frame = this._frame.clone() 178 | if (frame.width === 0 && frame.height === 0 && this._bitmap) { 179 | frame.width = this._bitmap.width 180 | frame.height = this._bitmap.height 181 | } 182 | this.texture.frame = frame 183 | this.texture._updateID++ 184 | this.tilingTexture = null 185 | } 186 | 187 | _speedUpCustomBlendModes = Sprite.prototype._speedUpCustomBlendModes 188 | 189 | /** 190 | * @method _renderWebGL 191 | * @param {Object} renderer 192 | * @private 193 | */ 194 | _renderWebGL(renderer) { 195 | if (this._bitmap) { 196 | this._bitmap.touch() 197 | this._bitmap.checkDirty() 198 | } 199 | 200 | this._speedUpCustomBlendModes(renderer) 201 | 202 | this._renderWebGL_PIXI(renderer) 203 | } 204 | 205 | // The important members from Pixi.js 206 | 207 | /** 208 | * The visibility of the tiling sprite. 209 | * 210 | * @property visible 211 | * @type Boolean 212 | */ 213 | 214 | /** 215 | * The x coordinate of the tiling sprite. 216 | * 217 | * @property x 218 | * @type Number 219 | */ 220 | 221 | /** 222 | * The y coordinate of the tiling sprite. 223 | * 224 | * @property y 225 | * @type Number 226 | */ 227 | } 228 | 229 | export { TilingSprite } 230 | -------------------------------------------------------------------------------- /js/libs/lz-string.js: -------------------------------------------------------------------------------- 1 | var LZString={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",_f:String.fromCharCode,compressToBase64:function(e){if(e==null)return"";var t="";var n,r,i,s,o,u,a;var f=0;e=LZString.compress(e);while(f>8;r=e.charCodeAt(f/2)&255;if(f/2+1>8;else i=NaN}else{n=e.charCodeAt((f-1)/2)&255;if((f+1)/2>8;i=e.charCodeAt((f+1)/2)&255}else r=i=NaN}f+=3;s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+LZString._keyStr.charAt(s)+LZString._keyStr.charAt(o)+LZString._keyStr.charAt(u)+LZString._keyStr.charAt(a)}return t},decompressFromBase64:function(e){if(e==null)return"";var t="",n=0,r,i,s,o,u,a,f,l,c=0,h=LZString._f;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(c>4;s=(a&15)<<4|f>>2;o=(f&3)<<6|l;if(n%2==0){r=i<<8;if(f!=64){t+=h(r|s)}if(l!=64){r=o<<8}}else{t=t+h(r|i);if(f!=64){r=s<<8}if(l!=64){t+=h(r|o)}}n+=3}return LZString.decompress(t)},compressToUTF16:function(e){if(e==null)return"";var t="",n,r,i,s=0,o=LZString._f;e=LZString.compress(e);for(n=0;n>1)+32);i=(r&1)<<14;break;case 1:t+=o(i+(r>>2)+32);i=(r&3)<<13;break;case 2:t+=o(i+(r>>3)+32);i=(r&7)<<12;break;case 3:t+=o(i+(r>>4)+32);i=(r&15)<<11;break;case 4:t+=o(i+(r>>5)+32);i=(r&31)<<10;break;case 5:t+=o(i+(r>>6)+32);i=(r&63)<<9;break;case 6:t+=o(i+(r>>7)+32);i=(r&127)<<8;break;case 7:t+=o(i+(r>>8)+32);i=(r&255)<<7;break;case 8:t+=o(i+(r>>9)+32);i=(r&511)<<6;break;case 9:t+=o(i+(r>>10)+32);i=(r&1023)<<5;break;case 10:t+=o(i+(r>>11)+32);i=(r&2047)<<4;break;case 11:t+=o(i+(r>>12)+32);i=(r&4095)<<3;break;case 12:t+=o(i+(r>>13)+32);i=(r&8191)<<2;break;case 13:t+=o(i+(r>>14)+32);i=(r&16383)<<1;break;case 14:t+=o(i+(r>>15)+32,(r&32767)+32);s=0;break}}return t+o(i+32)},decompressFromUTF16:function(e){if(e==null)return"";var t="",n,r,i=0,s=0,o=LZString._f;while(s>14);n=(r&16383)<<2;break;case 2:t+=o(n|r>>13);n=(r&8191)<<3;break;case 3:t+=o(n|r>>12);n=(r&4095)<<4;break;case 4:t+=o(n|r>>11);n=(r&2047)<<5;break;case 5:t+=o(n|r>>10);n=(r&1023)<<6;break;case 6:t+=o(n|r>>9);n=(r&511)<<7;break;case 7:t+=o(n|r>>8);n=(r&255)<<8;break;case 8:t+=o(n|r>>7);n=(r&127)<<9;break;case 9:t+=o(n|r>>6);n=(r&63)<<10;break;case 10:t+=o(n|r>>5);n=(r&31)<<11;break;case 11:t+=o(n|r>>4);n=(r&15)<<12;break;case 12:t+=o(n|r>>3);n=(r&7)<<13;break;case 13:t+=o(n|r>>2);n=(r&3)<<14;break;case 14:t+=o(n|r>>1);n=(r&1)<<15;break;case 15:t+=o(n|r);i=0;break}s++}return LZString.decompress(t)},compressToUint8Array:function(e){var t=LZString.compress(e);var n=new Uint8Array(t.length*2);for(var r=0,i=t.length;r>>8;n[r*2+1]=s%256}return n},decompressFromUint8Array:function(e){if(e===null||e===undefined){return LZString.decompress(e)}else{var t=new Array(e.length/2);for(var n=0,r=t.length;n>1}}else{n=1;for(t=0;t>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t>1}}a--;if(a==0){a=Math.pow(2,l);l++}r[o]=f++;u=String(s)}}if(u!==""){if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t>1}}else{n=1;for(t=0;t>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t>1}}a--;if(a==0){a=Math.pow(2,l);l++}}n=2;for(t=0;t>1}while(true){h=h<<1;if(p==15){c+=v(h);break}else p++}return c},decompress:function(e){if(e==null)return"";if(e=="")return null;var t=[],n,r=4,i=4,s=3,o="",u="",a,f,l,c,h,p,d,v=LZString._f,m={string:e,val:e.charCodeAt(0),position:32768,index:1};for(a=0;a<3;a+=1){t[a]=a}l=0;h=Math.pow(2,2);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(n=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 2:return""}t[3]=d;f=u=d;while(true){if(m.index>m.string.length){return""}l=0;h=Math.pow(2,s);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(d=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 2:return u}if(r==0){r=Math.pow(2,s);s++}if(t[d]){o=t[d]}else{if(d===i){o=f+f.charAt(0)}else{return null}}u+=o;t[i++]=f+o.charAt(0);r--;f=o;if(r==0){r=Math.pow(2,s);s++}}}};if(typeof module!=="undefined"&&module!=null){module.exports=LZString} 2 | -------------------------------------------------------------------------------- /js/libs/fpsmeter.js: -------------------------------------------------------------------------------- 1 | /*! FPSMeter 0.3.1 - 9th May 2013 | https://github.com/Darsain/fpsmeter */ 2 | (function(m,j){function s(a,e){for(var g in e)try{a.style[g]=e[g]}catch(j){}return a}function H(a){return null==a?String(a):"object"===typeof a||"function"===typeof a?Object.prototype.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase()||"object":typeof a}function R(a,e){if("array"!==H(e))return-1;if(e.indexOf)return e.indexOf(a);for(var g=0,j=e.length;gd.interval?(x=M(k),m()):(x=setTimeout(k,d.interval),P=M(m))}function G(a){a=a||window.event;a.preventDefault?(a.preventDefault(),a.stopPropagation()):(a.returnValue= 6 | !1,a.cancelBubble=!0);b.toggle()}function U(){d.toggleOn&&S(f.container,d.toggleOn,G,1);a.removeChild(f.container)}function V(){f.container&&U();h=D.theme[d.theme];y=h.compiledHeatmaps||[];if(!y.length&&h.heatmaps.length){for(p=0;p=m?m*(1+j):m+j-m*j;0===l?g="#000":(t=2*m-l,k=(l-t)/l,g*=6,n=Math.floor(g), 7 | v=g-n,v*=l*k,0===n||6===n?(n=l,k=t+v,l=t):1===n?(n=l-v,k=l,l=t):2===n?(n=t,k=l,l=t+v):3===n?(n=t,k=l-v):4===n?(n=t+v,k=t):(n=l,k=t,l-=v),g="#"+N(n)+N(k)+N(l));b[e]=g}}h.compiledHeatmaps=y}f.container=s(document.createElement("div"),h.container);f.count=f.container.appendChild(s(document.createElement("div"),h.count));f.legend=f.container.appendChild(s(document.createElement("div"),h.legend));f.graph=d.graph?f.container.appendChild(s(document.createElement("div"),h.graph)):0;w.length=0;for(var q in f)f[q]&& 8 | h[q].heatOn&&w.push({name:q,el:f[q]});u.length=0;if(f.graph){f.graph.style.width=d.history*h.column.width+(d.history-1)*h.column.spacing+"px";for(c=0;c 0) { 135 | this._canvasClearWindowRect(renderer, child) 136 | context.save() 137 | child.renderCanvas(renderer) 138 | context.restore() 139 | } 140 | } 141 | 142 | context.restore() 143 | 144 | renderer.context = realCanvasContext 145 | renderer.context.setTransform(1, 0, 0, 1, 0, 0) 146 | renderer.context.globalCompositeOperation = 'source-over' 147 | renderer.context.globalAlpha = 1 148 | renderer.context.drawImage(this._tempCanvas, 0, 0) 149 | 150 | for (var j = 0; j < this.children.length; j++) { 151 | if (!this.children[j]._isWindow) { 152 | this.children[j].renderCanvas(renderer) 153 | } 154 | } 155 | } 156 | 157 | /** 158 | * @method _canvasClearWindowRect 159 | * @param {Object} renderSession 160 | * @param {Window} window 161 | * @private 162 | */ 163 | WindowLayer.prototype._canvasClearWindowRect = function(renderSession, window) { 164 | var rx = this.x + window.x 165 | var ry = this.y + window.y + window.height / 2 * (1 - window._openness / 255) 166 | var rw = window.width 167 | var rh = window.height * window._openness / 255 168 | renderSession.context.clearRect(rx, ry, rw, rh) 169 | } 170 | 171 | /** 172 | * @method _renderWebGL 173 | * @param {Object} renderSession 174 | * @private 175 | */ 176 | WindowLayer.prototype.renderWebGL = function(renderer) { 177 | if (!this.visible || !this.renderable) { 178 | return 179 | } 180 | 181 | if (this.children.length == 0) { 182 | return 183 | } 184 | 185 | renderer.flush() 186 | this.filterArea.copy(this) 187 | renderer.filterManager.pushFilter(this, this.filters) 188 | renderer.currentRenderer.start() 189 | 190 | var shift = new PIXI.Point() 191 | var rt = renderer._activeRenderTarget 192 | var projectionMatrix = rt.projectionMatrix 193 | shift.x = Math.round((projectionMatrix.tx + 1) / 2 * rt.sourceFrame.width) 194 | shift.y = Math.round((projectionMatrix.ty + 1) / 2 * rt.sourceFrame.height) 195 | 196 | for (var i = 0; i < this.children.length; i++) { 197 | var child = this.children[i] 198 | if (child._isWindow && child.visible && child.openness > 0) { 199 | this._maskWindow(child, shift) 200 | renderer.maskManager.pushScissorMask(this, this._windowMask) 201 | renderer.clear() 202 | renderer.maskManager.popScissorMask() 203 | renderer.currentRenderer.start() 204 | child.renderWebGL(renderer) 205 | renderer.currentRenderer.flush() 206 | } 207 | } 208 | 209 | renderer.flush() 210 | renderer.filterManager.popFilter() 211 | renderer.maskManager.popScissorMask() 212 | 213 | for (var j = 0; j < this.children.length; j++) { 214 | if (!this.children[j]._isWindow) { 215 | this.children[j].renderWebGL(renderer) 216 | } 217 | } 218 | } 219 | 220 | /** 221 | * @method _maskWindow 222 | * @param {Window} window 223 | * @private 224 | */ 225 | WindowLayer.prototype._maskWindow = function(window, shift) { 226 | this._windowMask._currentBounds = null 227 | this._windowMask.boundsDirty = true 228 | var rect = this._windowRect 229 | rect.x = this.x + shift.x + window.x 230 | rect.y = 231 | this.x + 232 | shift.y + 233 | window.y + 234 | window.height / 2 * (1 - window._openness / 255) 235 | rect.width = window.width 236 | rect.height = window.height * window._openness / 255 237 | } 238 | 239 | // The important members from Pixi.js 240 | 241 | /** 242 | * The x coordinate of the window layer. 243 | * 244 | * @property x 245 | * @type Number 246 | */ 247 | 248 | /** 249 | * The y coordinate of the window layer. 250 | * 251 | * @property y 252 | * @type Number 253 | */ 254 | 255 | /** 256 | * [read-only] The array of children of the window layer. 257 | * 258 | * @property children 259 | * @type Array 260 | */ 261 | 262 | /** 263 | * [read-only] The object that contains the window layer. 264 | * 265 | * @property parent 266 | * @type Object 267 | */ 268 | 269 | /** 270 | * Adds a child to the container. 271 | * 272 | * @method addChild 273 | * @param {Object} child The child to add 274 | * @return {Object} The child that was added 275 | */ 276 | 277 | /** 278 | * Adds a child to the container at a specified index. 279 | * 280 | * @method addChildAt 281 | * @param {Object} child The child to add 282 | * @param {Number} index The index to place the child in 283 | * @return {Object} The child that was added 284 | */ 285 | 286 | /** 287 | * Removes a child from the container. 288 | * 289 | * @method removeChild 290 | * @param {Object} child The child to remove 291 | * @return {Object} The child that was removed 292 | */ 293 | 294 | /** 295 | * Removes a child from the specified index position. 296 | * 297 | * @method removeChildAt 298 | * @param {Number} index The index to get the child from 299 | * @return {Object} The child that was removed 300 | */ 301 | 302 | export { WindowLayer } 303 | -------------------------------------------------------------------------------- /src/core/cache.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The resource class. Allows to be collected as a garbage if not use for some time or ticks 4 | * 5 | * @class CacheEntry 6 | * @constructor 7 | * @param {ResourceManager} resource manager 8 | * @param {string} key, url of the resource 9 | * @param {string} item - Bitmap, HTML5Audio, WebAudio - whatever you want to store in the cache 10 | */ 11 | 12 | class CacheEntry { 13 | constructor(cache, key, item) { 14 | this.cache = cache 15 | this.key = key 16 | this.item = item 17 | this.cached = false 18 | this.touchTicks = 0 19 | this.touchSeconds = 0 20 | this.ttlTicks = 0 21 | this.ttlSeconds = 0 22 | this.freedByTTL = false 23 | } 24 | 25 | /** 26 | * frees the resource 27 | */ 28 | free = byTTL => { 29 | this.freedByTTL = byTTL || false 30 | if (this.cached) { 31 | this.cached = false 32 | delete this.cache._inner[this.key] 33 | } 34 | } 35 | 36 | /** 37 | * Allocates the resource 38 | * @returns {CacheEntry} 39 | */ 40 | allocate = () => { 41 | if (!this.cached) { 42 | this.cache._inner[this.key] = this 43 | this.cached = true 44 | } 45 | this.touch() 46 | return this 47 | } 48 | 49 | /** 50 | * Sets the time to live 51 | * @param {number} ticks TTL in ticks, 0 if not set 52 | * @param {number} time TTL in seconds, 0 if not set 53 | * @returns {CacheEntry} 54 | */ 55 | setTimeToLive = (ticks, seconds) => { 56 | this.ttlTicks = ticks || 0 57 | this.ttlSeconds = seconds || 0 58 | return this 59 | } 60 | isStillAlive = () => { 61 | const cache = this.cache 62 | return ( 63 | (this.ttlTicks == 0 || 64 | this.touchTicks + this.ttlTicks < cache.updateTicks) && 65 | (this.ttlSeconds == 0 || 66 | this.touchSeconds + this.ttlSeconds < cache.updateSeconds) 67 | ) 68 | } 69 | 70 | /** 71 | * makes sure that resource wont freed by Time To Live 72 | * if resource was already freed by TTL, put it in cache again 73 | */ 74 | touch = () => { 75 | const cache = this.cache 76 | if (this.cached) { 77 | this.touchTicks = cache.updateTicks 78 | this.touchSeconds = cache.updateSeconds 79 | } else if (this.freedByTTL) { 80 | this.freedByTTL = false 81 | if (!cache._inner[this.key]) { 82 | cache._inner[this.key] = this 83 | } 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Cache for images, audio, or any other kind of resource 90 | * @param manager 91 | * @constructor 92 | */ 93 | class CacheMap { 94 | constructor(manager) { 95 | this.manager = manager 96 | this._inner = {} 97 | this._lastRemovedEntries = {} 98 | this.updateTicks = 0 99 | this.lastCheckTTL = 0 100 | this.delayCheckTTL = 100.0 101 | this.updateSeconds = Date.now() 102 | } 103 | 104 | /** 105 | * checks ttl of all elements and removes dead ones 106 | */ 107 | checkTTL = () => { 108 | const cache = this._inner 109 | let temp = this._lastRemovedEntries 110 | if (!temp) { 111 | temp = [] 112 | this._lastRemovedEntries = temp 113 | } 114 | for (var key in cache) { 115 | var entry = cache[key] 116 | if (!entry.isStillAlive()) { 117 | temp.push(entry) 118 | } 119 | } 120 | for (var i = 0; i < temp.length; i++) { 121 | temp[i].free(true) 122 | } 123 | temp.length = 0 124 | } 125 | 126 | /** 127 | * cache item 128 | * @param key url of cache element 129 | * @returns {*|null} 130 | */ 131 | getItem = key => { 132 | const entry = this._inner[key] 133 | if (entry) { 134 | return entry.item 135 | } 136 | return null 137 | } 138 | 139 | clear = () => { 140 | const keys = Object.keys(this._inner) 141 | for (let i = 0; i < keys.length; i++) { 142 | this._inner[keys[i]].free() 143 | } 144 | } 145 | 146 | setItem = (key, item) => { 147 | return new CacheEntry(this, key, item).allocate() 148 | } 149 | 150 | update = (ticks, delta) => { 151 | this.updateTicks += ticks 152 | this.updateSeconds += delta 153 | if (this.updateSeconds >= this.delayCheckTTL + this.lastCheckTTL) { 154 | this.lastCheckTTL = this.updateSeconds 155 | this.checkTTL() 156 | } 157 | } 158 | } 159 | 160 | class ImageCache { 161 | limit = 10 * 1000 * 1000 162 | 163 | constructor() { 164 | this.initialize.apply(this, arguments) 165 | } 166 | initialize = () => { 167 | this._items = {} 168 | } 169 | add = (key, value) => { 170 | this._items[key] = { 171 | bitmap: value, 172 | touch: Date.now(), 173 | key: key 174 | } 175 | 176 | this._truncateCache() 177 | } 178 | reserve = (key, value, reservationId) => { 179 | if (!this._items[key]) { 180 | this._items[key] = { 181 | bitmap: value, 182 | touch: Date.now(), 183 | key: key 184 | } 185 | } 186 | 187 | this._items[key].reservationId = reservationId 188 | } 189 | releaseReservation = reservationId => { 190 | const items = this._items 191 | 192 | Object.keys(items) 193 | .map(function(key) { 194 | return items[key] 195 | }) 196 | .forEach(function(item) { 197 | if (item.reservationId === reservationId) { 198 | delete item.reservationId 199 | } 200 | }) 201 | } 202 | _truncateCache = () => { 203 | var items = this._items 204 | var sizeLeft = ImageCache.limit 205 | 206 | Object.keys(items) 207 | .map(function(key) { 208 | return items[key] 209 | }) 210 | .sort(function(a, b) { 211 | return b.touch - a.touch 212 | }) 213 | .forEach( 214 | function(item) { 215 | if (sizeLeft > 0 || this._mustBeHeld(item)) { 216 | var bitmap = item.bitmap 217 | sizeLeft -= bitmap.width * bitmap.height 218 | } else { 219 | delete items[item.key] 220 | } 221 | }.bind(this) 222 | ) 223 | } 224 | _mustBeHeld(item) { 225 | // request only is weak so It's purgeable 226 | if (item.bitmap.isRequestOnly()) return false 227 | // reserved item must be held 228 | if (item.reservationId) return true 229 | // not ready bitmap must be held (because of checking isReady()) 230 | if (!item.bitmap.isReady()) return true 231 | // then the item may purgeable 232 | return false 233 | } 234 | isReady = () => { 235 | const items = this._items 236 | return !Object.keys(items).some(function(key) { 237 | return !items[key].bitmap.isRequestOnly() && !items[key].bitmap.isReady() 238 | }) 239 | } 240 | getErrorBitmap = () => { 241 | const items = this._items 242 | let bitmap = null 243 | if ( 244 | Object.keys(items).some(function(key) { 245 | if (items[key].bitmap.isError()) { 246 | bitmap = items[key].bitmap 247 | return true 248 | } 249 | return false 250 | }) 251 | ) { 252 | return bitmap 253 | } 254 | 255 | return null 256 | } 257 | } 258 | // "get" is reserved for getters so we shouldn't thouch this right now 259 | ImageCache.prototype.get = function(key) { 260 | if (this._items[key]) { 261 | const item = this._items[key] 262 | item.touch = Date.now() 263 | return item.bitmap 264 | } 265 | 266 | return null 267 | } 268 | 269 | class RequestQueue { 270 | constructor() { 271 | this.initialize.apply(this, arguments) 272 | } 273 | initialize = () => { 274 | this._queue = [] 275 | } 276 | enqueue = (key, value) => { 277 | this._queue.push({ 278 | key: key, 279 | value: value 280 | }) 281 | } 282 | update = () => { 283 | if (this._queue.length === 0) return 284 | 285 | const top = this._queue[0] 286 | if (top.value.isRequestReady()) { 287 | this._queue.shift() 288 | if (this._queue.length !== 0) { 289 | this._queue[0].value.startRequest() 290 | } 291 | } else { 292 | top.value.startRequest() 293 | } 294 | } 295 | raisePriority = key => { 296 | for (let n = 0; n < this._queue.length; n++) { 297 | const item = this._queue[n] 298 | if (item.key === key) { 299 | this._queue.splice(n, 1) 300 | this._queue.unshift(item) 301 | break 302 | } 303 | } 304 | } 305 | clear = () => { 306 | this._queue.splice(0) 307 | } 308 | } 309 | 310 | export { CacheEntry, CacheMap, ImageCache, RequestQueue } 311 | -------------------------------------------------------------------------------- /src/core/misc.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The static class that handles JSON with object information. 4 | * 5 | * @class JsonEx 6 | */ 7 | function JsonEx() { 8 | throw new Error('This is a static class') 9 | } 10 | 11 | /** 12 | * The maximum depth of objects. 13 | * 14 | * @static 15 | * @property maxDepth 16 | * @type Number 17 | * @default 100 18 | */ 19 | JsonEx.maxDepth = 100 20 | 21 | JsonEx._id = 1 22 | JsonEx._generateId = function() { 23 | return JsonEx._id++ 24 | } 25 | 26 | /** 27 | * Converts an object to a JSON string with object information. 28 | * 29 | * @static 30 | * @method stringify 31 | * @param {Object} object The object to be converted 32 | * @return {String} The JSON string 33 | */ 34 | JsonEx.stringify = function(object) { 35 | var circular = [] 36 | JsonEx._id = 1 37 | var json = JSON.stringify(this._encode(object, circular, 0)) 38 | this._cleanMetadata(object) 39 | this._restoreCircularReference(circular) 40 | 41 | return json 42 | } 43 | 44 | JsonEx._restoreCircularReference = function(circulars) { 45 | circulars.forEach(function(circular) { 46 | var key = circular[0] 47 | var value = circular[1] 48 | var content = circular[2] 49 | 50 | value[key] = content 51 | }) 52 | } 53 | 54 | /** 55 | * Parses a JSON string and reconstructs the corresponding object. 56 | * 57 | * @static 58 | * @method parse 59 | * @param {String} json The JSON string 60 | * @return {Object} The reconstructed object 61 | */ 62 | JsonEx.parse = function(json) { 63 | var circular = [] 64 | var registry = {} 65 | var contents = this._decode(JSON.parse(json), circular, registry) 66 | this._cleanMetadata(contents) 67 | this._linkCircularReference(contents, circular, registry) 68 | 69 | return contents 70 | } 71 | 72 | JsonEx._linkCircularReference = function(contents, circulars, registry) { 73 | circulars.forEach(function(circular) { 74 | var key = circular[0] 75 | var value = circular[1] 76 | var id = circular[2] 77 | 78 | value[key] = registry[id] 79 | }) 80 | } 81 | 82 | JsonEx._cleanMetadata = function(object) { 83 | if (!object) return 84 | 85 | delete object['@'] 86 | delete object['@c'] 87 | 88 | if (typeof object === 'object') { 89 | Object.keys(object).forEach(function(key) { 90 | var value = object[key] 91 | if (typeof value === 'object') { 92 | JsonEx._cleanMetadata(value) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | /** 99 | * Makes a deep copy of the specified object. 100 | * 101 | * @static 102 | * @method makeDeepCopy 103 | * @param {Object} object The object to be copied 104 | * @return {Object} The copied object 105 | */ 106 | JsonEx.makeDeepCopy = function(object) { 107 | return this.parse(this.stringify(object)) 108 | } 109 | 110 | /** 111 | * @static 112 | * @method _encode 113 | * @param {Object} value 114 | * @param {Array} circular 115 | * @param {Number} depth 116 | * @return {Object} 117 | * @private 118 | */ 119 | JsonEx._encode = function(value, circular, depth) { 120 | depth = depth || 0 121 | if (++depth >= this.maxDepth) { 122 | throw new Error('Object too deep') 123 | } 124 | var type = Object.prototype.toString.call(value) 125 | if (type === '[object Object]' || type === '[object Array]') { 126 | value['@c'] = JsonEx._generateId() 127 | 128 | var constructorName = this._getConstructorName(value) 129 | if (constructorName !== 'Object' && constructorName !== 'Array') { 130 | value['@'] = constructorName 131 | } 132 | for (var key in value) { 133 | if (value.hasOwnProperty(key) && !key.match(/^@./)) { 134 | if (value[key] && typeof value[key] === 'object') { 135 | if (value[key]['@c']) { 136 | circular.push([key, value, value[key]]) 137 | value[key] = { '@r': value[key]['@c'] } 138 | } else { 139 | value[key] = this._encode(value[key], circular, depth + 1) 140 | 141 | if (value[key] instanceof Array) { 142 | //wrap array 143 | circular.push([key, value, value[key]]) 144 | 145 | value[key] = { 146 | '@c': value[key]['@c'], 147 | '@a': value[key] 148 | } 149 | } 150 | } 151 | } else { 152 | value[key] = this._encode(value[key], circular, depth + 1) 153 | } 154 | } 155 | } 156 | } 157 | depth-- 158 | return value 159 | } 160 | 161 | /** 162 | * @static 163 | * @method _decode 164 | * @param {Object} value 165 | * @param {Array} circular 166 | * @param {Object} registry 167 | * @return {Object} 168 | * @private 169 | */ 170 | JsonEx._decode = function(value, circular, registry) { 171 | var type = Object.prototype.toString.call(value) 172 | if (type === '[object Object]' || type === '[object Array]') { 173 | registry[value['@c']] = value 174 | 175 | if (value['@']) { 176 | var constructor = window[value['@']] 177 | if (constructor) { 178 | value = this._resetPrototype(value, constructor.prototype) 179 | } 180 | } 181 | for (var key in value) { 182 | if (value.hasOwnProperty(key)) { 183 | if (value[key] && value[key]['@a']) { 184 | //object is array wrapper 185 | var body = value[key]['@a'] 186 | body['@c'] = value[key]['@c'] 187 | value[key] = body 188 | } 189 | if (value[key] && value[key]['@r']) { 190 | //object is reference 191 | circular.push([key, value, value[key]['@r']]) 192 | } 193 | value[key] = this._decode(value[key], circular, registry) 194 | } 195 | } 196 | } 197 | return value 198 | } 199 | 200 | /** 201 | * @static 202 | * @method _getConstructorName 203 | * @param {Object} value 204 | * @return {String} 205 | * @private 206 | */ 207 | JsonEx._getConstructorName = function(value) { 208 | var name = value.constructor.name 209 | if (name === undefined) { 210 | var func = /^\s*function\s*([A-Za-z0-9_$]*)/ 211 | name = func.exec(value.constructor)[1] 212 | } 213 | return name 214 | } 215 | 216 | /** 217 | * @static 218 | * @method _resetPrototype 219 | * @param {Object} value 220 | * @param {Object} prototype 221 | * @return {Object} 222 | * @private 223 | */ 224 | JsonEx._resetPrototype = function(value, prototype) { 225 | if (Object.setPrototypeOf !== undefined) { 226 | Object.setPrototypeOf(value, prototype) 227 | } else if ('__proto__' in value) { 228 | value.__proto__ = prototype 229 | } else { 230 | var newValue = Object.create(prototype) 231 | for (var key in value) { 232 | if (value.hasOwnProperty(key)) { 233 | newValue[key] = value[key] 234 | } 235 | } 236 | value = newValue 237 | } 238 | return value 239 | } 240 | 241 | function Decrypter() { 242 | throw new Error('This is a static class') 243 | } 244 | 245 | Decrypter.hasEncryptedImages = false 246 | Decrypter.hasEncryptedAudio = false 247 | Decrypter._requestImgFile = [] 248 | Decrypter._headerlength = 16 249 | Decrypter._xhrOk = 400 250 | Decrypter._encryptionKey = '' 251 | Decrypter._ignoreList = ['img/system/Window.png'] 252 | Decrypter.SIGNATURE = '5250474d56000000' 253 | Decrypter.VER = '000301' 254 | Decrypter.REMAIN = '0000000000' 255 | 256 | Decrypter.checkImgIgnore = function(url) { 257 | for (var cnt = 0; cnt < this._ignoreList.length; cnt++) { 258 | if (url === this._ignoreList[cnt]) return true 259 | } 260 | return false 261 | } 262 | 263 | Decrypter.decryptImg = function(url, bitmap) { 264 | url = this.extToEncryptExt(url) 265 | 266 | var requestFile = new XMLHttpRequest() 267 | requestFile.open('GET', url) 268 | requestFile.responseType = 'arraybuffer' 269 | requestFile.send() 270 | 271 | requestFile.onload = function() { 272 | if (this.status < Decrypter._xhrOk) { 273 | var arrayBuffer = Decrypter.decryptArrayBuffer(requestFile.response) 274 | bitmap._image.src = Decrypter.createBlobUrl(arrayBuffer) 275 | bitmap._image.addEventListener( 276 | 'load', 277 | (bitmap._loadListener = Bitmap.prototype._onLoad.bind(bitmap)) 278 | ) 279 | bitmap._image.addEventListener( 280 | 'error', 281 | (bitmap._errorListener = 282 | bitmap._loader || Bitmap.prototype._onError.bind(bitmap)) 283 | ) 284 | } 285 | } 286 | 287 | requestFile.onerror = function() { 288 | if (bitmap._loader) { 289 | bitmap._loader() 290 | } else { 291 | bitmap._onError() 292 | } 293 | } 294 | } 295 | 296 | Decrypter.decryptHTML5Audio = function(url, bgm, pos) { 297 | var requestFile = new XMLHttpRequest() 298 | requestFile.open('GET', url) 299 | requestFile.responseType = 'arraybuffer' 300 | requestFile.send() 301 | 302 | requestFile.onload = function() { 303 | if (this.status < Decrypter._xhrOk) { 304 | var arrayBuffer = Decrypter.decryptArrayBuffer(requestFile.response) 305 | var url = Decrypter.createBlobUrl(arrayBuffer) 306 | AudioManager.createDecryptBuffer(url, bgm, pos) 307 | } 308 | } 309 | } 310 | 311 | Decrypter.cutArrayHeader = function(arrayBuffer, length) { 312 | return arrayBuffer.slice(length) 313 | } 314 | 315 | Decrypter.decryptArrayBuffer = function(arrayBuffer) { 316 | if (!arrayBuffer) return null 317 | var header = new Uint8Array(arrayBuffer, 0, this._headerlength) 318 | 319 | var i 320 | var ref = this.SIGNATURE + this.VER + this.REMAIN 321 | var refBytes = new Uint8Array(16) 322 | for (i = 0; i < this._headerlength; i++) { 323 | refBytes[i] = parseInt('0x' + ref.substr(i * 2, 2), 16) 324 | } 325 | for (i = 0; i < this._headerlength; i++) { 326 | if (header[i] !== refBytes[i]) { 327 | throw new Error('Header is wrong') 328 | } 329 | } 330 | 331 | arrayBuffer = this.cutArrayHeader(arrayBuffer, Decrypter._headerlength) 332 | var view = new DataView(arrayBuffer) 333 | this.readEncryptionkey() 334 | if (arrayBuffer) { 335 | var byteArray = new Uint8Array(arrayBuffer) 336 | for (i = 0; i < this._headerlength; i++) { 337 | byteArray[i] = byteArray[i] ^ parseInt(Decrypter._encryptionKey[i], 16) 338 | view.setUint8(i, byteArray[i]) 339 | } 340 | } 341 | 342 | return arrayBuffer 343 | } 344 | 345 | Decrypter.createBlobUrl = function(arrayBuffer) { 346 | var blob = new Blob([arrayBuffer]) 347 | return window.URL.createObjectURL(blob) 348 | } 349 | 350 | Decrypter.extToEncryptExt = function(url) { 351 | var ext = url.split('.').pop() 352 | var encryptedExt = ext 353 | 354 | if (ext === 'ogg') encryptedExt = '.rpgmvo' 355 | else if (ext === 'm4a') encryptedExt = '.rpgmvm' 356 | else if (ext === 'png') encryptedExt = '.rpgmvp' 357 | else encryptedExt = ext 358 | 359 | return url.slice(0, url.lastIndexOf(ext) - 1) + encryptedExt 360 | } 361 | 362 | Decrypter.readEncryptionkey = function() { 363 | this._encryptionKey = $dataSystem.encryptionKey 364 | .split(/(.{2})/) 365 | .filter(Boolean) 366 | } 367 | 368 | //----------------------------------------------------------------------------- 369 | /** 370 | * The static class that handles resource loading. 371 | * 372 | * @class ResourceHandler 373 | */ 374 | function ResourceHandler() { 375 | throw new Error('This is a static class') 376 | } 377 | 378 | ResourceHandler._reloaders = [] 379 | ResourceHandler._defaultRetryInterval = [500, 1000, 3000] 380 | 381 | ResourceHandler.createLoader = function( 382 | url, 383 | retryMethod, 384 | resignMethod, 385 | retryInterval 386 | ) { 387 | retryInterval = retryInterval || this._defaultRetryInterval 388 | var reloaders = this._reloaders 389 | var retryCount = 0 390 | return function() { 391 | if (retryCount < retryInterval.length) { 392 | setTimeout(retryMethod, retryInterval[retryCount]) 393 | retryCount++ 394 | } else { 395 | if (resignMethod) { 396 | resignMethod() 397 | } 398 | if (url) { 399 | if (reloaders.length === 0) { 400 | Graphics.printLoadingError(url) 401 | SceneManager.stop() 402 | } 403 | reloaders.push(function() { 404 | retryCount = 0 405 | retryMethod() 406 | }) 407 | } 408 | } 409 | } 410 | } 411 | 412 | ResourceHandler.exists = function() { 413 | return this._reloaders.length > 0 414 | } 415 | 416 | ResourceHandler.retry = function() { 417 | if (this._reloaders.length > 0) { 418 | Graphics.eraseLoadingError() 419 | SceneManager.resume() 420 | this._reloaders.forEach(function(reloader) { 421 | reloader() 422 | }) 423 | this._reloaders.length = 0 424 | } 425 | } 426 | 427 | export { JsonEx, Decrypter, ResourceHandler } 428 | -------------------------------------------------------------------------------- /src/core/TouchInput.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The static class that handles input data from the mouse and touchscreen. 4 | * 5 | * @class TouchInput 6 | */ 7 | function TouchInput() { 8 | throw new Error('This is a static class') 9 | } 10 | 11 | /** 12 | * Initializes the touch system. 13 | * 14 | * @static 15 | * @method initialize 16 | */ 17 | TouchInput.initialize = function() { 18 | this.clear() 19 | this._setupEventHandlers() 20 | } 21 | 22 | /** 23 | * The wait time of the pseudo key repeat in frames. 24 | * 25 | * @static 26 | * @property keyRepeatWait 27 | * @type Number 28 | */ 29 | TouchInput.keyRepeatWait = 24 30 | 31 | /** 32 | * The interval of the pseudo key repeat in frames. 33 | * 34 | * @static 35 | * @property keyRepeatInterval 36 | * @type Number 37 | */ 38 | TouchInput.keyRepeatInterval = 6 39 | 40 | /** 41 | * Clears all the touch data. 42 | * 43 | * @static 44 | * @method clear 45 | */ 46 | TouchInput.clear = function() { 47 | this._mousePressed = false 48 | this._screenPressed = false 49 | this._pressedTime = 0 50 | this._events = {} 51 | this._events.triggered = false 52 | this._events.cancelled = false 53 | this._events.moved = false 54 | this._events.released = false 55 | this._events.wheelX = 0 56 | this._events.wheelY = 0 57 | this._triggered = false 58 | this._cancelled = false 59 | this._moved = false 60 | this._released = false 61 | this._wheelX = 0 62 | this._wheelY = 0 63 | this._x = 0 64 | this._y = 0 65 | this._date = 0 66 | } 67 | 68 | /** 69 | * Updates the touch data. 70 | * 71 | * @static 72 | * @method update 73 | */ 74 | TouchInput.update = function() { 75 | this._triggered = this._events.triggered 76 | this._cancelled = this._events.cancelled 77 | this._moved = this._events.moved 78 | this._released = this._events.released 79 | this._wheelX = this._events.wheelX 80 | this._wheelY = this._events.wheelY 81 | this._events.triggered = false 82 | this._events.cancelled = false 83 | this._events.moved = false 84 | this._events.released = false 85 | this._events.wheelX = 0 86 | this._events.wheelY = 0 87 | if (this.isPressed()) { 88 | this._pressedTime++ 89 | } 90 | } 91 | 92 | /** 93 | * Checks whether the mouse button or touchscreen is currently pressed down. 94 | * 95 | * @static 96 | * @method isPressed 97 | * @return {Boolean} True if the mouse button or touchscreen is pressed 98 | */ 99 | TouchInput.isPressed = function() { 100 | return this._mousePressed || this._screenPressed 101 | } 102 | 103 | /** 104 | * Checks whether the left mouse button or touchscreen is just pressed. 105 | * 106 | * @static 107 | * @method isTriggered 108 | * @return {Boolean} True if the mouse button or touchscreen is triggered 109 | */ 110 | TouchInput.isTriggered = function() { 111 | return this._triggered 112 | } 113 | 114 | /** 115 | * Checks whether the left mouse button or touchscreen is just pressed 116 | * or a pseudo key repeat occurred. 117 | * 118 | * @static 119 | * @method isRepeated 120 | * @return {Boolean} True if the mouse button or touchscreen is repeated 121 | */ 122 | TouchInput.isRepeated = function() { 123 | return ( 124 | this.isPressed() && 125 | (this._triggered || 126 | (this._pressedTime >= this.keyRepeatWait && 127 | this._pressedTime % this.keyRepeatInterval === 0)) 128 | ) 129 | } 130 | 131 | /** 132 | * Checks whether the left mouse button or touchscreen is kept depressed. 133 | * 134 | * @static 135 | * @method isLongPressed 136 | * @return {Boolean} True if the left mouse button or touchscreen is long-pressed 137 | */ 138 | TouchInput.isLongPressed = function() { 139 | return this.isPressed() && this._pressedTime >= this.keyRepeatWait 140 | } 141 | 142 | /** 143 | * Checks whether the right mouse button is just pressed. 144 | * 145 | * @static 146 | * @method isCancelled 147 | * @return {Boolean} True if the right mouse button is just pressed 148 | */ 149 | TouchInput.isCancelled = function() { 150 | return this._cancelled 151 | } 152 | 153 | /** 154 | * Checks whether the mouse or a finger on the touchscreen is moved. 155 | * 156 | * @static 157 | * @method isMoved 158 | * @return {Boolean} True if the mouse or a finger on the touchscreen is moved 159 | */ 160 | TouchInput.isMoved = function() { 161 | return this._moved 162 | } 163 | 164 | /** 165 | * Checks whether the left mouse button or touchscreen is released. 166 | * 167 | * @static 168 | * @method isReleased 169 | * @return {Boolean} True if the mouse button or touchscreen is released 170 | */ 171 | TouchInput.isReleased = function() { 172 | return this._released 173 | } 174 | 175 | /** 176 | * [read-only] The horizontal scroll amount. 177 | * 178 | * @static 179 | * @property wheelX 180 | * @type Number 181 | */ 182 | Object.defineProperty(TouchInput, 'wheelX', { 183 | get: function() { 184 | return this._wheelX 185 | }, 186 | configurable: true 187 | }) 188 | 189 | /** 190 | * [read-only] The vertical scroll amount. 191 | * 192 | * @static 193 | * @property wheelY 194 | * @type Number 195 | */ 196 | Object.defineProperty(TouchInput, 'wheelY', { 197 | get: function() { 198 | return this._wheelY 199 | }, 200 | configurable: true 201 | }) 202 | 203 | /** 204 | * [read-only] The x coordinate on the canvas area of the latest touch event. 205 | * 206 | * @static 207 | * @property x 208 | * @type Number 209 | */ 210 | Object.defineProperty(TouchInput, 'x', { 211 | get: function() { 212 | return this._x 213 | }, 214 | configurable: true 215 | }) 216 | 217 | /** 218 | * [read-only] The y coordinate on the canvas area of the latest touch event. 219 | * 220 | * @static 221 | * @property y 222 | * @type Number 223 | */ 224 | Object.defineProperty(TouchInput, 'y', { 225 | get: function() { 226 | return this._y 227 | }, 228 | configurable: true 229 | }) 230 | 231 | /** 232 | * [read-only] The time of the last input in milliseconds. 233 | * 234 | * @static 235 | * @property date 236 | * @type Number 237 | */ 238 | Object.defineProperty(TouchInput, 'date', { 239 | get: function() { 240 | return this._date 241 | }, 242 | configurable: true 243 | }) 244 | 245 | /** 246 | * @static 247 | * @method _setupEventHandlers 248 | * @private 249 | */ 250 | TouchInput._setupEventHandlers = function() { 251 | var isSupportPassive = Utils.isSupportPassiveEvent() 252 | document.addEventListener('mousedown', this._onMouseDown.bind(this)) 253 | document.addEventListener('mousemove', this._onMouseMove.bind(this)) 254 | document.addEventListener('mouseup', this._onMouseUp.bind(this)) 255 | document.addEventListener('wheel', this._onWheel.bind(this)) 256 | document.addEventListener( 257 | 'touchstart', 258 | this._onTouchStart.bind(this), 259 | isSupportPassive ? { passive: false } : false 260 | ) 261 | document.addEventListener( 262 | 'touchmove', 263 | this._onTouchMove.bind(this), 264 | isSupportPassive ? { passive: false } : false 265 | ) 266 | document.addEventListener('touchend', this._onTouchEnd.bind(this)) 267 | document.addEventListener('touchcancel', this._onTouchCancel.bind(this)) 268 | document.addEventListener('pointerdown', this._onPointerDown.bind(this)) 269 | } 270 | 271 | /** 272 | * @static 273 | * @method _onMouseDown 274 | * @param {MouseEvent} event 275 | * @private 276 | */ 277 | TouchInput._onMouseDown = function(event) { 278 | if (event.button === 0) { 279 | this._onLeftButtonDown(event) 280 | } else if (event.button === 1) { 281 | this._onMiddleButtonDown(event) 282 | } else if (event.button === 2) { 283 | this._onRightButtonDown(event) 284 | } 285 | } 286 | 287 | /** 288 | * @static 289 | * @method _onLeftButtonDown 290 | * @param {MouseEvent} event 291 | * @private 292 | */ 293 | TouchInput._onLeftButtonDown = function(event) { 294 | var x = Graphics.pageToCanvasX(event.pageX) 295 | var y = Graphics.pageToCanvasY(event.pageY) 296 | if (Graphics.isInsideCanvas(x, y)) { 297 | this._mousePressed = true 298 | this._pressedTime = 0 299 | this._onTrigger(x, y) 300 | } 301 | } 302 | 303 | /** 304 | * @static 305 | * @method _onMiddleButtonDown 306 | * @param {MouseEvent} event 307 | * @private 308 | */ 309 | TouchInput._onMiddleButtonDown = function(event) {} 310 | 311 | /** 312 | * @static 313 | * @method _onRightButtonDown 314 | * @param {MouseEvent} event 315 | * @private 316 | */ 317 | TouchInput._onRightButtonDown = function(event) { 318 | var x = Graphics.pageToCanvasX(event.pageX) 319 | var y = Graphics.pageToCanvasY(event.pageY) 320 | if (Graphics.isInsideCanvas(x, y)) { 321 | this._onCancel(x, y) 322 | } 323 | } 324 | 325 | /** 326 | * @static 327 | * @method _onMouseMove 328 | * @param {MouseEvent} event 329 | * @private 330 | */ 331 | TouchInput._onMouseMove = function(event) { 332 | if (this._mousePressed) { 333 | var x = Graphics.pageToCanvasX(event.pageX) 334 | var y = Graphics.pageToCanvasY(event.pageY) 335 | this._onMove(x, y) 336 | } 337 | } 338 | 339 | /** 340 | * @static 341 | * @method _onMouseUp 342 | * @param {MouseEvent} event 343 | * @private 344 | */ 345 | TouchInput._onMouseUp = function(event) { 346 | if (event.button === 0) { 347 | var x = Graphics.pageToCanvasX(event.pageX) 348 | var y = Graphics.pageToCanvasY(event.pageY) 349 | this._mousePressed = false 350 | this._onRelease(x, y) 351 | } 352 | } 353 | 354 | /** 355 | * @static 356 | * @method _onWheel 357 | * @param {WheelEvent} event 358 | * @private 359 | */ 360 | TouchInput._onWheel = function(event) { 361 | this._events.wheelX += event.deltaX 362 | this._events.wheelY += event.deltaY 363 | event.preventDefault() 364 | } 365 | 366 | /** 367 | * @static 368 | * @method _onTouchStart 369 | * @param {TouchEvent} event 370 | * @private 371 | */ 372 | TouchInput._onTouchStart = function(event) { 373 | for (var i = 0; i < event.changedTouches.length; i++) { 374 | var touch = event.changedTouches[i] 375 | var x = Graphics.pageToCanvasX(touch.pageX) 376 | var y = Graphics.pageToCanvasY(touch.pageY) 377 | if (Graphics.isInsideCanvas(x, y)) { 378 | this._screenPressed = true 379 | this._pressedTime = 0 380 | if (event.touches.length >= 2) { 381 | this._onCancel(x, y) 382 | } else { 383 | this._onTrigger(x, y) 384 | } 385 | event.preventDefault() 386 | } 387 | } 388 | if (window.cordova || window.navigator.standalone) { 389 | event.preventDefault() 390 | } 391 | } 392 | 393 | /** 394 | * @static 395 | * @method _onTouchMove 396 | * @param {TouchEvent} event 397 | * @private 398 | */ 399 | TouchInput._onTouchMove = function(event) { 400 | for (var i = 0; i < event.changedTouches.length; i++) { 401 | var touch = event.changedTouches[i] 402 | var x = Graphics.pageToCanvasX(touch.pageX) 403 | var y = Graphics.pageToCanvasY(touch.pageY) 404 | this._onMove(x, y) 405 | } 406 | } 407 | 408 | /** 409 | * @static 410 | * @method _onTouchEnd 411 | * @param {TouchEvent} event 412 | * @private 413 | */ 414 | TouchInput._onTouchEnd = function(event) { 415 | for (var i = 0; i < event.changedTouches.length; i++) { 416 | var touch = event.changedTouches[i] 417 | var x = Graphics.pageToCanvasX(touch.pageX) 418 | var y = Graphics.pageToCanvasY(touch.pageY) 419 | this._screenPressed = false 420 | this._onRelease(x, y) 421 | } 422 | } 423 | 424 | /** 425 | * @static 426 | * @method _onTouchCancel 427 | * @param {TouchEvent} event 428 | * @private 429 | */ 430 | TouchInput._onTouchCancel = function(event) { 431 | this._screenPressed = false 432 | } 433 | 434 | /** 435 | * @static 436 | * @method _onPointerDown 437 | * @param {PointerEvent} event 438 | * @private 439 | */ 440 | TouchInput._onPointerDown = function(event) { 441 | if (event.pointerType === 'touch' && !event.isPrimary) { 442 | var x = Graphics.pageToCanvasX(event.pageX) 443 | var y = Graphics.pageToCanvasY(event.pageY) 444 | if (Graphics.isInsideCanvas(x, y)) { 445 | // For Microsoft Edge 446 | this._onCancel(x, y) 447 | event.preventDefault() 448 | } 449 | } 450 | } 451 | 452 | /** 453 | * @static 454 | * @method _onTrigger 455 | * @param {Number} x 456 | * @param {Number} y 457 | * @private 458 | */ 459 | TouchInput._onTrigger = function(x, y) { 460 | this._events.triggered = true 461 | this._x = x 462 | this._y = y 463 | this._date = Date.now() 464 | } 465 | 466 | /** 467 | * @static 468 | * @method _onCancel 469 | * @param {Number} x 470 | * @param {Number} y 471 | * @private 472 | */ 473 | TouchInput._onCancel = function(x, y) { 474 | this._events.cancelled = true 475 | this._x = x 476 | this._y = y 477 | } 478 | 479 | /** 480 | * @static 481 | * @method _onMove 482 | * @param {Number} x 483 | * @param {Number} y 484 | * @private 485 | */ 486 | TouchInput._onMove = function(x, y) { 487 | this._events.moved = true 488 | this._x = x 489 | this._y = y 490 | } 491 | 492 | /** 493 | * @static 494 | * @method _onRelease 495 | * @param {Number} x 496 | * @param {Number} y 497 | * @private 498 | */ 499 | TouchInput._onRelease = function(x, y) { 500 | this._events.released = true 501 | this._x = x 502 | this._y = y 503 | } 504 | 505 | export { TouchInput } 506 | -------------------------------------------------------------------------------- /src/core/Sprite.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The basic object that is rendered to the game screen. 4 | * 5 | * @class Sprite 6 | * @constructor 7 | * @param {Bitmap} bitmap The image for the sprite 8 | */ 9 | class Sprite extends PIXI.Sprite { 10 | static voidFilter = new PIXI.filters.VoidFilter() 11 | 12 | // Number of the created objects. 13 | static _counter = 0 14 | 15 | constructor(bitmap) { 16 | const texture = new PIXI.Texture(new PIXI.BaseTexture()) 17 | super(texture) 18 | 19 | this.initialize.apply(this, arguments) 20 | } 21 | 22 | // This is the constructor, but the whole framework calls this initialize method so we need to cut their calls before removing this (not only from here) 23 | initialize(bitmap) { 24 | const texture = new PIXI.Texture(new PIXI.BaseTexture()) 25 | PIXI.Sprite.call(this, texture) 26 | 27 | this._bitmap = null 28 | this._frame = new Rectangle() 29 | this._realFrame = new Rectangle() 30 | this._blendColor = [0, 0, 0, 0] 31 | this._colorTone = [0, 0, 0, 0] 32 | this._canvas = null 33 | this._context = null 34 | this._tintTexture = null 35 | 36 | /** 37 | * use heavy renderer that will reduce border artifacts and apply advanced blendModes 38 | * @type {boolean} 39 | * @private 40 | */ 41 | this._isPicture = false 42 | 43 | this.spriteId = Sprite._counter++ 44 | this.opaque = false 45 | 46 | this.bitmap = bitmap 47 | } 48 | 49 | /** 50 | * The image for the sprite. 51 | * 52 | * @property bitmap 53 | * @type Bitmap 54 | */ 55 | get bitmap() { 56 | return this._bitmap 57 | } 58 | set bitmap(value) { 59 | if (this._bitmap !== value) { 60 | this._bitmap = value 61 | 62 | if (value) { 63 | this._refreshFrame = true 64 | value.addLoadListener(this._onBitmapLoad.bind(this)) 65 | } else { 66 | this._refreshFrame = false 67 | this.texture.frame = Rectangle.emptyRectangle 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * The width of the sprite without the scale. 74 | * 75 | * @property width 76 | * @type Number 77 | */ 78 | get width() { 79 | return this._frame.width 80 | } 81 | set width(value) { 82 | this._frame.width = value 83 | this._refresh() 84 | } 85 | 86 | /** 87 | * The height of the sprite without the scale. 88 | * 89 | * @property height 90 | * @type Number 91 | */ 92 | get height() { 93 | return this._frame.height 94 | } 95 | set height(value) { 96 | this._frame.height = value 97 | this._refresh() 98 | } 99 | 100 | /** 101 | * The opacity of the sprite (0 to 255). 102 | * 103 | * @property opacity 104 | * @type Number 105 | */ 106 | get opacity() { 107 | return this.alpha * 255 108 | } 109 | set opacity(value) { 110 | this.alpha = value.clamp(0, 255) / 255 111 | } 112 | 113 | /** 114 | * Updates the sprite for each frame. 115 | * 116 | * @method update 117 | */ 118 | update() { 119 | this.children.forEach(function(child) { 120 | if (child.update) { 121 | child.update() 122 | } 123 | }) 124 | } 125 | 126 | /** 127 | * Sets the x and y at once. 128 | * 129 | * @method move 130 | * @param {Number} x The x coordinate of the sprite 131 | * @param {Number} y The y coordinate of the sprite 132 | */ 133 | move(x, y) { 134 | this.x = x 135 | this.y = y 136 | } 137 | 138 | /** 139 | * Sets the rectagle of the bitmap that the sprite displays. 140 | * 141 | * @method setFrame 142 | * @param {Number} x The x coordinate of the frame 143 | * @param {Number} y The y coordinate of the frame 144 | * @param {Number} width The width of the frame 145 | * @param {Number} height The height of the frame 146 | */ 147 | setFrame(x, y, width, height) { 148 | this._refreshFrame = false 149 | const frame = this._frame 150 | if ( 151 | x !== frame.x || 152 | y !== frame.y || 153 | width !== frame.width || 154 | height !== frame.height 155 | ) { 156 | frame.x = x 157 | frame.y = y 158 | frame.width = width 159 | frame.height = height 160 | this._refresh() 161 | } 162 | } 163 | 164 | /** 165 | * Gets the blend color for the sprite. 166 | * 167 | * @method getBlendColor 168 | * @return {Array} The blend color [r, g, b, a] 169 | */ 170 | getBlendColor() { 171 | return this._blendColor.clone() 172 | } 173 | 174 | /** 175 | * Sets the blend color for the sprite. 176 | * 177 | * @method setBlendColor 178 | * @param {Array} color The blend color [r, g, b, a] 179 | */ 180 | setBlendColor(color) { 181 | if (!(color instanceof Array)) { 182 | throw new Error('Argument must be an array') 183 | } 184 | if (!this._blendColor.equals(color)) { 185 | this._blendColor = color.clone() 186 | this._refresh() 187 | } 188 | } 189 | 190 | /** 191 | * Gets the color tone for the sprite. 192 | * 193 | * @method getColorTone 194 | * @return {Array} The color tone [r, g, b, gray] 195 | */ 196 | getColorTone() { 197 | return this._colorTone.clone() 198 | } 199 | 200 | /** 201 | * Sets the color tone for the sprite. 202 | * 203 | * @method setColorTone 204 | * @param {Array} tone The color tone [r, g, b, gray] 205 | */ 206 | setColorTone(tone) { 207 | if (!(tone instanceof Array)) { 208 | throw new Error('Argument must be an array') 209 | } 210 | if (!this._colorTone.equals(tone)) { 211 | this._colorTone = tone.clone() 212 | this._refresh() 213 | } 214 | } 215 | 216 | /** 217 | * @method _onBitmapLoad 218 | * @private 219 | */ 220 | _onBitmapLoad(bitmapLoaded) { 221 | if (bitmapLoaded === this._bitmap) { 222 | if (this._refreshFrame && this._bitmap) { 223 | this._refreshFrame = false 224 | this._frame.width = this._bitmap.width 225 | this._frame.height = this._bitmap.height 226 | } 227 | } 228 | 229 | this._refresh() 230 | } 231 | 232 | /** 233 | * @method _refresh 234 | * @private 235 | */ 236 | _refresh() { 237 | const frameX = Math.floor(this._frame.x) 238 | const frameY = Math.floor(this._frame.y) 239 | const frameW = Math.floor(this._frame.width) 240 | const frameH = Math.floor(this._frame.height) 241 | const bitmapW = this._bitmap ? this._bitmap.width : 0 242 | const bitmapH = this._bitmap ? this._bitmap.height : 0 243 | const realX = frameX.clamp(0, bitmapW) 244 | const realY = frameY.clamp(0, bitmapH) 245 | const realW = (frameW - realX + frameX).clamp(0, bitmapW - realX) 246 | const realH = (frameH - realY + frameY).clamp(0, bitmapH - realY) 247 | 248 | this._realFrame.x = realX 249 | this._realFrame.y = realY 250 | this._realFrame.width = realW 251 | this._realFrame.height = realH 252 | this.pivot.x = frameX - realX 253 | this.pivot.y = frameY - realY 254 | 255 | if (realW > 0 && realH > 0) { 256 | if (this._needsTint()) { 257 | this._createTinter(realW, realH) 258 | this._executeTint(realX, realY, realW, realH) 259 | this._tintTexture.update() 260 | this.texture.baseTexture = this._tintTexture 261 | this.texture.frame = new Rectangle(0, 0, realW, realH) 262 | } else { 263 | if (this._bitmap) { 264 | this.texture.baseTexture = this._bitmap.baseTexture 265 | } 266 | this.texture.frame = this._realFrame 267 | } 268 | } else if (this._bitmap) { 269 | this.texture.frame = Rectangle.emptyRectangle 270 | } else { 271 | this.texture.baseTexture.width = Math.max( 272 | this.texture.baseTexture.width, 273 | this._frame.x + this._frame.width 274 | ) 275 | this.texture.baseTexture.height = Math.max( 276 | this.texture.baseTexture.height, 277 | this._frame.y + this._frame.height 278 | ) 279 | this.texture.frame = this._frame 280 | } 281 | this.texture._updateID++ 282 | } 283 | 284 | /** 285 | * @method _isInBitmapRect 286 | * @param {Number} x 287 | * @param {Number} y 288 | * @param {Number} w 289 | * @param {Number} h 290 | * @return {Boolean} 291 | * @private 292 | */ 293 | _isInBitmapRect(x, y, w, h) { 294 | return ( 295 | this._bitmap && 296 | x + w > 0 && 297 | y + h > 0 && 298 | x < this._bitmap.width && 299 | y < this._bitmap.height 300 | ) 301 | } 302 | 303 | /** 304 | * @method _needsTint 305 | * @return {Boolean} 306 | * @private 307 | */ 308 | _needsTint() { 309 | const tone = this._colorTone 310 | return tone[0] || tone[1] || tone[2] || tone[3] || this._blendColor[3] > 0 311 | } 312 | 313 | /** 314 | * @method _createTinter 315 | * @param {Number} w 316 | * @param {Number} h 317 | * @private 318 | */ 319 | _createTinter(w, h) { 320 | if (!this._canvas) { 321 | this._canvas = document.createElement('canvas') 322 | this._context = this._canvas.getContext('2d') 323 | } 324 | 325 | this._canvas.width = w 326 | this._canvas.height = h 327 | 328 | if (!this._tintTexture) { 329 | this._tintTexture = new PIXI.BaseTexture(this._canvas) 330 | } 331 | 332 | this._tintTexture.width = w 333 | this._tintTexture.height = h 334 | this._tintTexture.scaleMode = this._bitmap.baseTexture.scaleMode 335 | } 336 | 337 | /** 338 | * @method _executeTint 339 | * @param {Number} x 340 | * @param {Number} y 341 | * @param {Number} w 342 | * @param {Number} h 343 | * @private 344 | */ 345 | _executeTint(x, y, w, h) { 346 | const context = this._context 347 | const tone = this._colorTone 348 | const color = this._blendColor 349 | 350 | context.globalCompositeOperation = 'copy' 351 | context.drawImage(this._bitmap.canvas, x, y, w, h, 0, 0, w, h) 352 | 353 | if (Graphics.canUseSaturationBlend()) { 354 | const gray = Math.max(0, tone[3]) 355 | context.globalCompositeOperation = 'saturation' 356 | context.fillStyle = 'rgba(255,255,255,' + gray / 255 + ')' 357 | context.fillRect(0, 0, w, h) 358 | } 359 | 360 | const r1 = Math.max(0, tone[0]) 361 | const g1 = Math.max(0, tone[1]) 362 | const b1 = Math.max(0, tone[2]) 363 | context.globalCompositeOperation = 'lighter' 364 | context.fillStyle = Utils.rgbToCssColor(r1, g1, b1) 365 | context.fillRect(0, 0, w, h) 366 | 367 | if (Graphics.canUseDifferenceBlend()) { 368 | context.globalCompositeOperation = 'difference' 369 | context.fillStyle = 'white' 370 | context.fillRect(0, 0, w, h) 371 | 372 | const r2 = Math.max(0, -tone[0]) 373 | const g2 = Math.max(0, -tone[1]) 374 | const b2 = Math.max(0, -tone[2]) 375 | context.globalCompositeOperation = 'lighter' 376 | context.fillStyle = Utils.rgbToCssColor(r2, g2, b2) 377 | context.fillRect(0, 0, w, h) 378 | 379 | context.globalCompositeOperation = 'difference' 380 | context.fillStyle = 'white' 381 | context.fillRect(0, 0, w, h) 382 | } 383 | 384 | const r3 = Math.max(0, color[0]) 385 | const g3 = Math.max(0, color[1]) 386 | const b3 = Math.max(0, color[2]) 387 | const a3 = Math.max(0, color[3]) 388 | context.globalCompositeOperation = 'source-atop' 389 | context.fillStyle = Utils.rgbToCssColor(r3, g3, b3) 390 | context.globalAlpha = a3 / 255 391 | context.fillRect(0, 0, w, h) 392 | 393 | context.globalCompositeOperation = 'destination-in' 394 | context.globalAlpha = 1 395 | context.drawImage(this._bitmap.canvas, x, y, w, h, 0, 0, w, h) 396 | } 397 | 398 | _renderCanvas_PIXI = super._renderCanvas 399 | _renderWebGL_PIXI = super._renderWebGL 400 | 401 | /** 402 | * @method _renderCanvas 403 | * @param {Object} renderer 404 | * @private 405 | */ 406 | _renderCanvas(renderer) { 407 | if (this.bitmap) { 408 | this.bitmap.touch() 409 | } 410 | if (this.bitmap && !this.bitmap.isReady()) { 411 | return 412 | } 413 | 414 | if (this.texture.frame.width > 0 && this.texture.frame.height > 0) { 415 | this._renderCanvas_PIXI(renderer) 416 | } 417 | } 418 | 419 | /** 420 | * checks if we need to speed up custom blendmodes 421 | * @param renderer 422 | * @private 423 | */ 424 | _speedUpCustomBlendModes(renderer) { 425 | const picture = renderer.plugins.picture 426 | const blend = this.blendMode 427 | if (renderer.renderingToScreen && renderer._activeRenderTarget.root) { 428 | if (picture.drawModes[blend]) { 429 | const stage = renderer._lastObjectRendered 430 | var f = stage._filters 431 | if (!f || !f[0]) { 432 | setTimeout(function() { 433 | var f = stage._filters 434 | if (!f || !f[0]) { 435 | stage.filters = [Sprite.voidFilter] 436 | stage.filterArea = new PIXI.Rectangle( 437 | 0, 438 | 0, 439 | Graphics.width, 440 | Graphics.height 441 | ) 442 | } 443 | }, 0) 444 | } 445 | } 446 | } 447 | } 448 | 449 | /** 450 | * @method _renderWebGL 451 | * @param {Object} renderer 452 | * @private 453 | */ 454 | _renderWebGL(renderer) { 455 | if (this.bitmap) { 456 | this.bitmap.touch() 457 | } 458 | if (this.bitmap && !this.bitmap.isReady()) { 459 | return 460 | } 461 | if (this.texture.frame.width > 0 && this.texture.frame.height > 0) { 462 | if (this._bitmap) { 463 | this._bitmap.checkDirty() 464 | } 465 | 466 | //copy of pixi-v4 internal code 467 | this.calculateVertices() 468 | 469 | if (this.pluginName === 'sprite' && this._isPicture) { 470 | // use heavy renderer, which reduces artifacts and applies corrent blendMode, 471 | // but does not use multitexture optimization 472 | this._speedUpCustomBlendModes(renderer) 473 | renderer.setObjectRenderer(renderer.plugins.picture) 474 | renderer.plugins.picture.render(this) 475 | } else { 476 | // use pixi super-speed renderer 477 | renderer.setObjectRenderer(renderer.plugins[this.pluginName]) 478 | renderer.plugins[this.pluginName].render(this) 479 | } 480 | } 481 | } 482 | } 483 | 484 | // The important members from Pixi.js 485 | 486 | /** 487 | * The visibility of the sprite. 488 | * 489 | * @property visible 490 | * @type Boolean 491 | */ 492 | 493 | /** 494 | * The x coordinate of the sprite. 495 | * 496 | * @property x 497 | * @type Number 498 | */ 499 | 500 | /** 501 | * The y coordinate of the sprite. 502 | * 503 | * @property y 504 | * @type Number 505 | */ 506 | 507 | /** 508 | * The origin point of the sprite. (0,0) to (1,1). 509 | * 510 | * @property anchor 511 | * @type Point 512 | */ 513 | 514 | /** 515 | * The scale factor of the sprite. 516 | * 517 | * @property scale 518 | * @type Point 519 | */ 520 | 521 | /** 522 | * The rotation of the sprite in radians. 523 | * 524 | * @property rotation 525 | * @type Number 526 | */ 527 | 528 | /** 529 | * The blend mode to be applied to the sprite. 530 | * 531 | * @property blendMode 532 | * @type Number 533 | */ 534 | 535 | /** 536 | * Sets the filters for the sprite. 537 | * 538 | * @property filters 539 | * @type Array 540 | */ 541 | 542 | /** 543 | * [read-only] The array of children of the sprite. 544 | * 545 | * @property children 546 | * @type Array 547 | */ 548 | 549 | /** 550 | * [read-only] The object that contains the sprite. 551 | * 552 | * @property parent 553 | * @type Object 554 | */ 555 | 556 | /** 557 | * Adds a child to the container. 558 | * 559 | * @method addChild 560 | * @param {Object} child The child to add 561 | * @return {Object} The child that was added 562 | */ 563 | 564 | /** 565 | * Adds a child to the container at a specified index. 566 | * 567 | * @method addChildAt 568 | * @param {Object} child The child to add 569 | * @param {Number} index The index to place the child in 570 | * @return {Object} The child that was added 571 | */ 572 | 573 | /** 574 | * Removes a child from the container. 575 | * 576 | * @method removeChild 577 | * @param {Object} child The child to remove 578 | * @return {Object} The child that was removed 579 | */ 580 | 581 | /** 582 | * Removes a child from the specified index position. 583 | * 584 | * @method removeChildAt 585 | * @param {Number} index The index to get the child from 586 | * @return {Object} The child that was removed 587 | */ 588 | 589 | export { Sprite } 590 | -------------------------------------------------------------------------------- /src/core/Window.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /** 3 | * The window in the game. 4 | * 5 | * @class Window 6 | * @constructor 7 | */ 8 | function Window() { 9 | this.initialize.apply(this, arguments) 10 | } 11 | 12 | Window.prototype = Object.create(PIXI.Container.prototype) 13 | Window.prototype.constructor = Window 14 | 15 | Window.prototype.initialize = function() { 16 | PIXI.Container.call(this) 17 | 18 | this._isWindow = true 19 | this._windowskin = null 20 | this._width = 0 21 | this._height = 0 22 | this._cursorRect = new Rectangle() 23 | this._openness = 255 24 | this._animationCount = 0 25 | 26 | this._padding = 18 27 | this._margin = 4 28 | this._colorTone = [0, 0, 0] 29 | 30 | this._windowSpriteContainer = null 31 | this._windowBackSprite = null 32 | this._windowCursorSprite = null 33 | this._windowFrameSprite = null 34 | this._windowContentsSprite = null 35 | this._windowArrowSprites = [] 36 | this._windowPauseSignSprite = null 37 | 38 | this._createAllParts() 39 | 40 | /** 41 | * The origin point of the window for scrolling. 42 | * 43 | * @property origin 44 | * @type Point 45 | */ 46 | this.origin = new Point() 47 | 48 | /** 49 | * The active state for the window. 50 | * 51 | * @property active 52 | * @type Boolean 53 | */ 54 | this.active = true 55 | 56 | /** 57 | * The visibility of the down scroll arrow. 58 | * 59 | * @property downArrowVisible 60 | * @type Boolean 61 | */ 62 | this.downArrowVisible = false 63 | 64 | /** 65 | * The visibility of the up scroll arrow. 66 | * 67 | * @property upArrowVisible 68 | * @type Boolean 69 | */ 70 | this.upArrowVisible = false 71 | 72 | /** 73 | * The visibility of the pause sign. 74 | * 75 | * @property pause 76 | * @type Boolean 77 | */ 78 | this.pause = false 79 | } 80 | 81 | /** 82 | * The image used as a window skin. 83 | * 84 | * @property windowskin 85 | * @type Bitmap 86 | */ 87 | Object.defineProperty(Window.prototype, 'windowskin', { 88 | get: function() { 89 | return this._windowskin 90 | }, 91 | set: function(value) { 92 | if (this._windowskin !== value) { 93 | this._windowskin = value 94 | this._windowskin.addLoadListener(this._onWindowskinLoad.bind(this)) 95 | } 96 | }, 97 | configurable: true 98 | }) 99 | 100 | /** 101 | * The bitmap used for the window contents. 102 | * 103 | * @property contents 104 | * @type Bitmap 105 | */ 106 | Object.defineProperty(Window.prototype, 'contents', { 107 | get: function() { 108 | return this._windowContentsSprite.bitmap 109 | }, 110 | set: function(value) { 111 | this._windowContentsSprite.bitmap = value 112 | }, 113 | configurable: true 114 | }) 115 | 116 | /** 117 | * The width of the window in pixels. 118 | * 119 | * @property width 120 | * @type Number 121 | */ 122 | Object.defineProperty(Window.prototype, 'width', { 123 | get: function() { 124 | return this._width 125 | }, 126 | set: function(value) { 127 | this._width = value 128 | this._refreshAllParts() 129 | }, 130 | configurable: true 131 | }) 132 | 133 | /** 134 | * The height of the window in pixels. 135 | * 136 | * @property height 137 | * @type Number 138 | */ 139 | Object.defineProperty(Window.prototype, 'height', { 140 | get: function() { 141 | return this._height 142 | }, 143 | set: function(value) { 144 | this._height = value 145 | this._refreshAllParts() 146 | }, 147 | configurable: true 148 | }) 149 | 150 | /** 151 | * The size of the padding between the frame and contents. 152 | * 153 | * @property padding 154 | * @type Number 155 | */ 156 | Object.defineProperty(Window.prototype, 'padding', { 157 | get: function() { 158 | return this._padding 159 | }, 160 | set: function(value) { 161 | this._padding = value 162 | this._refreshAllParts() 163 | }, 164 | configurable: true 165 | }) 166 | 167 | /** 168 | * The size of the margin for the window background. 169 | * 170 | * @property margin 171 | * @type Number 172 | */ 173 | Object.defineProperty(Window.prototype, 'margin', { 174 | get: function() { 175 | return this._margin 176 | }, 177 | set: function(value) { 178 | this._margin = value 179 | this._refreshAllParts() 180 | }, 181 | configurable: true 182 | }) 183 | 184 | /** 185 | * The opacity of the window without contents (0 to 255). 186 | * 187 | * @property opacity 188 | * @type Number 189 | */ 190 | Object.defineProperty(Window.prototype, 'opacity', { 191 | get: function() { 192 | return this._windowSpriteContainer.alpha * 255 193 | }, 194 | set: function(value) { 195 | this._windowSpriteContainer.alpha = value.clamp(0, 255) / 255 196 | }, 197 | configurable: true 198 | }) 199 | 200 | /** 201 | * The opacity of the window background (0 to 255). 202 | * 203 | * @property backOpacity 204 | * @type Number 205 | */ 206 | Object.defineProperty(Window.prototype, 'backOpacity', { 207 | get: function() { 208 | return this._windowBackSprite.alpha * 255 209 | }, 210 | set: function(value) { 211 | this._windowBackSprite.alpha = value.clamp(0, 255) / 255 212 | }, 213 | configurable: true 214 | }) 215 | 216 | /** 217 | * The opacity of the window contents (0 to 255). 218 | * 219 | * @property contentsOpacity 220 | * @type Number 221 | */ 222 | Object.defineProperty(Window.prototype, 'contentsOpacity', { 223 | get: function() { 224 | return this._windowContentsSprite.alpha * 255 225 | }, 226 | set: function(value) { 227 | this._windowContentsSprite.alpha = value.clamp(0, 255) / 255 228 | }, 229 | configurable: true 230 | }) 231 | 232 | /** 233 | * The openness of the window (0 to 255). 234 | * 235 | * @property openness 236 | * @type Number 237 | */ 238 | Object.defineProperty(Window.prototype, 'openness', { 239 | get: function() { 240 | return this._openness 241 | }, 242 | set: function(value) { 243 | if (this._openness !== value) { 244 | this._openness = value.clamp(0, 255) 245 | this._windowSpriteContainer.scale.y = this._openness / 255 246 | this._windowSpriteContainer.y = 247 | this.height / 2 * (1 - this._openness / 255) 248 | } 249 | }, 250 | configurable: true 251 | }) 252 | 253 | /** 254 | * Updates the window for each frame. 255 | * 256 | * @method update 257 | */ 258 | Window.prototype.update = function() { 259 | if (this.active) { 260 | this._animationCount++ 261 | } 262 | this.children.forEach(function(child) { 263 | if (child.update) { 264 | child.update() 265 | } 266 | }) 267 | } 268 | 269 | /** 270 | * Sets the x, y, width, and height all at once. 271 | * 272 | * @method move 273 | * @param {Number} x The x coordinate of the window 274 | * @param {Number} y The y coordinate of the window 275 | * @param {Number} width The width of the window 276 | * @param {Number} height The height of the window 277 | */ 278 | Window.prototype.move = function(x, y, width, height) { 279 | this.x = x || 0 280 | this.y = y || 0 281 | if (this._width !== width || this._height !== height) { 282 | this._width = width || 0 283 | this._height = height || 0 284 | this._refreshAllParts() 285 | } 286 | } 287 | 288 | /** 289 | * Returns true if the window is completely open (openness == 255). 290 | * 291 | * @method isOpen 292 | */ 293 | Window.prototype.isOpen = function() { 294 | return this._openness >= 255 295 | } 296 | 297 | /** 298 | * Returns true if the window is completely closed (openness == 0). 299 | * 300 | * @method isClosed 301 | */ 302 | Window.prototype.isClosed = function() { 303 | return this._openness <= 0 304 | } 305 | 306 | /** 307 | * Sets the position of the command cursor. 308 | * 309 | * @method setCursorRect 310 | * @param {Number} x The x coordinate of the cursor 311 | * @param {Number} y The y coordinate of the cursor 312 | * @param {Number} width The width of the cursor 313 | * @param {Number} height The height of the cursor 314 | */ 315 | Window.prototype.setCursorRect = function(x, y, width, height) { 316 | var cx = Math.floor(x || 0) 317 | var cy = Math.floor(y || 0) 318 | var cw = Math.floor(width || 0) 319 | var ch = Math.floor(height || 0) 320 | var rect = this._cursorRect 321 | if ( 322 | rect.x !== cx || 323 | rect.y !== cy || 324 | rect.width !== cw || 325 | rect.height !== ch 326 | ) { 327 | this._cursorRect.x = cx 328 | this._cursorRect.y = cy 329 | this._cursorRect.width = cw 330 | this._cursorRect.height = ch 331 | this._refreshCursor() 332 | } 333 | } 334 | 335 | /** 336 | * Changes the color of the background. 337 | * 338 | * @method setTone 339 | * @param {Number} r The red value in the range (-255, 255) 340 | * @param {Number} g The green value in the range (-255, 255) 341 | * @param {Number} b The blue value in the range (-255, 255) 342 | */ 343 | Window.prototype.setTone = function(r, g, b) { 344 | var tone = this._colorTone 345 | if (r !== tone[0] || g !== tone[1] || b !== tone[2]) { 346 | this._colorTone = [r, g, b] 347 | this._refreshBack() 348 | } 349 | } 350 | 351 | /** 352 | * Adds a child between the background and contents. 353 | * 354 | * @method addChildToBack 355 | * @param {Object} child The child to add 356 | * @return {Object} The child that was added 357 | */ 358 | Window.prototype.addChildToBack = function(child) { 359 | var containerIndex = this.children.indexOf(this._windowSpriteContainer) 360 | return this.addChildAt(child, containerIndex + 1) 361 | } 362 | 363 | /** 364 | * @method updateTransform 365 | * @private 366 | */ 367 | Window.prototype.updateTransform = function() { 368 | this._updateCursor() 369 | this._updateArrows() 370 | this._updatePauseSign() 371 | this._updateContents() 372 | PIXI.Container.prototype.updateTransform.call(this) 373 | } 374 | 375 | /** 376 | * @method _createAllParts 377 | * @private 378 | */ 379 | Window.prototype._createAllParts = function() { 380 | this._windowSpriteContainer = new PIXI.Container() 381 | this._windowBackSprite = new Sprite() 382 | this._windowCursorSprite = new Sprite() 383 | this._windowFrameSprite = new Sprite() 384 | this._windowContentsSprite = new Sprite() 385 | this._downArrowSprite = new Sprite() 386 | this._upArrowSprite = new Sprite() 387 | this._windowPauseSignSprite = new Sprite() 388 | this._windowBackSprite.bitmap = new Bitmap(1, 1) 389 | this._windowBackSprite.alpha = 192 / 255 390 | this.addChild(this._windowSpriteContainer) 391 | this._windowSpriteContainer.addChild(this._windowBackSprite) 392 | this._windowSpriteContainer.addChild(this._windowFrameSprite) 393 | this.addChild(this._windowCursorSprite) 394 | this.addChild(this._windowContentsSprite) 395 | this.addChild(this._downArrowSprite) 396 | this.addChild(this._upArrowSprite) 397 | this.addChild(this._windowPauseSignSprite) 398 | } 399 | 400 | /** 401 | * @method _onWindowskinLoad 402 | * @private 403 | */ 404 | Window.prototype._onWindowskinLoad = function() { 405 | this._refreshAllParts() 406 | } 407 | 408 | /** 409 | * @method _refreshAllParts 410 | * @private 411 | */ 412 | Window.prototype._refreshAllParts = function() { 413 | this._refreshBack() 414 | this._refreshFrame() 415 | this._refreshCursor() 416 | this._refreshContents() 417 | this._refreshArrows() 418 | this._refreshPauseSign() 419 | } 420 | 421 | /** 422 | * @method _refreshBack 423 | * @private 424 | */ 425 | Window.prototype._refreshBack = function() { 426 | var m = this._margin 427 | var w = this._width - m * 2 428 | var h = this._height - m * 2 429 | var bitmap = new Bitmap(w, h) 430 | 431 | this._windowBackSprite.bitmap = bitmap 432 | this._windowBackSprite.setFrame(0, 0, w, h) 433 | this._windowBackSprite.move(m, m) 434 | 435 | if (w > 0 && h > 0 && this._windowskin) { 436 | var p = 96 437 | bitmap.blt(this._windowskin, 0, 0, p, p, 0, 0, w, h) 438 | for (var y = 0; y < h; y += p) { 439 | for (var x = 0; x < w; x += p) { 440 | bitmap.blt(this._windowskin, 0, p, p, p, x, y, p, p) 441 | } 442 | } 443 | var tone = this._colorTone 444 | bitmap.adjustTone(tone[0], tone[1], tone[2]) 445 | } 446 | } 447 | 448 | /** 449 | * @method _refreshFrame 450 | * @private 451 | */ 452 | Window.prototype._refreshFrame = function() { 453 | var w = this._width 454 | var h = this._height 455 | var m = 24 456 | var bitmap = new Bitmap(w, h) 457 | 458 | this._windowFrameSprite.bitmap = bitmap 459 | this._windowFrameSprite.setFrame(0, 0, w, h) 460 | 461 | if (w > 0 && h > 0 && this._windowskin) { 462 | var skin = this._windowskin 463 | var p = 96 464 | var q = 96 465 | bitmap.blt(skin, p + m, 0 + 0, p - m * 2, m, m, 0, w - m * 2, m) 466 | bitmap.blt(skin, p + m, 0 + q - m, p - m * 2, m, m, h - m, w - m * 2, m) 467 | bitmap.blt(skin, p + 0, 0 + m, m, p - m * 2, 0, m, m, h - m * 2) 468 | bitmap.blt(skin, p + q - m, 0 + m, m, p - m * 2, w - m, m, m, h - m * 2) 469 | bitmap.blt(skin, p + 0, 0 + 0, m, m, 0, 0, m, m) 470 | bitmap.blt(skin, p + q - m, 0 + 0, m, m, w - m, 0, m, m) 471 | bitmap.blt(skin, p + 0, 0 + q - m, m, m, 0, h - m, m, m) 472 | bitmap.blt(skin, p + q - m, 0 + q - m, m, m, w - m, h - m, m, m) 473 | } 474 | } 475 | 476 | /** 477 | * @method _refreshCursor 478 | * @private 479 | */ 480 | Window.prototype._refreshCursor = function() { 481 | var pad = this._padding 482 | var x = this._cursorRect.x + pad - this.origin.x 483 | var y = this._cursorRect.y + pad - this.origin.y 484 | var w = this._cursorRect.width 485 | var h = this._cursorRect.height 486 | var m = 4 487 | var x2 = Math.max(x, pad) 488 | var y2 = Math.max(y, pad) 489 | var ox = x - x2 490 | var oy = y - y2 491 | var w2 = Math.min(w, this._width - pad - x2) 492 | var h2 = Math.min(h, this._height - pad - y2) 493 | var bitmap = new Bitmap(w2, h2) 494 | 495 | this._windowCursorSprite.bitmap = bitmap 496 | this._windowCursorSprite.setFrame(0, 0, w2, h2) 497 | this._windowCursorSprite.move(x2, y2) 498 | 499 | if (w > 0 && h > 0 && this._windowskin) { 500 | var skin = this._windowskin 501 | var p = 96 502 | var q = 48 503 | bitmap.blt( 504 | skin, 505 | p + m, 506 | p + m, 507 | q - m * 2, 508 | q - m * 2, 509 | ox + m, 510 | oy + m, 511 | w - m * 2, 512 | h - m * 2 513 | ) 514 | bitmap.blt(skin, p + m, p + 0, q - m * 2, m, ox + m, oy + 0, w - m * 2, m) 515 | bitmap.blt( 516 | skin, 517 | p + m, 518 | p + q - m, 519 | q - m * 2, 520 | m, 521 | ox + m, 522 | oy + h - m, 523 | w - m * 2, 524 | m 525 | ) 526 | bitmap.blt(skin, p + 0, p + m, m, q - m * 2, ox + 0, oy + m, m, h - m * 2) 527 | bitmap.blt( 528 | skin, 529 | p + q - m, 530 | p + m, 531 | m, 532 | q - m * 2, 533 | ox + w - m, 534 | oy + m, 535 | m, 536 | h - m * 2 537 | ) 538 | bitmap.blt(skin, p + 0, p + 0, m, m, ox + 0, oy + 0, m, m) 539 | bitmap.blt(skin, p + q - m, p + 0, m, m, ox + w - m, oy + 0, m, m) 540 | bitmap.blt(skin, p + 0, p + q - m, m, m, ox + 0, oy + h - m, m, m) 541 | bitmap.blt(skin, p + q - m, p + q - m, m, m, ox + w - m, oy + h - m, m, m) 542 | } 543 | } 544 | 545 | /** 546 | * @method _refreshContents 547 | * @private 548 | */ 549 | Window.prototype._refreshContents = function() { 550 | this._windowContentsSprite.move(this.padding, this.padding) 551 | } 552 | 553 | /** 554 | * @method _refreshArrows 555 | * @private 556 | */ 557 | Window.prototype._refreshArrows = function() { 558 | var w = this._width 559 | var h = this._height 560 | var p = 24 561 | var q = p / 2 562 | var sx = 96 + p 563 | var sy = 0 + p 564 | this._downArrowSprite.bitmap = this._windowskin 565 | this._downArrowSprite.anchor.x = 0.5 566 | this._downArrowSprite.anchor.y = 0.5 567 | this._downArrowSprite.setFrame(sx + q, sy + q + p, p, q) 568 | this._downArrowSprite.move(w / 2, h - q) 569 | this._upArrowSprite.bitmap = this._windowskin 570 | this._upArrowSprite.anchor.x = 0.5 571 | this._upArrowSprite.anchor.y = 0.5 572 | this._upArrowSprite.setFrame(sx + q, sy, p, q) 573 | this._upArrowSprite.move(w / 2, q) 574 | } 575 | 576 | /** 577 | * @method _refreshPauseSign 578 | * @private 579 | */ 580 | Window.prototype._refreshPauseSign = function() { 581 | var sx = 144 582 | var sy = 96 583 | var p = 24 584 | this._windowPauseSignSprite.bitmap = this._windowskin 585 | this._windowPauseSignSprite.anchor.x = 0.5 586 | this._windowPauseSignSprite.anchor.y = 1 587 | this._windowPauseSignSprite.move(this._width / 2, this._height) 588 | this._windowPauseSignSprite.setFrame(sx, sy, p, p) 589 | this._windowPauseSignSprite.alpha = 0 590 | } 591 | 592 | /** 593 | * @method _updateCursor 594 | * @private 595 | */ 596 | Window.prototype._updateCursor = function() { 597 | var blinkCount = this._animationCount % 40 598 | var cursorOpacity = this.contentsOpacity 599 | if (this.active) { 600 | if (blinkCount < 20) { 601 | cursorOpacity -= blinkCount * 8 602 | } else { 603 | cursorOpacity -= (40 - blinkCount) * 8 604 | } 605 | } 606 | this._windowCursorSprite.alpha = cursorOpacity / 255 607 | this._windowCursorSprite.visible = this.isOpen() 608 | } 609 | 610 | /** 611 | * @method _updateContents 612 | * @private 613 | */ 614 | Window.prototype._updateContents = function() { 615 | var w = this._width - this._padding * 2 616 | var h = this._height - this._padding * 2 617 | if (w > 0 && h > 0) { 618 | this._windowContentsSprite.setFrame(this.origin.x, this.origin.y, w, h) 619 | this._windowContentsSprite.visible = this.isOpen() 620 | } else { 621 | this._windowContentsSprite.visible = false 622 | } 623 | } 624 | 625 | /** 626 | * @method _updateArrows 627 | * @private 628 | */ 629 | Window.prototype._updateArrows = function() { 630 | this._downArrowSprite.visible = this.isOpen() && this.downArrowVisible 631 | this._upArrowSprite.visible = this.isOpen() && this.upArrowVisible 632 | } 633 | 634 | /** 635 | * @method _updatePauseSign 636 | * @private 637 | */ 638 | Window.prototype._updatePauseSign = function() { 639 | var sprite = this._windowPauseSignSprite 640 | var x = Math.floor(this._animationCount / 16) % 2 641 | var y = Math.floor(this._animationCount / 16 / 2) % 2 642 | var sx = 144 643 | var sy = 96 644 | var p = 24 645 | if (!this.pause) { 646 | sprite.alpha = 0 647 | } else if (sprite.alpha < 1) { 648 | sprite.alpha = Math.min(sprite.alpha + 0.1, 1) 649 | } 650 | sprite.setFrame(sx + x * p, sy + y * p, p, p) 651 | sprite.visible = this.isOpen() 652 | } 653 | 654 | // The important members from Pixi.js 655 | 656 | /** 657 | * The visibility of the window. 658 | * 659 | * @property visible 660 | * @type Boolean 661 | */ 662 | 663 | /** 664 | * The x coordinate of the window. 665 | * 666 | * @property x 667 | * @type Number 668 | */ 669 | 670 | /** 671 | * The y coordinate of the window. 672 | * 673 | * @property y 674 | * @type Number 675 | */ 676 | 677 | /** 678 | * [read-only] The array of children of the window. 679 | * 680 | * @property children 681 | * @type Array 682 | */ 683 | 684 | /** 685 | * [read-only] The object that contains the window. 686 | * 687 | * @property parent 688 | * @type Object 689 | */ 690 | 691 | /** 692 | * Adds a child to the container. 693 | * 694 | * @method addChild 695 | * @param {Object} child The child to add 696 | * @return {Object} The child that was added 697 | */ 698 | 699 | /** 700 | * Adds a child to the container at a specified index. 701 | * 702 | * @method addChildAt 703 | * @param {Object} child The child to add 704 | * @param {Number} index The index to place the child in 705 | * @return {Object} The child that was added 706 | */ 707 | 708 | /** 709 | * Removes a child from the container. 710 | * 711 | * @method removeChild 712 | * @param {Object} child The child to remove 713 | * @return {Object} The child that was removed 714 | */ 715 | 716 | /** 717 | * Removes a child from the specified index position. 718 | * 719 | * @method removeChildAt 720 | * @param {Number} index The index to get the child from 721 | * @return {Object} The child that was removed 722 | */ 723 | 724 | export { Window } 725 | -------------------------------------------------------------------------------- /src/windows/Window_Base.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Window_Base 3 | // 4 | // The superclass of all windows within the game. 5 | 6 | function Window_Base() { 7 | this.initialize.apply(this, arguments) 8 | } 9 | 10 | Window_Base.prototype = Object.create(Window.prototype) 11 | Window_Base.prototype.constructor = Window_Base 12 | 13 | Window_Base.prototype.initialize = function(x, y, width, height) { 14 | Window.prototype.initialize.call(this) 15 | this.loadWindowskin() 16 | this.move(x, y, width, height) 17 | this.updatePadding() 18 | this.updateBackOpacity() 19 | this.updateTone() 20 | this.createContents() 21 | this._opening = false 22 | this._closing = false 23 | this._dimmerSprite = null 24 | } 25 | 26 | Window_Base._iconWidth = 32 27 | Window_Base._iconHeight = 32 28 | Window_Base._faceWidth = 144 29 | Window_Base._faceHeight = 144 30 | 31 | Window_Base.prototype.lineHeight = function() { 32 | return 36 33 | } 34 | 35 | Window_Base.prototype.standardFontFace = function() { 36 | if ($gameSystem.isChinese()) { 37 | return 'SimHei, Heiti TC, sans-serif' 38 | } else if ($gameSystem.isKorean()) { 39 | return 'Dotum, AppleGothic, sans-serif' 40 | } else { 41 | return 'GameFont' 42 | } 43 | } 44 | 45 | Window_Base.prototype.standardFontSize = function() { 46 | return 28 47 | } 48 | 49 | Window_Base.prototype.standardPadding = function() { 50 | return 18 51 | } 52 | 53 | Window_Base.prototype.textPadding = function() { 54 | return 6 55 | } 56 | 57 | Window_Base.prototype.standardBackOpacity = function() { 58 | return 192 59 | } 60 | 61 | Window_Base.prototype.loadWindowskin = function() { 62 | this.windowskin = ImageManager.loadSystem('Window') 63 | } 64 | 65 | Window_Base.prototype.updatePadding = function() { 66 | this.padding = this.standardPadding() 67 | } 68 | 69 | Window_Base.prototype.updateBackOpacity = function() { 70 | this.backOpacity = this.standardBackOpacity() 71 | } 72 | 73 | Window_Base.prototype.contentsWidth = function() { 74 | return this.width - this.standardPadding() * 2 75 | } 76 | 77 | Window_Base.prototype.contentsHeight = function() { 78 | return this.height - this.standardPadding() * 2 79 | } 80 | 81 | Window_Base.prototype.fittingHeight = function(numLines) { 82 | return numLines * this.lineHeight() + this.standardPadding() * 2 83 | } 84 | 85 | Window_Base.prototype.updateTone = function() { 86 | var tone = $gameSystem.windowTone() 87 | this.setTone(tone[0], tone[1], tone[2]) 88 | } 89 | 90 | Window_Base.prototype.createContents = function() { 91 | this.contents = new Bitmap(this.contentsWidth(), this.contentsHeight()) 92 | this.resetFontSettings() 93 | } 94 | 95 | Window_Base.prototype.resetFontSettings = function() { 96 | this.contents.fontFace = this.standardFontFace() 97 | this.contents.fontSize = this.standardFontSize() 98 | this.resetTextColor() 99 | } 100 | 101 | Window_Base.prototype.resetTextColor = function() { 102 | this.changeTextColor(this.normalColor()) 103 | } 104 | 105 | Window_Base.prototype.update = function() { 106 | Window.prototype.update.call(this) 107 | this.updateTone() 108 | this.updateOpen() 109 | this.updateClose() 110 | this.updateBackgroundDimmer() 111 | } 112 | 113 | Window_Base.prototype.updateOpen = function() { 114 | if (this._opening) { 115 | this.openness += 32 116 | if (this.isOpen()) { 117 | this._opening = false 118 | } 119 | } 120 | } 121 | 122 | Window_Base.prototype.updateClose = function() { 123 | if (this._closing) { 124 | this.openness -= 32 125 | if (this.isClosed()) { 126 | this._closing = false 127 | } 128 | } 129 | } 130 | 131 | Window_Base.prototype.open = function() { 132 | if (!this.isOpen()) { 133 | this._opening = true 134 | } 135 | this._closing = false 136 | } 137 | 138 | Window_Base.prototype.close = function() { 139 | if (!this.isClosed()) { 140 | this._closing = true 141 | } 142 | this._opening = false 143 | } 144 | 145 | Window_Base.prototype.isOpening = function() { 146 | return this._opening 147 | } 148 | 149 | Window_Base.prototype.isClosing = function() { 150 | return this._closing 151 | } 152 | 153 | Window_Base.prototype.show = function() { 154 | this.visible = true 155 | } 156 | 157 | Window_Base.prototype.hide = function() { 158 | this.visible = false 159 | } 160 | 161 | Window_Base.prototype.activate = function() { 162 | this.active = true 163 | } 164 | 165 | Window_Base.prototype.deactivate = function() { 166 | this.active = false 167 | } 168 | 169 | Window_Base.prototype.textColor = function(n) { 170 | var px = 96 + (n % 8) * 12 + 6 171 | var py = 144 + Math.floor(n / 8) * 12 + 6 172 | return this.windowskin.getPixel(px, py) 173 | } 174 | 175 | Window_Base.prototype.normalColor = function() { 176 | return this.textColor(0) 177 | } 178 | 179 | Window_Base.prototype.systemColor = function() { 180 | return this.textColor(16) 181 | } 182 | 183 | Window_Base.prototype.crisisColor = function() { 184 | return this.textColor(17) 185 | } 186 | 187 | Window_Base.prototype.deathColor = function() { 188 | return this.textColor(18) 189 | } 190 | 191 | Window_Base.prototype.gaugeBackColor = function() { 192 | return this.textColor(19) 193 | } 194 | 195 | Window_Base.prototype.hpGaugeColor1 = function() { 196 | return this.textColor(20) 197 | } 198 | 199 | Window_Base.prototype.hpGaugeColor2 = function() { 200 | return this.textColor(21) 201 | } 202 | 203 | Window_Base.prototype.mpGaugeColor1 = function() { 204 | return this.textColor(22) 205 | } 206 | 207 | Window_Base.prototype.mpGaugeColor2 = function() { 208 | return this.textColor(23) 209 | } 210 | 211 | Window_Base.prototype.mpCostColor = function() { 212 | return this.textColor(23) 213 | } 214 | 215 | Window_Base.prototype.powerUpColor = function() { 216 | return this.textColor(24) 217 | } 218 | 219 | Window_Base.prototype.powerDownColor = function() { 220 | return this.textColor(25) 221 | } 222 | 223 | Window_Base.prototype.tpGaugeColor1 = function() { 224 | return this.textColor(28) 225 | } 226 | 227 | Window_Base.prototype.tpGaugeColor2 = function() { 228 | return this.textColor(29) 229 | } 230 | 231 | Window_Base.prototype.tpCostColor = function() { 232 | return this.textColor(29) 233 | } 234 | 235 | Window_Base.prototype.pendingColor = function() { 236 | return this.windowskin.getPixel(120, 120) 237 | } 238 | 239 | Window_Base.prototype.translucentOpacity = function() { 240 | return 160 241 | } 242 | 243 | Window_Base.prototype.changeTextColor = function(color) { 244 | this.contents.textColor = color 245 | } 246 | 247 | Window_Base.prototype.changePaintOpacity = function(enabled) { 248 | this.contents.paintOpacity = enabled ? 255 : this.translucentOpacity() 249 | } 250 | 251 | Window_Base.prototype.drawText = function(text, x, y, maxWidth, align) { 252 | this.contents.drawText(text, x, y, maxWidth, this.lineHeight(), align) 253 | } 254 | 255 | Window_Base.prototype.textWidth = function(text) { 256 | return this.contents.measureTextWidth(text) 257 | } 258 | 259 | Window_Base.prototype.drawTextEx = function(text, x, y) { 260 | if (text) { 261 | var textState = { index: 0, x: x, y: y, left: x } 262 | textState.text = this.convertEscapeCharacters(text) 263 | textState.height = this.calcTextHeight(textState, false) 264 | this.resetFontSettings() 265 | while (textState.index < textState.text.length) { 266 | this.processCharacter(textState) 267 | } 268 | return textState.x - x 269 | } else { 270 | return 0 271 | } 272 | } 273 | 274 | Window_Base.prototype.convertEscapeCharacters = function(text) { 275 | text = text.replace(/\\/g, '\x1b') 276 | text = text.replace(/\x1b\x1b/g, '\\') 277 | text = text.replace( 278 | /\x1bV\[(\d+)\]/gi, 279 | function() { 280 | return $gameVariables.value(parseInt(arguments[1])) 281 | }.bind(this) 282 | ) 283 | text = text.replace( 284 | /\x1bV\[(\d+)\]/gi, 285 | function() { 286 | return $gameVariables.value(parseInt(arguments[1])) 287 | }.bind(this) 288 | ) 289 | text = text.replace( 290 | /\x1bN\[(\d+)\]/gi, 291 | function() { 292 | return this.actorName(parseInt(arguments[1])) 293 | }.bind(this) 294 | ) 295 | text = text.replace( 296 | /\x1bP\[(\d+)\]/gi, 297 | function() { 298 | return this.partyMemberName(parseInt(arguments[1])) 299 | }.bind(this) 300 | ) 301 | text = text.replace(/\x1bG/gi, TextManager.currencyUnit) 302 | return text 303 | } 304 | 305 | Window_Base.prototype.actorName = function(n) { 306 | var actor = n >= 1 ? $gameActors.actor(n) : null 307 | return actor ? actor.name() : '' 308 | } 309 | 310 | Window_Base.prototype.partyMemberName = function(n) { 311 | var actor = n >= 1 ? $gameParty.members()[n - 1] : null 312 | return actor ? actor.name() : '' 313 | } 314 | 315 | Window_Base.prototype.processCharacter = function(textState) { 316 | switch (textState.text[textState.index]) { 317 | case '\n': 318 | this.processNewLine(textState) 319 | break 320 | case '\f': 321 | this.processNewPage(textState) 322 | break 323 | case '\x1b': 324 | this.processEscapeCharacter(this.obtainEscapeCode(textState), textState) 325 | break 326 | default: 327 | this.processNormalCharacter(textState) 328 | break 329 | } 330 | } 331 | 332 | Window_Base.prototype.processNormalCharacter = function(textState) { 333 | var c = textState.text[textState.index++] 334 | var w = this.textWidth(c) 335 | this.contents.drawText(c, textState.x, textState.y, w * 2, textState.height) 336 | textState.x += w 337 | } 338 | 339 | Window_Base.prototype.processNewLine = function(textState) { 340 | textState.x = textState.left 341 | textState.y += textState.height 342 | textState.height = this.calcTextHeight(textState, false) 343 | textState.index++ 344 | } 345 | 346 | Window_Base.prototype.processNewPage = function(textState) { 347 | textState.index++ 348 | } 349 | 350 | Window_Base.prototype.obtainEscapeCode = function(textState) { 351 | textState.index++ 352 | var regExp = /^[\$\.\|\^!><\{\}\\]|^[A-Z]+/i 353 | var arr = regExp.exec(textState.text.slice(textState.index)) 354 | if (arr) { 355 | textState.index += arr[0].length 356 | return arr[0].toUpperCase() 357 | } else { 358 | return '' 359 | } 360 | } 361 | 362 | Window_Base.prototype.obtainEscapeParam = function(textState) { 363 | var arr = /^\[\d+\]/.exec(textState.text.slice(textState.index)) 364 | if (arr) { 365 | textState.index += arr[0].length 366 | return parseInt(arr[0].slice(1)) 367 | } else { 368 | return '' 369 | } 370 | } 371 | 372 | Window_Base.prototype.processEscapeCharacter = function(code, textState) { 373 | switch (code) { 374 | case 'C': 375 | this.changeTextColor(this.textColor(this.obtainEscapeParam(textState))) 376 | break 377 | case 'I': 378 | this.processDrawIcon(this.obtainEscapeParam(textState), textState) 379 | break 380 | case '{': 381 | this.makeFontBigger() 382 | break 383 | case '}': 384 | this.makeFontSmaller() 385 | break 386 | } 387 | } 388 | 389 | Window_Base.prototype.processDrawIcon = function(iconIndex, textState) { 390 | this.drawIcon(iconIndex, textState.x + 2, textState.y + 2) 391 | textState.x += Window_Base._iconWidth + 4 392 | } 393 | 394 | Window_Base.prototype.makeFontBigger = function() { 395 | if (this.contents.fontSize <= 96) { 396 | this.contents.fontSize += 12 397 | } 398 | } 399 | 400 | Window_Base.prototype.makeFontSmaller = function() { 401 | if (this.contents.fontSize >= 24) { 402 | this.contents.fontSize -= 12 403 | } 404 | } 405 | 406 | Window_Base.prototype.calcTextHeight = function(textState, all) { 407 | var lastFontSize = this.contents.fontSize 408 | var textHeight = 0 409 | var lines = textState.text.slice(textState.index).split('\n') 410 | var maxLines = all ? lines.length : 1 411 | 412 | for (var i = 0; i < maxLines; i++) { 413 | var maxFontSize = this.contents.fontSize 414 | var regExp = /\x1b[\{\}]/g 415 | for (;;) { 416 | var array = regExp.exec(lines[i]) 417 | if (array) { 418 | if (array[0] === '\x1b{') { 419 | this.makeFontBigger() 420 | } 421 | if (array[0] === '\x1b}') { 422 | this.makeFontSmaller() 423 | } 424 | if (maxFontSize < this.contents.fontSize) { 425 | maxFontSize = this.contents.fontSize 426 | } 427 | } else { 428 | break 429 | } 430 | } 431 | textHeight += maxFontSize + 8 432 | } 433 | 434 | this.contents.fontSize = lastFontSize 435 | return textHeight 436 | } 437 | 438 | Window_Base.prototype.drawIcon = function(iconIndex, x, y) { 439 | var bitmap = ImageManager.loadSystem('IconSet') 440 | var pw = Window_Base._iconWidth 441 | var ph = Window_Base._iconHeight 442 | var sx = (iconIndex % 16) * pw 443 | var sy = Math.floor(iconIndex / 16) * ph 444 | this.contents.blt(bitmap, sx, sy, pw, ph, x, y) 445 | } 446 | 447 | Window_Base.prototype.drawFace = function( 448 | faceName, 449 | faceIndex, 450 | x, 451 | y, 452 | width, 453 | height 454 | ) { 455 | width = width || Window_Base._faceWidth 456 | height = height || Window_Base._faceHeight 457 | var bitmap = ImageManager.loadFace(faceName) 458 | var pw = Window_Base._faceWidth 459 | var ph = Window_Base._faceHeight 460 | var sw = Math.min(width, pw) 461 | var sh = Math.min(height, ph) 462 | var dx = Math.floor(x + Math.max(width - pw, 0) / 2) 463 | var dy = Math.floor(y + Math.max(height - ph, 0) / 2) 464 | var sx = (faceIndex % 4) * pw + (pw - sw) / 2 465 | var sy = Math.floor(faceIndex / 4) * ph + (ph - sh) / 2 466 | this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy) 467 | } 468 | 469 | Window_Base.prototype.drawCharacter = function( 470 | characterName, 471 | characterIndex, 472 | x, 473 | y 474 | ) { 475 | var bitmap = ImageManager.loadCharacter(characterName) 476 | var big = ImageManager.isBigCharacter(characterName) 477 | var pw = bitmap.width / (big ? 3 : 12) 478 | var ph = bitmap.height / (big ? 4 : 8) 479 | var n = characterIndex 480 | var sx = ((n % 4) * 3 + 1) * pw 481 | var sy = Math.floor(n / 4) * 4 * ph 482 | this.contents.blt(bitmap, sx, sy, pw, ph, x - pw / 2, y - ph) 483 | } 484 | 485 | Window_Base.prototype.drawGauge = function(x, y, width, rate, color1, color2) { 486 | var fillW = Math.floor(width * rate) 487 | var gaugeY = y + this.lineHeight() - 8 488 | this.contents.fillRect(x, gaugeY, width, 6, this.gaugeBackColor()) 489 | this.contents.gradientFillRect(x, gaugeY, fillW, 6, color1, color2) 490 | } 491 | 492 | Window_Base.prototype.hpColor = function(actor) { 493 | if (actor.isDead()) { 494 | return this.deathColor() 495 | } else if (actor.isDying()) { 496 | return this.crisisColor() 497 | } else { 498 | return this.normalColor() 499 | } 500 | } 501 | 502 | Window_Base.prototype.mpColor = function(actor) { 503 | return this.normalColor() 504 | } 505 | 506 | Window_Base.prototype.tpColor = function(actor) { 507 | return this.normalColor() 508 | } 509 | 510 | Window_Base.prototype.drawActorCharacter = function(actor, x, y) { 511 | this.drawCharacter(actor.characterName(), actor.characterIndex(), x, y) 512 | } 513 | 514 | Window_Base.prototype.drawActorFace = function(actor, x, y, width, height) { 515 | this.drawFace(actor.faceName(), actor.faceIndex(), x, y, width, height) 516 | } 517 | 518 | Window_Base.prototype.drawActorName = function(actor, x, y, width) { 519 | width = width || 168 520 | this.changeTextColor(this.hpColor(actor)) 521 | this.drawText(actor.name(), x, y, width) 522 | } 523 | 524 | Window_Base.prototype.drawActorClass = function(actor, x, y, width) { 525 | width = width || 168 526 | this.resetTextColor() 527 | this.drawText(actor.currentClass().name, x, y, width) 528 | } 529 | 530 | Window_Base.prototype.drawActorNickname = function(actor, x, y, width) { 531 | width = width || 270 532 | this.resetTextColor() 533 | this.drawText(actor.nickname(), x, y, width) 534 | } 535 | 536 | Window_Base.prototype.drawActorLevel = function(actor, x, y) { 537 | this.changeTextColor(this.systemColor()) 538 | this.drawText(TextManager.levelA, x, y, 48) 539 | this.resetTextColor() 540 | this.drawText(actor.level, x + 84, y, 36, 'right') 541 | } 542 | 543 | Window_Base.prototype.drawActorIcons = function(actor, x, y, width) { 544 | width = width || 144 545 | var icons = actor 546 | .allIcons() 547 | .slice(0, Math.floor(width / Window_Base._iconWidth)) 548 | for (var i = 0; i < icons.length; i++) { 549 | this.drawIcon(icons[i], x + Window_Base._iconWidth * i, y + 2) 550 | } 551 | } 552 | 553 | Window_Base.prototype.drawCurrentAndMax = function( 554 | current, 555 | max, 556 | x, 557 | y, 558 | width, 559 | color1, 560 | color2 561 | ) { 562 | var labelWidth = this.textWidth('HP') 563 | var valueWidth = this.textWidth('0000') 564 | var slashWidth = this.textWidth('/') 565 | var x1 = x + width - valueWidth 566 | var x2 = x1 - slashWidth 567 | var x3 = x2 - valueWidth 568 | if (x3 >= x + labelWidth) { 569 | this.changeTextColor(color1) 570 | this.drawText(current, x3, y, valueWidth, 'right') 571 | this.changeTextColor(color2) 572 | this.drawText('/', x2, y, slashWidth, 'right') 573 | this.drawText(max, x1, y, valueWidth, 'right') 574 | } else { 575 | this.changeTextColor(color1) 576 | this.drawText(current, x1, y, valueWidth, 'right') 577 | } 578 | } 579 | 580 | Window_Base.prototype.drawActorHp = function(actor, x, y, width) { 581 | width = width || 186 582 | var color1 = this.hpGaugeColor1() 583 | var color2 = this.hpGaugeColor2() 584 | this.drawGauge(x, y, width, actor.hpRate(), color1, color2) 585 | this.changeTextColor(this.systemColor()) 586 | this.drawText(TextManager.hpA, x, y, 44) 587 | this.drawCurrentAndMax( 588 | actor.hp, 589 | actor.mhp, 590 | x, 591 | y, 592 | width, 593 | this.hpColor(actor), 594 | this.normalColor() 595 | ) 596 | } 597 | 598 | Window_Base.prototype.drawActorMp = function(actor, x, y, width) { 599 | width = width || 186 600 | var color1 = this.mpGaugeColor1() 601 | var color2 = this.mpGaugeColor2() 602 | this.drawGauge(x, y, width, actor.mpRate(), color1, color2) 603 | this.changeTextColor(this.systemColor()) 604 | this.drawText(TextManager.mpA, x, y, 44) 605 | this.drawCurrentAndMax( 606 | actor.mp, 607 | actor.mmp, 608 | x, 609 | y, 610 | width, 611 | this.mpColor(actor), 612 | this.normalColor() 613 | ) 614 | } 615 | 616 | Window_Base.prototype.drawActorTp = function(actor, x, y, width) { 617 | width = width || 96 618 | var color1 = this.tpGaugeColor1() 619 | var color2 = this.tpGaugeColor2() 620 | this.drawGauge(x, y, width, actor.tpRate(), color1, color2) 621 | this.changeTextColor(this.systemColor()) 622 | this.drawText(TextManager.tpA, x, y, 44) 623 | this.changeTextColor(this.tpColor(actor)) 624 | this.drawText(actor.tp, x + width - 64, y, 64, 'right') 625 | } 626 | 627 | Window_Base.prototype.drawActorSimpleStatus = function(actor, x, y, width) { 628 | var lineHeight = this.lineHeight() 629 | var x2 = x + 180 630 | var width2 = Math.min(200, width - 180 - this.textPadding()) 631 | this.drawActorName(actor, x, y) 632 | this.drawActorLevel(actor, x, y + lineHeight * 1) 633 | this.drawActorIcons(actor, x, y + lineHeight * 2) 634 | this.drawActorClass(actor, x2, y) 635 | this.drawActorHp(actor, x2, y + lineHeight * 1, width2) 636 | this.drawActorMp(actor, x2, y + lineHeight * 2, width2) 637 | } 638 | 639 | Window_Base.prototype.drawItemName = function(item, x, y, width) { 640 | width = width || 312 641 | if (item) { 642 | var iconBoxWidth = Window_Base._iconWidth + 4 643 | this.resetTextColor() 644 | this.drawIcon(item.iconIndex, x + 2, y + 2) 645 | this.drawText(item.name, x + iconBoxWidth, y, width - iconBoxWidth) 646 | } 647 | } 648 | 649 | Window_Base.prototype.drawCurrencyValue = function(value, unit, x, y, width) { 650 | var unitWidth = Math.min(80, this.textWidth(unit)) 651 | this.resetTextColor() 652 | this.drawText(value, x, y, width - unitWidth - 6, 'right') 653 | this.changeTextColor(this.systemColor()) 654 | this.drawText(unit, x + width - unitWidth, y, unitWidth, 'right') 655 | } 656 | 657 | Window_Base.prototype.paramchangeTextColor = function(change) { 658 | if (change > 0) { 659 | return this.powerUpColor() 660 | } else if (change < 0) { 661 | return this.powerDownColor() 662 | } else { 663 | return this.normalColor() 664 | } 665 | } 666 | 667 | Window_Base.prototype.setBackgroundType = function(type) { 668 | if (type === 0) { 669 | this.opacity = 255 670 | } else { 671 | this.opacity = 0 672 | } 673 | if (type === 1) { 674 | this.showBackgroundDimmer() 675 | } else { 676 | this.hideBackgroundDimmer() 677 | } 678 | } 679 | 680 | Window_Base.prototype.showBackgroundDimmer = function() { 681 | if (!this._dimmerSprite) { 682 | this._dimmerSprite = new Sprite() 683 | this._dimmerSprite.bitmap = new Bitmap(0, 0) 684 | this.addChildToBack(this._dimmerSprite) 685 | } 686 | var bitmap = this._dimmerSprite.bitmap 687 | if (bitmap.width !== this.width || bitmap.height !== this.height) { 688 | this.refreshDimmerBitmap() 689 | } 690 | this._dimmerSprite.visible = true 691 | this.updateBackgroundDimmer() 692 | } 693 | 694 | Window_Base.prototype.hideBackgroundDimmer = function() { 695 | if (this._dimmerSprite) { 696 | this._dimmerSprite.visible = false 697 | } 698 | } 699 | 700 | Window_Base.prototype.updateBackgroundDimmer = function() { 701 | if (this._dimmerSprite) { 702 | this._dimmerSprite.opacity = this.openness 703 | } 704 | } 705 | 706 | Window_Base.prototype.refreshDimmerBitmap = function() { 707 | if (this._dimmerSprite) { 708 | var bitmap = this._dimmerSprite.bitmap 709 | var w = this.width 710 | var h = this.height 711 | var m = this.padding 712 | var c1 = this.dimColor1() 713 | var c2 = this.dimColor2() 714 | bitmap.resize(w, h) 715 | bitmap.gradientFillRect(0, 0, w, m, c2, c1, true) 716 | bitmap.fillRect(0, m, w, h - m * 2, c1) 717 | bitmap.gradientFillRect(0, h - m, w, m, c1, c2, true) 718 | this._dimmerSprite.setFrame(0, 0, w, h) 719 | } 720 | } 721 | 722 | Window_Base.prototype.dimColor1 = function() { 723 | return 'rgba(0, 0, 0, 0.6)' 724 | } 725 | 726 | Window_Base.prototype.dimColor2 = function() { 727 | return 'rgba(0, 0, 0, 0)' 728 | } 729 | 730 | Window_Base.prototype.canvasToLocalX = function(x) { 731 | var node = this 732 | while (node) { 733 | x -= node.x 734 | node = node.parent 735 | } 736 | return x 737 | } 738 | 739 | Window_Base.prototype.canvasToLocalY = function(y) { 740 | var node = this 741 | while (node) { 742 | y -= node.y 743 | node = node.parent 744 | } 745 | return y 746 | } 747 | 748 | Window_Base.prototype.reserveFaceImages = function() { 749 | $gameParty.members().forEach(function(actor) { 750 | ImageManager.reserveFace(actor.faceName()) 751 | }, this) 752 | } 753 | 754 | export { Window_Base } 755 | -------------------------------------------------------------------------------- /js/libs/pixi-picture.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || function (d, b) { 2 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 | function __() { this.constructor = d; } 4 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 | }; 6 | var PIXI; 7 | (function (PIXI) { 8 | var extras; 9 | (function (extras) { 10 | var shaderLib = [ 11 | { 12 | vertUniforms: "", 13 | vertCode: "vTextureCoord = aTextureCoord;", 14 | fragUniforms: "uniform vec4 uTextureClamp;", 15 | fragCode: "vec2 textureCoord = clamp(vTextureCoord, uTextureClamp.xy, uTextureClamp.zw);" 16 | }, 17 | { 18 | vertUniforms: "uniform mat3 uTransform;", 19 | vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;", 20 | fragUniforms: "", 21 | fragCode: "vec2 textureCoord = vTextureCoord;" 22 | }, 23 | { 24 | vertUniforms: "uniform mat3 uTransform;", 25 | vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;", 26 | fragUniforms: "uniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;", 27 | fragCode: "vec2 textureCoord = mod(vTextureCoord - uClampOffset, vec2(1.0, 1.0)) + uClampOffset;" + 28 | "\ntextureCoord = (uMapCoord * vec3(textureCoord, 1.0)).xy;" + 29 | "\ntextureCoord = clamp(textureCoord, uClampFrame.xy, uClampFrame.zw);" 30 | } 31 | ]; 32 | var PictureShader = (function (_super) { 33 | __extends(PictureShader, _super); 34 | function PictureShader(gl, vert, frag, tilingMode) { 35 | var lib = shaderLib[tilingMode]; 36 | _super.call(this, gl, vert.replace(/%SPRITE_UNIFORMS%/gi, lib.vertUniforms) 37 | .replace(/%SPRITE_CODE%/gi, lib.vertCode), frag.replace(/%SPRITE_UNIFORMS%/gi, lib.fragUniforms) 38 | .replace(/%SPRITE_CODE%/gi, lib.fragCode)); 39 | this.bind(); 40 | this.tilingMode = tilingMode; 41 | this.tempQuad = new PIXI.Quad(gl); 42 | this.tempQuad.initVao(this); 43 | this.uniforms.uColor = new Float32Array([1, 1, 1, 1]); 44 | this.uniforms.uSampler = [0, 1]; 45 | } 46 | PictureShader.blendVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\nuniform mat3 mapMatrix;\n\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n vMapCoord = (mapMatrix * vec3(aVertexPosition, 1.0)).xy;\n}\n"; 47 | return PictureShader; 48 | }(PIXI.Shader)); 49 | extras.PictureShader = PictureShader; 50 | })(extras = PIXI.extras || (PIXI.extras = {})); 51 | })(PIXI || (PIXI = {})); 52 | var PIXI; 53 | (function (PIXI) { 54 | var extras; 55 | (function (extras) { 56 | var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cs2 = Cs * 2.0 - 1.0;\n vec3 screen = Cb + Cs2 - Cb * Cs2;\n vec3 B;\n if (Cb.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cb.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cb.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n"; 57 | var HardLightShader = (function (_super) { 58 | __extends(HardLightShader, _super); 59 | function HardLightShader(gl, tilingMode) { 60 | _super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode); 61 | } 62 | return HardLightShader; 63 | }(extras.PictureShader)); 64 | extras.HardLightShader = HardLightShader; 65 | })(extras = PIXI.extras || (PIXI.extras = {})); 66 | })(PIXI || (PIXI = {})); 67 | var PIXI; 68 | (function (PIXI) { 69 | var extras; 70 | (function (extras) { 71 | function mapFilterBlendModesToPixi(gl, array) { 72 | if (array === void 0) { array = []; } 73 | array[PIXI.BLEND_MODES.OVERLAY] = [new extras.OverlayShader(gl, 0), new extras.OverlayShader(gl, 1), new extras.OverlayShader(gl, 2)]; 74 | array[PIXI.BLEND_MODES.HARD_LIGHT] = [new extras.HardLightShader(gl, 0), new extras.HardLightShader(gl, 1), new extras.HardLightShader(gl, 2)]; 75 | return array; 76 | } 77 | extras.mapFilterBlendModesToPixi = mapFilterBlendModesToPixi; 78 | })(extras = PIXI.extras || (PIXI.extras = {})); 79 | })(PIXI || (PIXI = {})); 80 | var PIXI; 81 | (function (PIXI) { 82 | var extras; 83 | (function (extras) { 84 | var normalFrag = "\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n\n vec4 sample = texture2D(uSampler[0], textureCoord);\n gl_FragColor = sample * uColor;\n}\n"; 85 | var normalVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n}\n"; 86 | var NormalShader = (function (_super) { 87 | __extends(NormalShader, _super); 88 | function NormalShader(gl, tilingMode) { 89 | _super.call(this, gl, normalVert, normalFrag, tilingMode); 90 | } 91 | return NormalShader; 92 | }(extras.PictureShader)); 93 | extras.NormalShader = NormalShader; 94 | })(extras = PIXI.extras || (PIXI.extras = {})); 95 | })(PIXI || (PIXI = {})); 96 | var PIXI; 97 | (function (PIXI) { 98 | var extras; 99 | (function (extras) { 100 | var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cb2 = Cb * 2.0 - 1.0;\n vec3 screen = Cb2 + Cs - Cb2 * Cs;\n vec3 B;\n if (Cs.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cs.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cs.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n"; 101 | var OverlayShader = (function (_super) { 102 | __extends(OverlayShader, _super); 103 | function OverlayShader(gl, tilingMode) { 104 | _super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode); 105 | } 106 | return OverlayShader; 107 | }(extras.PictureShader)); 108 | extras.OverlayShader = OverlayShader; 109 | })(extras = PIXI.extras || (PIXI.extras = {})); 110 | })(PIXI || (PIXI = {})); 111 | var PIXI; 112 | (function (PIXI) { 113 | var extras; 114 | (function (extras) { 115 | function nextPow2(v) { 116 | v += (v === 0) ? 1 : 0; 117 | --v; 118 | v |= v >>> 1; 119 | v |= v >>> 2; 120 | v |= v >>> 4; 121 | v |= v >>> 8; 122 | v |= v >>> 16; 123 | return v + 1; 124 | } 125 | var PictureRenderer = (function (_super) { 126 | __extends(PictureRenderer, _super); 127 | function PictureRenderer(renderer) { 128 | _super.call(this, renderer); 129 | } 130 | PictureRenderer.prototype.onContextChange = function () { 131 | var gl = this.renderer.gl; 132 | this.drawModes = extras.mapFilterBlendModesToPixi(gl); 133 | this.normalShader = [new extras.NormalShader(gl, 0), new extras.NormalShader(gl, 1), new extras.NormalShader(gl, 2)]; 134 | this._tempClamp = new Float32Array(4); 135 | this._tempColor = new Float32Array(4); 136 | this._tempRect = new PIXI.Rectangle(); 137 | this._tempRect2 = new PIXI.Rectangle(); 138 | this._tempRect3 = new PIXI.Rectangle(); 139 | this._tempMatrix = new PIXI.Matrix(); 140 | this._tempMatrix2 = new PIXI.Matrix(); 141 | this._bigBuf = new Uint8Array(1 << 20); 142 | this._renderTexture = new PIXI.BaseRenderTexture(1024, 1024); 143 | }; 144 | PictureRenderer.prototype.start = function () { 145 | }; 146 | PictureRenderer.prototype.flush = function () { 147 | }; 148 | PictureRenderer.prototype._getRenderTexture = function (minWidth, minHeight) { 149 | if (this._renderTexture.width < minWidth || 150 | this._renderTexture.height < minHeight) { 151 | minHeight = nextPow2(minWidth); 152 | minHeight = nextPow2(minHeight); 153 | this._renderTexture.resize(minWidth, minHeight); 154 | } 155 | return this._renderTexture; 156 | }; 157 | PictureRenderer.prototype._getBuf = function (size) { 158 | var buf = this._bigBuf; 159 | if (buf.length < size) { 160 | size = nextPow2(size); 161 | buf = new Uint8Array(size); 162 | this._bigBuf = buf; 163 | } 164 | return buf; 165 | }; 166 | PictureRenderer.prototype.render = function (sprite) { 167 | if (!sprite.texture.valid) { 168 | return; 169 | } 170 | var tilingMode = 0; 171 | if (sprite.tileTransform) { 172 | tilingMode = this._isSimpleSprite(sprite) ? 1 : 2; 173 | } 174 | var blendShader = this.drawModes[sprite.blendMode]; 175 | if (blendShader) { 176 | this._renderBlend(sprite, blendShader[tilingMode]); 177 | } 178 | else { 179 | this._renderNormal(sprite, this.normalShader[tilingMode]); 180 | } 181 | }; 182 | PictureRenderer.prototype._renderNormal = function (sprite, shader) { 183 | var renderer = this.renderer; 184 | renderer.bindShader(shader); 185 | renderer.state.setBlendMode(sprite.blendMode); 186 | this._renderInner(sprite, shader); 187 | }; 188 | PictureRenderer.prototype._renderBlend = function (sprite, shader) { 189 | var renderer = this.renderer; 190 | var spriteBounds = sprite.getBounds(); 191 | var renderTarget = renderer._activeRenderTarget; 192 | var matrix = renderTarget.projectionMatrix; 193 | var flipX = matrix.a < 0; 194 | var flipY = matrix.d < 0; 195 | var resolution = renderTarget.resolution; 196 | var screen = this._tempRect; 197 | var fr = renderTarget.sourceFrame || renderTarget.destinationFrame; 198 | screen.x = 0; 199 | screen.y = 0; 200 | screen.width = fr.width; 201 | screen.height = fr.height; 202 | var bounds = this._tempRect2; 203 | var fbw = fr.width * resolution, fbh = fr.height * resolution; 204 | bounds.x = (spriteBounds.x + matrix.tx / matrix.a) * resolution + fbw / 2; 205 | bounds.y = (spriteBounds.y + matrix.ty / matrix.d) * resolution + fbh / 2; 206 | bounds.width = spriteBounds.width * resolution; 207 | bounds.height = spriteBounds.height * resolution; 208 | if (flipX) { 209 | bounds.y = fbw - bounds.width - bounds.x; 210 | } 211 | if (flipY) { 212 | bounds.y = fbh - bounds.height - bounds.y; 213 | } 214 | var screenBounds = this._tempRect3; 215 | var x_1 = Math.floor(Math.max(screen.x, bounds.x)); 216 | var x_2 = Math.ceil(Math.min(screen.x + screen.width, bounds.x + bounds.width)); 217 | var y_1 = Math.floor(Math.max(screen.y, bounds.y)); 218 | var y_2 = Math.ceil(Math.min(screen.y + screen.height, bounds.y + bounds.height)); 219 | var pixelsWidth = x_2 - x_1; 220 | var pixelsHeight = y_2 - y_1; 221 | if (pixelsWidth <= 0 || pixelsHeight <= 0) { 222 | return; 223 | } 224 | var rt = this._getRenderTexture(pixelsWidth, pixelsHeight); 225 | renderer.bindTexture(rt, 1, true); 226 | var gl = renderer.gl; 227 | if (renderer.renderingToScreen && renderTarget.root) { 228 | var buf = this._getBuf(pixelsWidth * pixelsHeight * 4); 229 | gl.readPixels(x_1, y_1, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf); 230 | gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf); 231 | } 232 | else { 233 | gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, x_1, y_1, pixelsWidth, pixelsHeight); 234 | } 235 | renderer.bindShader(shader); 236 | renderer.state.setBlendMode(PIXI.BLEND_MODES.NORMAL); 237 | if (shader.uniforms.mapMatrix) { 238 | var mapMatrix = this._tempMatrix; 239 | mapMatrix.a = bounds.width / rt.width / spriteBounds.width; 240 | if (flipX) { 241 | mapMatrix.a = -mapMatrix.a; 242 | mapMatrix.tx = (bounds.x - x_1) / rt.width - (spriteBounds.x + spriteBounds.width) * mapMatrix.a; 243 | } 244 | else { 245 | mapMatrix.tx = (bounds.x - x_1) / rt.width - spriteBounds.x * mapMatrix.a; 246 | } 247 | mapMatrix.d = bounds.height / rt.height / spriteBounds.height; 248 | if (flipY) { 249 | mapMatrix.d = -mapMatrix.d; 250 | mapMatrix.ty = (bounds.y - y_1) / rt.height - (spriteBounds.y + spriteBounds.height) * mapMatrix.d; 251 | } 252 | else { 253 | mapMatrix.ty = (bounds.y - y_1) / rt.height - spriteBounds.y * mapMatrix.d; 254 | } 255 | shader.uniforms.mapMatrix = mapMatrix.toArray(true); 256 | } 257 | this._renderInner(sprite, shader); 258 | }; 259 | PictureRenderer.prototype._renderInner = function (sprite, shader) { 260 | var renderer = this.renderer; 261 | if (shader.tilingMode > 0) { 262 | this._renderWithShader(sprite, shader.tilingMode === 1, shader); 263 | } 264 | else { 265 | this._renderSprite(sprite, shader); 266 | } 267 | }; 268 | PictureRenderer.prototype._renderWithShader = function (ts, isSimple, shader) { 269 | var quad = shader.tempQuad; 270 | var renderer = this.renderer; 271 | renderer.bindVao(quad.vao); 272 | var vertices = quad.vertices; 273 | var _width = ts._width; 274 | var _height = ts._height; 275 | var _anchorX = ts._anchor._x; 276 | var _anchorY = ts._anchor._y; 277 | var w0 = _width * (1 - _anchorX); 278 | var w1 = _width * -_anchorX; 279 | var h0 = _height * (1 - _anchorY); 280 | var h1 = _height * -_anchorY; 281 | var wt = ts.transform.worldTransform; 282 | var a = wt.a; 283 | var b = wt.b; 284 | var c = wt.c; 285 | var d = wt.d; 286 | var tx = wt.tx; 287 | var ty = wt.ty; 288 | vertices[0] = (a * w1) + (c * h1) + tx; 289 | vertices[1] = (d * h1) + (b * w1) + ty; 290 | vertices[2] = (a * w0) + (c * h1) + tx; 291 | vertices[3] = (d * h1) + (b * w0) + ty; 292 | vertices[4] = (a * w0) + (c * h0) + tx; 293 | vertices[5] = (d * h0) + (b * w0) + ty; 294 | vertices[6] = (a * w1) + (c * h0) + tx; 295 | vertices[7] = (d * h0) + (b * w1) + ty; 296 | vertices = quad.uvs; 297 | vertices[0] = vertices[6] = -ts.anchor.x; 298 | vertices[1] = vertices[3] = -ts.anchor.y; 299 | vertices[2] = vertices[4] = 1.0 - ts.anchor.x; 300 | vertices[5] = vertices[7] = 1.0 - ts.anchor.y; 301 | quad.upload(); 302 | var tex = ts._texture; 303 | var lt = ts.tileTransform.localTransform; 304 | var uv = ts.uvTransform; 305 | var mapCoord = uv.mapCoord; 306 | var uClampFrame = uv.uClampFrame; 307 | var uClampOffset = uv.uClampOffset; 308 | var w = tex.width; 309 | var h = tex.height; 310 | var W = _width; 311 | var H = _height; 312 | var tempMat = this._tempMatrix2; 313 | tempMat.set(lt.a * w / W, lt.b * w / H, lt.c * h / W, lt.d * h / H, lt.tx / W, lt.ty / H); 314 | tempMat.invert(); 315 | if (isSimple) { 316 | tempMat.append(mapCoord); 317 | } 318 | else { 319 | shader.uniforms.uMapCoord = mapCoord.toArray(true); 320 | shader.uniforms.uClampFrame = uClampFrame; 321 | shader.uniforms.uClampOffset = uClampOffset; 322 | } 323 | shader.uniforms.uTransform = tempMat.toArray(true); 324 | var color = this._tempColor; 325 | var alpha = ts.worldAlpha; 326 | PIXI.utils.hex2rgb(ts.tint, color); 327 | color[0] *= alpha; 328 | color[1] *= alpha; 329 | color[2] *= alpha; 330 | color[3] = alpha; 331 | shader.uniforms.uColor = color; 332 | renderer.bindTexture(tex, 0, true); 333 | quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); 334 | }; 335 | PictureRenderer.prototype._renderSprite = function (sprite, shader) { 336 | var renderer = this.renderer; 337 | var quad = shader.tempQuad; 338 | renderer.bindVao(quad.vao); 339 | var uvs = sprite.texture._uvs; 340 | var vertices = quad.vertices; 341 | var vd = sprite.vertexData; 342 | for (var i = 0; i < 8; i++) { 343 | quad.vertices[i] = vd[i]; 344 | } 345 | quad.uvs[0] = uvs.x0; 346 | quad.uvs[1] = uvs.y0; 347 | quad.uvs[2] = uvs.x1; 348 | quad.uvs[3] = uvs.y1; 349 | quad.uvs[4] = uvs.x2; 350 | quad.uvs[5] = uvs.y2; 351 | quad.uvs[6] = uvs.x3; 352 | quad.uvs[7] = uvs.y3; 353 | quad.upload(); 354 | var frame = sprite.texture.frame; 355 | var base = sprite.texture.baseTexture; 356 | var clamp = this._tempClamp; 357 | var eps = 0.5 / base.resolution; 358 | clamp[0] = (frame.x + eps) / base.width; 359 | clamp[1] = (frame.y + eps) / base.height; 360 | clamp[2] = (frame.x + frame.width - eps) / base.width; 361 | clamp[3] = (frame.y + frame.height - eps) / base.height; 362 | shader.uniforms.uTextureClamp = clamp; 363 | var color = this._tempColor; 364 | PIXI.utils.hex2rgb(sprite.tint, color); 365 | var alpha = sprite.worldAlpha; 366 | color[0] *= alpha; 367 | color[1] *= alpha; 368 | color[2] *= alpha; 369 | color[3] = alpha; 370 | shader.uniforms.uColor = color; 371 | renderer.bindTexture(base, 0, true); 372 | quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); 373 | }; 374 | PictureRenderer.prototype._isSimpleSprite = function (ts) { 375 | var renderer = this.renderer; 376 | var tex = ts._texture; 377 | var baseTex = tex.baseTexture; 378 | var isSimple = baseTex.isPowerOfTwo && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; 379 | if (isSimple) { 380 | if (!baseTex._glTextures[renderer.CONTEXT_UID]) { 381 | if (baseTex.wrapMode === PIXI.WRAP_MODES.CLAMP) { 382 | baseTex.wrapMode = PIXI.WRAP_MODES.REPEAT; 383 | } 384 | } 385 | else { 386 | isSimple = baseTex.wrapMode !== PIXI.WRAP_MODES.CLAMP; 387 | } 388 | } 389 | return isSimple; 390 | }; 391 | return PictureRenderer; 392 | }(PIXI.ObjectRenderer)); 393 | extras.PictureRenderer = PictureRenderer; 394 | PIXI.WebGLRenderer.registerPlugin('picture', PictureRenderer); 395 | PIXI.CanvasRenderer.registerPlugin('picture', PIXI.CanvasSpriteRenderer); 396 | })(extras = PIXI.extras || (PIXI.extras = {})); 397 | })(PIXI || (PIXI = {})); 398 | var PIXI; 399 | (function (PIXI) { 400 | var extras; 401 | (function (extras) { 402 | var PictureSprite = (function (_super) { 403 | __extends(PictureSprite, _super); 404 | function PictureSprite(texture) { 405 | _super.call(this, texture); 406 | this.pluginName = 'picture'; 407 | } 408 | return PictureSprite; 409 | }(PIXI.Sprite)); 410 | extras.PictureSprite = PictureSprite; 411 | })(extras = PIXI.extras || (PIXI.extras = {})); 412 | })(PIXI || (PIXI = {})); 413 | var PIXI; 414 | (function (PIXI) { 415 | var extras; 416 | (function (extras) { 417 | var PictureTilingSprite = (function (_super) { 418 | __extends(PictureTilingSprite, _super); 419 | function PictureTilingSprite(texture) { 420 | _super.call(this, texture); 421 | this.pluginName = 'picture'; 422 | } 423 | return PictureTilingSprite; 424 | }(extras.TilingSprite)); 425 | extras.PictureTilingSprite = PictureTilingSprite; 426 | })(extras = PIXI.extras || (PIXI.extras = {})); 427 | })(PIXI || (PIXI = {})); 428 | //# sourceMappingURL=pixi-picture.js.map -------------------------------------------------------------------------------- /src/core/Input.js: -------------------------------------------------------------------------------- 1 | // //----------------------------------------------------------------------------- 2 | // /** 3 | // * The static class that handles input data from the keyboard and gamepads. 4 | // * 5 | // * @class Input 6 | // */ 7 | // class Input { 8 | // constructor() { 9 | // throw new Error('This is a static class') 10 | // } 11 | 12 | // /** 13 | // * Initializes the input system. 14 | // * 15 | // * @static 16 | // * @method initialize 17 | // */ 18 | // static initialize() { 19 | // this.clear() 20 | // this._wrapNwjsAlert() 21 | // this._setupEventHandlers() 22 | // } 23 | 24 | // /** 25 | // * The wait time of the key repeat in frames. 26 | // * 27 | // * @static 28 | // * @property keyRepeatWait 29 | // * @type Number 30 | // */ 31 | // static keyRepeatWait = 24 32 | 33 | // /** 34 | // * The interval of the key repeat in frames. 35 | // * 36 | // * @static 37 | // * @property keyRepeatInterval 38 | // * @type Number 39 | // */ 40 | // static keyRepeatInterval = 6 41 | 42 | // /** 43 | // * A hash table to convert from a virtual key code to a mapped key name. 44 | // * 45 | // * @static 46 | // * @property keyMapper 47 | // * @type Object 48 | // */ 49 | // static keyMapper = { 50 | // 9: 'tab', // tab 51 | // 13: 'ok', // enter 52 | // 16: 'shift', // shift 53 | // 17: 'control', // control 54 | // 18: 'control', // alt 55 | // 27: 'escape', // escape 56 | // 32: 'ok', // space 57 | // 33: 'pageup', // pageup 58 | // 34: 'pagedown', // pagedown 59 | // 37: 'left', // left arrow 60 | // 38: 'up', // up arrow 61 | // 39: 'right', // right arrow 62 | // 40: 'down', // down arrow 63 | // 45: 'escape', // insert 64 | // 81: 'pageup', // Q 65 | // 87: 'pagedown', // W 66 | // 88: 'escape', // X 67 | // 90: 'ok', // Z 68 | // 96: 'escape', // numpad 0 69 | // 98: 'down', // numpad 2 70 | // 100: 'left', // numpad 4 71 | // 102: 'right', // numpad 6 72 | // 104: 'up', // numpad 8 73 | // 120: 'debug' // F9 74 | // } 75 | 76 | // /** 77 | // * A hash table to convert from a gamepad button to a mapped key name. 78 | // * 79 | // * @static 80 | // * @property gamepadMapper 81 | // * @type Object 82 | // */ 83 | // static gamepadMapper = { 84 | // 0: 'ok', // A 85 | // 1: 'cancel', // B 86 | // 2: 'shift', // X 87 | // 3: 'menu', // Y 88 | // 4: 'pageup', // LB 89 | // 5: 'pagedown', // RB 90 | // 12: 'up', // D-pad up 91 | // 13: 'down', // D-pad down 92 | // 14: 'left', // D-pad left 93 | // 15: 'right' // D-pad right 94 | // } 95 | 96 | // /** 97 | // * Clears all the input data. 98 | // * 99 | // * @static 100 | // * @method clear 101 | // */ 102 | // static clear() { 103 | // this._currentState = {} 104 | // this._previousState = {} 105 | // this._gamepadStates = [] 106 | // this._latestButton = null 107 | // this._pressedTime = 0 108 | // this._dir4 = 0 109 | // this._dir8 = 0 110 | // this._preferredAxis = '' 111 | // this._date = 0 112 | // } 113 | 114 | // /** 115 | // * Updates the input data. 116 | // * 117 | // * @static 118 | // * @method update 119 | // */ 120 | // static update() { 121 | // this._pollGamepads() 122 | // if (this._currentState[this._latestButton]) { 123 | // this._pressedTime++ 124 | // } else { 125 | // this._latestButton = null 126 | // } 127 | // for (let name in this._currentState) { 128 | // if (this._currentState[name] && !this._previousState[name]) { 129 | // this._latestButton = name 130 | // this._pressedTime = 0 131 | // this._date = Date.now() 132 | // } 133 | // this._previousState[name] = this._currentState[name] 134 | // } 135 | // this._updateDirection() 136 | // } 137 | 138 | // /** 139 | // * Checks whether a key is currently pressed down. 140 | // * 141 | // * @static 142 | // * @method isPressed 143 | // * @param {String} keyName The mapped name of the key 144 | // * @return {Boolean} True if the key is pressed 145 | // */ 146 | // static isPressed(keyName) { 147 | // if (this._isEscapeCompatible(keyName) && this.isPressed('escape')) { 148 | // return true 149 | // } else { 150 | // return !!this._currentState[keyName] 151 | // } 152 | // } 153 | 154 | // /** 155 | // * Checks whether a key is just pressed. 156 | // * 157 | // * @static 158 | // * @method isTriggered 159 | // * @param {String} keyName The mapped name of the key 160 | // * @return {Boolean} True if the key is triggered 161 | // */ 162 | // static isTriggered(keyName) { 163 | // if (this._isEscapeCompatible(keyName) && this.isTriggered('escape')) { 164 | // return true 165 | // } else { 166 | // return this._latestButton === keyName && this._pressedTime === 0 167 | // } 168 | // } 169 | 170 | // /** 171 | // * Checks whether a key is just pressed or a key repeat occurred. 172 | // * 173 | // * @static 174 | // * @method isRepeated 175 | // * @param {String} keyName The mapped name of the key 176 | // * @return {Boolean} True if the key is repeated 177 | // */ 178 | // static isRepeated(keyName) { 179 | // if (this._isEscapeCompatible(keyName) && this.isRepeated('escape')) { 180 | // return true 181 | // } else { 182 | // return ( 183 | // this._latestButton === keyName && 184 | // (this._pressedTime === 0 || 185 | // (this._pressedTime >= this.keyRepeatWait && 186 | // this._pressedTime % this.keyRepeatInterval === 0)) 187 | // ) 188 | // } 189 | // } 190 | 191 | // /** 192 | // * Checks whether a key is kept depressed. 193 | // * 194 | // * @static 195 | // * @method isLongPressed 196 | // * @param {String} keyName The mapped name of the key 197 | // * @return {Boolean} True if the key is long-pressed 198 | // */ 199 | // static isLongPressed(keyName) { 200 | // if (this._isEscapeCompatible(keyName) && this.isLongPressed('escape')) { 201 | // return true 202 | // } else { 203 | // return ( 204 | // this._latestButton === keyName && 205 | // this._pressedTime >= this.keyRepeatWait 206 | // ) 207 | // } 208 | // } 209 | 210 | // /** 211 | // * [read-only] The four direction value as a number of the numpad, or 0 for neutral. 212 | // * 213 | // * @static 214 | // * @property dir4 215 | // * @type Number 216 | // */ 217 | // get dir4() { 218 | // return this._dir4 219 | // } 220 | 221 | // /** 222 | // * [read-only] The eight direction value as a number of the numpad, or 0 for neutral. 223 | // * 224 | // * @static 225 | // * @property dir8 226 | // * @type Number 227 | // */ 228 | // get dir8() { 229 | // return this._dir8 230 | // } 231 | 232 | // /** 233 | // * [read-only] The time of the last input in milliseconds. 234 | // * 235 | // * @static 236 | // * @property date 237 | // * @type Number 238 | // */ 239 | // get date() { 240 | // return this._date 241 | // } 242 | 243 | // /** 244 | // * @static 245 | // * @method _wrapNwjsAlert 246 | // * @private 247 | // */ 248 | // static _wrapNwjsAlert() { 249 | // if (Utils.isNwjs()) { 250 | // const _alert = window.alert 251 | // window.alert = function() { 252 | // const gui = require('nw.gui') 253 | // const win = gui.Window.get() 254 | // _alert.apply(this, arguments) 255 | // win.focus() 256 | // Input.clear() 257 | // } 258 | // } 259 | // } 260 | 261 | // /** 262 | // * @static 263 | // * @method _setupEventHandlers 264 | // * @private 265 | // */ 266 | // static _setupEventHandlers() { 267 | // document.addEventListener('keydown', this._onKeyDown.bind(this)) 268 | // document.addEventListener('keyup', this._onKeyUp.bind(this)) 269 | // window.addEventListener('blur', this._onLostFocus.bind(this)) 270 | // } 271 | 272 | // /** 273 | // * @static 274 | // * @method _onKeyDown 275 | // * @param {KeyboardEvent} event 276 | // * @private 277 | // */ 278 | // static _onKeyDown(event) { 279 | // if (this._shouldPreventDefault(event.keyCode)) { 280 | // event.preventDefault() 281 | // } 282 | // if (event.keyCode === 144) { 283 | // // Numlock 284 | // this.clear() 285 | // } 286 | // const buttonName = this.keyMapper[event.keyCode] 287 | // if (ResourceHandler.exists() && buttonName === 'ok') { 288 | // ResourceHandler.retry() 289 | // } else if (buttonName) { 290 | // this._currentState[buttonName] = true 291 | // } 292 | // } 293 | 294 | // /** 295 | // * @static 296 | // * @method _shouldPreventDefault 297 | // * @param {Number} keyCode 298 | // * @private 299 | // */ 300 | // static _shouldPreventDefault(keyCode) { 301 | // switch (keyCode) { 302 | // case 8: // backspace 303 | // case 33: // pageup 304 | // case 34: // pagedown 305 | // case 37: // left arrow 306 | // case 38: // up arrow 307 | // case 39: // right arrow 308 | // case 40: // down arrow 309 | // return true 310 | // } 311 | // return false 312 | // } 313 | 314 | // /** 315 | // * @static 316 | // * @method _onKeyUp 317 | // * @param {KeyboardEvent} event 318 | // * @private 319 | // */ 320 | // static _onKeyUp(event) { 321 | // const buttonName = this.keyMapper[event.keyCode] 322 | // if (buttonName) { 323 | // this._currentState[buttonName] = false 324 | // } 325 | // if (event.keyCode === 0) { 326 | // // For QtWebEngine on OS X 327 | // this.clear() 328 | // } 329 | // } 330 | 331 | // /** 332 | // * @static 333 | // * @method _onLostFocus 334 | // * @private 335 | // */ 336 | // static _onLostFocus() { 337 | // this.clear() 338 | // } 339 | 340 | // /** 341 | // * @static 342 | // * @method _pollGamepads 343 | // * @private 344 | // */ 345 | // static _pollGamepads() { 346 | // if (navigator.getGamepads) { 347 | // const gamepads = navigator.getGamepads() 348 | // if (gamepads) { 349 | // for (let i = 0; i < gamepads.length; i++) { 350 | // const gamepad = gamepads[i] 351 | // if (gamepad && gamepad.connected) { 352 | // this._updateGamepadState(gamepad) 353 | // } 354 | // } 355 | // } 356 | // } 357 | // } 358 | 359 | // /** 360 | // * @static 361 | // * @method _updateGamepadState 362 | // * @param {Gamepad} gamepad 363 | // * @param {Number} index 364 | // * @private 365 | // */ 366 | // static _updateGamepadState(gamepad) { 367 | // const lastState = this._gamepadStates[gamepad.index] || [] 368 | // const newState = [] 369 | // const buttons = gamepad.buttons 370 | // const axes = gamepad.axes 371 | // const threshold = 0.5 372 | // newState[12] = false 373 | // newState[13] = false 374 | // newState[14] = false 375 | // newState[15] = false 376 | // for (let i = 0; i < buttons.length; i++) { 377 | // newState[i] = buttons[i].pressed 378 | // } 379 | // if (axes[1] < -threshold) { 380 | // newState[12] = true // up 381 | // } else if (axes[1] > threshold) { 382 | // newState[13] = true // down 383 | // } 384 | // if (axes[0] < -threshold) { 385 | // newState[14] = true // left 386 | // } else if (axes[0] > threshold) { 387 | // newState[15] = true // right 388 | // } 389 | // for (let j = 0; j < newState.length; j++) { 390 | // if (newState[j] !== lastState[j]) { 391 | // const buttonName = this.gamepadMapper[j] 392 | // if (buttonName) { 393 | // this._currentState[buttonName] = newState[j] 394 | // } 395 | // } 396 | // } 397 | // this._gamepadStates[gamepad.index] = newState 398 | // } 399 | 400 | // /** 401 | // * @static 402 | // * @method _updateDirection 403 | // * @private 404 | // */ 405 | // static _updateDirection() { 406 | // let x = this._signX() 407 | // let y = this._signY() 408 | 409 | // this._dir8 = this._makeNumpadDirection(x, y) 410 | 411 | // if (x !== 0 && y !== 0) { 412 | // if (this._preferredAxis === 'x') { 413 | // y = 0 414 | // } else { 415 | // x = 0 416 | // } 417 | // } else if (x !== 0) { 418 | // this._preferredAxis = 'y' 419 | // } else if (y !== 0) { 420 | // this._preferredAxis = 'x' 421 | // } 422 | 423 | // this._dir4 = this._makeNumpadDirection(x, y) 424 | // } 425 | 426 | // /** 427 | // * @static 428 | // * @method _signX 429 | // * @private 430 | // */ 431 | // static _signX() { 432 | // let x = 0 433 | 434 | // if (this.isPressed('left')) { 435 | // x-- 436 | // } 437 | // if (this.isPressed('right')) { 438 | // x++ 439 | // } 440 | // return x 441 | // } 442 | 443 | // /** 444 | // * @static 445 | // * @method _signY 446 | // * @private 447 | // */ 448 | // static _signY() { 449 | // let y = 0 450 | 451 | // if (this.isPressed('up')) { 452 | // y-- 453 | // } 454 | // if (this.isPressed('down')) { 455 | // y++ 456 | // } 457 | // return y 458 | // } 459 | 460 | // /** 461 | // * @static 462 | // * @method _makeNumpadDirection 463 | // * @param {Number} x 464 | // * @param {Number} y 465 | // * @return {Number} 466 | // * @private 467 | // */ 468 | // static _makeNumpadDirection(x, y) { 469 | // if (x !== 0 || y !== 0) { 470 | // return 5 - y * 3 + x 471 | // } 472 | // return 0 473 | // } 474 | 475 | // /** 476 | // * @static 477 | // * @method _isEscapeCompatible 478 | // * @param {String} keyName 479 | // * @return {Boolean} 480 | // * @private 481 | // */ 482 | // static _isEscapeCompatible(keyName) { 483 | // return keyName === 'cancel' || keyName === 'menu' 484 | // } 485 | // } 486 | 487 | //----------------------------------------------------------------------------- 488 | /** 489 | * The static class that handles input data from the keyboard and gamepads. 490 | * 491 | * @class Input 492 | */ 493 | function Input() { 494 | throw new Error('This is a static class') 495 | } 496 | 497 | /** 498 | * Initializes the input system. 499 | * 500 | * @static 501 | * @method initialize 502 | */ 503 | Input.initialize = function() { 504 | this.clear() 505 | this._wrapNwjsAlert() 506 | this._setupEventHandlers() 507 | } 508 | 509 | /** 510 | * The wait time of the key repeat in frames. 511 | * 512 | * @static 513 | * @property keyRepeatWait 514 | * @type Number 515 | */ 516 | Input.keyRepeatWait = 24 517 | 518 | /** 519 | * The interval of the key repeat in frames. 520 | * 521 | * @static 522 | * @property keyRepeatInterval 523 | * @type Number 524 | */ 525 | Input.keyRepeatInterval = 6 526 | 527 | /** 528 | * A hash table to convert from a virtual key code to a mapped key name. 529 | * 530 | * @static 531 | * @property keyMapper 532 | * @type Object 533 | */ 534 | Input.keyMapper = { 535 | 9: 'tab', // tab 536 | 13: 'ok', // enter 537 | 16: 'shift', // shift 538 | 17: 'control', // control 539 | 18: 'control', // alt 540 | 27: 'escape', // escape 541 | 32: 'ok', // space 542 | 33: 'pageup', // pageup 543 | 34: 'pagedown', // pagedown 544 | 37: 'left', // left arrow 545 | 38: 'up', // up arrow 546 | 39: 'right', // right arrow 547 | 40: 'down', // down arrow 548 | 45: 'escape', // insert 549 | 81: 'pageup', // Q 550 | 87: 'pagedown', // W 551 | 88: 'escape', // X 552 | 90: 'ok', // Z 553 | 96: 'escape', // numpad 0 554 | 98: 'down', // numpad 2 555 | 100: 'left', // numpad 4 556 | 102: 'right', // numpad 6 557 | 104: 'up', // numpad 8 558 | 120: 'debug' // F9 559 | } 560 | 561 | /** 562 | * A hash table to convert from a gamepad button to a mapped key name. 563 | * 564 | * @static 565 | * @property gamepadMapper 566 | * @type Object 567 | */ 568 | Input.gamepadMapper = { 569 | 0: 'ok', // A 570 | 1: 'cancel', // B 571 | 2: 'shift', // X 572 | 3: 'menu', // Y 573 | 4: 'pageup', // LB 574 | 5: 'pagedown', // RB 575 | 12: 'up', // D-pad up 576 | 13: 'down', // D-pad down 577 | 14: 'left', // D-pad left 578 | 15: 'right' // D-pad right 579 | } 580 | 581 | /** 582 | * Clears all the input data. 583 | * 584 | * @static 585 | * @method clear 586 | */ 587 | Input.clear = function() { 588 | this._currentState = {} 589 | this._previousState = {} 590 | this._gamepadStates = [] 591 | this._latestButton = null 592 | this._pressedTime = 0 593 | this._dir4 = 0 594 | this._dir8 = 0 595 | this._preferredAxis = '' 596 | this._date = 0 597 | } 598 | 599 | /** 600 | * Updates the input data. 601 | * 602 | * @static 603 | * @method update 604 | */ 605 | Input.update = function() { 606 | this._pollGamepads() 607 | if (this._currentState[this._latestButton]) { 608 | this._pressedTime++ 609 | } else { 610 | this._latestButton = null 611 | } 612 | for (var name in this._currentState) { 613 | if (this._currentState[name] && !this._previousState[name]) { 614 | this._latestButton = name 615 | this._pressedTime = 0 616 | this._date = Date.now() 617 | } 618 | this._previousState[name] = this._currentState[name] 619 | } 620 | this._updateDirection() 621 | } 622 | 623 | /** 624 | * Checks whether a key is currently pressed down. 625 | * 626 | * @static 627 | * @method isPressed 628 | * @param {String} keyName The mapped name of the key 629 | * @return {Boolean} True if the key is pressed 630 | */ 631 | Input.isPressed = function(keyName) { 632 | if (this._isEscapeCompatible(keyName) && this.isPressed('escape')) { 633 | return true 634 | } else { 635 | return !!this._currentState[keyName] 636 | } 637 | } 638 | 639 | /** 640 | * Checks whether a key is just pressed. 641 | * 642 | * @static 643 | * @method isTriggered 644 | * @param {String} keyName The mapped name of the key 645 | * @return {Boolean} True if the key is triggered 646 | */ 647 | Input.isTriggered = function(keyName) { 648 | if (this._isEscapeCompatible(keyName) && this.isTriggered('escape')) { 649 | return true 650 | } else { 651 | return this._latestButton === keyName && this._pressedTime === 0 652 | } 653 | } 654 | 655 | /** 656 | * Checks whether a key is just pressed or a key repeat occurred. 657 | * 658 | * @static 659 | * @method isRepeated 660 | * @param {String} keyName The mapped name of the key 661 | * @return {Boolean} True if the key is repeated 662 | */ 663 | Input.isRepeated = function(keyName) { 664 | if (this._isEscapeCompatible(keyName) && this.isRepeated('escape')) { 665 | return true 666 | } else { 667 | return ( 668 | this._latestButton === keyName && 669 | (this._pressedTime === 0 || 670 | (this._pressedTime >= this.keyRepeatWait && 671 | this._pressedTime % this.keyRepeatInterval === 0)) 672 | ) 673 | } 674 | } 675 | 676 | /** 677 | * Checks whether a key is kept depressed. 678 | * 679 | * @static 680 | * @method isLongPressed 681 | * @param {String} keyName The mapped name of the key 682 | * @return {Boolean} True if the key is long-pressed 683 | */ 684 | Input.isLongPressed = function(keyName) { 685 | if (this._isEscapeCompatible(keyName) && this.isLongPressed('escape')) { 686 | return true 687 | } else { 688 | return ( 689 | this._latestButton === keyName && this._pressedTime >= this.keyRepeatWait 690 | ) 691 | } 692 | } 693 | 694 | /** 695 | * [read-only] The four direction value as a number of the numpad, or 0 for neutral. 696 | * 697 | * @static 698 | * @property dir4 699 | * @type Number 700 | */ 701 | Object.defineProperty(Input, 'dir4', { 702 | get: function() { 703 | return this._dir4 704 | }, 705 | configurable: true 706 | }) 707 | 708 | /** 709 | * [read-only] The eight direction value as a number of the numpad, or 0 for neutral. 710 | * 711 | * @static 712 | * @property dir8 713 | * @type Number 714 | */ 715 | Object.defineProperty(Input, 'dir8', { 716 | get: function() { 717 | return this._dir8 718 | }, 719 | configurable: true 720 | }) 721 | 722 | /** 723 | * [read-only] The time of the last input in milliseconds. 724 | * 725 | * @static 726 | * @property date 727 | * @type Number 728 | */ 729 | Object.defineProperty(Input, 'date', { 730 | get: function() { 731 | return this._date 732 | }, 733 | configurable: true 734 | }) 735 | 736 | /** 737 | * @static 738 | * @method _wrapNwjsAlert 739 | * @private 740 | */ 741 | Input._wrapNwjsAlert = function() { 742 | if (Utils.isNwjs()) { 743 | var _alert = window.alert 744 | window.alert = function() { 745 | var gui = require('nw.gui') 746 | var win = gui.Window.get() 747 | _alert.apply(this, arguments) 748 | win.focus() 749 | Input.clear() 750 | } 751 | } 752 | } 753 | 754 | /** 755 | * @static 756 | * @method _setupEventHandlers 757 | * @private 758 | */ 759 | Input._setupEventHandlers = function() { 760 | document.addEventListener('keydown', this._onKeyDown.bind(this)) 761 | document.addEventListener('keyup', this._onKeyUp.bind(this)) 762 | window.addEventListener('blur', this._onLostFocus.bind(this)) 763 | } 764 | 765 | /** 766 | * @static 767 | * @method _onKeyDown 768 | * @param {KeyboardEvent} event 769 | * @private 770 | */ 771 | Input._onKeyDown = function(event) { 772 | if (this._shouldPreventDefault(event.keyCode)) { 773 | event.preventDefault() 774 | } 775 | if (event.keyCode === 144) { 776 | // Numlock 777 | this.clear() 778 | } 779 | var buttonName = this.keyMapper[event.keyCode] 780 | if (ResourceHandler.exists() && buttonName === 'ok') { 781 | ResourceHandler.retry() 782 | } else if (buttonName) { 783 | this._currentState[buttonName] = true 784 | } 785 | } 786 | 787 | /** 788 | * @static 789 | * @method _shouldPreventDefault 790 | * @param {Number} keyCode 791 | * @private 792 | */ 793 | Input._shouldPreventDefault = function(keyCode) { 794 | switch (keyCode) { 795 | case 8: // backspace 796 | case 33: // pageup 797 | case 34: // pagedown 798 | case 37: // left arrow 799 | case 38: // up arrow 800 | case 39: // right arrow 801 | case 40: // down arrow 802 | return true 803 | } 804 | return false 805 | } 806 | 807 | /** 808 | * @static 809 | * @method _onKeyUp 810 | * @param {KeyboardEvent} event 811 | * @private 812 | */ 813 | Input._onKeyUp = function(event) { 814 | var buttonName = this.keyMapper[event.keyCode] 815 | if (buttonName) { 816 | this._currentState[buttonName] = false 817 | } 818 | if (event.keyCode === 0) { 819 | // For QtWebEngine on OS X 820 | this.clear() 821 | } 822 | } 823 | 824 | /** 825 | * @static 826 | * @method _onLostFocus 827 | * @private 828 | */ 829 | Input._onLostFocus = function() { 830 | this.clear() 831 | } 832 | 833 | /** 834 | * @static 835 | * @method _pollGamepads 836 | * @private 837 | */ 838 | Input._pollGamepads = function() { 839 | if (navigator.getGamepads) { 840 | var gamepads = navigator.getGamepads() 841 | if (gamepads) { 842 | for (var i = 0; i < gamepads.length; i++) { 843 | var gamepad = gamepads[i] 844 | if (gamepad && gamepad.connected) { 845 | this._updateGamepadState(gamepad) 846 | } 847 | } 848 | } 849 | } 850 | } 851 | 852 | /** 853 | * @static 854 | * @method _updateGamepadState 855 | * @param {Gamepad} gamepad 856 | * @param {Number} index 857 | * @private 858 | */ 859 | Input._updateGamepadState = function(gamepad) { 860 | var lastState = this._gamepadStates[gamepad.index] || [] 861 | var newState = [] 862 | var buttons = gamepad.buttons 863 | var axes = gamepad.axes 864 | var threshold = 0.5 865 | newState[12] = false 866 | newState[13] = false 867 | newState[14] = false 868 | newState[15] = false 869 | for (var i = 0; i < buttons.length; i++) { 870 | newState[i] = buttons[i].pressed 871 | } 872 | if (axes[1] < -threshold) { 873 | newState[12] = true // up 874 | } else if (axes[1] > threshold) { 875 | newState[13] = true // down 876 | } 877 | if (axes[0] < -threshold) { 878 | newState[14] = true // left 879 | } else if (axes[0] > threshold) { 880 | newState[15] = true // right 881 | } 882 | for (var j = 0; j < newState.length; j++) { 883 | if (newState[j] !== lastState[j]) { 884 | var buttonName = this.gamepadMapper[j] 885 | if (buttonName) { 886 | this._currentState[buttonName] = newState[j] 887 | } 888 | } 889 | } 890 | this._gamepadStates[gamepad.index] = newState 891 | } 892 | 893 | /** 894 | * @static 895 | * @method _updateDirection 896 | * @private 897 | */ 898 | Input._updateDirection = function() { 899 | var x = this._signX() 900 | var y = this._signY() 901 | 902 | this._dir8 = this._makeNumpadDirection(x, y) 903 | 904 | if (x !== 0 && y !== 0) { 905 | if (this._preferredAxis === 'x') { 906 | y = 0 907 | } else { 908 | x = 0 909 | } 910 | } else if (x !== 0) { 911 | this._preferredAxis = 'y' 912 | } else if (y !== 0) { 913 | this._preferredAxis = 'x' 914 | } 915 | 916 | this._dir4 = this._makeNumpadDirection(x, y) 917 | } 918 | 919 | /** 920 | * @static 921 | * @method _signX 922 | * @private 923 | */ 924 | Input._signX = function() { 925 | var x = 0 926 | 927 | if (this.isPressed('left')) { 928 | x-- 929 | } 930 | if (this.isPressed('right')) { 931 | x++ 932 | } 933 | return x 934 | } 935 | 936 | /** 937 | * @static 938 | * @method _signY 939 | * @private 940 | */ 941 | Input._signY = function() { 942 | var y = 0 943 | 944 | if (this.isPressed('up')) { 945 | y-- 946 | } 947 | if (this.isPressed('down')) { 948 | y++ 949 | } 950 | return y 951 | } 952 | 953 | /** 954 | * @static 955 | * @method _makeNumpadDirection 956 | * @param {Number} x 957 | * @param {Number} y 958 | * @return {Number} 959 | * @private 960 | */ 961 | Input._makeNumpadDirection = function(x, y) { 962 | if (x !== 0 || y !== 0) { 963 | return 5 - y * 3 + x 964 | } 965 | return 0 966 | } 967 | 968 | /** 969 | * @static 970 | * @method _isEscapeCompatible 971 | * @param {String} keyName 972 | * @return {Boolean} 973 | * @private 974 | */ 975 | Input._isEscapeCompatible = function(keyName) { 976 | return keyName === 'cancel' || keyName === 'menu' 977 | } 978 | 979 | export { Input } 980 | --------------------------------------------------------------------------------