· 6 years ago · Sep 10, 2019, 10:38 AM
1//=============================================================================
2// rpg_core.js v1.6.2
3//=============================================================================
4
5//-----------------------------------------------------------------------------
6/**
7 * This is not a class, but contains some methods that will be added to the
8 * standard Javascript objects.
9 *
10 * @class JsExtensions
11 */
12function JsExtensions() {
13 throw new Error('This is not a class');
14}
15
16/**
17 * Returns a number whose value is limited to the given range.
18 *
19 * @method Number.prototype.clamp
20 * @param {Number} min The lower boundary
21 * @param {Number} max The upper boundary
22 * @return {Number} A number in the range (min, max)
23 */
24Number.prototype.clamp = function(min, max) {
25 return Math.min(Math.max(this, min), max);
26};
27
28/**
29 * Returns a modulo value which is always positive.
30 *
31 * @method Number.prototype.mod
32 * @param {Number} n The divisor
33 * @return {Number} A modulo value
34 */
35Number.prototype.mod = function(n) {
36 return ((this % n) + n) % n;
37};
38
39/**
40 * Replaces %1, %2 and so on in the string to the arguments.
41 *
42 * @method String.prototype.format
43 * @param {Any} ...args The objects to format
44 * @return {String} A formatted string
45 */
46String.prototype.format = function() {
47 var args = arguments;
48 return this.replace(/%([0-9]+)/g, function(s, n) {
49 return args[Number(n) - 1];
50 });
51};
52
53/**
54 * Makes a number string with leading zeros.
55 *
56 * @method String.prototype.padZero
57 * @param {Number} length The length of the output string
58 * @return {String} A string with leading zeros
59 */
60String.prototype.padZero = function(length){
61 var s = this;
62 while (s.length < length) {
63 s = '0' + s;
64 }
65 return s;
66};
67
68/**
69 * Makes a number string with leading zeros.
70 *
71 * @method Number.prototype.padZero
72 * @param {Number} length The length of the output string
73 * @return {String} A string with leading zeros
74 */
75Number.prototype.padZero = function(length){
76 return String(this).padZero(length);
77};
78
79Object.defineProperties(Array.prototype, {
80 /**
81 * Checks whether the two arrays are same.
82 *
83 * @method Array.prototype.equals
84 * @param {Array} array The array to compare to
85 * @return {Boolean} True if the two arrays are same
86 */
87 equals: {
88 enumerable: false,
89 value: function(array) {
90 if (!array || this.length !== array.length) {
91 return false;
92 }
93 for (var i = 0; i < this.length; i++) {
94 if (this[i] instanceof Array && array[i] instanceof Array) {
95 if (!this[i].equals(array[i])) {
96 return false;
97 }
98 } else if (this[i] !== array[i]) {
99 return false;
100 }
101 }
102 return true;
103 }
104 },
105 /**
106 * Makes a shallow copy of the array.
107 *
108 * @method Array.prototype.clone
109 * @return {Array} A shallow copy of the array
110 */
111 clone: {
112 enumerable: false,
113 value: function() {
114 return this.slice(0);
115 }
116 },
117 /**
118 * Checks whether the array contains a given element.
119 *
120 * @method Array.prototype.contains
121 * @param {Any} element The element to search for
122 * @return {Boolean} True if the array contains a given element
123 */
124 contains : {
125 enumerable: false,
126 value: function(element) {
127 return this.indexOf(element) >= 0;
128 }
129 }
130});
131
132/**
133 * Checks whether the string contains a given string.
134 *
135 * @method String.prototype.contains
136 * @param {String} string The string to search for
137 * @return {Boolean} True if the string contains a given string
138 */
139String.prototype.contains = function(string) {
140 return this.indexOf(string) >= 0;
141};
142
143/**
144 * Generates a random integer in the range (0, max-1).
145 *
146 * @static
147 * @method Math.randomInt
148 * @param {Number} max The upper boundary (excluded)
149 * @return {Number} A random integer
150 */
151Math.randomInt = function(max) {
152 return Math.floor(max * Math.random());
153};
154
155//-----------------------------------------------------------------------------
156/**
157 * The static class that defines utility methods.
158 *
159 * @class Utils
160 */
161function Utils() {
162 throw new Error('This is a static class');
163}
164
165/**
166 * The name of the RPG Maker. 'MV' in the current version.
167 *
168 * @static
169 * @property RPGMAKER_NAME
170 * @type String
171 * @final
172 */
173Utils.RPGMAKER_NAME = 'MV';
174
175/**
176 * The version of the RPG Maker.
177 *
178 * @static
179 * @property RPGMAKER_VERSION
180 * @type String
181 * @final
182 */
183Utils.RPGMAKER_VERSION = "1.6.1";
184
185/**
186 * Checks whether the option is in the query string.
187 *
188 * @static
189 * @method isOptionValid
190 * @param {String} name The option name
191 * @return {Boolean} True if the option is in the query string
192 */
193Utils.isOptionValid = function(name) {
194 if (location.search.slice(1).split('&').contains(name)) {return 1;};
195 if (typeof nw !== "undefined" && nw.App.argv.length > 0 && nw.App.argv[0].split('&').contains(name)) {return 1;};
196 return 0;
197};
198
199/**
200 * Checks whether the platform is NW.js.
201 *
202 * @static
203 * @method isNwjs
204 * @return {Boolean} True if the platform is NW.js
205 */
206Utils.isNwjs = function() {
207 return typeof require === 'function' && typeof process === 'object';
208};
209
210/**
211 * Checks whether the platform is a mobile device.
212 *
213 * @static
214 * @method isMobileDevice
215 * @return {Boolean} True if the platform is a mobile device
216 */
217Utils.isMobileDevice = function() {
218 var r = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
219 return !!navigator.userAgent.match(r);
220};
221
222/**
223 * Checks whether the browser is Mobile Safari.
224 *
225 * @static
226 * @method isMobileSafari
227 * @return {Boolean} True if the browser is Mobile Safari
228 */
229Utils.isMobileSafari = function() {
230 var agent = navigator.userAgent;
231 return !!(agent.match(/iPhone|iPad|iPod/) && agent.match(/AppleWebKit/) &&
232 !agent.match('CriOS'));
233};
234
235/**
236 * Checks whether the browser is Android Chrome.
237 *
238 * @static
239 * @method isAndroidChrome
240 * @return {Boolean} True if the browser is Android Chrome
241 */
242Utils.isAndroidChrome = function() {
243 var agent = navigator.userAgent;
244 return !!(agent.match(/Android/) && agent.match(/Chrome/));
245};
246
247/**
248 * Checks whether the browser can read files in the game folder.
249 *
250 * @static
251 * @method canReadGameFiles
252 * @return {Boolean} True if the browser can read files in the game folder
253 */
254Utils.canReadGameFiles = function() {
255 var scripts = document.getElementsByTagName('script');
256 var lastScript = scripts[scripts.length - 1];
257 var xhr = new XMLHttpRequest();
258 try {
259 xhr.open('GET', lastScript.src);
260 xhr.overrideMimeType('text/javascript');
261 xhr.send();
262 return true;
263 } catch (e) {
264 return false;
265 }
266};
267
268/**
269 * Makes a CSS color string from RGB values.
270 *
271 * @static
272 * @method rgbToCssColor
273 * @param {Number} r The red value in the range (0, 255)
274 * @param {Number} g The green value in the range (0, 255)
275 * @param {Number} b The blue value in the range (0, 255)
276 * @return {String} CSS color string
277 */
278Utils.rgbToCssColor = function(r, g, b) {
279 r = Math.round(r);
280 g = Math.round(g);
281 b = Math.round(b);
282 return 'rgb(' + r + ',' + g + ',' + b + ')';
283};
284
285Utils._id = 1;
286Utils.generateRuntimeId = function(){
287 return Utils._id++;
288};
289
290Utils._supportPassiveEvent = null;
291/**
292 * Test this browser support passive event feature
293 *
294 * @static
295 * @method isSupportPassiveEvent
296 * @return {Boolean} this browser support passive event or not
297 */
298Utils.isSupportPassiveEvent = function() {
299 if (typeof Utils._supportPassiveEvent === "boolean") {
300 return Utils._supportPassiveEvent;
301 }
302 // test support passive event
303 // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
304 var passive = false;
305 var options = Object.defineProperty({}, "passive", {
306 get: function() { passive = true; }
307 });
308 window.addEventListener("test", null, options);
309 Utils._supportPassiveEvent = passive;
310 return passive;
311}
312
313//-----------------------------------------------------------------------------
314/**
315 * The resource class. Allows to be collected as a garbage if not use for some time or ticks
316 *
317 * @class CacheEntry
318 * @constructor
319 * @param {ResourceManager} resource manager
320 * @param {string} key, url of the resource
321 * @param {string} item - Bitmap, HTML5Audio, WebAudio - whatever you want to store in the cache
322 */
323function CacheEntry(cache, key, item) {
324 this.cache = cache;
325 this.key = key;
326 this.item = item;
327 this.cached = false;
328 this.touchTicks = 0;
329 this.touchSeconds = 0;
330 this.ttlTicks = 0;
331 this.ttlSeconds = 0;
332 this.freedByTTL = false;
333}
334
335/**
336 * frees the resource
337 */
338CacheEntry.prototype.free = function (byTTL) {
339 this.freedByTTL = byTTL || false;
340 if (this.cached) {
341 this.cached = false;
342 delete this.cache._inner[this.key];
343 }
344};
345
346/**
347 * Allocates the resource
348 * @returns {CacheEntry}
349 */
350CacheEntry.prototype.allocate = function () {
351 if (!this.cached) {
352 this.cache._inner[this.key] = this;
353 this.cached = true;
354 }
355 this.touch();
356 return this;
357};
358
359/**
360 * Sets the time to live
361 * @param {number} ticks TTL in ticks, 0 if not set
362 * @param {number} time TTL in seconds, 0 if not set
363 * @returns {CacheEntry}
364 */
365CacheEntry.prototype.setTimeToLive = function (ticks, seconds) {
366 this.ttlTicks = ticks || 0;
367 this.ttlSeconds = seconds || 0;
368 return this;
369};
370
371CacheEntry.prototype.isStillAlive = function () {
372 var cache = this.cache;
373 return ((this.ttlTicks == 0) || (this.touchTicks + this.ttlTicks < cache.updateTicks )) &&
374 ((this.ttlSeconds == 0) || (this.touchSeconds + this.ttlSeconds < cache.updateSeconds ));
375};
376
377/**
378 * makes sure that resource wont freed by Time To Live
379 * if resource was already freed by TTL, put it in cache again
380 */
381CacheEntry.prototype.touch = function () {
382 var cache = this.cache;
383 if (this.cached) {
384 this.touchTicks = cache.updateTicks;
385 this.touchSeconds = cache.updateSeconds;
386 } else if (this.freedByTTL) {
387 this.freedByTTL = false;
388 if (!cache._inner[this.key]) {
389 cache._inner[this.key] = this;
390 }
391 }
392};
393
394/**
395 * Cache for images, audio, or any other kind of resource
396 * @param manager
397 * @constructor
398 */
399function CacheMap(manager) {
400 this.manager = manager;
401 this._inner = {};
402 this._lastRemovedEntries = {};
403 this.updateTicks = 0;
404 this.lastCheckTTL = 0;
405 this.delayCheckTTL = 100.0;
406 this.updateSeconds = Date.now();
407}
408
409/**
410 * checks ttl of all elements and removes dead ones
411 */
412CacheMap.prototype.checkTTL = function () {
413 var cache = this._inner;
414 var temp = this._lastRemovedEntries;
415 if (!temp) {
416 temp = [];
417 this._lastRemovedEntries = temp;
418 }
419 for (var key in cache) {
420 var entry = cache[key];
421 if (!entry.isStillAlive()) {
422 temp.push(entry);
423 }
424 }
425 for (var i = 0; i < temp.length; i++) {
426 temp[i].free(true);
427 }
428 temp.length = 0;
429};
430
431/**
432 * cache item
433 * @param key url of cache element
434 * @returns {*|null}
435 */
436CacheMap.prototype.getItem = function (key) {
437 var entry = this._inner[key];
438 if (entry) {
439 return entry.item;
440 }
441 return null;
442};
443
444CacheMap.prototype.clear = function () {
445 var keys = Object.keys(this._inner);
446 for (var i = 0; i < keys.length; i++) {
447 this._inner[keys[i]].free();
448 }
449};
450
451CacheMap.prototype.setItem = function (key, item) {
452 return new CacheEntry(this, key, item).allocate();
453};
454
455CacheMap.prototype.update = function(ticks, delta) {
456 this.updateTicks += ticks;
457 this.updateSeconds += delta;
458 if (this.updateSeconds >= this.delayCheckTTL + this.lastCheckTTL) {
459 this.lastCheckTTL = this.updateSeconds;
460 this.checkTTL();
461 }
462};
463
464function ImageCache(){
465 this.initialize.apply(this, arguments);
466}
467
468ImageCache.limit = 10 * 1000 * 1000;
469
470ImageCache.prototype.initialize = function(){
471 this._items = {};
472};
473
474ImageCache.prototype.add = function(key, value){
475 this._items[key] = {
476 bitmap: value,
477 touch: Date.now(),
478 key: key
479 };
480
481 this._truncateCache();
482};
483
484ImageCache.prototype.get = function(key){
485 if(this._items[key]){
486 var item = this._items[key];
487 item.touch = Date.now();
488 return item.bitmap;
489 }
490
491 return null;
492};
493
494ImageCache.prototype.reserve = function(key, value, reservationId){
495 if(!this._items[key]){
496 this._items[key] = {
497 bitmap: value,
498 touch: Date.now(),
499 key: key
500 };
501 }
502
503 this._items[key].reservationId = reservationId;
504};
505
506ImageCache.prototype.releaseReservation = function(reservationId){
507 var items = this._items;
508
509 Object.keys(items)
510 .map(function(key){return items[key];})
511 .forEach(function(item){
512 if(item.reservationId === reservationId){
513 delete item.reservationId;
514 }
515 });
516};
517
518ImageCache.prototype._truncateCache = function(){
519 var items = this._items;
520 var sizeLeft = ImageCache.limit;
521
522 Object.keys(items).map(function(key){
523 return items[key];
524 }).sort(function(a, b){
525 return b.touch - a.touch;
526 }).forEach(function(item){
527 if(sizeLeft > 0 || this._mustBeHeld(item)){
528 var bitmap = item.bitmap;
529 sizeLeft -= bitmap.width * bitmap.height;
530 }else{
531 delete items[item.key];
532 }
533 }.bind(this));
534};
535
536ImageCache.prototype._mustBeHeld = function(item){
537 // request only is weak so It's purgeable
538 if(item.bitmap.isRequestOnly()) return false;
539 // reserved item must be held
540 if(item.reservationId) return true;
541 // not ready bitmap must be held (because of checking isReady())
542 if(!item.bitmap.isReady()) return true;
543 // then the item may purgeable
544 return false;
545};
546
547ImageCache.prototype.isReady = function(){
548 var items = this._items;
549 return !Object.keys(items).some(function(key){
550 return !items[key].bitmap.isRequestOnly() && !items[key].bitmap.isReady();
551 });
552};
553
554ImageCache.prototype.getErrorBitmap = function(){
555 var items = this._items;
556 var bitmap = null;
557 if(Object.keys(items).some(function(key){
558 if(items[key].bitmap.isError()){
559 bitmap = items[key].bitmap;
560 return true;
561 }
562 return false;
563 })) {
564 return bitmap;
565 }
566
567 return null;
568};
569function RequestQueue(){
570 this.initialize.apply(this, arguments);
571}
572
573RequestQueue.prototype.initialize = function(){
574 this._queue = [];
575};
576
577RequestQueue.prototype.enqueue = function(key, value){
578 this._queue.push({
579 key: key,
580 value: value,
581 });
582};
583
584RequestQueue.prototype.update = function(){
585 if(this._queue.length === 0) return;
586
587 var top = this._queue[0];
588 if(top.value.isRequestReady()){
589 this._queue.shift();
590 if(this._queue.length !== 0){
591 this._queue[0].value.startRequest();
592 }
593 }else{
594 top.value.startRequest();
595 }
596};
597
598RequestQueue.prototype.raisePriority = function(key){
599 for(var n = 0; n < this._queue.length; n++){
600 var item = this._queue[n];
601 if(item.key === key){
602 this._queue.splice(n, 1);
603 this._queue.unshift(item);
604 break;
605 }
606 }
607};
608
609RequestQueue.prototype.clear = function(){
610 this._queue.splice(0);
611};
612//-----------------------------------------------------------------------------
613/**
614 * The point class.
615 *
616 * @class Point
617 * @constructor
618 * @param {Number} x The x coordinate
619 * @param {Number} y The y coordinate
620 */
621function Point() {
622 this.initialize.apply(this, arguments);
623}
624
625Point.prototype = Object.create(PIXI.Point.prototype);
626Point.prototype.constructor = Point;
627
628Point.prototype.initialize = function(x, y) {
629 PIXI.Point.call(this, x, y);
630};
631
632/**
633 * The x coordinate.
634 *
635 * @property x
636 * @type Number
637 */
638
639/**
640 * The y coordinate.
641 *
642 * @property y
643 * @type Number
644 */
645
646//-----------------------------------------------------------------------------
647/**
648 * The rectangle class.
649 *
650 * @class Rectangle
651 * @constructor
652 * @param {Number} x The x coordinate for the upper-left corner
653 * @param {Number} y The y coordinate for the upper-left corner
654 * @param {Number} width The width of the rectangle
655 * @param {Number} height The height of the rectangle
656 */
657function Rectangle() {
658 this.initialize.apply(this, arguments);
659}
660
661Rectangle.prototype = Object.create(PIXI.Rectangle.prototype);
662Rectangle.prototype.constructor = Rectangle;
663
664Rectangle.prototype.initialize = function(x, y, width, height) {
665 PIXI.Rectangle.call(this, x, y, width, height);
666};
667
668/**
669 * @static
670 * @property emptyRectangle
671 * @type Rectangle
672 * @private
673 */
674Rectangle.emptyRectangle = new Rectangle(0, 0, 0, 0);
675
676/**
677 * The x coordinate for the upper-left corner.
678 *
679 * @property x
680 * @type Number
681 */
682
683/**
684 * The y coordinate for the upper-left corner.
685 *
686 * @property y
687 * @type Number
688 */
689
690/**
691 * The width of the rectangle.
692 *
693 * @property width
694 * @type Number
695 */
696
697/**
698 * The height of the rectangle.
699 *
700 * @property height
701 * @type Number
702 */
703
704//-----------------------------------------------------------------------------
705/**
706 * The basic object that represents an image.
707 *
708 * @class Bitmap
709 * @constructor
710 * @param {Number} width The width of the bitmap
711 * @param {Number} height The height of the bitmap
712 */
713function Bitmap() {
714 this.initialize.apply(this, arguments);
715}
716
717//for iOS. img consumes memory. so reuse it.
718Bitmap._reuseImages = [];
719
720
721/**
722 * Bitmap states(Bitmap._loadingState):
723 *
724 * none:
725 * Empty Bitmap
726 *
727 * pending:
728 * Url requested, but pending to load until startRequest called
729 *
730 * purged:
731 * Url request completed and purged.
732 *
733 * requesting:
734 * Requesting supplied URI now.
735 *
736 * requestCompleted:
737 * Request completed
738 *
739 * decrypting:
740 * requesting encrypted data from supplied URI or decrypting it.
741 *
742 * decryptCompleted:
743 * Decrypt completed
744 *
745 * loaded:
746 * loaded. isReady() === true, so It's usable.
747 *
748 * error:
749 * error occurred
750 *
751 */
752
753
754Bitmap.prototype._createCanvas = function(width, height){
755 this.__canvas = this.__canvas || document.createElement('canvas');
756 this.__context = this.__canvas.getContext('2d');
757
758 this.__canvas.width = Math.max(width || 0, 1);
759 this.__canvas.height = Math.max(height || 0, 1);
760
761 if(this._image){
762 var w = Math.max(this._image.width || 0, 1);
763 var h = Math.max(this._image.height || 0, 1);
764 this.__canvas.width = w;
765 this.__canvas.height = h;
766 this._createBaseTexture(this._canvas);
767
768 this.__context.drawImage(this._image, 0, 0);
769 }
770
771 this._setDirty();
772};
773
774Bitmap.prototype._createBaseTexture = function(source){
775 this.__baseTexture = new PIXI.BaseTexture(source);
776 this.__baseTexture.mipmap = false;
777 this.__baseTexture.width = source.width;
778 this.__baseTexture.height = source.height;
779
780 if (this._smooth) {
781 this._baseTexture.scaleMode = PIXI.SCALE_MODES.LINEAR;
782 } else {
783 this._baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;
784 }
785};
786
787Bitmap.prototype._clearImgInstance = function(){
788 this._image.src = "";
789 this._image.onload = null;
790 this._image.onerror = null;
791 this._errorListener = null;
792 this._loadListener = null;
793
794 Bitmap._reuseImages.push(this._image);
795 this._image = null;
796};
797
798//
799//We don't want to waste memory, so creating canvas is deferred.
800//
801Object.defineProperties(Bitmap.prototype, {
802 _canvas: {
803 get: function(){
804 if(!this.__canvas)this._createCanvas();
805 return this.__canvas;
806 }
807 },
808 _context: {
809 get: function(){
810 if(!this.__context)this._createCanvas();
811 return this.__context;
812 }
813 },
814
815 _baseTexture: {
816 get: function(){
817 if(!this.__baseTexture) this._createBaseTexture(this._image || this.__canvas);
818 return this.__baseTexture;
819 }
820 }
821});
822
823Bitmap.prototype._renewCanvas = function(){
824 var newImage = this._image;
825 if(newImage && this.__canvas && (this.__canvas.width < newImage.width || this.__canvas.height < newImage.height)){
826 this._createCanvas();
827 }
828};
829
830Bitmap.prototype.initialize = function(width, height) {
831 if(!this._defer){
832 this._createCanvas(width, height);
833 }
834
835 this._image = null;
836 this._url = '';
837 this._paintOpacity = 255;
838 this._smooth = false;
839 this._loadListeners = [];
840 this._loadingState = 'none';
841 this._decodeAfterRequest = false;
842
843 /**
844 * Cache entry, for images. In all cases _url is the same as cacheEntry.key
845 * @type CacheEntry
846 */
847 this.cacheEntry = null;
848
849 /**
850 * The face name of the font.
851 *
852 * @property fontFace
853 * @type String
854 */
855 this.fontFace = 'GameFont';
856
857 /**
858 * The size of the font in pixels.
859 *
860 * @property fontSize
861 * @type Number
862 */
863 this.fontSize = 28;
864
865 /**
866 * Whether the font is italic.
867 *
868 * @property fontItalic
869 * @type Boolean
870 */
871 this.fontItalic = false;
872
873 /**
874 * The color of the text in CSS format.
875 *
876 * @property textColor
877 * @type String
878 */
879 this.textColor = '#ffffff';
880
881 /**
882 * The color of the outline of the text in CSS format.
883 *
884 * @property outlineColor
885 * @type String
886 */
887 this.outlineColor = 'rgba(0, 0, 0, 0.5)';
888
889 /**
890 * The width of the outline of the text.
891 *
892 * @property outlineWidth
893 * @type Number
894 */
895 this.outlineWidth = 4;
896};
897
898/**
899 * Loads a image file and returns a new bitmap object.
900 *
901 * @static
902 * @method load
903 * @param {String} url The image url of the texture
904 * @return Bitmap
905 */
906Bitmap.load = function(url) {
907 var bitmap = Object.create(Bitmap.prototype);
908 bitmap._defer = true;
909 bitmap.initialize();
910
911 bitmap._decodeAfterRequest = true;
912 bitmap._requestImage(url);
913
914 return bitmap;
915};
916
917/**
918 * Takes a snapshot of the game screen and returns a new bitmap object.
919 *
920 * @static
921 * @method snap
922 * @param {Stage} stage The stage object
923 * @return Bitmap
924 */
925Bitmap.snap = function(stage) {
926 var width = Graphics.width;
927 var height = Graphics.height;
928 var bitmap = new Bitmap(width, height);
929 var context = bitmap._context;
930 var renderTexture = PIXI.RenderTexture.create(width, height);
931 if (stage) {
932 Graphics._renderer.render(stage, renderTexture);
933 stage.worldTransform.identity();
934 var canvas = null;
935 if (Graphics.isWebGL()) {
936 canvas = Graphics._renderer.extract.canvas(renderTexture);
937 } else {
938 canvas = renderTexture.baseTexture._canvasRenderTarget.canvas;
939 }
940 context.drawImage(canvas, 0, 0);
941 } else {
942
943 }
944 renderTexture.destroy({ destroyBase: true });
945 bitmap._setDirty();
946 return bitmap;
947};
948
949/**
950 * Checks whether the bitmap is ready to render.
951 *
952 * @method isReady
953 * @return {Boolean} True if the bitmap is ready to render
954 */
955Bitmap.prototype.isReady = function() {
956 return this._loadingState === 'loaded' || this._loadingState === 'none';
957};
958
959/**
960 * Checks whether a loading error has occurred.
961 *
962 * @method isError
963 * @return {Boolean} True if a loading error has occurred
964 */
965Bitmap.prototype.isError = function() {
966 return this._loadingState === 'error';
967};
968
969/**
970 * touch the resource
971 * @method touch
972 */
973Bitmap.prototype.touch = function() {
974 if (this.cacheEntry) {
975 this.cacheEntry.touch();
976 }
977};
978
979/**
980 * [read-only] The url of the image file.
981 *
982 * @property url
983 * @type String
984 */
985Object.defineProperty(Bitmap.prototype, 'url', {
986 get: function() {
987 return this._url;
988 },
989 configurable: true
990});
991
992/**
993 * [read-only] The base texture that holds the image.
994 *
995 * @property baseTexture
996 * @type PIXI.BaseTexture
997 */
998Object.defineProperty(Bitmap.prototype, 'baseTexture', {
999 get: function() {
1000 return this._baseTexture;
1001 },
1002 configurable: true
1003});
1004
1005/**
1006 * [read-only] The bitmap canvas.
1007 *
1008 * @property canvas
1009 * @type HTMLCanvasElement
1010 */
1011Object.defineProperty(Bitmap.prototype, 'canvas', {
1012 get: function() {
1013 return this._canvas;
1014 },
1015 configurable: true
1016});
1017
1018/**
1019 * [read-only] The 2d context of the bitmap canvas.
1020 *
1021 * @property context
1022 * @type CanvasRenderingContext2D
1023 */
1024Object.defineProperty(Bitmap.prototype, 'context', {
1025 get: function() {
1026 return this._context;
1027 },
1028 configurable: true
1029});
1030
1031/**
1032 * [read-only] The width of the bitmap.
1033 *
1034 * @property width
1035 * @type Number
1036 */
1037Object.defineProperty(Bitmap.prototype, 'width', {
1038 get: function() {
1039 if(this.isReady()){
1040 return this._image? this._image.width: this._canvas.width;
1041 }
1042
1043 return 0;
1044 },
1045 configurable: true
1046});
1047
1048/**
1049 * [read-only] The height of the bitmap.
1050 *
1051 * @property height
1052 * @type Number
1053 */
1054Object.defineProperty(Bitmap.prototype, 'height', {
1055 get: function() {
1056 if(this.isReady()){
1057 return this._image? this._image.height: this._canvas.height;
1058 }
1059
1060 return 0;
1061 },
1062 configurable: true
1063});
1064
1065/**
1066 * [read-only] The rectangle of the bitmap.
1067 *
1068 * @property rect
1069 * @type Rectangle
1070 */
1071Object.defineProperty(Bitmap.prototype, 'rect', {
1072 get: function() {
1073 return new Rectangle(0, 0, this.width, this.height);
1074 },
1075 configurable: true
1076});
1077
1078/**
1079 * Whether the smooth scaling is applied.
1080 *
1081 * @property smooth
1082 * @type Boolean
1083 */
1084Object.defineProperty(Bitmap.prototype, 'smooth', {
1085 get: function() {
1086 return this._smooth;
1087 },
1088 set: function(value) {
1089 if (this._smooth !== value) {
1090 this._smooth = value;
1091 if(this.__baseTexture){
1092 if (this._smooth) {
1093 this._baseTexture.scaleMode = PIXI.SCALE_MODES.LINEAR;
1094 } else {
1095 this._baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;
1096 }
1097 }
1098 }
1099 },
1100 configurable: true
1101});
1102
1103/**
1104 * The opacity of the drawing object in the range (0, 255).
1105 *
1106 * @property paintOpacity
1107 * @type Number
1108 */
1109Object.defineProperty(Bitmap.prototype, 'paintOpacity', {
1110 get: function() {
1111 return this._paintOpacity;
1112 },
1113 set: function(value) {
1114 if (this._paintOpacity !== value) {
1115 this._paintOpacity = value;
1116 this._context.globalAlpha = this._paintOpacity / 255;
1117 }
1118 },
1119 configurable: true
1120});
1121
1122/**
1123 * Resizes the bitmap.
1124 *
1125 * @method resize
1126 * @param {Number} width The new width of the bitmap
1127 * @param {Number} height The new height of the bitmap
1128 */
1129Bitmap.prototype.resize = function(width, height) {
1130 width = Math.max(width || 0, 1);
1131 height = Math.max(height || 0, 1);
1132 this._canvas.width = width;
1133 this._canvas.height = height;
1134 this._baseTexture.width = width;
1135 this._baseTexture.height = height;
1136};
1137
1138/**
1139 * Performs a block transfer.
1140 *
1141 * @method blt
1142 * @param {Bitmap} source The bitmap to draw
1143 * @param {Number} sx The x coordinate in the source
1144 * @param {Number} sy The y coordinate in the source
1145 * @param {Number} sw The width of the source image
1146 * @param {Number} sh The height of the source image
1147 * @param {Number} dx The x coordinate in the destination
1148 * @param {Number} dy The y coordinate in the destination
1149 * @param {Number} [dw=sw] The width to draw the image in the destination
1150 * @param {Number} [dh=sh] The height to draw the image in the destination
1151 */
1152Bitmap.prototype.blt = function(source, sx, sy, sw, sh, dx, dy, dw, dh) {
1153 dw = dw || sw;
1154 dh = dh || sh;
1155 if (sx >= 0 && sy >= 0 && sw > 0 && sh > 0 && dw > 0 && dh > 0 &&
1156 sx + sw <= source.width && sy + sh <= source.height) {
1157 this._context.globalCompositeOperation = 'source-over';
1158 this._context.drawImage(source._canvas, sx, sy, sw, sh, dx, dy, dw, dh);
1159 this._setDirty();
1160 }
1161};
1162
1163/**
1164 * Performs a block transfer, using assumption that original image was not modified (no hue)
1165 *
1166 * @method blt
1167 * @param {Bitmap} source The bitmap to draw
1168 * @param {Number} sx The x coordinate in the source
1169 * @param {Number} sy The y coordinate in the source
1170 * @param {Number} sw The width of the source image
1171 * @param {Number} sh The height of the source image
1172 * @param {Number} dx The x coordinate in the destination
1173 * @param {Number} dy The y coordinate in the destination
1174 * @param {Number} [dw=sw] The width to draw the image in the destination
1175 * @param {Number} [dh=sh] The height to draw the image in the destination
1176 */
1177Bitmap.prototype.bltImage = function(source, sx, sy, sw, sh, dx, dy, dw, dh) {
1178 dw = dw || sw;
1179 dh = dh || sh;
1180 if (sx >= 0 && sy >= 0 && sw > 0 && sh > 0 && dw > 0 && dh > 0 &&
1181 sx + sw <= source.width && sy + sh <= source.height) {
1182 this._context.globalCompositeOperation = 'source-over';
1183 this._context.drawImage(source._image, sx, sy, sw, sh, dx, dy, dw, dh);
1184 this._setDirty();
1185 }
1186};
1187
1188/**
1189 * Returns pixel color at the specified point.
1190 *
1191 * @method getPixel
1192 * @param {Number} x The x coordinate of the pixel in the bitmap
1193 * @param {Number} y The y coordinate of the pixel in the bitmap
1194 * @return {String} The pixel color (hex format)
1195 */
1196Bitmap.prototype.getPixel = function(x, y) {
1197 var data = this._context.getImageData(x, y, 1, 1).data;
1198 var result = '#';
1199 for (var i = 0; i < 3; i++) {
1200 result += data[i].toString(16).padZero(2);
1201 }
1202 return result;
1203};
1204
1205/**
1206 * Returns alpha pixel value at the specified point.
1207 *
1208 * @method getAlphaPixel
1209 * @param {Number} x The x coordinate of the pixel in the bitmap
1210 * @param {Number} y The y coordinate of the pixel in the bitmap
1211 * @return {String} The alpha value
1212 */
1213Bitmap.prototype.getAlphaPixel = function(x, y) {
1214 var data = this._context.getImageData(x, y, 1, 1).data;
1215 return data[3];
1216};
1217
1218/**
1219 * Clears the specified rectangle.
1220 *
1221 * @method clearRect
1222 * @param {Number} x The x coordinate for the upper-left corner
1223 * @param {Number} y The y coordinate for the upper-left corner
1224 * @param {Number} width The width of the rectangle to clear
1225 * @param {Number} height The height of the rectangle to clear
1226 */
1227Bitmap.prototype.clearRect = function(x, y, width, height) {
1228 this._context.clearRect(x, y, width, height);
1229 this._setDirty();
1230};
1231
1232/**
1233 * Clears the entire bitmap.
1234 *
1235 * @method clear
1236 */
1237Bitmap.prototype.clear = function() {
1238 this.clearRect(0, 0, this.width, this.height);
1239};
1240
1241/**
1242 * Fills the specified rectangle.
1243 *
1244 * @method fillRect
1245 * @param {Number} x The x coordinate for the upper-left corner
1246 * @param {Number} y The y coordinate for the upper-left corner
1247 * @param {Number} width The width of the rectangle to fill
1248 * @param {Number} height The height of the rectangle to fill
1249 * @param {String} color The color of the rectangle in CSS format
1250 */
1251Bitmap.prototype.fillRect = function(x, y, width, height, color) {
1252 var context = this._context;
1253 context.save();
1254 context.fillStyle = color;
1255 context.fillRect(x, y, width, height);
1256 context.restore();
1257 this._setDirty();
1258};
1259
1260/**
1261 * Fills the entire bitmap.
1262 *
1263 * @method fillAll
1264 * @param {String} color The color of the rectangle in CSS format
1265 */
1266Bitmap.prototype.fillAll = function(color) {
1267 this.fillRect(0, 0, this.width, this.height, color);
1268};
1269
1270/**
1271 * Draws the rectangle with a gradation.
1272 *
1273 * @method gradientFillRect
1274 * @param {Number} x The x coordinate for the upper-left corner
1275 * @param {Number} y The y coordinate for the upper-left corner
1276 * @param {Number} width The width of the rectangle to fill
1277 * @param {Number} height The height of the rectangle to fill
1278 * @param {String} color1 The gradient starting color
1279 * @param {String} color2 The gradient ending color
1280 * @param {Boolean} vertical Wether the gradient should be draw as vertical or not
1281 */
1282Bitmap.prototype.gradientFillRect = function(x, y, width, height, color1,
1283 color2, vertical) {
1284 var context = this._context;
1285 var grad;
1286 if (vertical) {
1287 grad = context.createLinearGradient(x, y, x, y + height);
1288 } else {
1289 grad = context.createLinearGradient(x, y, x + width, y);
1290 }
1291 grad.addColorStop(0, color1);
1292 grad.addColorStop(1, color2);
1293 context.save();
1294 context.fillStyle = grad;
1295 context.fillRect(x, y, width, height);
1296 context.restore();
1297 this._setDirty();
1298};
1299
1300/**
1301 * Draw a bitmap in the shape of a circle
1302 *
1303 * @method drawCircle
1304 * @param {Number} x The x coordinate based on the circle center
1305 * @param {Number} y The y coordinate based on the circle center
1306 * @param {Number} radius The radius of the circle
1307 * @param {String} color The color of the circle in CSS format
1308 */
1309Bitmap.prototype.drawCircle = function(x, y, radius, color) {
1310 var context = this._context;
1311 context.save();
1312 context.fillStyle = color;
1313 context.beginPath();
1314 context.arc(x, y, radius, 0, Math.PI * 2, false);
1315 context.fill();
1316 context.restore();
1317 this._setDirty();
1318};
1319
1320/**
1321 * Draws the outline text to the bitmap.
1322 *
1323 * @method drawText
1324 * @param {String} text The text that will be drawn
1325 * @param {Number} x The x coordinate for the left of the text
1326 * @param {Number} y The y coordinate for the top of the text
1327 * @param {Number} maxWidth The maximum allowed width of the text
1328 * @param {Number} lineHeight The height of the text line
1329 * @param {String} align The alignment of the text
1330 */
1331Bitmap.prototype.drawText = function(text, x, y, maxWidth, lineHeight, align) {
1332 // Note: Firefox has a bug with textBaseline: Bug 737852
1333 // So we use 'alphabetic' here.
1334 if (text !== undefined) {
1335 var tx = x;
1336 var ty = y + lineHeight - (lineHeight - this.fontSize * 0.7) / 2;
1337 var context = this._context;
1338 var alpha = context.globalAlpha;
1339 maxWidth = maxWidth || 0xffffffff;
1340 if (align === 'center') {
1341 tx += maxWidth / 2;
1342 }
1343 if (align === 'right') {
1344 tx += maxWidth;
1345 }
1346 context.save();
1347 context.font = this._makeFontNameText();
1348 context.textAlign = align;
1349 context.textBaseline = 'alphabetic';
1350 context.globalAlpha = 1;
1351 this._drawTextOutline(text, tx, ty, maxWidth);
1352 context.globalAlpha = alpha;
1353 this._drawTextBody(text, tx, ty, maxWidth);
1354 context.restore();
1355 this._setDirty();
1356 }
1357};
1358
1359/**
1360 * Returns the width of the specified text.
1361 *
1362 * @method measureTextWidth
1363 * @param {String} text The text to be measured
1364 * @return {Number} The width of the text in pixels
1365 */
1366Bitmap.prototype.measureTextWidth = function(text) {
1367 var context = this._context;
1368 context.save();
1369 context.font = this._makeFontNameText();
1370 var width = context.measureText(text).width;
1371 context.restore();
1372 return width;
1373};
1374
1375/**
1376 * Changes the color tone of the entire bitmap.
1377 *
1378 * @method adjustTone
1379 * @param {Number} r The red strength in the range (-255, 255)
1380 * @param {Number} g The green strength in the range (-255, 255)
1381 * @param {Number} b The blue strength in the range (-255, 255)
1382 */
1383Bitmap.prototype.adjustTone = function(r, g, b) {
1384 if ((r || g || b) && this.width > 0 && this.height > 0) {
1385 var context = this._context;
1386 var imageData = context.getImageData(0, 0, this.width, this.height);
1387 var pixels = imageData.data;
1388 for (var i = 0; i < pixels.length; i += 4) {
1389 pixels[i + 0] += r;
1390 pixels[i + 1] += g;
1391 pixels[i + 2] += b;
1392 }
1393 context.putImageData(imageData, 0, 0);
1394 this._setDirty();
1395 }
1396};
1397
1398/**
1399 * Rotates the hue of the entire bitmap.
1400 *
1401 * @method rotateHue
1402 * @param {Number} offset The hue offset in 360 degrees
1403 */
1404Bitmap.prototype.rotateHue = function(offset) {
1405 function rgbToHsl(r, g, b) {
1406 var cmin = Math.min(r, g, b);
1407 var cmax = Math.max(r, g, b);
1408 var h = 0;
1409 var s = 0;
1410 var l = (cmin + cmax) / 2;
1411 var delta = cmax - cmin;
1412
1413 if (delta > 0) {
1414 if (r === cmax) {
1415 h = 60 * (((g - b) / delta + 6) % 6);
1416 } else if (g === cmax) {
1417 h = 60 * ((b - r) / delta + 2);
1418 } else {
1419 h = 60 * ((r - g) / delta + 4);
1420 }
1421 s = delta / (255 - Math.abs(2 * l - 255));
1422 }
1423 return [h, s, l];
1424 }
1425
1426 function hslToRgb(h, s, l) {
1427 var c = (255 - Math.abs(2 * l - 255)) * s;
1428 var x = c * (1 - Math.abs((h / 60) % 2 - 1));
1429 var m = l - c / 2;
1430 var cm = c + m;
1431 var xm = x + m;
1432
1433 if (h < 60) {
1434 return [cm, xm, m];
1435 } else if (h < 120) {
1436 return [xm, cm, m];
1437 } else if (h < 180) {
1438 return [m, cm, xm];
1439 } else if (h < 240) {
1440 return [m, xm, cm];
1441 } else if (h < 300) {
1442 return [xm, m, cm];
1443 } else {
1444 return [cm, m, xm];
1445 }
1446 }
1447
1448 if (offset && this.width > 0 && this.height > 0) {
1449 offset = ((offset % 360) + 360) % 360;
1450 var context = this._context;
1451 var imageData = context.getImageData(0, 0, this.width, this.height);
1452 var pixels = imageData.data;
1453 for (var i = 0; i < pixels.length; i += 4) {
1454 var hsl = rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
1455 var h = (hsl[0] + offset) % 360;
1456 var s = hsl[1];
1457 var l = hsl[2];
1458 var rgb = hslToRgb(h, s, l);
1459 pixels[i + 0] = rgb[0];
1460 pixels[i + 1] = rgb[1];
1461 pixels[i + 2] = rgb[2];
1462 }
1463 context.putImageData(imageData, 0, 0);
1464 this._setDirty();
1465 }
1466};
1467
1468/**
1469 * Applies a blur effect to the bitmap.
1470 *
1471 * @method blur
1472 */
1473Bitmap.prototype.blur = function() {
1474 for (var i = 0; i < 2; i++) {
1475 var w = this.width;
1476 var h = this.height;
1477 var canvas = this._canvas;
1478 var context = this._context;
1479 var tempCanvas = document.createElement('canvas');
1480 var tempContext = tempCanvas.getContext('2d');
1481 tempCanvas.width = w + 2;
1482 tempCanvas.height = h + 2;
1483 tempContext.drawImage(canvas, 0, 0, w, h, 1, 1, w, h);
1484 tempContext.drawImage(canvas, 0, 0, w, 1, 1, 0, w, 1);
1485 tempContext.drawImage(canvas, 0, 0, 1, h, 0, 1, 1, h);
1486 tempContext.drawImage(canvas, 0, h - 1, w, 1, 1, h + 1, w, 1);
1487 tempContext.drawImage(canvas, w - 1, 0, 1, h, w + 1, 1, 1, h);
1488 context.save();
1489 context.fillStyle = 'black';
1490 context.fillRect(0, 0, w, h);
1491 context.globalCompositeOperation = 'lighter';
1492 context.globalAlpha = 1 / 9;
1493 for (var y = 0; y < 3; y++) {
1494 for (var x = 0; x < 3; x++) {
1495 context.drawImage(tempCanvas, x, y, w, h, 0, 0, w, h);
1496 }
1497 }
1498 context.restore();
1499 }
1500 this._setDirty();
1501};
1502
1503/**
1504 * Add a callback function that will be called when the bitmap is loaded.
1505 *
1506 * @method addLoadListener
1507 * @param {Function} listner The callback function
1508 */
1509Bitmap.prototype.addLoadListener = function(listner) {
1510 if (!this.isReady()) {
1511 this._loadListeners.push(listner);
1512 } else {
1513 listner(this);
1514 }
1515};
1516
1517/**
1518 * @method _makeFontNameText
1519 * @private
1520 */
1521Bitmap.prototype._makeFontNameText = function() {
1522 return (this.fontItalic ? 'Italic ' : '') +
1523 this.fontSize + 'px ' + this.fontFace;
1524};
1525
1526/**
1527 * @method _drawTextOutline
1528 * @param {String} text
1529 * @param {Number} tx
1530 * @param {Number} ty
1531 * @param {Number} maxWidth
1532 * @private
1533 */
1534Bitmap.prototype._drawTextOutline = function(text, tx, ty, maxWidth) {
1535 var context = this._context;
1536 context.strokeStyle = this.outlineColor;
1537 context.lineWidth = this.outlineWidth;
1538 context.lineJoin = 'round';
1539 context.strokeText(text, tx, ty, maxWidth);
1540};
1541
1542/**
1543 * @method _drawTextBody
1544 * @param {String} text
1545 * @param {Number} tx
1546 * @param {Number} ty
1547 * @param {Number} maxWidth
1548 * @private
1549 */
1550Bitmap.prototype._drawTextBody = function(text, tx, ty, maxWidth) {
1551 var context = this._context;
1552 context.fillStyle = this.textColor;
1553 context.fillText(text, tx, ty, maxWidth);
1554};
1555
1556/**
1557 * @method _onLoad
1558 * @private
1559 */
1560Bitmap.prototype._onLoad = function() {
1561 this._image.removeEventListener('load', this._loadListener);
1562 this._image.removeEventListener('error', this._errorListener);
1563
1564 this._renewCanvas();
1565
1566 switch(this._loadingState){
1567 case 'requesting':
1568 this._loadingState = 'requestCompleted';
1569 if(this._decodeAfterRequest){
1570 this.decode();
1571 }else{
1572 this._loadingState = 'purged';
1573 this._clearImgInstance();
1574 }
1575 break;
1576
1577 case 'decrypting':
1578 window.URL.revokeObjectURL(this._image.src);
1579 this._loadingState = 'decryptCompleted';
1580 if(this._decodeAfterRequest){
1581 this.decode();
1582 }else{
1583 this._loadingState = 'purged';
1584 this._clearImgInstance();
1585 }
1586 break;
1587 }
1588};
1589
1590Bitmap.prototype.decode = function(){
1591 switch(this._loadingState){
1592 case 'requestCompleted': case 'decryptCompleted':
1593 this._loadingState = 'loaded';
1594
1595 if(!this.__canvas) this._createBaseTexture(this._image);
1596 this._setDirty();
1597 this._callLoadListeners();
1598 break;
1599
1600 case 'requesting': case 'decrypting':
1601 this._decodeAfterRequest = true;
1602 if (!this._loader) {
1603 this._loader = ResourceHandler.createLoader(this._url, this._requestImage.bind(this, this._url), this._onError.bind(this));
1604 this._image.removeEventListener('error', this._errorListener);
1605 this._image.addEventListener('error', this._errorListener = this._loader);
1606 }
1607 break;
1608
1609 case 'pending': case 'purged': case 'error':
1610 this._decodeAfterRequest = true;
1611 this._requestImage(this._url);
1612 break;
1613 }
1614};
1615
1616/**
1617 * @method _callLoadListeners
1618 * @private
1619 */
1620Bitmap.prototype._callLoadListeners = function() {
1621 while (this._loadListeners.length > 0) {
1622 var listener = this._loadListeners.shift();
1623 listener(this);
1624 }
1625};
1626
1627/**
1628 * @method _onError
1629 * @private
1630 */
1631Bitmap.prototype._onError = function() {
1632 this._image.removeEventListener('load', this._loadListener);
1633 this._image.removeEventListener('error', this._errorListener);
1634 this._loadingState = 'error';
1635};
1636
1637/**
1638 * @method _setDirty
1639 * @private
1640 */
1641Bitmap.prototype._setDirty = function() {
1642 this._dirty = true;
1643};
1644
1645/**
1646 * updates texture is bitmap was dirty
1647 * @method checkDirty
1648 */
1649Bitmap.prototype.checkDirty = function() {
1650 if (this._dirty) {
1651 this._baseTexture.update();
1652 this._dirty = false;
1653 }
1654};
1655
1656Bitmap.request = function(url){
1657 var bitmap = Object.create(Bitmap.prototype);
1658 bitmap._defer = true;
1659 bitmap.initialize();
1660
1661 bitmap._url = url;
1662 bitmap._loadingState = 'pending';
1663
1664 return bitmap;
1665};
1666
1667Bitmap.prototype._requestImage = function(url){
1668 if(Bitmap._reuseImages.length !== 0){
1669 this._image = Bitmap._reuseImages.pop();
1670 }else{
1671 this._image = new Image();
1672 }
1673
1674 if (this._decodeAfterRequest && !this._loader) {
1675 this._loader = ResourceHandler.createLoader(url, this._requestImage.bind(this, url), this._onError.bind(this));
1676 }
1677
1678 this._image = new Image();
1679 this._url = url;
1680 this._loadingState = 'requesting';
1681
1682 if(!Decrypter.checkImgIgnore(url) && Decrypter.hasEncryptedImages) {
1683 this._loadingState = 'decrypting';
1684 Decrypter.decryptImg(url, this);
1685 } else {
1686 this._image.src = url;
1687
1688 this._image.addEventListener('load', this._loadListener = Bitmap.prototype._onLoad.bind(this));
1689 this._image.addEventListener('error', this._errorListener = this._loader || Bitmap.prototype._onError.bind(this));
1690 }
1691};
1692
1693Bitmap.prototype.isRequestOnly = function(){
1694 return !(this._decodeAfterRequest || this.isReady());
1695};
1696
1697Bitmap.prototype.isRequestReady = function(){
1698 return this._loadingState !== 'pending' &&
1699 this._loadingState !== 'requesting' &&
1700 this._loadingState !== 'decrypting';
1701};
1702
1703Bitmap.prototype.startRequest = function(){
1704 if(this._loadingState === 'pending'){
1705 this._decodeAfterRequest = false;
1706 this._requestImage(this._url);
1707 }
1708};
1709
1710//-----------------------------------------------------------------------------
1711/**
1712 * The static class that carries out graphics processing.
1713 *
1714 * @class Graphics
1715 */
1716function Graphics() {
1717 throw new Error('This is a static class');
1718}
1719
1720Graphics._cssFontLoading = document.fonts && document.fonts.ready;
1721Graphics._fontLoaded = null;
1722Graphics._videoVolume = 1;
1723
1724/**
1725 * Initializes the graphics system.
1726 *
1727 * @static
1728 * @method initialize
1729 * @param {Number} width The width of the game screen
1730 * @param {Number} height The height of the game screen
1731 * @param {String} type The type of the renderer.
1732 * 'canvas', 'webgl', or 'auto'.
1733 */
1734Graphics.initialize = function(width, height, type) {
1735 this._width = width || 800;
1736 this._height = height || 600;
1737 this._rendererType = type || 'auto';
1738 this._boxWidth = this._width;
1739 this._boxHeight = this._height;
1740
1741 this._scale = 1;
1742 this._realScale = 1;
1743
1744 this._errorShowed = false;
1745 this._errorPrinter = null;
1746 this._canvas = null;
1747 this._video = null;
1748 this._videoUnlocked = false;
1749 this._videoLoading = false;
1750 this._upperCanvas = null;
1751 this._renderer = null;
1752 this._fpsMeter = null;
1753 this._modeBox = null;
1754 this._skipCount = 0;
1755 this._maxSkip = 3;
1756 this._rendered = false;
1757 this._loadingImage = null;
1758 this._loadingCount = 0;
1759 this._fpsMeterToggled = false;
1760 this._stretchEnabled = this._defaultStretchMode();
1761
1762 this._canUseDifferenceBlend = false;
1763 this._canUseSaturationBlend = false;
1764 this._hiddenCanvas = null;
1765
1766 this._testCanvasBlendModes();
1767 this._modifyExistingElements();
1768 this._updateRealScale();
1769 this._createAllElements();
1770 this._disableTextSelection();
1771 this._disableContextMenu();
1772 this._setupEventHandlers();
1773 this._setupCssFontLoading();
1774};
1775
1776
1777Graphics._setupCssFontLoading = function(){
1778 if(Graphics._cssFontLoading){
1779 document.fonts.ready.then(function(fonts){
1780 Graphics._fontLoaded = fonts;
1781 }).catch(function(error){
1782 SceneManager.onError(error);
1783 });
1784 }
1785};
1786
1787Graphics.canUseCssFontLoading = function(){
1788 return !!this._cssFontLoading;
1789};
1790
1791/**
1792 * The total frame count of the game screen.
1793 *
1794 * @static
1795 * @property frameCount
1796 * @type Number
1797 */
1798Graphics.frameCount = 0;
1799
1800/**
1801 * The alias of PIXI.blendModes.NORMAL.
1802 *
1803 * @static
1804 * @property BLEND_NORMAL
1805 * @type Number
1806 * @final
1807 */
1808Graphics.BLEND_NORMAL = 0;
1809
1810/**
1811 * The alias of PIXI.blendModes.ADD.
1812 *
1813 * @static
1814 * @property BLEND_ADD
1815 * @type Number
1816 * @final
1817 */
1818Graphics.BLEND_ADD = 1;
1819
1820/**
1821 * The alias of PIXI.blendModes.MULTIPLY.
1822 *
1823 * @static
1824 * @property BLEND_MULTIPLY
1825 * @type Number
1826 * @final
1827 */
1828Graphics.BLEND_MULTIPLY = 2;
1829
1830/**
1831 * The alias of PIXI.blendModes.SCREEN.
1832 *
1833 * @static
1834 * @property BLEND_SCREEN
1835 * @type Number
1836 * @final
1837 */
1838Graphics.BLEND_SCREEN = 3;
1839
1840/**
1841 * Marks the beginning of each frame for FPSMeter.
1842 *
1843 * @static
1844 * @method tickStart
1845 */
1846Graphics.tickStart = function() {
1847 if (this._fpsMeter) {
1848 this._fpsMeter.tickStart();
1849 }
1850};
1851
1852/**
1853 * Marks the end of each frame for FPSMeter.
1854 *
1855 * @static
1856 * @method tickEnd
1857 */
1858Graphics.tickEnd = function() {
1859 if (this._fpsMeter && this._rendered) {
1860 this._fpsMeter.tick();
1861 }
1862};
1863
1864/**
1865 * Renders the stage to the game screen.
1866 *
1867 * @static
1868 * @method render
1869 * @param {Stage} stage The stage object to be rendered
1870 */
1871Graphics.render = function(stage) {
1872 if (this._skipCount === 0) {
1873 var startTime = Date.now();
1874 if (stage) {
1875 this._renderer.render(stage);
1876 if (this._renderer.gl && this._renderer.gl.flush) {
1877 this._renderer.gl.flush();
1878 }
1879 }
1880 var endTime = Date.now();
1881 var elapsed = endTime - startTime;
1882 this._skipCount = Math.min(Math.floor(elapsed / 15), this._maxSkip);
1883 this._rendered = true;
1884 } else {
1885 this._skipCount--;
1886 this._rendered = false;
1887 }
1888 this.frameCount++;
1889};
1890
1891/**
1892 * Checks whether the renderer type is WebGL.
1893 *
1894 * @static
1895 * @method isWebGL
1896 * @return {Boolean} True if the renderer type is WebGL
1897 */
1898Graphics.isWebGL = function() {
1899 return this._renderer && this._renderer.type === PIXI.RENDERER_TYPE.WEBGL;
1900};
1901
1902/**
1903 * Checks whether the current browser supports WebGL.
1904 *
1905 * @static
1906 * @method hasWebGL
1907 * @return {Boolean} True if the current browser supports WebGL.
1908 */
1909Graphics.hasWebGL = function() {
1910 try {
1911 var canvas = document.createElement('canvas');
1912 return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
1913 } catch (e) {
1914 return false;
1915 }
1916};
1917
1918/**
1919 * Checks whether the canvas blend mode 'difference' is supported.
1920 *
1921 * @static
1922 * @method canUseDifferenceBlend
1923 * @return {Boolean} True if the canvas blend mode 'difference' is supported
1924 */
1925Graphics.canUseDifferenceBlend = function() {
1926 return this._canUseDifferenceBlend;
1927};
1928
1929/**
1930 * Checks whether the canvas blend mode 'saturation' is supported.
1931 *
1932 * @static
1933 * @method canUseSaturationBlend
1934 * @return {Boolean} True if the canvas blend mode 'saturation' is supported
1935 */
1936Graphics.canUseSaturationBlend = function() {
1937 return this._canUseSaturationBlend;
1938};
1939
1940/**
1941 * Sets the source of the "Now Loading" image.
1942 *
1943 * @static
1944 * @method setLoadingImage
1945 */
1946Graphics.setLoadingImage = function(src) {
1947 this._loadingImage = new Image();
1948 this._loadingImage.src = src;
1949};
1950
1951/**
1952 * Initializes the counter for displaying the "Now Loading" image.
1953 *
1954 * @static
1955 * @method startLoading
1956 */
1957Graphics.startLoading = function() {
1958 this._loadingCount = 0;
1959};
1960
1961/**
1962 * Increments the loading counter and displays the "Now Loading" image if necessary.
1963 *
1964 * @static
1965 * @method updateLoading
1966 */
1967Graphics.updateLoading = function() {
1968 this._loadingCount++;
1969 this._paintUpperCanvas();
1970 this._upperCanvas.style.opacity = 1;
1971};
1972
1973/**
1974 * Erases the "Now Loading" image.
1975 *
1976 * @static
1977 * @method endLoading
1978 */
1979Graphics.endLoading = function() {
1980 this._clearUpperCanvas();
1981 this._upperCanvas.style.opacity = 0;
1982};
1983
1984/**
1985 * Displays the loading error text to the screen.
1986 *
1987 * @static
1988 * @method printLoadingError
1989 * @param {String} url The url of the resource failed to load
1990 */
1991Graphics.printLoadingError = function(url) {
1992 if (this._errorPrinter && !this._errorShowed) {
1993 this._errorPrinter.innerHTML = this._makeErrorHtml('Loading Error', 'Failed to load: ' + url);
1994 var button = document.createElement('button');
1995 button.innerHTML = 'Retry';
1996 button.style.fontSize = '24px';
1997 button.style.color = '#ffffff';
1998 button.style.backgroundColor = '#000000';
1999 button.onmousedown = button.ontouchstart = function(event) {
2000 ResourceHandler.retry();
2001 event.stopPropagation();
2002 };
2003 this._errorPrinter.appendChild(button);
2004 this._loadingCount = -Infinity;
2005 }
2006};
2007
2008/**
2009 * Erases the loading error text.
2010 *
2011 * @static
2012 * @method eraseLoadingError
2013 */
2014Graphics.eraseLoadingError = function() {
2015 if (this._errorPrinter && !this._errorShowed) {
2016 this._errorPrinter.innerHTML = '';
2017 this.startLoading();
2018 }
2019};
2020
2021/**
2022 * Displays the error text to the screen.
2023 *
2024 * @static
2025 * @method printError
2026 * @param {String} name The name of the error
2027 * @param {String} message The message of the error
2028 */
2029Graphics.printError = function(name, message) {
2030 this._errorShowed = true;
2031 if (this._errorPrinter) {
2032 this._errorPrinter.innerHTML = this._makeErrorHtml(name, message);
2033 }
2034 this._applyCanvasFilter();
2035 this._clearUpperCanvas();
2036};
2037
2038/**
2039 * Shows the FPSMeter element.
2040 *
2041 * @static
2042 * @method showFps
2043 */
2044Graphics.showFps = function() {
2045 if (this._fpsMeter) {
2046 this._fpsMeter.show();
2047 this._modeBox.style.opacity = 1;
2048 }
2049};
2050
2051/**
2052 * Hides the FPSMeter element.
2053 *
2054 * @static
2055 * @method hideFps
2056 */
2057Graphics.hideFps = function() {
2058 if (this._fpsMeter) {
2059 this._fpsMeter.hide();
2060 this._modeBox.style.opacity = 0;
2061 }
2062};
2063
2064/**
2065 * Loads a font file.
2066 *
2067 * @static
2068 * @method loadFont
2069 * @param {String} name The face name of the font
2070 * @param {String} url The url of the font file
2071 */
2072Graphics.loadFont = function(name, url) {
2073 var style = document.createElement('style');
2074 var head = document.getElementsByTagName('head');
2075 var rule = '@font-face { font-family: "' + name + '"; src: url("' + url + '"); }';
2076 style.type = 'text/css';
2077 head.item(0).appendChild(style);
2078 style.sheet.insertRule(rule, 0);
2079 this._createFontLoader(name);
2080};
2081
2082/**
2083 * Checks whether the font file is loaded.
2084 *
2085 * @static
2086 * @method isFontLoaded
2087 * @param {String} name The face name of the font
2088 * @return {Boolean} True if the font file is loaded
2089 */
2090Graphics.isFontLoaded = function(name) {
2091 if (Graphics._cssFontLoading) {
2092 if(Graphics._fontLoaded){
2093 return Graphics._fontLoaded.check('10px "'+name+'"');
2094 }
2095
2096 return false;
2097 } else {
2098 if (!this._hiddenCanvas) {
2099 this._hiddenCanvas = document.createElement('canvas');
2100 }
2101 var context = this._hiddenCanvas.getContext('2d');
2102 var text = 'abcdefghijklmnopqrstuvwxyz';
2103 var width1, width2;
2104 context.font = '40px ' + name + ', sans-serif';
2105 width1 = context.measureText(text).width;
2106 context.font = '40px sans-serif';
2107 width2 = context.measureText(text).width;
2108 return width1 !== width2;
2109 }
2110};
2111
2112/**
2113 * Starts playback of a video.
2114 *
2115 * @static
2116 * @method playVideo
2117 * @param {String} src
2118 */
2119Graphics.playVideo = function(src) {
2120 this._videoLoader = ResourceHandler.createLoader(null, this._playVideo.bind(this, src), this._onVideoError.bind(this));
2121 this._playVideo(src);
2122};
2123
2124/**
2125 * @static
2126 * @method _playVideo
2127 * @param {String} src
2128 * @private
2129 */
2130Graphics._playVideo = function(src) {
2131 this._video.src = src;
2132 this._video.onloadeddata = this._onVideoLoad.bind(this);
2133 this._video.onerror = this._videoLoader;
2134 this._video.onended = this._onVideoEnd.bind(this);
2135 this._video.load();
2136 this._videoLoading = true;
2137};
2138
2139/**
2140 * Checks whether the video is playing.
2141 *
2142 * @static
2143 * @method isVideoPlaying
2144 * @return {Boolean} True if the video is playing
2145 */
2146Graphics.isVideoPlaying = function() {
2147 return this._videoLoading || this._isVideoVisible();
2148};
2149
2150/**
2151 * Checks whether the browser can play the specified video type.
2152 *
2153 * @static
2154 * @method canPlayVideoType
2155 * @param {String} type The video type to test support for
2156 * @return {Boolean} True if the browser can play the specified video type
2157 */
2158Graphics.canPlayVideoType = function(type) {
2159 return this._video && this._video.canPlayType(type);
2160};
2161
2162/**
2163 * Sets volume of a video.
2164 *
2165 * @static
2166 * @method setVideoVolume
2167 * @param {Number} value
2168 */
2169Graphics.setVideoVolume = function(value) {
2170 this._videoVolume = value;
2171 if (this._video) {
2172 this._video.volume = this._videoVolume;
2173 }
2174};
2175
2176/**
2177 * Converts an x coordinate on the page to the corresponding
2178 * x coordinate on the canvas area.
2179 *
2180 * @static
2181 * @method pageToCanvasX
2182 * @param {Number} x The x coordinate on the page to be converted
2183 * @return {Number} The x coordinate on the canvas area
2184 */
2185Graphics.pageToCanvasX = function(x) {
2186 if (this._canvas) {
2187 var left = this._canvas.offsetLeft;
2188 return Math.round((x - left) / this._realScale);
2189 } else {
2190 return 0;
2191 }
2192};
2193
2194/**
2195 * Converts a y coordinate on the page to the corresponding
2196 * y coordinate on the canvas area.
2197 *
2198 * @static
2199 * @method pageToCanvasY
2200 * @param {Number} y The y coordinate on the page to be converted
2201 * @return {Number} The y coordinate on the canvas area
2202 */
2203Graphics.pageToCanvasY = function(y) {
2204 if (this._canvas) {
2205 var top = this._canvas.offsetTop;
2206 return Math.round((y - top) / this._realScale);
2207 } else {
2208 return 0;
2209 }
2210};
2211
2212/**
2213 * Checks whether the specified point is inside the game canvas area.
2214 *
2215 * @static
2216 * @method isInsideCanvas
2217 * @param {Number} x The x coordinate on the canvas area
2218 * @param {Number} y The y coordinate on the canvas area
2219 * @return {Boolean} True if the specified point is inside the game canvas area
2220 */
2221Graphics.isInsideCanvas = function(x, y) {
2222 return (x >= 0 && x < this._width && y >= 0 && y < this._height);
2223};
2224
2225/**
2226 * Calls pixi.js garbage collector
2227 */
2228Graphics.callGC = function() {
2229 if (Graphics.isWebGL()) {
2230 Graphics._renderer.textureGC.run();
2231 }
2232};
2233
2234
2235/**
2236 * The width of the game screen.
2237 *
2238 * @static
2239 * @property width
2240 * @type Number
2241 */
2242Object.defineProperty(Graphics, 'width', {
2243 get: function() {
2244 return this._width;
2245 },
2246 set: function(value) {
2247 if (this._width !== value) {
2248 this._width = value;
2249 this._updateAllElements();
2250 }
2251 },
2252 configurable: true
2253});
2254
2255/**
2256 * The height of the game screen.
2257 *
2258 * @static
2259 * @property height
2260 * @type Number
2261 */
2262Object.defineProperty(Graphics, 'height', {
2263 get: function() {
2264 return this._height;
2265 },
2266 set: function(value) {
2267 if (this._height !== value) {
2268 this._height = value;
2269 this._updateAllElements();
2270 }
2271 },
2272 configurable: true
2273});
2274
2275/**
2276 * The width of the window display area.
2277 *
2278 * @static
2279 * @property boxWidth
2280 * @type Number
2281 */
2282Object.defineProperty(Graphics, 'boxWidth', {
2283 get: function() {
2284 return this._boxWidth;
2285 },
2286 set: function(value) {
2287 this._boxWidth = value;
2288 },
2289 configurable: true
2290});
2291
2292/**
2293 * The height of the window display area.
2294 *
2295 * @static
2296 * @property boxHeight
2297 * @type Number
2298 */
2299Object.defineProperty(Graphics, 'boxHeight', {
2300 get: function() {
2301 return this._boxHeight;
2302 },
2303 set: function(value) {
2304 this._boxHeight = value;
2305 },
2306 configurable: true
2307});
2308
2309/**
2310 * The zoom scale of the game screen.
2311 *
2312 * @static
2313 * @property scale
2314 * @type Number
2315 */
2316Object.defineProperty(Graphics, 'scale', {
2317 get: function() {
2318 return this._scale;
2319 },
2320 set: function(value) {
2321 if (this._scale !== value) {
2322 this._scale = value;
2323 this._updateAllElements();
2324 }
2325 },
2326 configurable: true
2327});
2328
2329/**
2330 * @static
2331 * @method _createAllElements
2332 * @private
2333 */
2334Graphics._createAllElements = function() {
2335 this._createErrorPrinter();
2336 this._createCanvas();
2337 this._createVideo();
2338 this._createUpperCanvas();
2339 this._createRenderer();
2340 this._createFPSMeter();
2341 this._createModeBox();
2342 this._createGameFontLoader();
2343};
2344
2345/**
2346 * @static
2347 * @method _updateAllElements
2348 * @private
2349 */
2350Graphics._updateAllElements = function() {
2351 this._updateRealScale();
2352 this._updateErrorPrinter();
2353 this._updateCanvas();
2354 this._updateVideo();
2355 this._updateUpperCanvas();
2356 this._updateRenderer();
2357 this._paintUpperCanvas();
2358};
2359
2360/**
2361 * @static
2362 * @method _updateRealScale
2363 * @private
2364 */
2365Graphics._updateRealScale = function() {
2366 if (this._stretchEnabled) {
2367 var h = window.innerWidth / this._width;
2368 var v = window.innerHeight / this._height;
2369 if (h >= 1 && h - 0.01 <= 1) h = 1;
2370 if (v >= 1 && v - 0.01 <= 1) v = 1;
2371 this._realScale = Math.min(h, v);
2372 } else {
2373 this._realScale = this._scale;
2374 }
2375};
2376
2377/**
2378 * @static
2379 * @method _makeErrorHtml
2380 * @param {String} name
2381 * @param {String} message
2382 * @return {String}
2383 * @private
2384 */
2385Graphics._makeErrorHtml = function(name, message) {
2386 return ('<font color="yellow"><b>' + name + '</b></font><br>' +
2387 '<font color="white">' + message + '</font><br>');
2388};
2389
2390/**
2391 * @static
2392 * @method _defaultStretchMode
2393 * @private
2394 */
2395Graphics._defaultStretchMode = function() {
2396 return Utils.isNwjs() || Utils.isMobileDevice();
2397};
2398
2399/**
2400 * @static
2401 * @method _testCanvasBlendModes
2402 * @private
2403 */
2404Graphics._testCanvasBlendModes = function() {
2405 var canvas, context, imageData1, imageData2;
2406 canvas = document.createElement('canvas');
2407 canvas.width = 1;
2408 canvas.height = 1;
2409 context = canvas.getContext('2d');
2410 context.globalCompositeOperation = 'source-over';
2411 context.fillStyle = 'white';
2412 context.fillRect(0, 0, 1, 1);
2413 context.globalCompositeOperation = 'difference';
2414 context.fillStyle = 'white';
2415 context.fillRect(0, 0, 1, 1);
2416 imageData1 = context.getImageData(0, 0, 1, 1);
2417 context.globalCompositeOperation = 'source-over';
2418 context.fillStyle = 'black';
2419 context.fillRect(0, 0, 1, 1);
2420 context.globalCompositeOperation = 'saturation';
2421 context.fillStyle = 'white';
2422 context.fillRect(0, 0, 1, 1);
2423 imageData2 = context.getImageData(0, 0, 1, 1);
2424 this._canUseDifferenceBlend = imageData1.data[0] === 0;
2425 this._canUseSaturationBlend = imageData2.data[0] === 0;
2426};
2427
2428/**
2429 * @static
2430 * @method _modifyExistingElements
2431 * @private
2432 */
2433Graphics._modifyExistingElements = function() {
2434 var elements = document.getElementsByTagName('*');
2435 for (var i = 0; i < elements.length; i++) {
2436 if (elements[i].style.zIndex > 0) {
2437 elements[i].style.zIndex = 0;
2438 }
2439 }
2440};
2441
2442/**
2443 * @static
2444 * @method _createErrorPrinter
2445 * @private
2446 */
2447Graphics._createErrorPrinter = function() {
2448 this._errorPrinter = document.createElement('p');
2449 this._errorPrinter.id = 'ErrorPrinter';
2450 this._updateErrorPrinter();
2451 document.body.appendChild(this._errorPrinter);
2452};
2453
2454/**
2455 * @static
2456 * @method _updateErrorPrinter
2457 * @private
2458 */
2459Graphics._updateErrorPrinter = function() {
2460 this._errorPrinter.width = this._width * 0.9;
2461 this._errorPrinter.height = 40;
2462 this._errorPrinter.style.textAlign = 'center';
2463 this._errorPrinter.style.textShadow = '1px 1px 3px #000';
2464 this._errorPrinter.style.fontSize = '20px';
2465 this._errorPrinter.style.zIndex = 99;
2466 this._centerElement(this._errorPrinter);
2467};
2468
2469/**
2470 * @static
2471 * @method _createCanvas
2472 * @private
2473 */
2474Graphics._createCanvas = function() {
2475 this._canvas = document.createElement('canvas');
2476 this._canvas.id = 'GameCanvas';
2477 this._updateCanvas();
2478 document.body.appendChild(this._canvas);
2479};
2480
2481/**
2482 * @static
2483 * @method _updateCanvas
2484 * @private
2485 */
2486Graphics._updateCanvas = function() {
2487 this._canvas.width = this._width;
2488 this._canvas.height = this._height;
2489 this._canvas.style.zIndex = 1;
2490 this._centerElement(this._canvas);
2491};
2492
2493/**
2494 * @static
2495 * @method _createVideo
2496 * @private
2497 */
2498Graphics._createVideo = function() {
2499 this._video = document.createElement('video');
2500 this._video.id = 'GameVideo';
2501 this._video.style.opacity = 0;
2502 this._video.setAttribute('playsinline', '');
2503 this._video.volume = this._videoVolume;
2504 this._updateVideo();
2505 makeVideoPlayableInline(this._video);
2506 document.body.appendChild(this._video);
2507};
2508
2509/**
2510 * @static
2511 * @method _updateVideo
2512 * @private
2513 */
2514Graphics._updateVideo = function() {
2515 this._video.width = this._width;
2516 this._video.height = this._height;
2517 this._video.style.zIndex = 2;
2518 this._centerElement(this._video);
2519};
2520
2521/**
2522 * @static
2523 * @method _createUpperCanvas
2524 * @private
2525 */
2526Graphics._createUpperCanvas = function() {
2527 this._upperCanvas = document.createElement('canvas');
2528 this._upperCanvas.id = 'UpperCanvas';
2529 this._updateUpperCanvas();
2530 document.body.appendChild(this._upperCanvas);
2531};
2532
2533/**
2534 * @static
2535 * @method _updateUpperCanvas
2536 * @private
2537 */
2538Graphics._updateUpperCanvas = function() {
2539 this._upperCanvas.width = this._width;
2540 this._upperCanvas.height = this._height;
2541 this._upperCanvas.style.zIndex = 3;
2542 this._centerElement(this._upperCanvas);
2543};
2544
2545/**
2546 * @static
2547 * @method _clearUpperCanvas
2548 * @private
2549 */
2550Graphics._clearUpperCanvas = function() {
2551 var context = this._upperCanvas.getContext('2d');
2552 context.clearRect(0, 0, this._width, this._height);
2553};
2554
2555/**
2556 * @static
2557 * @method _paintUpperCanvas
2558 * @private
2559 */
2560Graphics._paintUpperCanvas = function() {
2561 this._clearUpperCanvas();
2562 if (this._loadingImage && this._loadingCount >= 20) {
2563 var context = this._upperCanvas.getContext('2d');
2564 var dx = (this._width - this._loadingImage.width) / 2;
2565 var dy = (this._height - this._loadingImage.height) / 2;
2566 var alpha = ((this._loadingCount - 20) / 30).clamp(0, 1);
2567 context.save();
2568 context.globalAlpha = alpha;
2569 context.drawImage(this._loadingImage, dx, dy);
2570 context.restore();
2571 }
2572};
2573
2574/**
2575 * @static
2576 * @method _createRenderer
2577 * @private
2578 */
2579Graphics._createRenderer = function() {
2580 PIXI.dontSayHello = true;
2581 var width = this._width;
2582 var height = this._height;
2583 var options = { view: this._canvas };
2584 try {
2585 switch (this._rendererType) {
2586 case 'canvas':
2587 this._renderer = new PIXI.CanvasRenderer(width, height, options);
2588 break;
2589 case 'webgl':
2590 this._renderer = new PIXI.WebGLRenderer(width, height, options);
2591 break;
2592 default:
2593 this._renderer = PIXI.autoDetectRenderer(width, height, options);
2594 break;
2595 }
2596
2597 if(this._renderer && this._renderer.textureGC)
2598 this._renderer.textureGC.maxIdle = 1;
2599
2600 } catch (e) {
2601 this._renderer = null;
2602 }
2603};
2604
2605/**
2606 * @static
2607 * @method _updateRenderer
2608 * @private
2609 */
2610Graphics._updateRenderer = function() {
2611 if (this._renderer) {
2612 this._renderer.resize(this._width, this._height);
2613 }
2614};
2615
2616/**
2617 * @static
2618 * @method _createFPSMeter
2619 * @private
2620 */
2621Graphics._createFPSMeter = function() {
2622 var options = { graph: 1, decimals: 0, theme: 'transparent', toggleOn: null };
2623 this._fpsMeter = new FPSMeter(options);
2624 this._fpsMeter.hide();
2625};
2626
2627/**
2628 * @static
2629 * @method _createModeBox
2630 * @private
2631 */
2632Graphics._createModeBox = function() {
2633 var box = document.createElement('div');
2634 box.id = 'modeTextBack';
2635 box.style.position = 'absolute';
2636 box.style.left = '5px';
2637 box.style.top = '5px';
2638 box.style.width = '119px';
2639 box.style.height = '58px';
2640 box.style.background = 'rgba(0,0,0,0.2)';
2641 box.style.zIndex = 9;
2642 box.style.opacity = 0;
2643
2644 var text = document.createElement('div');
2645 text.id = 'modeText';
2646 text.style.position = 'absolute';
2647 text.style.left = '0px';
2648 text.style.top = '41px';
2649 text.style.width = '119px';
2650 text.style.fontSize = '12px';
2651 text.style.fontFamily = 'monospace';
2652 text.style.color = 'white';
2653 text.style.textAlign = 'center';
2654 text.style.textShadow = '1px 1px 0 rgba(0,0,0,0.5)';
2655 text.innerHTML = this.isWebGL() ? 'WebGL mode' : 'Canvas mode';
2656
2657 document.body.appendChild(box);
2658 box.appendChild(text);
2659
2660 this._modeBox = box;
2661};
2662
2663/**
2664 * @static
2665 * @method _createGameFontLoader
2666 * @private
2667 */
2668Graphics._createGameFontLoader = function() {
2669 this._createFontLoader('GameFont');
2670};
2671
2672/**
2673 * @static
2674 * @method _createFontLoader
2675 * @param {String} name
2676 * @private
2677 */
2678Graphics._createFontLoader = function(name) {
2679 var div = document.createElement('div');
2680 var text = document.createTextNode('.');
2681 div.style.fontFamily = name;
2682 div.style.fontSize = '0px';
2683 div.style.color = 'transparent';
2684 div.style.position = 'absolute';
2685 div.style.margin = 'auto';
2686 div.style.top = '0px';
2687 div.style.left = '0px';
2688 div.style.width = '1px';
2689 div.style.height = '1px';
2690 div.appendChild(text);
2691 document.body.appendChild(div);
2692};
2693
2694/**
2695 * @static
2696 * @method _centerElement
2697 * @param {HTMLElement} element
2698 * @private
2699 */
2700Graphics._centerElement = function(element) {
2701 var width = element.width * this._realScale;
2702 var height = element.height * this._realScale;
2703 element.style.position = 'absolute';
2704 element.style.margin = 'auto';
2705 element.style.top = 0;
2706 element.style.left = 0;
2707 element.style.right = 0;
2708 element.style.bottom = 0;
2709 element.style.width = width + 'px';
2710 element.style.height = height + 'px';
2711};
2712
2713/**
2714 * @static
2715 * @method _disableTextSelection
2716 * @private
2717 */
2718Graphics._disableTextSelection = function() {
2719 var body = document.body;
2720 body.style.userSelect = 'none';
2721 body.style.webkitUserSelect = 'none';
2722 body.style.msUserSelect = 'none';
2723 body.style.mozUserSelect = 'none';
2724};
2725
2726/**
2727 * @static
2728 * @method _disableContextMenu
2729 * @private
2730 */
2731Graphics._disableContextMenu = function() {
2732 var elements = document.body.getElementsByTagName('*');
2733 var oncontextmenu = function() { return false; };
2734 for (var i = 0; i < elements.length; i++) {
2735 elements[i].oncontextmenu = oncontextmenu;
2736 }
2737};
2738
2739/**
2740 * @static
2741 * @method _applyCanvasFilter
2742 * @private
2743 */
2744Graphics._applyCanvasFilter = function() {
2745 if (this._canvas) {
2746 this._canvas.style.opacity = 0.5;
2747 this._canvas.style.filter = 'blur(8px)';
2748 this._canvas.style.webkitFilter = 'blur(8px)';
2749 }
2750};
2751
2752/**
2753 * @static
2754 * @method _onVideoLoad
2755 * @private
2756 */
2757Graphics._onVideoLoad = function() {
2758 this._video.play();
2759 this._updateVisibility(true);
2760 this._videoLoading = false;
2761};
2762
2763/**
2764 * @static
2765 * @method _onVideoError
2766 * @private
2767 */
2768Graphics._onVideoError = function() {
2769 this._updateVisibility(false);
2770 this._videoLoading = false;
2771};
2772
2773/**
2774 * @static
2775 * @method _onVideoEnd
2776 * @private
2777 */
2778Graphics._onVideoEnd = function() {
2779 this._updateVisibility(false);
2780};
2781
2782/**
2783 * @static
2784 * @method _updateVisibility
2785 * @param {Boolean} videoVisible
2786 * @private
2787 */
2788Graphics._updateVisibility = function(videoVisible) {
2789 this._video.style.opacity = videoVisible ? 1 : 0;
2790 this._canvas.style.opacity = videoVisible ? 0 : 1;
2791};
2792
2793/**
2794 * @static
2795 * @method _isVideoVisible
2796 * @return {Boolean}
2797 * @private
2798 */
2799Graphics._isVideoVisible = function() {
2800 return this._video.style.opacity > 0;
2801};
2802
2803/**
2804 * @static
2805 * @method _setupEventHandlers
2806 * @private
2807 */
2808Graphics._setupEventHandlers = function() {
2809 window.addEventListener('resize', this._onWindowResize.bind(this));
2810 document.addEventListener('keydown', this._onKeyDown.bind(this));
2811 document.addEventListener('keydown', this._onTouchEnd.bind(this));
2812 document.addEventListener('mousedown', this._onTouchEnd.bind(this));
2813 document.addEventListener('touchend', this._onTouchEnd.bind(this));
2814};
2815
2816/**
2817 * @static
2818 * @method _onWindowResize
2819 * @private
2820 */
2821Graphics._onWindowResize = function() {
2822 this._updateAllElements();
2823};
2824
2825/**
2826 * @static
2827 * @method _onKeyDown
2828 * @param {KeyboardEvent} event
2829 * @private
2830 */
2831Graphics._onKeyDown = function(event) {
2832 if (!event.ctrlKey && !event.altKey) {
2833 switch (event.keyCode) {
2834 case 113: // F2
2835 event.preventDefault();
2836 this._switchFPSMeter();
2837 break;
2838 case 114: // F3
2839 event.preventDefault();
2840 this._switchStretchMode();
2841 break;
2842 case 115: // F4
2843 event.preventDefault();
2844 this._switchFullScreen();
2845 break;
2846 }
2847 }
2848};
2849
2850/**
2851 * @static
2852 * @method _onTouchEnd
2853 * @param {TouchEvent} event
2854 * @private
2855 */
2856Graphics._onTouchEnd = function(event) {
2857 if (!this._videoUnlocked) {
2858 this._video.play();
2859 this._videoUnlocked = true;
2860 }
2861 if (this._isVideoVisible() && this._video.paused) {
2862 this._video.play();
2863 }
2864};
2865
2866/**
2867 * @static
2868 * @method _switchFPSMeter
2869 * @private
2870 */
2871Graphics._switchFPSMeter = function() {
2872 if (this._fpsMeter.isPaused) {
2873 this.showFps();
2874 this._fpsMeter.showFps();
2875 this._fpsMeterToggled = false;
2876 } else if (!this._fpsMeterToggled) {
2877 this._fpsMeter.showDuration();
2878 this._fpsMeterToggled = true;
2879 } else {
2880 this.hideFps();
2881 }
2882};
2883
2884/**
2885 * @static
2886 * @method _switchStretchMode
2887 * @return {Boolean}
2888 * @private
2889 */
2890Graphics._switchStretchMode = function() {
2891 this._stretchEnabled = !this._stretchEnabled;
2892 this._updateAllElements();
2893};
2894
2895/**
2896 * @static
2897 * @method _switchFullScreen
2898 * @private
2899 */
2900Graphics._switchFullScreen = function() {
2901 if (this._isFullScreen()) {
2902 this._requestFullScreen();
2903 } else {
2904 this._cancelFullScreen();
2905 }
2906};
2907
2908/**
2909 * @static
2910 * @method _isFullScreen
2911 * @return {Boolean}
2912 * @private
2913 */
2914Graphics._isFullScreen = function() {
2915 return ((document.fullScreenElement && document.fullScreenElement !== null) ||
2916 (!document.mozFullScreen && !document.webkitFullscreenElement &&
2917 !document.msFullscreenElement));
2918};
2919
2920/**
2921 * @static
2922 * @method _requestFullScreen
2923 * @private
2924 */
2925Graphics._requestFullScreen = function() {
2926 var element = document.body;
2927 if (element.requestFullScreen) {
2928 element.requestFullScreen();
2929 } else if (element.mozRequestFullScreen) {
2930 element.mozRequestFullScreen();
2931 } else if (element.webkitRequestFullScreen) {
2932 element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
2933 } else if (element.msRequestFullscreen) {
2934 element.msRequestFullscreen();
2935 }
2936};
2937
2938/**
2939 * @static
2940 * @method _cancelFullScreen
2941 * @private
2942 */
2943Graphics._cancelFullScreen = function() {
2944 if (document.cancelFullScreen) {
2945 document.cancelFullScreen();
2946 } else if (document.mozCancelFullScreen) {
2947 document.mozCancelFullScreen();
2948 } else if (document.webkitCancelFullScreen) {
2949 document.webkitCancelFullScreen();
2950 } else if (document.msExitFullscreen) {
2951 document.msExitFullscreen();
2952 }
2953};
2954
2955//-----------------------------------------------------------------------------
2956/**
2957 * The static class that handles input data from the keyboard and gamepads.
2958 *
2959 * @class Input
2960 */
2961function Input() {
2962 throw new Error('This is a static class');
2963}
2964
2965/**
2966 * Initializes the input system.
2967 *
2968 * @static
2969 * @method initialize
2970 */
2971Input.initialize = function() {
2972 this.clear();
2973 this._wrapNwjsAlert();
2974 this._setupEventHandlers();
2975};
2976
2977/**
2978 * The wait time of the key repeat in frames.
2979 *
2980 * @static
2981 * @property keyRepeatWait
2982 * @type Number
2983 */
2984Input.keyRepeatWait = 24;
2985
2986/**
2987 * The interval of the key repeat in frames.
2988 *
2989 * @static
2990 * @property keyRepeatInterval
2991 * @type Number
2992 */
2993Input.keyRepeatInterval = 6;
2994
2995/**
2996 * A hash table to convert from a virtual key code to a mapped key name.
2997 *
2998 * @static
2999 * @property keyMapper
3000 * @type Object
3001 */
3002Input.keyMapper = {
3003 9: 'tab', // tab
3004 13: 'ok', // enter
3005 16: 'shift', // shift
3006 17: 'control', // control
3007 18: 'control', // alt
3008 27: 'escape', // escape
3009 32: 'ok', // space
3010 33: 'pageup', // pageup
3011 34: 'pagedown', // pagedown
3012 37: 'left', // left arrow
3013 38: 'up', // up arrow
3014 39: 'right', // right arrow
3015 40: 'down', // down arrow
3016 45: 'escape', // insert
3017 81: 'pageup', // Q
3018 87: 'pagedown', // W
3019 88: 'escape', // X
3020 90: 'ok', // Z
3021 96: 'escape', // numpad 0
3022 98: 'down', // numpad 2
3023 100: 'left', // numpad 4
3024 102: 'right', // numpad 6
3025 104: 'up', // numpad 8
3026 120: 'debug' // F9
3027};
3028
3029/**
3030 * A hash table to convert from a gamepad button to a mapped key name.
3031 *
3032 * @static
3033 * @property gamepadMapper
3034 * @type Object
3035 */
3036Input.gamepadMapper = {
3037 0: 'ok', // A
3038 1: 'cancel', // B
3039 2: 'shift', // X
3040 3: 'menu', // Y
3041 4: 'pageup', // LB
3042 5: 'pagedown', // RB
3043 12: 'up', // D-pad up
3044 13: 'down', // D-pad down
3045 14: 'left', // D-pad left
3046 15: 'right', // D-pad right
3047};
3048
3049/**
3050 * Clears all the input data.
3051 *
3052 * @static
3053 * @method clear
3054 */
3055Input.clear = function() {
3056 this._currentState = {};
3057 this._previousState = {};
3058 this._gamepadStates = [];
3059 this._latestButton = null;
3060 this._pressedTime = 0;
3061 this._dir4 = 0;
3062 this._dir8 = 0;
3063 this._preferredAxis = '';
3064 this._date = 0;
3065};
3066
3067/**
3068 * Updates the input data.
3069 *
3070 * @static
3071 * @method update
3072 */
3073Input.update = function() {
3074 this._pollGamepads();
3075 if (this._currentState[this._latestButton]) {
3076 this._pressedTime++;
3077 } else {
3078 this._latestButton = null;
3079 }
3080 for (var name in this._currentState) {
3081 if (this._currentState[name] && !this._previousState[name]) {
3082 this._latestButton = name;
3083 this._pressedTime = 0;
3084 this._date = Date.now();
3085 }
3086 this._previousState[name] = this._currentState[name];
3087 }
3088 this._updateDirection();
3089};
3090
3091/**
3092 * Checks whether a key is currently pressed down.
3093 *
3094 * @static
3095 * @method isPressed
3096 * @param {String} keyName The mapped name of the key
3097 * @return {Boolean} True if the key is pressed
3098 */
3099Input.isPressed = function(keyName) {
3100 if (this._isEscapeCompatible(keyName) && this.isPressed('escape')) {
3101 return true;
3102 } else {
3103 return !!this._currentState[keyName];
3104 }
3105};
3106
3107/**
3108 * Checks whether a key is just pressed.
3109 *
3110 * @static
3111 * @method isTriggered
3112 * @param {String} keyName The mapped name of the key
3113 * @return {Boolean} True if the key is triggered
3114 */
3115Input.isTriggered = function(keyName) {
3116 if (this._isEscapeCompatible(keyName) && this.isTriggered('escape')) {
3117 return true;
3118 } else {
3119 return this._latestButton === keyName && this._pressedTime === 0;
3120 }
3121};
3122
3123/**
3124 * Checks whether a key is just pressed or a key repeat occurred.
3125 *
3126 * @static
3127 * @method isRepeated
3128 * @param {String} keyName The mapped name of the key
3129 * @return {Boolean} True if the key is repeated
3130 */
3131Input.isRepeated = function(keyName) {
3132 if (this._isEscapeCompatible(keyName) && this.isRepeated('escape')) {
3133 return true;
3134 } else {
3135 return (this._latestButton === keyName &&
3136 (this._pressedTime === 0 ||
3137 (this._pressedTime >= this.keyRepeatWait &&
3138 this._pressedTime % this.keyRepeatInterval === 0)));
3139 }
3140};
3141
3142/**
3143 * Checks whether a key is kept depressed.
3144 *
3145 * @static
3146 * @method isLongPressed
3147 * @param {String} keyName The mapped name of the key
3148 * @return {Boolean} True if the key is long-pressed
3149 */
3150Input.isLongPressed = function(keyName) {
3151 if (this._isEscapeCompatible(keyName) && this.isLongPressed('escape')) {
3152 return true;
3153 } else {
3154 return (this._latestButton === keyName &&
3155 this._pressedTime >= this.keyRepeatWait);
3156 }
3157};
3158
3159/**
3160 * [read-only] The four direction value as a number of the numpad, or 0 for neutral.
3161 *
3162 * @static
3163 * @property dir4
3164 * @type Number
3165 */
3166Object.defineProperty(Input, 'dir4', {
3167 get: function() {
3168 return this._dir4;
3169 },
3170 configurable: true
3171});
3172
3173/**
3174 * [read-only] The eight direction value as a number of the numpad, or 0 for neutral.
3175 *
3176 * @static
3177 * @property dir8
3178 * @type Number
3179 */
3180Object.defineProperty(Input, 'dir8', {
3181 get: function() {
3182 return this._dir8;
3183 },
3184 configurable: true
3185});
3186
3187/**
3188 * [read-only] The time of the last input in milliseconds.
3189 *
3190 * @static
3191 * @property date
3192 * @type Number
3193 */
3194Object.defineProperty(Input, 'date', {
3195 get: function() {
3196 return this._date;
3197 },
3198 configurable: true
3199});
3200
3201/**
3202 * @static
3203 * @method _wrapNwjsAlert
3204 * @private
3205 */
3206Input._wrapNwjsAlert = function() {
3207 if (Utils.isNwjs()) {
3208 var _alert = window.alert;
3209 window.alert = function() {
3210 var gui = require('nw.gui');
3211 var win = gui.Window.get();
3212 _alert.apply(this, arguments);
3213 win.focus();
3214 Input.clear();
3215 };
3216 }
3217};
3218
3219/**
3220 * @static
3221 * @method _setupEventHandlers
3222 * @private
3223 */
3224Input._setupEventHandlers = function() {
3225 document.addEventListener('keydown', this._onKeyDown.bind(this));
3226 document.addEventListener('keyup', this._onKeyUp.bind(this));
3227 window.addEventListener('blur', this._onLostFocus.bind(this));
3228};
3229
3230/**
3231 * @static
3232 * @method _onKeyDown
3233 * @param {KeyboardEvent} event
3234 * @private
3235 */
3236Input._onKeyDown = function(event) {
3237 if (this._shouldPreventDefault(event.keyCode)) {
3238 event.preventDefault();
3239 }
3240 if (event.keyCode === 144) { // Numlock
3241 this.clear();
3242 }
3243 var buttonName = this.keyMapper[event.keyCode];
3244 if (ResourceHandler.exists() && buttonName === 'ok') {
3245 ResourceHandler.retry();
3246 } else if (buttonName) {
3247 this._currentState[buttonName] = true;
3248 }
3249};
3250
3251/**
3252 * @static
3253 * @method _shouldPreventDefault
3254 * @param {Number} keyCode
3255 * @private
3256 */
3257Input._shouldPreventDefault = function(keyCode) {
3258 switch (keyCode) {
3259 case 8: // backspace
3260 case 33: // pageup
3261 case 34: // pagedown
3262 case 37: // left arrow
3263 case 38: // up arrow
3264 case 39: // right arrow
3265 case 40: // down arrow
3266 return true;
3267 }
3268 return false;
3269};
3270
3271/**
3272 * @static
3273 * @method _onKeyUp
3274 * @param {KeyboardEvent} event
3275 * @private
3276 */
3277Input._onKeyUp = function(event) {
3278 var buttonName = this.keyMapper[event.keyCode];
3279 if (buttonName) {
3280 this._currentState[buttonName] = false;
3281 }
3282 if (event.keyCode === 0) { // For QtWebEngine on OS X
3283 this.clear();
3284 }
3285};
3286
3287/**
3288 * @static
3289 * @method _onLostFocus
3290 * @private
3291 */
3292Input._onLostFocus = function() {
3293 this.clear();
3294};
3295
3296/**
3297 * @static
3298 * @method _pollGamepads
3299 * @private
3300 */
3301Input._pollGamepads = function() {
3302 if (navigator.getGamepads) {
3303 var gamepads = navigator.getGamepads();
3304 if (gamepads) {
3305 for (var i = 0; i < gamepads.length; i++) {
3306 var gamepad = gamepads[i];
3307 if (gamepad && gamepad.connected) {
3308 this._updateGamepadState(gamepad);
3309 }
3310 }
3311 }
3312 }
3313};
3314
3315/**
3316 * @static
3317 * @method _updateGamepadState
3318 * @param {Gamepad} gamepad
3319 * @param {Number} index
3320 * @private
3321 */
3322Input._updateGamepadState = function(gamepad) {
3323 var lastState = this._gamepadStates[gamepad.index] || [];
3324 var newState = [];
3325 var buttons = gamepad.buttons;
3326 var axes = gamepad.axes;
3327 var threshold = 0.5;
3328 newState[12] = false;
3329 newState[13] = false;
3330 newState[14] = false;
3331 newState[15] = false;
3332 for (var i = 0; i < buttons.length; i++) {
3333 newState[i] = buttons[i].pressed;
3334 }
3335 if (axes[1] < -threshold) {
3336 newState[12] = true; // up
3337 } else if (axes[1] > threshold) {
3338 newState[13] = true; // down
3339 }
3340 if (axes[0] < -threshold) {
3341 newState[14] = true; // left
3342 } else if (axes[0] > threshold) {
3343 newState[15] = true; // right
3344 }
3345 for (var j = 0; j < newState.length; j++) {
3346 if (newState[j] !== lastState[j]) {
3347 var buttonName = this.gamepadMapper[j];
3348 if (buttonName) {
3349 this._currentState[buttonName] = newState[j];
3350 }
3351 }
3352 }
3353 this._gamepadStates[gamepad.index] = newState;
3354};
3355
3356/**
3357 * @static
3358 * @method _updateDirection
3359 * @private
3360 */
3361Input._updateDirection = function() {
3362 var x = this._signX();
3363 var y = this._signY();
3364
3365 this._dir8 = this._makeNumpadDirection(x, y);
3366
3367 if (x !== 0 && y !== 0) {
3368 if (this._preferredAxis === 'x') {
3369 y = 0;
3370 } else {
3371 x = 0;
3372 }
3373 } else if (x !== 0) {
3374 this._preferredAxis = 'y';
3375 } else if (y !== 0) {
3376 this._preferredAxis = 'x';
3377 }
3378
3379 this._dir4 = this._makeNumpadDirection(x, y);
3380};
3381
3382/**
3383 * @static
3384 * @method _signX
3385 * @private
3386 */
3387Input._signX = function() {
3388 var x = 0;
3389
3390 if (this.isPressed('left')) {
3391 x--;
3392 }
3393 if (this.isPressed('right')) {
3394 x++;
3395 }
3396 return x;
3397};
3398
3399/**
3400 * @static
3401 * @method _signY
3402 * @private
3403 */
3404Input._signY = function() {
3405 var y = 0;
3406
3407 if (this.isPressed('up')) {
3408 y--;
3409 }
3410 if (this.isPressed('down')) {
3411 y++;
3412 }
3413 return y;
3414};
3415
3416/**
3417 * @static
3418 * @method _makeNumpadDirection
3419 * @param {Number} x
3420 * @param {Number} y
3421 * @return {Number}
3422 * @private
3423 */
3424Input._makeNumpadDirection = function(x, y) {
3425 if (x !== 0 || y !== 0) {
3426 return 5 - y * 3 + x;
3427 }
3428 return 0;
3429};
3430
3431/**
3432 * @static
3433 * @method _isEscapeCompatible
3434 * @param {String} keyName
3435 * @return {Boolean}
3436 * @private
3437 */
3438Input._isEscapeCompatible = function(keyName) {
3439 return keyName === 'cancel' || keyName === 'menu';
3440};
3441
3442//-----------------------------------------------------------------------------
3443/**
3444 * The static class that handles input data from the mouse and touchscreen.
3445 *
3446 * @class TouchInput
3447 */
3448function TouchInput() {
3449 throw new Error('This is a static class');
3450}
3451
3452/**
3453 * Initializes the touch system.
3454 *
3455 * @static
3456 * @method initialize
3457 */
3458TouchInput.initialize = function() {
3459 this.clear();
3460 this._setupEventHandlers();
3461};
3462
3463/**
3464 * The wait time of the pseudo key repeat in frames.
3465 *
3466 * @static
3467 * @property keyRepeatWait
3468 * @type Number
3469 */
3470TouchInput.keyRepeatWait = 24;
3471
3472/**
3473 * The interval of the pseudo key repeat in frames.
3474 *
3475 * @static
3476 * @property keyRepeatInterval
3477 * @type Number
3478 */
3479TouchInput.keyRepeatInterval = 6;
3480
3481/**
3482 * Clears all the touch data.
3483 *
3484 * @static
3485 * @method clear
3486 */
3487TouchInput.clear = function() {
3488 this._mousePressed = false;
3489 this._screenPressed = false;
3490 this._pressedTime = 0;
3491 this._events = {};
3492 this._events.triggered = false;
3493 this._events.cancelled = false;
3494 this._events.moved = false;
3495 this._events.released = false;
3496 this._events.wheelX = 0;
3497 this._events.wheelY = 0;
3498 this._triggered = false;
3499 this._cancelled = false;
3500 this._moved = false;
3501 this._released = false;
3502 this._wheelX = 0;
3503 this._wheelY = 0;
3504 this._x = 0;
3505 this._y = 0;
3506 this._date = 0;
3507};
3508
3509/**
3510 * Updates the touch data.
3511 *
3512 * @static
3513 * @method update
3514 */
3515TouchInput.update = function() {
3516 this._triggered = this._events.triggered;
3517 this._cancelled = this._events.cancelled;
3518 this._moved = this._events.moved;
3519 this._released = this._events.released;
3520 this._wheelX = this._events.wheelX;
3521 this._wheelY = this._events.wheelY;
3522 this._events.triggered = false;
3523 this._events.cancelled = false;
3524 this._events.moved = false;
3525 this._events.released = false;
3526 this._events.wheelX = 0;
3527 this._events.wheelY = 0;
3528 if (this.isPressed()) {
3529 this._pressedTime++;
3530 }
3531};
3532
3533/**
3534 * Checks whether the mouse button or touchscreen is currently pressed down.
3535 *
3536 * @static
3537 * @method isPressed
3538 * @return {Boolean} True if the mouse button or touchscreen is pressed
3539 */
3540TouchInput.isPressed = function() {
3541 return this._mousePressed || this._screenPressed;
3542};
3543
3544/**
3545 * Checks whether the left mouse button or touchscreen is just pressed.
3546 *
3547 * @static
3548 * @method isTriggered
3549 * @return {Boolean} True if the mouse button or touchscreen is triggered
3550 */
3551TouchInput.isTriggered = function() {
3552 return this._triggered;
3553};
3554
3555/**
3556 * Checks whether the left mouse button or touchscreen is just pressed
3557 * or a pseudo key repeat occurred.
3558 *
3559 * @static
3560 * @method isRepeated
3561 * @return {Boolean} True if the mouse button or touchscreen is repeated
3562 */
3563TouchInput.isRepeated = function() {
3564 return (this.isPressed() &&
3565 (this._triggered ||
3566 (this._pressedTime >= this.keyRepeatWait &&
3567 this._pressedTime % this.keyRepeatInterval === 0)));
3568};
3569
3570/**
3571 * Checks whether the left mouse button or touchscreen is kept depressed.
3572 *
3573 * @static
3574 * @method isLongPressed
3575 * @return {Boolean} True if the left mouse button or touchscreen is long-pressed
3576 */
3577TouchInput.isLongPressed = function() {
3578 return this.isPressed() && this._pressedTime >= this.keyRepeatWait;
3579};
3580
3581/**
3582 * Checks whether the right mouse button is just pressed.
3583 *
3584 * @static
3585 * @method isCancelled
3586 * @return {Boolean} True if the right mouse button is just pressed
3587 */
3588TouchInput.isCancelled = function() {
3589 return this._cancelled;
3590};
3591
3592/**
3593 * Checks whether the mouse or a finger on the touchscreen is moved.
3594 *
3595 * @static
3596 * @method isMoved
3597 * @return {Boolean} True if the mouse or a finger on the touchscreen is moved
3598 */
3599TouchInput.isMoved = function() {
3600 return this._moved;
3601};
3602
3603/**
3604 * Checks whether the left mouse button or touchscreen is released.
3605 *
3606 * @static
3607 * @method isReleased
3608 * @return {Boolean} True if the mouse button or touchscreen is released
3609 */
3610TouchInput.isReleased = function() {
3611 return this._released;
3612};
3613
3614/**
3615 * [read-only] The horizontal scroll amount.
3616 *
3617 * @static
3618 * @property wheelX
3619 * @type Number
3620 */
3621Object.defineProperty(TouchInput, 'wheelX', {
3622 get: function() {
3623 return this._wheelX;
3624 },
3625 configurable: true
3626});
3627
3628/**
3629 * [read-only] The vertical scroll amount.
3630 *
3631 * @static
3632 * @property wheelY
3633 * @type Number
3634 */
3635Object.defineProperty(TouchInput, 'wheelY', {
3636 get: function() {
3637 return this._wheelY;
3638 },
3639 configurable: true
3640});
3641
3642/**
3643 * [read-only] The x coordinate on the canvas area of the latest touch event.
3644 *
3645 * @static
3646 * @property x
3647 * @type Number
3648 */
3649Object.defineProperty(TouchInput, 'x', {
3650 get: function() {
3651 return this._x;
3652 },
3653 configurable: true
3654});
3655
3656/**
3657 * [read-only] The y coordinate on the canvas area of the latest touch event.
3658 *
3659 * @static
3660 * @property y
3661 * @type Number
3662 */
3663Object.defineProperty(TouchInput, 'y', {
3664 get: function() {
3665 return this._y;
3666 },
3667 configurable: true
3668});
3669
3670/**
3671 * [read-only] The time of the last input in milliseconds.
3672 *
3673 * @static
3674 * @property date
3675 * @type Number
3676 */
3677Object.defineProperty(TouchInput, 'date', {
3678 get: function() {
3679 return this._date;
3680 },
3681 configurable: true
3682});
3683
3684/**
3685 * @static
3686 * @method _setupEventHandlers
3687 * @private
3688 */
3689TouchInput._setupEventHandlers = function() {
3690 var isSupportPassive = Utils.isSupportPassiveEvent();
3691 document.addEventListener('mousedown', this._onMouseDown.bind(this));
3692 document.addEventListener('mousemove', this._onMouseMove.bind(this));
3693 document.addEventListener('mouseup', this._onMouseUp.bind(this));
3694 document.addEventListener('wheel', this._onWheel.bind(this));
3695 document.addEventListener('touchstart', this._onTouchStart.bind(this), isSupportPassive ? {passive: false} : false);
3696 document.addEventListener('touchmove', this._onTouchMove.bind(this), isSupportPassive ? {passive: false} : false);
3697 document.addEventListener('touchend', this._onTouchEnd.bind(this));
3698 document.addEventListener('touchcancel', this._onTouchCancel.bind(this));
3699 document.addEventListener('pointerdown', this._onPointerDown.bind(this));
3700};
3701
3702/**
3703 * @static
3704 * @method _onMouseDown
3705 * @param {MouseEvent} event
3706 * @private
3707 */
3708TouchInput._onMouseDown = function(event) {
3709 if (event.button === 0) {
3710 this._onLeftButtonDown(event);
3711 } else if (event.button === 1) {
3712 this._onMiddleButtonDown(event);
3713 } else if (event.button === 2) {
3714 this._onRightButtonDown(event);
3715 }
3716};
3717
3718/**
3719 * @static
3720 * @method _onLeftButtonDown
3721 * @param {MouseEvent} event
3722 * @private
3723 */
3724TouchInput._onLeftButtonDown = function(event) {
3725 var x = Graphics.pageToCanvasX(event.pageX);
3726 var y = Graphics.pageToCanvasY(event.pageY);
3727 if (Graphics.isInsideCanvas(x, y)) {
3728 this._mousePressed = true;
3729 this._pressedTime = 0;
3730 this._onTrigger(x, y);
3731 }
3732};
3733
3734/**
3735 * @static
3736 * @method _onMiddleButtonDown
3737 * @param {MouseEvent} event
3738 * @private
3739 */
3740TouchInput._onMiddleButtonDown = function(event) {
3741};
3742
3743/**
3744 * @static
3745 * @method _onRightButtonDown
3746 * @param {MouseEvent} event
3747 * @private
3748 */
3749TouchInput._onRightButtonDown = function(event) {
3750 var x = Graphics.pageToCanvasX(event.pageX);
3751 var y = Graphics.pageToCanvasY(event.pageY);
3752 if (Graphics.isInsideCanvas(x, y)) {
3753 this._onCancel(x, y);
3754 }
3755};
3756
3757/**
3758 * @static
3759 * @method _onMouseMove
3760 * @param {MouseEvent} event
3761 * @private
3762 */
3763TouchInput._onMouseMove = function(event) {
3764 if (this._mousePressed) {
3765 var x = Graphics.pageToCanvasX(event.pageX);
3766 var y = Graphics.pageToCanvasY(event.pageY);
3767 this._onMove(x, y);
3768 }
3769};
3770
3771/**
3772 * @static
3773 * @method _onMouseUp
3774 * @param {MouseEvent} event
3775 * @private
3776 */
3777TouchInput._onMouseUp = function(event) {
3778 if (event.button === 0) {
3779 var x = Graphics.pageToCanvasX(event.pageX);
3780 var y = Graphics.pageToCanvasY(event.pageY);
3781 this._mousePressed = false;
3782 this._onRelease(x, y);
3783 }
3784};
3785
3786/**
3787 * @static
3788 * @method _onWheel
3789 * @param {WheelEvent} event
3790 * @private
3791 */
3792TouchInput._onWheel = function(event) {
3793 this._events.wheelX += event.deltaX;
3794 this._events.wheelY += event.deltaY;
3795 event.preventDefault();
3796};
3797
3798/**
3799 * @static
3800 * @method _onTouchStart
3801 * @param {TouchEvent} event
3802 * @private
3803 */
3804TouchInput._onTouchStart = function(event) {
3805 for (var i = 0; i < event.changedTouches.length; i++) {
3806 var touch = event.changedTouches[i];
3807 var x = Graphics.pageToCanvasX(touch.pageX);
3808 var y = Graphics.pageToCanvasY(touch.pageY);
3809 if (Graphics.isInsideCanvas(x, y)) {
3810 this._screenPressed = true;
3811 this._pressedTime = 0;
3812 if (event.touches.length >= 2) {
3813 this._onCancel(x, y);
3814 } else {
3815 this._onTrigger(x, y);
3816 }
3817 event.preventDefault();
3818 }
3819 }
3820 if (window.cordova || window.navigator.standalone) {
3821 event.preventDefault();
3822 }
3823};
3824
3825/**
3826 * @static
3827 * @method _onTouchMove
3828 * @param {TouchEvent} event
3829 * @private
3830 */
3831TouchInput._onTouchMove = function(event) {
3832 for (var i = 0; i < event.changedTouches.length; i++) {
3833 var touch = event.changedTouches[i];
3834 var x = Graphics.pageToCanvasX(touch.pageX);
3835 var y = Graphics.pageToCanvasY(touch.pageY);
3836 this._onMove(x, y);
3837 }
3838};
3839
3840/**
3841 * @static
3842 * @method _onTouchEnd
3843 * @param {TouchEvent} event
3844 * @private
3845 */
3846TouchInput._onTouchEnd = function(event) {
3847 for (var i = 0; i < event.changedTouches.length; i++) {
3848 var touch = event.changedTouches[i];
3849 var x = Graphics.pageToCanvasX(touch.pageX);
3850 var y = Graphics.pageToCanvasY(touch.pageY);
3851 this._screenPressed = false;
3852 this._onRelease(x, y);
3853 }
3854};
3855
3856/**
3857 * @static
3858 * @method _onTouchCancel
3859 * @param {TouchEvent} event
3860 * @private
3861 */
3862TouchInput._onTouchCancel = function(event) {
3863 this._screenPressed = false;
3864};
3865
3866/**
3867 * @static
3868 * @method _onPointerDown
3869 * @param {PointerEvent} event
3870 * @private
3871 */
3872TouchInput._onPointerDown = function(event) {
3873 if (event.pointerType === 'touch' && !event.isPrimary) {
3874 var x = Graphics.pageToCanvasX(event.pageX);
3875 var y = Graphics.pageToCanvasY(event.pageY);
3876 if (Graphics.isInsideCanvas(x, y)) {
3877 // For Microsoft Edge
3878 this._onCancel(x, y);
3879 event.preventDefault();
3880 }
3881 }
3882};
3883
3884/**
3885 * @static
3886 * @method _onTrigger
3887 * @param {Number} x
3888 * @param {Number} y
3889 * @private
3890 */
3891TouchInput._onTrigger = function(x, y) {
3892 this._events.triggered = true;
3893 this._x = x;
3894 this._y = y;
3895 this._date = Date.now();
3896};
3897
3898/**
3899 * @static
3900 * @method _onCancel
3901 * @param {Number} x
3902 * @param {Number} y
3903 * @private
3904 */
3905TouchInput._onCancel = function(x, y) {
3906 this._events.cancelled = true;
3907 this._x = x;
3908 this._y = y;
3909};
3910
3911/**
3912 * @static
3913 * @method _onMove
3914 * @param {Number} x
3915 * @param {Number} y
3916 * @private
3917 */
3918TouchInput._onMove = function(x, y) {
3919 this._events.moved = true;
3920 this._x = x;
3921 this._y = y;
3922};
3923
3924/**
3925 * @static
3926 * @method _onRelease
3927 * @param {Number} x
3928 * @param {Number} y
3929 * @private
3930 */
3931TouchInput._onRelease = function(x, y) {
3932 this._events.released = true;
3933 this._x = x;
3934 this._y = y;
3935};
3936
3937//-----------------------------------------------------------------------------
3938/**
3939 * The basic object that is rendered to the game screen.
3940 *
3941 * @class Sprite
3942 * @constructor
3943 * @param {Bitmap} bitmap The image for the sprite
3944 */
3945function Sprite() {
3946 this.initialize.apply(this, arguments);
3947}
3948
3949Sprite.prototype = Object.create(PIXI.projection.Sprite3d.prototype);
3950Sprite.prototype.constructor = Sprite;
3951
3952Sprite.voidFilter = new PIXI.filters.VoidFilter();
3953
3954Sprite.prototype.initialize = function(bitmap) {
3955 var texture = new PIXI.Texture(new PIXI.BaseTexture());
3956
3957 PIXI.projection.Sprite3d.call(this, texture);
3958 //PIXI.Sprite.call(this, texture);
3959
3960 this._bitmap = null;
3961 this._frame = new Rectangle();
3962 this._realFrame = new Rectangle();
3963 this._blendColor = [0, 0, 0, 0];
3964 this._colorTone = [0, 0, 0, 0];
3965 this._canvas = null;
3966 this._context = null;
3967 this._tintTexture = null;
3968
3969 /**
3970 * use heavy renderer that will reduce border artifacts and apply advanced blendModes
3971 * @type {boolean}
3972 * @private
3973 */
3974 this._isPicture = false;
3975
3976 this.spriteId = Sprite._counter++;
3977 this.opaque = false;
3978
3979 this.bitmap = bitmap;
3980};
3981
3982// Number of the created objects.
3983Sprite._counter = 0;
3984
3985/**
3986 * The image for the sprite.
3987 *
3988 * @property bitmap
3989 * @type Bitmap
3990 */
3991Object.defineProperty(Sprite.prototype, 'bitmap', {
3992 get: function() {
3993 return this._bitmap;
3994 },
3995 set: function(value) {
3996 if (this._bitmap !== value) {
3997 this._bitmap = value;
3998
3999 if(value){
4000 this._refreshFrame = true;
4001 value.addLoadListener(this._onBitmapLoad.bind(this));
4002 }else{
4003 this._refreshFrame = false;
4004 this.texture.frame = Rectangle.emptyRectangle;
4005 }
4006 }
4007 },
4008 configurable: true
4009});
4010
4011/**
4012 * The width of the sprite without the scale.
4013 *
4014 * @property width
4015 * @type Number
4016 */
4017Object.defineProperty(Sprite.prototype, 'width', {
4018 get: function() {
4019 return this._frame.width;
4020 },
4021 set: function(value) {
4022 this._frame.width = value;
4023 this._refresh();
4024 },
4025 configurable: true
4026});
4027
4028/**
4029 * The height of the sprite without the scale.
4030 *
4031 * @property height
4032 * @type Number
4033 */
4034Object.defineProperty(Sprite.prototype, 'height', {
4035 get: function() {
4036 return this._frame.height;
4037 },
4038 set: function(value) {
4039 this._frame.height = value;
4040 this._refresh();
4041 },
4042 configurable: true
4043});
4044
4045/**
4046 * The opacity of the sprite (0 to 255).
4047 *
4048 * @property opacity
4049 * @type Number
4050 */
4051Object.defineProperty(Sprite.prototype, 'opacity', {
4052 get: function() {
4053 return this.alpha * 255;
4054 },
4055 set: function(value) {
4056 this.alpha = value.clamp(0, 255) / 255;
4057 },
4058 configurable: true
4059});
4060
4061/**
4062 * Updates the sprite for each frame.
4063 *
4064 * @method update
4065 */
4066Sprite.prototype.update = function() {
4067 this.children.forEach(function(child) {
4068 if (child.update) {
4069 child.update();
4070 }
4071 });
4072};
4073
4074/**
4075 * Sets the x and y at once.
4076 *
4077 * @method move
4078 * @param {Number} x The x coordinate of the sprite
4079 * @param {Number} y The y coordinate of the sprite
4080 */
4081Sprite.prototype.move = function(x, y) {
4082 this.x = x;
4083 this.y = y;
4084};
4085
4086/**
4087 * Sets the rectagle of the bitmap that the sprite displays.
4088 *
4089 * @method setFrame
4090 * @param {Number} x The x coordinate of the frame
4091 * @param {Number} y The y coordinate of the frame
4092 * @param {Number} width The width of the frame
4093 * @param {Number} height The height of the frame
4094 */
4095Sprite.prototype.setFrame = function(x, y, width, height) {
4096 this._refreshFrame = false;
4097 var frame = this._frame;
4098 if (x !== frame.x || y !== frame.y ||
4099 width !== frame.width || height !== frame.height) {
4100 frame.x = x;
4101 frame.y = y;
4102 frame.width = width;
4103 frame.height = height;
4104 this._refresh();
4105 }
4106};
4107
4108/**
4109 * Gets the blend color for the sprite.
4110 *
4111 * @method getBlendColor
4112 * @return {Array} The blend color [r, g, b, a]
4113 */
4114Sprite.prototype.getBlendColor = function() {
4115 return this._blendColor.clone();
4116};
4117
4118/**
4119 * Sets the blend color for the sprite.
4120 *
4121 * @method setBlendColor
4122 * @param {Array} color The blend color [r, g, b, a]
4123 */
4124Sprite.prototype.setBlendColor = function(color) {
4125 if (!(color instanceof Array)) {
4126 throw new Error('Argument must be an array');
4127 }
4128 if (!this._blendColor.equals(color)) {
4129 this._blendColor = color.clone();
4130 this._refresh();
4131 }
4132};
4133
4134/**
4135 * Gets the color tone for the sprite.
4136 *
4137 * @method getColorTone
4138 * @return {Array} The color tone [r, g, b, gray]
4139 */
4140Sprite.prototype.getColorTone = function() {
4141 return this._colorTone.clone();
4142};
4143
4144/**
4145 * Sets the color tone for the sprite.
4146 *
4147 * @method setColorTone
4148 * @param {Array} tone The color tone [r, g, b, gray]
4149 */
4150Sprite.prototype.setColorTone = function(tone) {
4151 if (!(tone instanceof Array)) {
4152 throw new Error('Argument must be an array');
4153 }
4154 if (!this._colorTone.equals(tone)) {
4155 this._colorTone = tone.clone();
4156 this._refresh();
4157 }
4158};
4159
4160/**
4161 * @method _onBitmapLoad
4162 * @private
4163 */
4164Sprite.prototype._onBitmapLoad = function(bitmapLoaded) {
4165 if(bitmapLoaded === this._bitmap){
4166 if (this._refreshFrame && this._bitmap) {
4167 this._refreshFrame = false;
4168 this._frame.width = this._bitmap.width;
4169 this._frame.height = this._bitmap.height;
4170 }
4171 }
4172
4173 this._refresh();
4174};
4175
4176/**
4177 * @method _refresh
4178 * @private
4179 */
4180Sprite.prototype._refresh = function() {
4181 var frameX = Math.floor(this._frame.x);
4182 var frameY = Math.floor(this._frame.y);
4183 var frameW = Math.floor(this._frame.width);
4184 var frameH = Math.floor(this._frame.height);
4185 var bitmapW = this._bitmap ? this._bitmap.width : 0;
4186 var bitmapH = this._bitmap ? this._bitmap.height : 0;
4187 var realX = frameX.clamp(0, bitmapW);
4188 var realY = frameY.clamp(0, bitmapH);
4189 var realW = (frameW - realX + frameX).clamp(0, bitmapW - realX);
4190 var realH = (frameH - realY + frameY).clamp(0, bitmapH - realY);
4191
4192 this._realFrame.x = realX;
4193 this._realFrame.y = realY;
4194 this._realFrame.width = realW;
4195 this._realFrame.height = realH;
4196 this.pivot.x = frameX - realX;
4197 this.pivot.y = frameY - realY;
4198
4199 if (realW > 0 && realH > 0) {
4200 if (this._needsTint()) {
4201 this._createTinter(realW, realH);
4202 this._executeTint(realX, realY, realW, realH);
4203 this._tintTexture.update();
4204 this.texture.baseTexture = this._tintTexture;
4205 this.texture.frame = new Rectangle(0, 0, realW, realH);
4206 } else {
4207 if (this._bitmap) {
4208 this.texture.baseTexture = this._bitmap.baseTexture;
4209 }
4210 this.texture.frame = this._realFrame;
4211 }
4212 } else if (this._bitmap) {
4213 this.texture.frame = Rectangle.emptyRectangle;
4214 } else {
4215 this.texture.baseTexture.width = Math.max(this.texture.baseTexture.width, this._frame.x + this._frame.width);
4216 this.texture.baseTexture.height = Math.max(this.texture.baseTexture.height, this._frame.y + this._frame.height);
4217 this.texture.frame = this._frame;
4218 }
4219 this.texture._updateID++;
4220};
4221
4222/**
4223 * @method _isInBitmapRect
4224 * @param {Number} x
4225 * @param {Number} y
4226 * @param {Number} w
4227 * @param {Number} h
4228 * @return {Boolean}
4229 * @private
4230 */
4231Sprite.prototype._isInBitmapRect = function(x, y, w, h) {
4232 return (this._bitmap && x + w > 0 && y + h > 0 &&
4233 x < this._bitmap.width && y < this._bitmap.height);
4234};
4235
4236/**
4237 * @method _needsTint
4238 * @return {Boolean}
4239 * @private
4240 */
4241Sprite.prototype._needsTint = function() {
4242 var tone = this._colorTone;
4243 return tone[0] || tone[1] || tone[2] || tone[3] || this._blendColor[3] > 0;
4244};
4245
4246/**
4247 * @method _createTinter
4248 * @param {Number} w
4249 * @param {Number} h
4250 * @private
4251 */
4252Sprite.prototype._createTinter = function(w, h) {
4253 if (!this._canvas) {
4254 this._canvas = document.createElement('canvas');
4255 this._context = this._canvas.getContext('2d');
4256 }
4257
4258 this._canvas.width = w;
4259 this._canvas.height = h;
4260
4261 if (!this._tintTexture) {
4262 this._tintTexture = new PIXI.BaseTexture(this._canvas);
4263 }
4264
4265 this._tintTexture.width = w;
4266 this._tintTexture.height = h;
4267 this._tintTexture.scaleMode = this._bitmap.baseTexture.scaleMode;
4268};
4269
4270/**
4271 * @method _executeTint
4272 * @param {Number} x
4273 * @param {Number} y
4274 * @param {Number} w
4275 * @param {Number} h
4276 * @private
4277 */
4278Sprite.prototype._executeTint = function(x, y, w, h) {
4279 var context = this._context;
4280 var tone = this._colorTone;
4281 var color = this._blendColor;
4282
4283 context.globalCompositeOperation = 'copy';
4284 context.drawImage(this._bitmap.canvas, x, y, w, h, 0, 0, w, h);
4285
4286 if (Graphics.canUseSaturationBlend()) {
4287 var gray = Math.max(0, tone[3]);
4288 context.globalCompositeOperation = 'saturation';
4289 context.fillStyle = 'rgba(255,255,255,' + gray / 255 + ')';
4290 context.fillRect(0, 0, w, h);
4291 }
4292
4293 var r1 = Math.max(0, tone[0]);
4294 var g1 = Math.max(0, tone[1]);
4295 var b1 = Math.max(0, tone[2]);
4296 context.globalCompositeOperation = 'lighter';
4297 context.fillStyle = Utils.rgbToCssColor(r1, g1, b1);
4298 context.fillRect(0, 0, w, h);
4299
4300 if (Graphics.canUseDifferenceBlend()) {
4301 context.globalCompositeOperation = 'difference';
4302 context.fillStyle = 'white';
4303 context.fillRect(0, 0, w, h);
4304
4305 var r2 = Math.max(0, -tone[0]);
4306 var g2 = Math.max(0, -tone[1]);
4307 var b2 = Math.max(0, -tone[2]);
4308 context.globalCompositeOperation = 'lighter';
4309 context.fillStyle = Utils.rgbToCssColor(r2, g2, b2);
4310 context.fillRect(0, 0, w, h);
4311
4312 context.globalCompositeOperation = 'difference';
4313 context.fillStyle = 'white';
4314 context.fillRect(0, 0, w, h);
4315 }
4316
4317 var r3 = Math.max(0, color[0]);
4318 var g3 = Math.max(0, color[1]);
4319 var b3 = Math.max(0, color[2]);
4320 var a3 = Math.max(0, color[3]);
4321 context.globalCompositeOperation = 'source-atop';
4322 context.fillStyle = Utils.rgbToCssColor(r3, g3, b3);
4323 context.globalAlpha = a3 / 255;
4324 context.fillRect(0, 0, w, h);
4325
4326 context.globalCompositeOperation = 'destination-in';
4327 context.globalAlpha = 1;
4328 context.drawImage(this._bitmap.canvas, x, y, w, h, 0, 0, w, h);
4329};
4330
4331Sprite.prototype._renderCanvas_PIXI = PIXI.Sprite.prototype._renderCanvas;
4332Sprite.prototype._renderWebGL_PIXI = PIXI.Sprite.prototype._renderWebGL;
4333
4334/**
4335 * @method _renderCanvas
4336 * @param {Object} renderer
4337 * @private
4338 */
4339Sprite.prototype._renderCanvas = function(renderer) {
4340 if (this.bitmap) {
4341 this.bitmap.touch();
4342 }
4343 if(this.bitmap && !this.bitmap.isReady()){
4344 return;
4345 }
4346
4347 if (this.texture.frame.width > 0 && this.texture.frame.height > 0) {
4348 this._renderCanvas_PIXI(renderer);
4349 }
4350};
4351
4352/**
4353 * checks if we need to speed up custom blendmodes
4354 * @param renderer
4355 * @private
4356 */
4357Sprite.prototype._speedUpCustomBlendModes = function(renderer) {
4358 var picture = renderer.plugins.picture;
4359 var blend = this.blendMode;
4360 if (renderer.renderingToScreen && renderer._activeRenderTarget.root) {
4361 if (picture.drawModes[blend]) {
4362 var stage = renderer._lastObjectRendered;
4363 var f = stage._filters;
4364 if (!f || !f[0]) {
4365 setTimeout(function () {
4366 var f = stage._filters;
4367 if (!f || !f[0]) {
4368 stage.filters = [Sprite.voidFilter];
4369 stage.filterArea = new PIXI.Rectangle(0, 0, Graphics.width, Graphics.height);
4370 }
4371 }, 0);
4372 }
4373 }
4374 }
4375};
4376
4377/**
4378 * @method _renderWebGL
4379 * @param {Object} renderer
4380 * @private
4381 */
4382Sprite.prototype._renderWebGL = function(renderer) {
4383 if (this.bitmap) {
4384 this.bitmap.touch();
4385 }
4386 if(this.bitmap && !this.bitmap.isReady()){
4387 return;
4388 }
4389 if (this.texture.frame.width > 0 && this.texture.frame.height > 0) {
4390 if (this._bitmap) {
4391 this._bitmap.checkDirty();
4392 }
4393
4394 //copy of pixi-v4 internal code
4395 this.calculateVertices();
4396
4397 if (this.pluginName === 'sprite' && this._isPicture) {
4398 // use heavy renderer, which reduces artifacts and applies corrent blendMode,
4399 // but does not use multitexture optimization
4400 this._speedUpCustomBlendModes(renderer);
4401 renderer.setObjectRenderer(renderer.plugins.picture);
4402 renderer.plugins.picture.render(this);
4403 } else {
4404 // use pixi super-speed renderer
4405 renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
4406 renderer.plugins[this.pluginName].render(this);
4407 }
4408 }
4409};
4410
4411// The important members from Pixi.js
4412
4413/**
4414 * The visibility of the sprite.
4415 *
4416 * @property visible
4417 * @type Boolean
4418 */
4419
4420/**
4421 * The x coordinate of the sprite.
4422 *
4423 * @property x
4424 * @type Number
4425 */
4426
4427/**
4428 * The y coordinate of the sprite.
4429 *
4430 * @property y
4431 * @type Number
4432 */
4433
4434/**
4435 * The origin point of the sprite. (0,0) to (1,1).
4436 *
4437 * @property anchor
4438 * @type Point
4439 */
4440
4441/**
4442 * The scale factor of the sprite.
4443 *
4444 * @property scale
4445 * @type Point
4446 */
4447
4448/**
4449 * The rotation of the sprite in radians.
4450 *
4451 * @property rotation
4452 * @type Number
4453 */
4454
4455/**
4456 * The blend mode to be applied to the sprite.
4457 *
4458 * @property blendMode
4459 * @type Number
4460 */
4461
4462/**
4463 * Sets the filters for the sprite.
4464 *
4465 * @property filters
4466 * @type Array
4467 */
4468
4469/**
4470 * [read-only] The array of children of the sprite.
4471 *
4472 * @property children
4473 * @type Array
4474 */
4475
4476/**
4477 * [read-only] The object that contains the sprite.
4478 *
4479 * @property parent
4480 * @type Object
4481 */
4482
4483/**
4484 * Adds a child to the container.
4485 *
4486 * @method addChild
4487 * @param {Object} child The child to add
4488 * @return {Object} The child that was added
4489 */
4490
4491/**
4492 * Adds a child to the container at a specified index.
4493 *
4494 * @method addChildAt
4495 * @param {Object} child The child to add
4496 * @param {Number} index The index to place the child in
4497 * @return {Object} The child that was added
4498 */
4499
4500/**
4501 * Removes a child from the container.
4502 *
4503 * @method removeChild
4504 * @param {Object} child The child to remove
4505 * @return {Object} The child that was removed
4506 */
4507
4508/**
4509 * Removes a child from the specified index position.
4510 *
4511 * @method removeChildAt
4512 * @param {Number} index The index to get the child from
4513 * @return {Object} The child that was removed
4514 */
4515
4516//-----------------------------------------------------------------------------
4517/**
4518 * The tilemap which displays 2D tile-based game map.
4519 *
4520 * @class Tilemap
4521 * @constructor
4522 */
4523function Tilemap() {
4524 this.initialize.apply(this, arguments);
4525}
4526
4527Tilemap.prototype = Object.create(PIXI.Container.prototype);
4528Tilemap.prototype.constructor = Tilemap;
4529
4530Tilemap.prototype.initialize = function() {
4531 PIXI.Container.call(this);
4532
4533 this._margin = 20;
4534 this._width = Graphics.width + this._margin * 2;
4535 this._height = Graphics.height + this._margin * 2;
4536 this._tileWidth = 48;
4537 this._tileHeight = 48;
4538 this._mapWidth = 0;
4539 this._mapHeight = 0;
4540 this._mapData = null;
4541 this._layerWidth = 0;
4542 this._layerHeight = 0;
4543 this._lastTiles = [];
4544
4545 /**
4546 * The bitmaps used as a tileset.
4547 *
4548 * @property bitmaps
4549 * @type Array
4550 */
4551 this.bitmaps = [];
4552
4553 /**
4554 * The origin point of the tilemap for scrolling.
4555 *
4556 * @property origin
4557 * @type Point
4558 */
4559 this.origin = new Point();
4560
4561 /**
4562 * The tileset flags.
4563 *
4564 * @property flags
4565 * @type Array
4566 */
4567 this.flags = [];
4568
4569 /**
4570 * The animation count for autotiles.
4571 *
4572 * @property animationCount
4573 * @type Number
4574 */
4575 this.animationCount = 0;
4576
4577 /**
4578 * Whether the tilemap loops horizontal.
4579 *
4580 * @property horizontalWrap
4581 * @type Boolean
4582 */
4583 this.horizontalWrap = false;
4584
4585 /**
4586 * Whether the tilemap loops vertical.
4587 *
4588 * @property verticalWrap
4589 * @type Boolean
4590 */
4591 this.verticalWrap = false;
4592
4593 this._createLayers();
4594 this.refresh();
4595};
4596
4597/**
4598 * The width of the screen in pixels.
4599 *
4600 * @property width
4601 * @type Number
4602 */
4603Object.defineProperty(Tilemap.prototype, 'width', {
4604 get: function() {
4605 return this._width;
4606 },
4607 set: function(value) {
4608 if (this._width !== value) {
4609 this._width = value;
4610 this._createLayers();
4611 }
4612 }
4613});
4614
4615/**
4616 * The height of the screen in pixels.
4617 *
4618 * @property height
4619 * @type Number
4620 */
4621Object.defineProperty(Tilemap.prototype, 'height', {
4622 get: function() {
4623 return this._height;
4624 },
4625 set: function(value) {
4626 if (this._height !== value) {
4627 this._height = value;
4628 this._createLayers();
4629 }
4630 }
4631});
4632
4633/**
4634 * The width of a tile in pixels.
4635 *
4636 * @property tileWidth
4637 * @type Number
4638 */
4639Object.defineProperty(Tilemap.prototype, 'tileWidth', {
4640 get: function() {
4641 return this._tileWidth;
4642 },
4643 set: function(value) {
4644 if (this._tileWidth !== value) {
4645 this._tileWidth = value;
4646 this._createLayers();
4647 }
4648 }
4649});
4650
4651/**
4652 * The height of a tile in pixels.
4653 *
4654 * @property tileHeight
4655 * @type Number
4656 */
4657Object.defineProperty(Tilemap.prototype, 'tileHeight', {
4658 get: function() {
4659 return this._tileHeight;
4660 },
4661 set: function(value) {
4662 if (this._tileHeight !== value) {
4663 this._tileHeight = value;
4664 this._createLayers();
4665 }
4666 }
4667});
4668
4669/**
4670 * Sets the tilemap data.
4671 *
4672 * @method setData
4673 * @param {Number} width The width of the map in number of tiles
4674 * @param {Number} height The height of the map in number of tiles
4675 * @param {Array} data The one dimensional array for the map data
4676 */
4677Tilemap.prototype.setData = function(width, height, data) {
4678 this._mapWidth = width;
4679 this._mapHeight = height;
4680 this._mapData = data;
4681};
4682
4683/**
4684 * Checks whether the tileset is ready to render.
4685 *
4686 * @method isReady
4687 * @type Boolean
4688 * @return {Boolean} True if the tilemap is ready
4689 */
4690Tilemap.prototype.isReady = function() {
4691 for (var i = 0; i < this.bitmaps.length; i++) {
4692 if (this.bitmaps[i] && !this.bitmaps[i].isReady()) {
4693 return false;
4694 }
4695 }
4696 return true;
4697};
4698
4699/**
4700 * Updates the tilemap for each frame.
4701 *
4702 * @method update
4703 */
4704Tilemap.prototype.update = function() {
4705 this.animationCount++;
4706 this.animationFrame = Math.floor(this.animationCount / 30);
4707 this.children.forEach(function(child) {
4708 if (child.update) {
4709 child.update();
4710 }
4711 });
4712 for (var i=0; i<this.bitmaps.length;i++) {
4713 if (this.bitmaps[i]) {
4714 this.bitmaps[i].touch();
4715 }
4716 }
4717};
4718
4719/**
4720 * Forces to repaint the entire tilemap.
4721 *
4722 * @method refresh
4723 */
4724Tilemap.prototype.refresh = function() {
4725 this._lastTiles.length = 0;
4726};
4727
4728/**
4729 * Forces to refresh the tileset
4730 *
4731 * @method refresh
4732 */
4733Tilemap.prototype.refreshTileset = function() {
4734
4735};
4736
4737/**
4738 * @method updateTransform
4739 * @private
4740 */
4741Tilemap.prototype.updateTransform = function() {
4742 var ox = Math.floor(this.origin.x);
4743 var oy = Math.floor(this.origin.y);
4744 var startX = Math.floor((ox - this._margin) / this._tileWidth);
4745 var startY = Math.floor((oy - this._margin) / this._tileHeight);
4746 this._updateLayerPositions(startX, startY);
4747 if (this._needsRepaint || this._lastAnimationFrame !== this.animationFrame ||
4748 this._lastStartX !== startX || this._lastStartY !== startY) {
4749 this._frameUpdated = this._lastAnimationFrame !== this.animationFrame;
4750 this._lastAnimationFrame = this.animationFrame;
4751 this._lastStartX = startX;
4752 this._lastStartY = startY;
4753 this._paintAllTiles(startX, startY);
4754 this._needsRepaint = false;
4755 }
4756 this._sortChildren();
4757 PIXI.Container.prototype.updateTransform.call(this);
4758};
4759
4760/**
4761 * @method _createLayers
4762 * @private
4763 */
4764Tilemap.prototype._createLayers = function() {
4765 var width = this._width;
4766 var height = this._height;
4767 var margin = this._margin;
4768 var tileCols = Math.ceil(width / this._tileWidth) + 1;
4769 var tileRows = Math.ceil(height / this._tileHeight) + 1;
4770 var layerWidth = tileCols * this._tileWidth;
4771 var layerHeight = tileRows * this._tileHeight;
4772 this._lowerBitmap = new Bitmap(layerWidth, layerHeight);
4773 this._upperBitmap = new Bitmap(layerWidth, layerHeight);
4774 this._layerWidth = layerWidth;
4775 this._layerHeight = layerHeight;
4776
4777 /*
4778 * Z coordinate:
4779 *
4780 * 0 : Lower tiles
4781 * 1 : Lower characters
4782 * 3 : Normal characters
4783 * 4 : Upper tiles
4784 * 5 : Upper characters
4785 * 6 : Airship shadow
4786 * 7 : Balloon
4787 * 8 : Animation
4788 * 9 : Destination
4789 */
4790
4791 this._lowerLayer = new Sprite();
4792 this._lowerLayer.move(-margin, -margin, width, height);
4793 this._lowerLayer.z = 0;
4794
4795 this._upperLayer = new Sprite();
4796 this._upperLayer.move(-margin, -margin, width, height);
4797 this._upperLayer.z = 4;
4798
4799 for (var i = 0; i < 4; i++) {
4800 this._lowerLayer.addChild(new Sprite(this._lowerBitmap));
4801 this._upperLayer.addChild(new Sprite(this._upperBitmap));
4802 }
4803
4804 this.addChild(this._lowerLayer);
4805 this.addChild(this._upperLayer);
4806};
4807
4808/**
4809 * @method _updateLayerPositions
4810 * @param {Number} startX
4811 * @param {Number} startY
4812 * @private
4813 */
4814Tilemap.prototype._updateLayerPositions = function(startX, startY) {
4815 var m = this._margin;
4816 var ox = Math.floor(this.origin.x);
4817 var oy = Math.floor(this.origin.y);
4818 var x2 = (ox - m).mod(this._layerWidth);
4819 var y2 = (oy - m).mod(this._layerHeight);
4820 var w1 = this._layerWidth - x2;
4821 var h1 = this._layerHeight - y2;
4822 var w2 = this._width - w1;
4823 var h2 = this._height - h1;
4824
4825 for (var i = 0; i < 2; i++) {
4826 var children;
4827 if (i === 0) {
4828 children = this._lowerLayer.children;
4829 } else {
4830 children = this._upperLayer.children;
4831 }
4832 children[0].move(0, 0, w1, h1);
4833 children[0].setFrame(x2, y2, w1, h1);
4834 children[1].move(w1, 0, w2, h1);
4835 children[1].setFrame(0, y2, w2, h1);
4836 children[2].move(0, h1, w1, h2);
4837 children[2].setFrame(x2, 0, w1, h2);
4838 children[3].move(w1, h1, w2, h2);
4839 children[3].setFrame(0, 0, w2, h2);
4840 }
4841};
4842
4843/**
4844 * @method _paintAllTiles
4845 * @param {Number} startX
4846 * @param {Number} startY
4847 * @private
4848 */
4849Tilemap.prototype._paintAllTiles = function(startX, startY) {
4850 var tileCols = Math.ceil(this._width / this._tileWidth) + 1;
4851 var tileRows = Math.ceil(this._height / this._tileHeight) + 1;
4852 for (var y = 0; y < tileRows; y++) {
4853 for (var x = 0; x < tileCols; x++) {
4854 this._paintTiles(startX, startY, x, y);
4855 }
4856 }
4857};
4858
4859/**
4860 * @method _paintTiles
4861 * @param {Number} startX
4862 * @param {Number} startY
4863 * @param {Number} x
4864 * @param {Number} y
4865 * @private
4866 */
4867Tilemap.prototype._paintTiles = function(startX, startY, x, y) {
4868 var tableEdgeVirtualId = 10000;
4869 var mx = startX + x;
4870 var my = startY + y;
4871 var dx = (mx * this._tileWidth).mod(this._layerWidth);
4872 var dy = (my * this._tileHeight).mod(this._layerHeight);
4873 var lx = dx / this._tileWidth;
4874 var ly = dy / this._tileHeight;
4875 var tileId0 = this._readMapData(mx, my, 0);
4876 var tileId1 = this._readMapData(mx, my, 1);
4877 var tileId2 = this._readMapData(mx, my, 2);
4878 var tileId3 = this._readMapData(mx, my, 3);
4879 var shadowBits = this._readMapData(mx, my, 4);
4880 var upperTileId1 = this._readMapData(mx, my - 1, 1);
4881 var lowerTiles = [];
4882 var upperTiles = [];
4883
4884 if (this._isHigherTile(tileId0)) {
4885 upperTiles.push(tileId0);
4886 } else {
4887 lowerTiles.push(tileId0);
4888 }
4889 if (this._isHigherTile(tileId1)) {
4890 upperTiles.push(tileId1);
4891 } else {
4892 lowerTiles.push(tileId1);
4893 }
4894
4895 lowerTiles.push(-shadowBits);
4896
4897 if (this._isTableTile(upperTileId1) && !this._isTableTile(tileId1)) {
4898 if (!Tilemap.isShadowingTile(tileId0)) {
4899 lowerTiles.push(tableEdgeVirtualId + upperTileId1);
4900 }
4901 }
4902
4903 if (this._isOverpassPosition(mx, my)) {
4904 upperTiles.push(tileId2);
4905 upperTiles.push(tileId3);
4906 } else {
4907 if (this._isHigherTile(tileId2)) {
4908 upperTiles.push(tileId2);
4909 } else {
4910 lowerTiles.push(tileId2);
4911 }
4912 if (this._isHigherTile(tileId3)) {
4913 upperTiles.push(tileId3);
4914 } else {
4915 lowerTiles.push(tileId3);
4916 }
4917 }
4918
4919 var lastLowerTiles = this._readLastTiles(0, lx, ly);
4920 if (!lowerTiles.equals(lastLowerTiles) ||
4921 (Tilemap.isTileA1(tileId0) && this._frameUpdated)) {
4922 this._lowerBitmap.clearRect(dx, dy, this._tileWidth, this._tileHeight);
4923 for (var i = 0; i < lowerTiles.length; i++) {
4924 var lowerTileId = lowerTiles[i];
4925 if (lowerTileId < 0) {
4926 this._drawShadow(this._lowerBitmap, shadowBits, dx, dy);
4927 } else if (lowerTileId >= tableEdgeVirtualId) {
4928 this._drawTableEdge(this._lowerBitmap, upperTileId1, dx, dy);
4929 } else {
4930 this._drawTile(this._lowerBitmap, lowerTileId, dx, dy);
4931 }
4932 }
4933 this._writeLastTiles(0, lx, ly, lowerTiles);
4934 }
4935
4936 var lastUpperTiles = this._readLastTiles(1, lx, ly);
4937 if (!upperTiles.equals(lastUpperTiles)) {
4938 this._upperBitmap.clearRect(dx, dy, this._tileWidth, this._tileHeight);
4939 for (var j = 0; j < upperTiles.length; j++) {
4940 this._drawTile(this._upperBitmap, upperTiles[j], dx, dy);
4941 }
4942 this._writeLastTiles(1, lx, ly, upperTiles);
4943 }
4944};
4945
4946/**
4947 * @method _readLastTiles
4948 * @param {Number} i
4949 * @param {Number} x
4950 * @param {Number} y
4951 * @private
4952 */
4953Tilemap.prototype._readLastTiles = function(i, x, y) {
4954 var array1 = this._lastTiles[i];
4955 if (array1) {
4956 var array2 = array1[y];
4957 if (array2) {
4958 var tiles = array2[x];
4959 if (tiles) {
4960 return tiles;
4961 }
4962 }
4963 }
4964 return [];
4965};
4966
4967/**
4968 * @method _writeLastTiles
4969 * @param {Number} i
4970 * @param {Number} x
4971 * @param {Number} y
4972 * @param {Array} tiles
4973 * @private
4974 */
4975Tilemap.prototype._writeLastTiles = function(i, x, y, tiles) {
4976 var array1 = this._lastTiles[i];
4977 if (!array1) {
4978 array1 = this._lastTiles[i] = [];
4979 }
4980 var array2 = array1[y];
4981 if (!array2) {
4982 array2 = array1[y] = [];
4983 }
4984 array2[x] = tiles;
4985};
4986
4987/**
4988 * @method _drawTile
4989 * @param {Bitmap} bitmap
4990 * @param {Number} tileId
4991 * @param {Number} dx
4992 * @param {Number} dy
4993 * @private
4994 */
4995Tilemap.prototype._drawTile = function(bitmap, tileId, dx, dy) {
4996 if (Tilemap.isVisibleTile(tileId)) {
4997 if (Tilemap.isAutotile(tileId)) {
4998 this._drawAutotile(bitmap, tileId, dx, dy);
4999 } else {
5000 this._drawNormalTile(bitmap, tileId, dx, dy);
5001 }
5002 }
5003};
5004
5005/**
5006 * @method _drawNormalTile
5007 * @param {Bitmap} bitmap
5008 * @param {Number} tileId
5009 * @param {Number} dx
5010 * @param {Number} dy
5011 * @private
5012 */
5013Tilemap.prototype._drawNormalTile = function(bitmap, tileId, dx, dy) {
5014 var setNumber = 0;
5015
5016 if (Tilemap.isTileA5(tileId)) {
5017 setNumber = 4;
5018 } else {
5019 setNumber = 5 + Math.floor(tileId / 256);
5020 }
5021
5022 var w = this._tileWidth;
5023 var h = this._tileHeight;
5024 var sx = (Math.floor(tileId / 128) % 2 * 8 + tileId % 8) * w;
5025 var sy = (Math.floor(tileId % 256 / 8) % 16) * h;
5026
5027 var source = this.bitmaps[setNumber];
5028 if (source) {
5029 bitmap.bltImage(source, sx, sy, w, h, dx, dy, w, h);
5030 }
5031};
5032
5033/**
5034 * @method _drawAutotile
5035 * @param {Bitmap} bitmap
5036 * @param {Number} tileId
5037 * @param {Number} dx
5038 * @param {Number} dy
5039 * @private
5040 */
5041Tilemap.prototype._drawAutotile = function(bitmap, tileId, dx, dy) {
5042 var autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
5043 var kind = Tilemap.getAutotileKind(tileId);
5044 var shape = Tilemap.getAutotileShape(tileId);
5045 var tx = kind % 8;
5046 var ty = Math.floor(kind / 8);
5047 var bx = 0;
5048 var by = 0;
5049 var setNumber = 0;
5050 var isTable = false;
5051
5052 if (Tilemap.isTileA1(tileId)) {
5053 var waterSurfaceIndex = [0, 1, 2, 1][this.animationFrame % 4];
5054 setNumber = 0;
5055 if (kind === 0) {
5056 bx = waterSurfaceIndex * 2;
5057 by = 0;
5058 } else if (kind === 1) {
5059 bx = waterSurfaceIndex * 2;
5060 by = 3;
5061 } else if (kind === 2) {
5062 bx = 6;
5063 by = 0;
5064 } else if (kind === 3) {
5065 bx = 6;
5066 by = 3;
5067 } else {
5068 bx = Math.floor(tx / 4) * 8;
5069 by = ty * 6 + Math.floor(tx / 2) % 2 * 3;
5070 if (kind % 2 === 0) {
5071 bx += waterSurfaceIndex * 2;
5072 }
5073 else {
5074 bx += 6;
5075 autotileTable = Tilemap.WATERFALL_AUTOTILE_TABLE;
5076 by += this.animationFrame % 3;
5077 }
5078 }
5079 } else if (Tilemap.isTileA2(tileId)) {
5080 setNumber = 1;
5081 bx = tx * 2;
5082 by = (ty - 2) * 3;
5083 isTable = this._isTableTile(tileId);
5084 } else if (Tilemap.isTileA3(tileId)) {
5085 setNumber = 2;
5086 bx = tx * 2;
5087 by = (ty - 6) * 2;
5088 autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
5089 } else if (Tilemap.isTileA4(tileId)) {
5090 setNumber = 3;
5091 bx = tx * 2;
5092 by = Math.floor((ty - 10) * 2.5 + (ty % 2 === 1 ? 0.5 : 0));
5093 if (ty % 2 === 1) {
5094 autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
5095 }
5096 }
5097
5098 var table = autotileTable[shape];
5099 var source = this.bitmaps[setNumber];
5100
5101 if (table && source) {
5102 var w1 = this._tileWidth / 2;
5103 var h1 = this._tileHeight / 2;
5104 for (var i = 0; i < 4; i++) {
5105 var qsx = table[i][0];
5106 var qsy = table[i][1];
5107 var sx1 = (bx * 2 + qsx) * w1;
5108 var sy1 = (by * 2 + qsy) * h1;
5109 var dx1 = dx + (i % 2) * w1;
5110 var dy1 = dy + Math.floor(i / 2) * h1;
5111 if (isTable && (qsy === 1 || qsy === 5)) {
5112 var qsx2 = qsx;
5113 var qsy2 = 3;
5114 if (qsy === 1) {
5115 qsx2 = [0,3,2,1][qsx];
5116 }
5117 var sx2 = (bx * 2 + qsx2) * w1;
5118 var sy2 = (by * 2 + qsy2) * h1;
5119 bitmap.bltImage(source, sx2, sy2, w1, h1, dx1, dy1, w1, h1);
5120 dy1 += h1/2;
5121 bitmap.bltImage(source, sx1, sy1, w1, h1/2, dx1, dy1, w1, h1/2);
5122 } else {
5123 bitmap.bltImage(source, sx1, sy1, w1, h1, dx1, dy1, w1, h1);
5124 }
5125 }
5126 }
5127};
5128
5129/**
5130 * @method _drawTableEdge
5131 * @param {Bitmap} bitmap
5132 * @param {Number} tileId
5133 * @param {Number} dx
5134 * @param {Number} dy
5135 * @private
5136 */
5137Tilemap.prototype._drawTableEdge = function(bitmap, tileId, dx, dy) {
5138 if (Tilemap.isTileA2(tileId)) {
5139 var autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
5140 var kind = Tilemap.getAutotileKind(tileId);
5141 var shape = Tilemap.getAutotileShape(tileId);
5142 var tx = kind % 8;
5143 var ty = Math.floor(kind / 8);
5144 var setNumber = 1;
5145 var bx = tx * 2;
5146 var by = (ty - 2) * 3;
5147 var table = autotileTable[shape];
5148
5149 if (table) {
5150 var source = this.bitmaps[setNumber];
5151 var w1 = this._tileWidth / 2;
5152 var h1 = this._tileHeight / 2;
5153 for (var i = 0; i < 2; i++) {
5154 var qsx = table[2 + i][0];
5155 var qsy = table[2 + i][1];
5156 var sx1 = (bx * 2 + qsx) * w1;
5157 var sy1 = (by * 2 + qsy) * h1 + h1/2;
5158 var dx1 = dx + (i % 2) * w1;
5159 var dy1 = dy + Math.floor(i / 2) * h1;
5160 bitmap.bltImage(source, sx1, sy1, w1, h1/2, dx1, dy1, w1, h1/2);
5161 }
5162 }
5163 }
5164};
5165
5166/**
5167 * @method _drawShadow
5168 * @param {Bitmap} bitmap
5169 * @param {Number} shadowBits
5170 * @param {Number} dx
5171 * @param {Number} dy
5172 * @private
5173 */
5174Tilemap.prototype._drawShadow = function(bitmap, shadowBits, dx, dy) {
5175 if (shadowBits & 0x0f) {
5176 var w1 = this._tileWidth / 2;
5177 var h1 = this._tileHeight / 2;
5178 var color = 'rgba(0,0,0,0.5)';
5179 for (var i = 0; i < 4; i++) {
5180 if (shadowBits & (1 << i)) {
5181 var dx1 = dx + (i % 2) * w1;
5182 var dy1 = dy + Math.floor(i / 2) * h1;
5183 bitmap.fillRect(dx1, dy1, w1, h1, color);
5184 }
5185 }
5186 }
5187};
5188
5189/**
5190 * @method _readMapData
5191 * @param {Number} x
5192 * @param {Number} y
5193 * @param {Number} z
5194 * @return {Number}
5195 * @private
5196 */
5197Tilemap.prototype._readMapData = function(x, y, z) {
5198 if (this._mapData) {
5199 var width = this._mapWidth;
5200 var height = this._mapHeight;
5201 if (this.horizontalWrap) {
5202 x = x.mod(width);
5203 }
5204 if (this.verticalWrap) {
5205 y = y.mod(height);
5206 }
5207 if (x >= 0 && x < width && y >= 0 && y < height) {
5208 return this._mapData[(z * height + y) * width + x] || 0;
5209 } else {
5210 return 0;
5211 }
5212 } else {
5213 return 0;
5214 }
5215};
5216
5217/**
5218 * @method _isHigherTile
5219 * @param {Number} tileId
5220 * @return {Boolean}
5221 * @private
5222 */
5223Tilemap.prototype._isHigherTile = function(tileId) {
5224 return this.flags[tileId] & 0x10;
5225};
5226
5227/**
5228 * @method _isTableTile
5229 * @param {Number} tileId
5230 * @return {Boolean}
5231 * @private
5232 */
5233Tilemap.prototype._isTableTile = function(tileId) {
5234 return Tilemap.isTileA2(tileId) && (this.flags[tileId] & 0x80);
5235};
5236
5237/**
5238 * @method _isOverpassPosition
5239 * @param {Number} mx
5240 * @param {Number} my
5241 * @return {Boolean}
5242 * @private
5243 */
5244Tilemap.prototype._isOverpassPosition = function(mx, my) {
5245 return false;
5246};
5247
5248/**
5249 * @method _sortChildren
5250 * @private
5251 */
5252Tilemap.prototype._sortChildren = function() {
5253 this.children.sort(this._compareChildOrder.bind(this));
5254};
5255
5256/**
5257 * @method _compareChildOrder
5258 * @param {Object} a
5259 * @param {Object} b
5260 * @private
5261 */
5262Tilemap.prototype._compareChildOrder = function(a, b) {
5263 if (a.z !== b.z) {
5264 return a.z - b.z;
5265 } else if (a.y !== b.y) {
5266 return a.y - b.y;
5267 } else {
5268 return a.spriteId - b.spriteId;
5269 }
5270};
5271
5272// Tile type checkers
5273
5274Tilemap.TILE_ID_B = 0;
5275Tilemap.TILE_ID_C = 256;
5276Tilemap.TILE_ID_D = 512;
5277Tilemap.TILE_ID_E = 768;
5278Tilemap.TILE_ID_A5 = 1536;
5279Tilemap.TILE_ID_A1 = 2048;
5280Tilemap.TILE_ID_A2 = 2816;
5281Tilemap.TILE_ID_A3 = 4352;
5282Tilemap.TILE_ID_A4 = 5888;
5283Tilemap.TILE_ID_MAX = 8192;
5284
5285Tilemap.isVisibleTile = function(tileId) {
5286 return tileId > 0 && tileId < this.TILE_ID_MAX;
5287};
5288
5289Tilemap.isAutotile = function(tileId) {
5290 return tileId >= this.TILE_ID_A1;
5291};
5292
5293Tilemap.getAutotileKind = function(tileId) {
5294 return Math.floor((tileId - this.TILE_ID_A1) / 48);
5295};
5296
5297Tilemap.getAutotileShape = function(tileId) {
5298 return (tileId - this.TILE_ID_A1) % 48;
5299};
5300
5301Tilemap.makeAutotileId = function(kind, shape) {
5302 return this.TILE_ID_A1 + kind * 48 + shape;
5303};
5304
5305Tilemap.isSameKindTile = function(tileID1, tileID2) {
5306 if (this.isAutotile(tileID1) && this.isAutotile(tileID2)) {
5307 return this.getAutotileKind(tileID1) === this.getAutotileKind(tileID2);
5308 } else {
5309 return tileID1 === tileID2;
5310 }
5311};
5312
5313Tilemap.isTileA1 = function(tileId) {
5314 return tileId >= this.TILE_ID_A1 && tileId < this.TILE_ID_A2;
5315};
5316
5317Tilemap.isTileA2 = function(tileId) {
5318 return tileId >= this.TILE_ID_A2 && tileId < this.TILE_ID_A3;
5319};
5320
5321Tilemap.isTileA3 = function(tileId) {
5322 return tileId >= this.TILE_ID_A3 && tileId < this.TILE_ID_A4;
5323};
5324
5325Tilemap.isTileA4 = function(tileId) {
5326 return tileId >= this.TILE_ID_A4 && tileId < this.TILE_ID_MAX;
5327};
5328
5329Tilemap.isTileA5 = function(tileId) {
5330 return tileId >= this.TILE_ID_A5 && tileId < this.TILE_ID_A1;
5331};
5332
5333Tilemap.isWaterTile = function(tileId) {
5334 if (this.isTileA1(tileId)) {
5335 return !(tileId >= this.TILE_ID_A1 + 96 && tileId < this.TILE_ID_A1 + 192);
5336 } else {
5337 return false;
5338 }
5339};
5340
5341Tilemap.isWaterfallTile = function(tileId) {
5342 if (tileId >= this.TILE_ID_A1 + 192 && tileId < this.TILE_ID_A2) {
5343 return this.getAutotileKind(tileId) % 2 === 1;
5344 } else {
5345 return false;
5346 }
5347};
5348
5349Tilemap.isGroundTile = function(tileId) {
5350 return this.isTileA1(tileId) || this.isTileA2(tileId) || this.isTileA5(tileId);
5351};
5352
5353Tilemap.isShadowingTile = function(tileId) {
5354 return this.isTileA3(tileId) || this.isTileA4(tileId);
5355};
5356
5357Tilemap.isRoofTile = function(tileId) {
5358 return this.isTileA3(tileId) && this.getAutotileKind(tileId) % 16 < 8;
5359};
5360
5361Tilemap.isWallTopTile = function(tileId) {
5362 return this.isTileA4(tileId) && this.getAutotileKind(tileId) % 16 < 8;
5363};
5364
5365Tilemap.isWallSideTile = function(tileId) {
5366 return (this.isTileA3(tileId) || this.isTileA4(tileId)) &&
5367 this.getAutotileKind(tileId) % 16 >= 8;
5368};
5369
5370Tilemap.isWallTile = function(tileId) {
5371 return this.isWallTopTile(tileId) || this.isWallSideTile(tileId);
5372};
5373
5374Tilemap.isFloorTypeAutotile = function(tileId) {
5375 return (this.isTileA1(tileId) && !this.isWaterfallTile(tileId)) ||
5376 this.isTileA2(tileId) || this.isWallTopTile(tileId);
5377};
5378
5379Tilemap.isWallTypeAutotile = function(tileId) {
5380 return this.isRoofTile(tileId) || this.isWallSideTile(tileId);
5381};
5382
5383Tilemap.isWaterfallTypeAutotile = function(tileId) {
5384 return this.isWaterfallTile(tileId);
5385};
5386
5387// Autotile shape number to coordinates of tileset images
5388
5389Tilemap.FLOOR_AUTOTILE_TABLE = [
5390 [[2,4],[1,4],[2,3],[1,3]],[[2,0],[1,4],[2,3],[1,3]],
5391 [[2,4],[3,0],[2,3],[1,3]],[[2,0],[3,0],[2,3],[1,3]],
5392 [[2,4],[1,4],[2,3],[3,1]],[[2,0],[1,4],[2,3],[3,1]],
5393 [[2,4],[3,0],[2,3],[3,1]],[[2,0],[3,0],[2,3],[3,1]],
5394 [[2,4],[1,4],[2,1],[1,3]],[[2,0],[1,4],[2,1],[1,3]],
5395 [[2,4],[3,0],[2,1],[1,3]],[[2,0],[3,0],[2,1],[1,3]],
5396 [[2,4],[1,4],[2,1],[3,1]],[[2,0],[1,4],[2,1],[3,1]],
5397 [[2,4],[3,0],[2,1],[3,1]],[[2,0],[3,0],[2,1],[3,1]],
5398 [[0,4],[1,4],[0,3],[1,3]],[[0,4],[3,0],[0,3],[1,3]],
5399 [[0,4],[1,4],[0,3],[3,1]],[[0,4],[3,0],[0,3],[3,1]],
5400 [[2,2],[1,2],[2,3],[1,3]],[[2,2],[1,2],[2,3],[3,1]],
5401 [[2,2],[1,2],[2,1],[1,3]],[[2,2],[1,2],[2,1],[3,1]],
5402 [[2,4],[3,4],[2,3],[3,3]],[[2,4],[3,4],[2,1],[3,3]],
5403 [[2,0],[3,4],[2,3],[3,3]],[[2,0],[3,4],[2,1],[3,3]],
5404 [[2,4],[1,4],[2,5],[1,5]],[[2,0],[1,4],[2,5],[1,5]],
5405 [[2,4],[3,0],[2,5],[1,5]],[[2,0],[3,0],[2,5],[1,5]],
5406 [[0,4],[3,4],[0,3],[3,3]],[[2,2],[1,2],[2,5],[1,5]],
5407 [[0,2],[1,2],[0,3],[1,3]],[[0,2],[1,2],[0,3],[3,1]],
5408 [[2,2],[3,2],[2,3],[3,3]],[[2,2],[3,2],[2,1],[3,3]],
5409 [[2,4],[3,4],[2,5],[3,5]],[[2,0],[3,4],[2,5],[3,5]],
5410 [[0,4],[1,4],[0,5],[1,5]],[[0,4],[3,0],[0,5],[1,5]],
5411 [[0,2],[3,2],[0,3],[3,3]],[[0,2],[1,2],[0,5],[1,5]],
5412 [[0,4],[3,4],[0,5],[3,5]],[[2,2],[3,2],[2,5],[3,5]],
5413 [[0,2],[3,2],[0,5],[3,5]],[[0,0],[1,0],[0,1],[1,1]]
5414];
5415
5416Tilemap.WALL_AUTOTILE_TABLE = [
5417 [[2,2],[1,2],[2,1],[1,1]],[[0,2],[1,2],[0,1],[1,1]],
5418 [[2,0],[1,0],[2,1],[1,1]],[[0,0],[1,0],[0,1],[1,1]],
5419 [[2,2],[3,2],[2,1],[3,1]],[[0,2],[3,2],[0,1],[3,1]],
5420 [[2,0],[3,0],[2,1],[3,1]],[[0,0],[3,0],[0,1],[3,1]],
5421 [[2,2],[1,2],[2,3],[1,3]],[[0,2],[1,2],[0,3],[1,3]],
5422 [[2,0],[1,0],[2,3],[1,3]],[[0,0],[1,0],[0,3],[1,3]],
5423 [[2,2],[3,2],[2,3],[3,3]],[[0,2],[3,2],[0,3],[3,3]],
5424 [[2,0],[3,0],[2,3],[3,3]],[[0,0],[3,0],[0,3],[3,3]]
5425];
5426
5427Tilemap.WATERFALL_AUTOTILE_TABLE = [
5428 [[2,0],[1,0],[2,1],[1,1]],[[0,0],[1,0],[0,1],[1,1]],
5429 [[2,0],[3,0],[2,1],[3,1]],[[0,0],[3,0],[0,1],[3,1]]
5430];
5431
5432// The important members from Pixi.js
5433
5434/**
5435 * [read-only] The array of children of the tilemap.
5436 *
5437 * @property children
5438 * @type Array
5439 */
5440
5441/**
5442 * [read-only] The object that contains the tilemap.
5443 *
5444 * @property parent
5445 * @type Object
5446 */
5447
5448/**
5449 * Adds a child to the container.
5450 *
5451 * @method addChild
5452 * @param {Object} child The child to add
5453 * @return {Object} The child that was added
5454 */
5455
5456/**
5457 * Adds a child to the container at a specified index.
5458 *
5459 * @method addChildAt
5460 * @param {Object} child The child to add
5461 * @param {Number} index The index to place the child in
5462 * @return {Object} The child that was added
5463 */
5464
5465/**
5466 * Removes a child from the container.
5467 *
5468 * @method removeChild
5469 * @param {Object} child The child to remove
5470 * @return {Object} The child that was removed
5471 */
5472
5473/**
5474 * Removes a child from the specified index position.
5475 *
5476 * @method removeChildAt
5477 * @param {Number} index The index to get the child from
5478 * @return {Object} The child that was removed
5479 */
5480
5481//-----------------------------------------------------------------------------
5482/**
5483 * The tilemap which displays 2D tile-based game map using shaders
5484 *
5485 * @class Tilemap
5486 * @constructor
5487 */
5488function ShaderTilemap() {
5489 Tilemap.apply(this, arguments);
5490 this.roundPixels = true;
5491}
5492
5493ShaderTilemap.prototype = Object.create(Tilemap.prototype);
5494ShaderTilemap.prototype.constructor = ShaderTilemap;
5495
5496// we need this constant for some platforms (Samsung S4, S5, Tab4, HTC One H8)
5497PIXI.glCore.VertexArrayObject.FORCE_NATIVE = true;
5498PIXI.settings.GC_MODE = PIXI.GC_MODES.AUTO;
5499PIXI.tilemap.TileRenderer.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
5500PIXI.tilemap.TileRenderer.DO_CLEAR = true;
5501
5502/**
5503 * Uploads animation state in renderer
5504 *
5505 * @method _hackRenderer
5506 * @private
5507 */
5508ShaderTilemap.prototype._hackRenderer = function(renderer) {
5509 var af = this.animationFrame % 4;
5510 if (af==3) af = 1;
5511 renderer.plugins.tilemap.tileAnim[0] = af * this._tileWidth;
5512 renderer.plugins.tilemap.tileAnim[1] = (this.animationFrame % 3) * this._tileHeight;
5513 return renderer;
5514};
5515
5516/**
5517 * PIXI render method
5518 *
5519 * @method renderCanvas
5520 * @param {Object} pixi renderer
5521 */
5522ShaderTilemap.prototype.renderCanvas = function(renderer) {
5523 this._hackRenderer(renderer);
5524 PIXI.Container.prototype.renderCanvas.call(this, renderer);
5525};
5526
5527
5528/**
5529 * PIXI render method
5530 *
5531 * @method renderWebGL
5532 * @param {Object} pixi renderer
5533 */
5534ShaderTilemap.prototype.renderWebGL = function(renderer) {
5535 this._hackRenderer(renderer);
5536 PIXI.Container.prototype.renderWebGL.call(this, renderer);
5537};
5538
5539/**
5540 * Forces to repaint the entire tilemap AND update bitmaps list if needed
5541 *
5542 * @method refresh
5543 */
5544ShaderTilemap.prototype.refresh = function() {
5545 if (this._lastBitmapLength !== this.bitmaps.length) {
5546 this._lastBitmapLength = this.bitmaps.length;
5547 this.refreshTileset();
5548 };
5549 this._needsRepaint = true;
5550};
5551
5552/**
5553 * Call after you update tileset
5554 *
5555 * @method updateBitmaps
5556 */
5557ShaderTilemap.prototype.refreshTileset = function() {
5558 var bitmaps = this.bitmaps.map(function(x) { return x._baseTexture ? new PIXI.Texture(x._baseTexture) : x; } );
5559 this.lowerLayer.setBitmaps(bitmaps);
5560 this.upperLayer.setBitmaps(bitmaps);
5561};
5562
5563/**
5564 * @method updateTransform
5565 * @private
5566 */
5567ShaderTilemap.prototype.updateTransform = function() {
5568 if (this.roundPixels) {
5569 var ox = Math.floor(this.origin.x);
5570 var oy = Math.floor(this.origin.y);
5571 } else {
5572 ox = this.origin.x;
5573 oy = this.origin.y;
5574 }
5575 var startX = Math.floor((ox - this._margin) / this._tileWidth);
5576 var startY = Math.floor((oy - this._margin) / this._tileHeight);
5577 this._updateLayerPositions(startX, startY);
5578 if (this._needsRepaint ||
5579 this._lastStartX !== startX || this._lastStartY !== startY) {
5580 this._lastStartX = startX;
5581 this._lastStartY = startY;
5582 this._paintAllTiles(startX, startY);
5583 this._needsRepaint = false;
5584 }
5585 this._sortChildren();
5586 PIXI.Container.prototype.updateTransform.call(this);
5587};
5588
5589/**
5590 * @method _createLayers
5591 * @private
5592 */
5593ShaderTilemap.prototype._createLayers = function() {
5594 var width = this._width;
5595 var height = this._height;
5596 var margin = this._margin;
5597 var tileCols = Math.ceil(width / this._tileWidth) + 1;
5598 var tileRows = Math.ceil(height / this._tileHeight) + 1;
5599 var layerWidth = this._layerWidth = tileCols * this._tileWidth;
5600 var layerHeight = this._layerHeight = tileRows * this._tileHeight;
5601 this._needsRepaint = true;
5602
5603 if (!this.lowerZLayer) {
5604 //@hackerham: create layers only in initialization. Doesn't depend on width/height
5605 this.addChild(this.lowerZLayer = new PIXI.tilemap.ZLayer(this, 0));
5606 this.addChild(this.upperZLayer = new PIXI.tilemap.ZLayer(this, 4));
5607
5608 var parameters = PluginManager.parameters('ShaderTilemap');
5609 var useSquareShader = Number(parameters.hasOwnProperty('squareShader') ? parameters['squareShader'] : 0);
5610
5611 this.lowerZLayer.addChild(this.lowerLayer = new PIXI.tilemap.CompositeRectTileLayer(0, [], useSquareShader));
5612 this.lowerLayer.shadowColor = new Float32Array([0.0, 0.0, 0.0, 0.5]);
5613 this.upperZLayer.addChild(this.upperLayer = new PIXI.tilemap.CompositeRectTileLayer(4, [], useSquareShader));
5614 }
5615};
5616
5617/**
5618 * @method _updateLayerPositions
5619 * @param {Number} startX
5620 * @param {Number} startY
5621 * @private
5622 */
5623ShaderTilemap.prototype._updateLayerPositions = function(startX, startY) {
5624 if (this.roundPixels) {
5625 var ox = Math.floor(this.origin.x);
5626 var oy = Math.floor(this.origin.y);
5627 } else {
5628 ox = this.origin.x;
5629 oy = this.origin.y;
5630 }
5631 this.lowerZLayer.position.x = startX * this._tileWidth - ox;
5632 this.lowerZLayer.position.y = startY * this._tileHeight - oy;
5633 this.upperZLayer.position.x = startX * this._tileWidth - ox;
5634 this.upperZLayer.position.y = startY * this._tileHeight - oy;
5635};
5636
5637/**
5638 * @method _paintAllTiles
5639 * @param {Number} startX
5640 * @param {Number} startY
5641 * @private
5642 */
5643ShaderTilemap.prototype._paintAllTiles = function(startX, startY) {
5644 this.lowerZLayer.clear();
5645 this.upperZLayer.clear();
5646 var tileCols = Math.ceil(this._width / this._tileWidth) + 1;
5647 var tileRows = Math.ceil(this._height / this._tileHeight) + 1;
5648 for (var y = 0; y < tileRows; y++) {
5649 for (var x = 0; x < tileCols; x++) {
5650 this._paintTiles(startX, startY, x, y);
5651 }
5652 }
5653};
5654
5655/**
5656 * @method _paintTiles
5657 * @param {Number} startX
5658 * @param {Number} startY
5659 * @param {Number} x
5660 * @param {Number} y
5661 * @private
5662 */
5663ShaderTilemap.prototype._paintTiles = function(startX, startY, x, y) {
5664 var mx = startX + x;
5665 var my = startY + y;
5666 var dx = x * this._tileWidth, dy = y * this._tileHeight;
5667 var tileId0 = this._readMapData(mx, my, 0);
5668 var tileId1 = this._readMapData(mx, my, 1);
5669 var tileId2 = this._readMapData(mx, my, 2);
5670 var tileId3 = this._readMapData(mx, my, 3);
5671 var shadowBits = this._readMapData(mx, my, 4);
5672 var upperTileId1 = this._readMapData(mx, my - 1, 1);
5673 var lowerLayer = this.lowerLayer.children[0];
5674 var upperLayer = this.upperLayer.children[0];
5675
5676 if (this._isHigherTile(tileId0)) {
5677 this._drawTile(upperLayer, tileId0, dx, dy);
5678 } else {
5679 this._drawTile(lowerLayer, tileId0, dx, dy);
5680 }
5681 if (this._isHigherTile(tileId1)) {
5682 this._drawTile(upperLayer, tileId1, dx, dy);
5683 } else {
5684 this._drawTile(lowerLayer, tileId1, dx, dy);
5685 }
5686
5687 this._drawShadow(lowerLayer, shadowBits, dx, dy);
5688 if (this._isTableTile(upperTileId1) && !this._isTableTile(tileId1)) {
5689 if (!Tilemap.isShadowingTile(tileId0)) {
5690 this._drawTableEdge(lowerLayer, upperTileId1, dx, dy);
5691 }
5692 }
5693
5694 if (this._isOverpassPosition(mx, my)) {
5695 this._drawTile(upperLayer, tileId2, dx, dy);
5696 this._drawTile(upperLayer, tileId3, dx, dy);
5697 } else {
5698 if (this._isHigherTile(tileId2)) {
5699 this._drawTile(upperLayer, tileId2, dx, dy);
5700 } else {
5701 this._drawTile(lowerLayer, tileId2, dx, dy);
5702 }
5703 if (this._isHigherTile(tileId3)) {
5704 this._drawTile(upperLayer, tileId3, dx, dy);
5705 } else {
5706 this._drawTile(lowerLayer, tileId3, dx, dy);
5707 }
5708 }
5709};
5710
5711/**
5712 * @method _drawTile
5713 * @param {Array} layers
5714 * @param {Number} tileId
5715 * @param {Number} dx
5716 * @param {Number} dy
5717 * @private
5718 */
5719ShaderTilemap.prototype._drawTile = function(layer, tileId, dx, dy) {
5720 if (Tilemap.isVisibleTile(tileId)) {
5721 if (Tilemap.isAutotile(tileId)) {
5722 this._drawAutotile(layer, tileId, dx, dy);
5723 } else {
5724 this._drawNormalTile(layer, tileId, dx, dy);
5725 }
5726 }
5727};
5728
5729/**
5730 * @method _drawNormalTile
5731 * @param {Array} layers
5732 * @param {Number} tileId
5733 * @param {Number} dx
5734 * @param {Number} dy
5735 * @private
5736 */
5737ShaderTilemap.prototype._drawNormalTile = function(layer, tileId, dx, dy) {
5738 var setNumber = 0;
5739
5740 if (Tilemap.isTileA5(tileId)) {
5741 setNumber = 4;
5742 } else {
5743 setNumber = 5 + Math.floor(tileId / 256);
5744 }
5745
5746 var w = this._tileWidth;
5747 var h = this._tileHeight;
5748 var sx = (Math.floor(tileId / 128) % 2 * 8 + tileId % 8) * w;
5749 var sy = (Math.floor(tileId % 256 / 8) % 16) * h;
5750
5751 layer.addRect(setNumber, sx, sy, dx, dy, w, h);
5752};
5753
5754/**
5755 * @method _drawAutotile
5756 * @param {Array} layers
5757 * @param {Number} tileId
5758 * @param {Number} dx
5759 * @param {Number} dy
5760 * @private
5761 */
5762ShaderTilemap.prototype._drawAutotile = function(layer, tileId, dx, dy) {
5763 var autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
5764 var kind = Tilemap.getAutotileKind(tileId);
5765 var shape = Tilemap.getAutotileShape(tileId);
5766 var tx = kind % 8;
5767 var ty = Math.floor(kind / 8);
5768 var bx = 0;
5769 var by = 0;
5770 var setNumber = 0;
5771 var isTable = false;
5772 var animX = 0, animY = 0;
5773
5774 if (Tilemap.isTileA1(tileId)) {
5775 setNumber = 0;
5776 if (kind === 0) {
5777 animX = 2;
5778 by = 0;
5779 } else if (kind === 1) {
5780 animX = 2;
5781 by = 3;
5782 } else if (kind === 2) {
5783 bx = 6;
5784 by = 0;
5785 } else if (kind === 3) {
5786 bx = 6;
5787 by = 3;
5788 } else {
5789 bx = Math.floor(tx / 4) * 8;
5790 by = ty * 6 + Math.floor(tx / 2) % 2 * 3;
5791 if (kind % 2 === 0) {
5792 animX = 2;
5793 }
5794 else {
5795 bx += 6;
5796 autotileTable = Tilemap.WATERFALL_AUTOTILE_TABLE;
5797 animY = 1;
5798 }
5799 }
5800 } else if (Tilemap.isTileA2(tileId)) {
5801 setNumber = 1;
5802 bx = tx * 2;
5803 by = (ty - 2) * 3;
5804 isTable = this._isTableTile(tileId);
5805 } else if (Tilemap.isTileA3(tileId)) {
5806 setNumber = 2;
5807 bx = tx * 2;
5808 by = (ty - 6) * 2;
5809 autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
5810 } else if (Tilemap.isTileA4(tileId)) {
5811 setNumber = 3;
5812 bx = tx * 2;
5813 by = Math.floor((ty - 10) * 2.5 + (ty % 2 === 1 ? 0.5 : 0));
5814 if (ty % 2 === 1) {
5815 autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
5816 }
5817 }
5818
5819 var table = autotileTable[shape];
5820 var w1 = this._tileWidth / 2;
5821 var h1 = this._tileHeight / 2;
5822 for (var i = 0; i < 4; i++) {
5823 var qsx = table[i][0];
5824 var qsy = table[i][1];
5825 var sx1 = (bx * 2 + qsx) * w1;
5826 var sy1 = (by * 2 + qsy) * h1;
5827 var dx1 = dx + (i % 2) * w1;
5828 var dy1 = dy + Math.floor(i / 2) * h1;
5829 if (isTable && (qsy === 1 || qsy === 5)) {
5830 var qsx2 = qsx;
5831 var qsy2 = 3;
5832 if (qsy === 1) {
5833 //qsx2 = [0, 3, 2, 1][qsx];
5834 qsx2 = (4-qsx)%4;
5835 }
5836 var sx2 = (bx * 2 + qsx2) * w1;
5837 var sy2 = (by * 2 + qsy2) * h1;
5838 layer.addRect(setNumber, sx2, sy2, dx1, dy1, w1, h1, animX, animY);
5839 layer.addRect(setNumber, sx1, sy1, dx1, dy1+h1/2, w1, h1/2, animX, animY);
5840 } else {
5841 layer.addRect(setNumber, sx1, sy1, dx1, dy1, w1, h1, animX, animY);
5842 }
5843 }
5844};
5845
5846/**
5847 * @method _drawTableEdge
5848 * @param {Array} layers
5849 * @param {Number} tileId
5850 * @param {Number} dx
5851 * @param {Number} dy
5852 * @private
5853 */
5854ShaderTilemap.prototype._drawTableEdge = function(layer, tileId, dx, dy) {
5855 if (Tilemap.isTileA2(tileId)) {
5856 var autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
5857 var kind = Tilemap.getAutotileKind(tileId);
5858 var shape = Tilemap.getAutotileShape(tileId);
5859 var tx = kind % 8;
5860 var ty = Math.floor(kind / 8);
5861 var setNumber = 1;
5862 var bx = tx * 2;
5863 var by = (ty - 2) * 3;
5864 var table = autotileTable[shape];
5865 var w1 = this._tileWidth / 2;
5866 var h1 = this._tileHeight / 2;
5867 for (var i = 0; i < 2; i++) {
5868 var qsx = table[2 + i][0];
5869 var qsy = table[2 + i][1];
5870 var sx1 = (bx * 2 + qsx) * w1;
5871 var sy1 = (by * 2 + qsy) * h1 + h1 / 2;
5872 var dx1 = dx + (i % 2) * w1;
5873 var dy1 = dy + Math.floor(i / 2) * h1;
5874 layer.addRect(setNumber, sx1, sy1, dx1, dy1, w1, h1/2);
5875 }
5876 }
5877};
5878
5879/**
5880 * @method _drawShadow
5881 * @param {Number} shadowBits
5882 * @param {Number} dx
5883 * @param {Number} dy
5884 * @private
5885 */
5886ShaderTilemap.prototype._drawShadow = function(layer, shadowBits, dx, dy) {
5887 if (shadowBits & 0x0f) {
5888 var w1 = this._tileWidth / 2;
5889 var h1 = this._tileHeight / 2;
5890 for (var i = 0; i < 4; i++) {
5891 if (shadowBits & (1 << i)) {
5892 var dx1 = dx + (i % 2) * w1;
5893 var dy1 = dy + Math.floor(i / 2) * h1;
5894 layer.addRect(-1, 0, 0, dx1, dy1, w1, h1);
5895 }
5896 }
5897 }
5898};
5899//-----------------------------------------------------------------------------
5900/**
5901 * The sprite object for a tiling image.
5902 *
5903 * @class TilingSprite
5904 * @constructor
5905 * @param {Bitmap} bitmap The image for the tiling sprite
5906 */
5907function TilingSprite() {
5908 this.initialize.apply(this, arguments);
5909}
5910
5911TilingSprite.prototype = Object.create(PIXI.extras.PictureTilingSprite.prototype);
5912TilingSprite.prototype.constructor = TilingSprite;
5913
5914TilingSprite.prototype.initialize = function(bitmap) {
5915 var texture = new PIXI.Texture(new PIXI.BaseTexture());
5916
5917 PIXI.extras.PictureTilingSprite.call(this, texture);
5918
5919 this._bitmap = null;
5920 this._width = 0;
5921 this._height = 0;
5922 this._frame = new Rectangle();
5923 this.spriteId = Sprite._counter++;
5924 /**
5925 * The origin point of the tiling sprite for scrolling.
5926 *
5927 * @property origin
5928 * @type Point
5929 */
5930 this.origin = new Point();
5931
5932 this.bitmap = bitmap;
5933};
5934
5935TilingSprite.prototype._renderCanvas_PIXI = PIXI.extras.PictureTilingSprite.prototype._renderCanvas;
5936TilingSprite.prototype._renderWebGL_PIXI = PIXI.extras.PictureTilingSprite.prototype._renderWebGL;
5937
5938/**
5939 * @method _renderCanvas
5940 * @param {Object} renderer
5941 * @private
5942 */
5943TilingSprite.prototype._renderCanvas = function(renderer) {
5944 if (this._bitmap) {
5945 this._bitmap.touch();
5946 }
5947 if (this.texture.frame.width > 0 && this.texture.frame.height > 0) {
5948 this._renderCanvas_PIXI(renderer);
5949 }
5950};
5951
5952/**
5953 * @method _renderWebGL
5954 * @param {Object} renderer
5955 * @private
5956 */
5957TilingSprite.prototype._renderWebGL = function(renderer) {
5958 if (this._bitmap) {
5959 this._bitmap.touch();
5960 }
5961 if (this.texture.frame.width > 0 && this.texture.frame.height > 0) {
5962 if (this._bitmap) {
5963 this._bitmap.checkDirty();
5964 }
5965 this._renderWebGL_PIXI(renderer);
5966 }
5967};
5968
5969/**
5970 * The image for the tiling sprite.
5971 *
5972 * @property bitmap
5973 * @type Bitmap
5974 */
5975Object.defineProperty(TilingSprite.prototype, 'bitmap', {
5976 get: function() {
5977 return this._bitmap;
5978 },
5979 set: function(value) {
5980 if (this._bitmap !== value) {
5981 this._bitmap = value;
5982 if (this._bitmap) {
5983 this._bitmap.addLoadListener(this._onBitmapLoad.bind(this));
5984 } else {
5985 this.texture.frame = Rectangle.emptyRectangle;
5986 }
5987 }
5988 },
5989 configurable: true
5990});
5991
5992/**
5993 * The opacity of the tiling sprite (0 to 255).
5994 *
5995 * @property opacity
5996 * @type Number
5997 */
5998Object.defineProperty(TilingSprite.prototype, 'opacity', {
5999 get: function() {
6000 return this.alpha * 255;
6001 },
6002 set: function(value) {
6003 this.alpha = value.clamp(0, 255) / 255;
6004 },
6005 configurable: true
6006});
6007
6008/**
6009 * Updates the tiling sprite for each frame.
6010 *
6011 * @method update
6012 */
6013TilingSprite.prototype.update = function() {
6014 this.children.forEach(function(child) {
6015 if (child.update) {
6016 child.update();
6017 }
6018 });
6019};
6020
6021/**
6022 * Sets the x, y, width, and height all at once.
6023 *
6024 * @method move
6025 * @param {Number} x The x coordinate of the tiling sprite
6026 * @param {Number} y The y coordinate of the tiling sprite
6027 * @param {Number} width The width of the tiling sprite
6028 * @param {Number} height The height of the tiling sprite
6029 */
6030TilingSprite.prototype.move = function(x, y, width, height) {
6031 this.x = x || 0;
6032 this.y = y || 0;
6033 this._width = width || 0;
6034 this._height = height || 0;
6035};
6036
6037/**
6038 * Specifies the region of the image that the tiling sprite will use.
6039 *
6040 * @method setFrame
6041 * @param {Number} x The x coordinate of the frame
6042 * @param {Number} y The y coordinate of the frame
6043 * @param {Number} width The width of the frame
6044 * @param {Number} height The height of the frame
6045 */
6046TilingSprite.prototype.setFrame = function(x, y, width, height) {
6047 this._frame.x = x;
6048 this._frame.y = y;
6049 this._frame.width = width;
6050 this._frame.height = height;
6051 this._refresh();
6052};
6053
6054/**
6055 * @method updateTransform
6056 * @private
6057 */
6058TilingSprite.prototype.updateTransform = function() {
6059 this.tilePosition.x = Math.round(-this.origin.x);
6060 this.tilePosition.y = Math.round(-this.origin.y);
6061 this.updateTransformTS();
6062};
6063
6064TilingSprite.prototype.updateTransformTS = PIXI.extras.TilingSprite.prototype.updateTransform;
6065
6066/**
6067 * @method _onBitmapLoad
6068 * @private
6069 */
6070TilingSprite.prototype._onBitmapLoad = function() {
6071 this.texture.baseTexture = this._bitmap.baseTexture;
6072 this._refresh();
6073};
6074
6075/**
6076 * @method _refresh
6077 * @private
6078 */
6079TilingSprite.prototype._refresh = function() {
6080 var frame = this._frame.clone();
6081 if (frame.width === 0 && frame.height === 0 && this._bitmap) {
6082 frame.width = this._bitmap.width;
6083 frame.height = this._bitmap.height;
6084 }
6085 this.texture.frame = frame;
6086 this.texture._updateID++;
6087 this.tilingTexture = null;
6088};
6089
6090
6091TilingSprite.prototype._speedUpCustomBlendModes = Sprite.prototype._speedUpCustomBlendModes;
6092
6093/**
6094 * @method _renderWebGL
6095 * @param {Object} renderer
6096 * @private
6097 */
6098TilingSprite.prototype._renderWebGL = function(renderer) {
6099 if (this._bitmap) {
6100 this._bitmap.touch();
6101 this._bitmap.checkDirty();
6102 }
6103
6104 this._speedUpCustomBlendModes(renderer);
6105
6106 this._renderWebGL_PIXI(renderer);
6107};
6108
6109// The important members from Pixi.js
6110
6111/**
6112 * The visibility of the tiling sprite.
6113 *
6114 * @property visible
6115 * @type Boolean
6116 */
6117
6118/**
6119 * The x coordinate of the tiling sprite.
6120 *
6121 * @property x
6122 * @type Number
6123 */
6124
6125/**
6126 * The y coordinate of the tiling sprite.
6127 *
6128 * @property y
6129 * @type Number
6130 */
6131
6132//-----------------------------------------------------------------------------
6133/**
6134 * The sprite which covers the entire game screen.
6135 *
6136 * @class ScreenSprite
6137 * @constructor
6138 */
6139function ScreenSprite() {
6140 this.initialize.apply(this, arguments);
6141}
6142
6143ScreenSprite.prototype = Object.create(PIXI.Container.prototype);
6144ScreenSprite.prototype.constructor = ScreenSprite;
6145
6146ScreenSprite.prototype.initialize = function () {
6147 PIXI.Container.call(this);
6148
6149 this._graphics = new PIXI.Graphics();
6150 this.addChild(this._graphics);
6151 this.opacity = 0;
6152
6153 this._red = -1;
6154 this._green = -1;
6155 this._blue = -1;
6156 this._colorText = '';
6157 this.setBlack();
6158};
6159
6160/**
6161 * The opacity of the sprite (0 to 255).
6162 *
6163 * @property opacity
6164 * @type Number
6165 */
6166Object.defineProperty(ScreenSprite.prototype, 'opacity', {
6167 get: function () {
6168 return this.alpha * 255;
6169 },
6170 set: function (value) {
6171 this.alpha = value.clamp(0, 255) / 255;
6172 },
6173 configurable: true
6174});
6175
6176ScreenSprite.YEPWarned = false;
6177ScreenSprite.warnYep = function () {
6178 if (!ScreenSprite.YEPWarned) {
6179 console.log("Deprecation warning. Please update YEP_CoreEngine. ScreenSprite is not a sprite, it has graphics inside.");
6180 ScreenSprite.YEPWarned = true;
6181 }
6182};
6183
6184Object.defineProperty(ScreenSprite.prototype, 'anchor', {
6185 get: function () {
6186 ScreenSprite.warnYep();
6187 this.scale.x = 1;
6188 this.scale.y = 1;
6189 return {x: 0, y: 0};
6190 },
6191 set: function (value) {
6192 this.alpha = value.clamp(0, 255) / 255;
6193 },
6194 configurable: true
6195});
6196
6197Object.defineProperty(ScreenSprite.prototype, 'blendMode', {
6198 get: function () {
6199 return this._graphics.blendMode;
6200 },
6201 set: function (value) {
6202 this._graphics.blendMode = value;
6203 },
6204 configurable: true
6205});
6206
6207/**
6208 * Sets black to the color of the screen sprite.
6209 *
6210 * @method setBlack
6211 */
6212ScreenSprite.prototype.setBlack = function () {
6213 this.setColor(0, 0, 0);
6214};
6215
6216/**
6217 * Sets white to the color of the screen sprite.
6218 *
6219 * @method setWhite
6220 */
6221ScreenSprite.prototype.setWhite = function () {
6222 this.setColor(255, 255, 255);
6223};
6224
6225/**
6226 * Sets the color of the screen sprite by values.
6227 *
6228 * @method setColor
6229 * @param {Number} r The red value in the range (0, 255)
6230 * @param {Number} g The green value in the range (0, 255)
6231 * @param {Number} b The blue value in the range (0, 255)
6232 */
6233ScreenSprite.prototype.setColor = function (r, g, b) {
6234 if (this._red !== r || this._green !== g || this._blue !== b) {
6235 r = Math.round(r || 0).clamp(0, 255);
6236 g = Math.round(g || 0).clamp(0, 255);
6237 b = Math.round(b || 0).clamp(0, 255);
6238 this._red = r;
6239 this._green = g;
6240 this._blue = b;
6241 this._colorText = Utils.rgbToCssColor(r, g, b);
6242
6243 var graphics = this._graphics;
6244 graphics.clear();
6245 var intColor = (r << 16) | (g << 8) | b;
6246 graphics.beginFill(intColor, 1);
6247 //whole screen with zoom. BWAHAHAHAHA
6248 graphics.drawRect(-Graphics.width * 5, -Graphics.height * 5, Graphics.width * 10, Graphics.height * 10);
6249 }
6250};
6251
6252//-----------------------------------------------------------------------------
6253/**
6254 * The window in the game.
6255 *
6256 * @class Window
6257 * @constructor
6258 */
6259function Window() {
6260 this.initialize.apply(this, arguments);
6261}
6262
6263Window.prototype = Object.create(PIXI.Container.prototype);
6264Window.prototype.constructor = Window;
6265
6266Window.prototype.initialize = function() {
6267 PIXI.Container.call(this);
6268
6269 this._isWindow = true;
6270 this._windowskin = null;
6271 this._width = 0;
6272 this._height = 0;
6273 this._cursorRect = new Rectangle();
6274 this._openness = 255;
6275 this._animationCount = 0;
6276
6277 this._padding = 18;
6278 this._margin = 4;
6279 this._colorTone = [0, 0, 0];
6280
6281 this._windowSpriteContainer = null;
6282 this._windowBackSprite = null;
6283 this._windowCursorSprite = null;
6284 this._windowFrameSprite = null;
6285 this._windowContentsSprite = null;
6286 this._windowArrowSprites = [];
6287 this._windowPauseSignSprite = null;
6288
6289 this._createAllParts();
6290
6291 /**
6292 * The origin point of the window for scrolling.
6293 *
6294 * @property origin
6295 * @type Point
6296 */
6297 this.origin = new Point();
6298
6299 /**
6300 * The active state for the window.
6301 *
6302 * @property active
6303 * @type Boolean
6304 */
6305 this.active = true;
6306
6307 /**
6308 * The visibility of the down scroll arrow.
6309 *
6310 * @property downArrowVisible
6311 * @type Boolean
6312 */
6313 this.downArrowVisible = false;
6314
6315 /**
6316 * The visibility of the up scroll arrow.
6317 *
6318 * @property upArrowVisible
6319 * @type Boolean
6320 */
6321 this.upArrowVisible = false;
6322
6323 /**
6324 * The visibility of the pause sign.
6325 *
6326 * @property pause
6327 * @type Boolean
6328 */
6329 this.pause = false;
6330};
6331
6332/**
6333 * The image used as a window skin.
6334 *
6335 * @property windowskin
6336 * @type Bitmap
6337 */
6338Object.defineProperty(Window.prototype, 'windowskin', {
6339 get: function() {
6340 return this._windowskin;
6341 },
6342 set: function(value) {
6343 if (this._windowskin !== value) {
6344 this._windowskin = value;
6345 this._windowskin.addLoadListener(this._onWindowskinLoad.bind(this));
6346 }
6347 },
6348 configurable: true
6349});
6350
6351/**
6352 * The bitmap used for the window contents.
6353 *
6354 * @property contents
6355 * @type Bitmap
6356 */
6357Object.defineProperty(Window.prototype, 'contents', {
6358 get: function() {
6359 return this._windowContentsSprite.bitmap;
6360 },
6361 set: function(value) {
6362 this._windowContentsSprite.bitmap = value;
6363 },
6364 configurable: true
6365});
6366
6367/**
6368 * The width of the window in pixels.
6369 *
6370 * @property width
6371 * @type Number
6372 */
6373Object.defineProperty(Window.prototype, 'width', {
6374 get: function() {
6375 return this._width;
6376 },
6377 set: function(value) {
6378 this._width = value;
6379 this._refreshAllParts();
6380 },
6381 configurable: true
6382});
6383
6384/**
6385 * The height of the window in pixels.
6386 *
6387 * @property height
6388 * @type Number
6389 */
6390Object.defineProperty(Window.prototype, 'height', {
6391 get: function() {
6392 return this._height;
6393 },
6394 set: function(value) {
6395 this._height = value;
6396 this._refreshAllParts();
6397 },
6398 configurable: true
6399});
6400
6401/**
6402 * The size of the padding between the frame and contents.
6403 *
6404 * @property padding
6405 * @type Number
6406 */
6407Object.defineProperty(Window.prototype, 'padding', {
6408 get: function() {
6409 return this._padding;
6410 },
6411 set: function(value) {
6412 this._padding = value;
6413 this._refreshAllParts();
6414 },
6415 configurable: true
6416});
6417
6418/**
6419 * The size of the margin for the window background.
6420 *
6421 * @property margin
6422 * @type Number
6423 */
6424Object.defineProperty(Window.prototype, 'margin', {
6425 get: function() {
6426 return this._margin;
6427 },
6428 set: function(value) {
6429 this._margin = value;
6430 this._refreshAllParts();
6431 },
6432 configurable: true
6433});
6434
6435/**
6436 * The opacity of the window without contents (0 to 255).
6437 *
6438 * @property opacity
6439 * @type Number
6440 */
6441Object.defineProperty(Window.prototype, 'opacity', {
6442 get: function() {
6443 return this._windowSpriteContainer.alpha * 255;
6444 },
6445 set: function(value) {
6446 this._windowSpriteContainer.alpha = value.clamp(0, 255) / 255;
6447 },
6448 configurable: true
6449});
6450
6451/**
6452 * The opacity of the window background (0 to 255).
6453 *
6454 * @property backOpacity
6455 * @type Number
6456 */
6457Object.defineProperty(Window.prototype, 'backOpacity', {
6458 get: function() {
6459 return this._windowBackSprite.alpha * 255;
6460 },
6461 set: function(value) {
6462 this._windowBackSprite.alpha = value.clamp(0, 255) / 255;
6463 },
6464 configurable: true
6465});
6466
6467/**
6468 * The opacity of the window contents (0 to 255).
6469 *
6470 * @property contentsOpacity
6471 * @type Number
6472 */
6473Object.defineProperty(Window.prototype, 'contentsOpacity', {
6474 get: function() {
6475 return this._windowContentsSprite.alpha * 255;
6476 },
6477 set: function(value) {
6478 this._windowContentsSprite.alpha = value.clamp(0, 255) / 255;
6479 },
6480 configurable: true
6481});
6482
6483/**
6484 * The openness of the window (0 to 255).
6485 *
6486 * @property openness
6487 * @type Number
6488 */
6489Object.defineProperty(Window.prototype, 'openness', {
6490 get: function() {
6491 return this._openness;
6492 },
6493 set: function(value) {
6494 if (this._openness !== value) {
6495 this._openness = value.clamp(0, 255);
6496 this._windowSpriteContainer.scale.y = this._openness / 255;
6497 this._windowSpriteContainer.y = this.height / 2 * (1 - this._openness / 255);
6498 }
6499 },
6500 configurable: true
6501});
6502
6503/**
6504 * Updates the window for each frame.
6505 *
6506 * @method update
6507 */
6508Window.prototype.update = function() {
6509 if (this.active) {
6510 this._animationCount++;
6511 }
6512 this.children.forEach(function(child) {
6513 if (child.update) {
6514 child.update();
6515 }
6516 });
6517};
6518
6519/**
6520 * Sets the x, y, width, and height all at once.
6521 *
6522 * @method move
6523 * @param {Number} x The x coordinate of the window
6524 * @param {Number} y The y coordinate of the window
6525 * @param {Number} width The width of the window
6526 * @param {Number} height The height of the window
6527 */
6528Window.prototype.move = function(x, y, width, height) {
6529 this.x = x || 0;
6530 this.y = y || 0;
6531 if (this._width !== width || this._height !== height) {
6532 this._width = width || 0;
6533 this._height = height || 0;
6534 this._refreshAllParts();
6535 }
6536};
6537
6538/**
6539 * Returns true if the window is completely open (openness == 255).
6540 *
6541 * @method isOpen
6542 */
6543Window.prototype.isOpen = function() {
6544 return this._openness >= 255;
6545};
6546
6547/**
6548 * Returns true if the window is completely closed (openness == 0).
6549 *
6550 * @method isClosed
6551 */
6552Window.prototype.isClosed = function() {
6553 return this._openness <= 0;
6554};
6555
6556/**
6557 * Sets the position of the command cursor.
6558 *
6559 * @method setCursorRect
6560 * @param {Number} x The x coordinate of the cursor
6561 * @param {Number} y The y coordinate of the cursor
6562 * @param {Number} width The width of the cursor
6563 * @param {Number} height The height of the cursor
6564 */
6565Window.prototype.setCursorRect = function(x, y, width, height) {
6566 var cx = Math.floor(x || 0);
6567 var cy = Math.floor(y || 0);
6568 var cw = Math.floor(width || 0);
6569 var ch = Math.floor(height || 0);
6570 var rect = this._cursorRect;
6571 if (rect.x !== cx || rect.y !== cy || rect.width !== cw || rect.height !== ch) {
6572 this._cursorRect.x = cx;
6573 this._cursorRect.y = cy;
6574 this._cursorRect.width = cw;
6575 this._cursorRect.height = ch;
6576 this._refreshCursor();
6577 }
6578};
6579
6580/**
6581 * Changes the color of the background.
6582 *
6583 * @method setTone
6584 * @param {Number} r The red value in the range (-255, 255)
6585 * @param {Number} g The green value in the range (-255, 255)
6586 * @param {Number} b The blue value in the range (-255, 255)
6587 */
6588Window.prototype.setTone = function(r, g, b) {
6589 var tone = this._colorTone;
6590 if (r !== tone[0] || g !== tone[1] || b !== tone[2]) {
6591 this._colorTone = [r, g, b];
6592 this._refreshBack();
6593 }
6594};
6595
6596/**
6597 * Adds a child between the background and contents.
6598 *
6599 * @method addChildToBack
6600 * @param {Object} child The child to add
6601 * @return {Object} The child that was added
6602 */
6603Window.prototype.addChildToBack = function(child) {
6604 var containerIndex = this.children.indexOf(this._windowSpriteContainer);
6605 return this.addChildAt(child, containerIndex + 1);
6606};
6607
6608/**
6609 * @method updateTransform
6610 * @private
6611 */
6612Window.prototype.updateTransform = function() {
6613 this._updateCursor();
6614 this._updateArrows();
6615 this._updatePauseSign();
6616 this._updateContents();
6617 PIXI.Container.prototype.updateTransform.call(this);
6618};
6619
6620/**
6621 * @method _createAllParts
6622 * @private
6623 */
6624Window.prototype._createAllParts = function() {
6625 this._windowSpriteContainer = new PIXI.Container();
6626 this._windowBackSprite = new Sprite();
6627 this._windowCursorSprite = new Sprite();
6628 this._windowFrameSprite = new Sprite();
6629 this._windowContentsSprite = new Sprite();
6630 this._downArrowSprite = new Sprite();
6631 this._upArrowSprite = new Sprite();
6632 this._windowPauseSignSprite = new Sprite();
6633 this._windowBackSprite.bitmap = new Bitmap(1, 1);
6634 this._windowBackSprite.alpha = 192 / 255;
6635 this.addChild(this._windowSpriteContainer);
6636 this._windowSpriteContainer.addChild(this._windowBackSprite);
6637 this._windowSpriteContainer.addChild(this._windowFrameSprite);
6638 this.addChild(this._windowCursorSprite);
6639 this.addChild(this._windowContentsSprite);
6640 this.addChild(this._downArrowSprite);
6641 this.addChild(this._upArrowSprite);
6642 this.addChild(this._windowPauseSignSprite);
6643};
6644
6645/**
6646 * @method _onWindowskinLoad
6647 * @private
6648 */
6649Window.prototype._onWindowskinLoad = function() {
6650 this._refreshAllParts();
6651};
6652
6653/**
6654 * @method _refreshAllParts
6655 * @private
6656 */
6657Window.prototype._refreshAllParts = function() {
6658 this._refreshBack();
6659 this._refreshFrame();
6660 this._refreshCursor();
6661 this._refreshContents();
6662 this._refreshArrows();
6663 this._refreshPauseSign();
6664};
6665
6666/**
6667 * @method _refreshBack
6668 * @private
6669 */
6670Window.prototype._refreshBack = function() {
6671 var m = this._margin;
6672 var w = this._width - m * 2;
6673 var h = this._height - m * 2;
6674 var bitmap = new Bitmap(w, h);
6675
6676 this._windowBackSprite.bitmap = bitmap;
6677 this._windowBackSprite.setFrame(0, 0, w, h);
6678 this._windowBackSprite.move(m, m);
6679
6680 if (w > 0 && h > 0 && this._windowskin) {
6681 var p = 96;
6682 bitmap.blt(this._windowskin, 0, 0, p, p, 0, 0, w, h);
6683 for (var y = 0; y < h; y += p) {
6684 for (var x = 0; x < w; x += p) {
6685 bitmap.blt(this._windowskin, 0, p, p, p, x, y, p, p);
6686 }
6687 }
6688 var tone = this._colorTone;
6689 bitmap.adjustTone(tone[0], tone[1], tone[2]);
6690 }
6691};
6692
6693/**
6694 * @method _refreshFrame
6695 * @private
6696 */
6697Window.prototype._refreshFrame = function() {
6698 var w = this._width;
6699 var h = this._height;
6700 var m = 24;
6701 var bitmap = new Bitmap(w, h);
6702
6703 this._windowFrameSprite.bitmap = bitmap;
6704 this._windowFrameSprite.setFrame(0, 0, w, h);
6705
6706 if (w > 0 && h > 0 && this._windowskin) {
6707 var skin = this._windowskin;
6708 var p = 96;
6709 var q = 96;
6710 bitmap.blt(skin, p+m, 0+0, p-m*2, m, m, 0, w-m*2, m);
6711 bitmap.blt(skin, p+m, 0+q-m, p-m*2, m, m, h-m, w-m*2, m);
6712 bitmap.blt(skin, p+0, 0+m, m, p-m*2, 0, m, m, h-m*2);
6713 bitmap.blt(skin, p+q-m, 0+m, m, p-m*2, w-m, m, m, h-m*2);
6714 bitmap.blt(skin, p+0, 0+0, m, m, 0, 0, m, m);
6715 bitmap.blt(skin, p+q-m, 0+0, m, m, w-m, 0, m, m);
6716 bitmap.blt(skin, p+0, 0+q-m, m, m, 0, h-m, m, m);
6717 bitmap.blt(skin, p+q-m, 0+q-m, m, m, w-m, h-m, m, m);
6718 }
6719};
6720
6721/**
6722 * @method _refreshCursor
6723 * @private
6724 */
6725Window.prototype._refreshCursor = function() {
6726 var pad = this._padding;
6727 var x = this._cursorRect.x + pad - this.origin.x;
6728 var y = this._cursorRect.y + pad - this.origin.y;
6729 var w = this._cursorRect.width;
6730 var h = this._cursorRect.height;
6731 var m = 4;
6732 var x2 = Math.max(x, pad);
6733 var y2 = Math.max(y, pad);
6734 var ox = x - x2;
6735 var oy = y - y2;
6736 var w2 = Math.min(w, this._width - pad - x2);
6737 var h2 = Math.min(h, this._height - pad - y2);
6738 var bitmap = new Bitmap(w2, h2);
6739
6740 this._windowCursorSprite.bitmap = bitmap;
6741 this._windowCursorSprite.setFrame(0, 0, w2, h2);
6742 this._windowCursorSprite.move(x2, y2);
6743
6744 if (w > 0 && h > 0 && this._windowskin) {
6745 var skin = this._windowskin;
6746 var p = 96;
6747 var q = 48;
6748 bitmap.blt(skin, p+m, p+m, q-m*2, q-m*2, ox+m, oy+m, w-m*2, h-m*2);
6749 bitmap.blt(skin, p+m, p+0, q-m*2, m, ox+m, oy+0, w-m*2, m);
6750 bitmap.blt(skin, p+m, p+q-m, q-m*2, m, ox+m, oy+h-m, w-m*2, m);
6751 bitmap.blt(skin, p+0, p+m, m, q-m*2, ox+0, oy+m, m, h-m*2);
6752 bitmap.blt(skin, p+q-m, p+m, m, q-m*2, ox+w-m, oy+m, m, h-m*2);
6753 bitmap.blt(skin, p+0, p+0, m, m, ox+0, oy+0, m, m);
6754 bitmap.blt(skin, p+q-m, p+0, m, m, ox+w-m, oy+0, m, m);
6755 bitmap.blt(skin, p+0, p+q-m, m, m, ox+0, oy+h-m, m, m);
6756 bitmap.blt(skin, p+q-m, p+q-m, m, m, ox+w-m, oy+h-m, m, m);
6757 }
6758};
6759
6760/**
6761 * @method _refreshContents
6762 * @private
6763 */
6764Window.prototype._refreshContents = function() {
6765 this._windowContentsSprite.move(this.padding, this.padding);
6766};
6767
6768/**
6769 * @method _refreshArrows
6770 * @private
6771 */
6772Window.prototype._refreshArrows = function() {
6773 var w = this._width;
6774 var h = this._height;
6775 var p = 24;
6776 var q = p/2;
6777 var sx = 96+p;
6778 var sy = 0+p;
6779 this._downArrowSprite.bitmap = this._windowskin;
6780 this._downArrowSprite.anchor.x = 0.5;
6781 this._downArrowSprite.anchor.y = 0.5;
6782 this._downArrowSprite.setFrame(sx+q, sy+q+p, p, q);
6783 this._downArrowSprite.move(w/2, h-q);
6784 this._upArrowSprite.bitmap = this._windowskin;
6785 this._upArrowSprite.anchor.x = 0.5;
6786 this._upArrowSprite.anchor.y = 0.5;
6787 this._upArrowSprite.setFrame(sx+q, sy, p, q);
6788 this._upArrowSprite.move(w/2, q);
6789};
6790
6791/**
6792 * @method _refreshPauseSign
6793 * @private
6794 */
6795Window.prototype._refreshPauseSign = function() {
6796 var sx = 144;
6797 var sy = 96;
6798 var p = 24;
6799 this._windowPauseSignSprite.bitmap = this._windowskin;
6800 this._windowPauseSignSprite.anchor.x = 0.5;
6801 this._windowPauseSignSprite.anchor.y = 1;
6802 this._windowPauseSignSprite.move(this._width / 2, this._height);
6803 this._windowPauseSignSprite.setFrame(sx, sy, p, p);
6804 this._windowPauseSignSprite.alpha = 0;
6805};
6806
6807/**
6808 * @method _updateCursor
6809 * @private
6810 */
6811Window.prototype._updateCursor = function() {
6812 var blinkCount = this._animationCount % 40;
6813 var cursorOpacity = this.contentsOpacity;
6814 if (this.active) {
6815 if (blinkCount < 20) {
6816 cursorOpacity -= blinkCount * 8;
6817 } else {
6818 cursorOpacity -= (40 - blinkCount) * 8;
6819 }
6820 }
6821 this._windowCursorSprite.alpha = cursorOpacity / 255;
6822 this._windowCursorSprite.visible = this.isOpen();
6823};
6824
6825/**
6826 * @method _updateContents
6827 * @private
6828 */
6829Window.prototype._updateContents = function() {
6830 var w = this._width - this._padding * 2;
6831 var h = this._height - this._padding * 2;
6832 if (w > 0 && h > 0) {
6833 this._windowContentsSprite.setFrame(this.origin.x, this.origin.y, w, h);
6834 this._windowContentsSprite.visible = this.isOpen();
6835 } else {
6836 this._windowContentsSprite.visible = false;
6837 }
6838};
6839
6840/**
6841 * @method _updateArrows
6842 * @private
6843 */
6844Window.prototype._updateArrows = function() {
6845 this._downArrowSprite.visible = this.isOpen() && this.downArrowVisible;
6846 this._upArrowSprite.visible = this.isOpen() && this.upArrowVisible;
6847};
6848
6849/**
6850 * @method _updatePauseSign
6851 * @private
6852 */
6853Window.prototype._updatePauseSign = function() {
6854 var sprite = this._windowPauseSignSprite;
6855 var x = Math.floor(this._animationCount / 16) % 2;
6856 var y = Math.floor(this._animationCount / 16 / 2) % 2;
6857 var sx = 144;
6858 var sy = 96;
6859 var p = 24;
6860 if (!this.pause) {
6861 sprite.alpha = 0;
6862 } else if (sprite.alpha < 1) {
6863 sprite.alpha = Math.min(sprite.alpha + 0.1, 1);
6864 }
6865 sprite.setFrame(sx+x*p, sy+y*p, p, p);
6866 sprite.visible = this.isOpen();
6867};
6868
6869// The important members from Pixi.js
6870
6871/**
6872 * The visibility of the window.
6873 *
6874 * @property visible
6875 * @type Boolean
6876 */
6877
6878/**
6879 * The x coordinate of the window.
6880 *
6881 * @property x
6882 * @type Number
6883 */
6884
6885/**
6886 * The y coordinate of the window.
6887 *
6888 * @property y
6889 * @type Number
6890 */
6891
6892/**
6893 * [read-only] The array of children of the window.
6894 *
6895 * @property children
6896 * @type Array
6897 */
6898
6899/**
6900 * [read-only] The object that contains the window.
6901 *
6902 * @property parent
6903 * @type Object
6904 */
6905
6906/**
6907 * Adds a child to the container.
6908 *
6909 * @method addChild
6910 * @param {Object} child The child to add
6911 * @return {Object} The child that was added
6912 */
6913
6914/**
6915 * Adds a child to the container at a specified index.
6916 *
6917 * @method addChildAt
6918 * @param {Object} child The child to add
6919 * @param {Number} index The index to place the child in
6920 * @return {Object} The child that was added
6921 */
6922
6923/**
6924 * Removes a child from the container.
6925 *
6926 * @method removeChild
6927 * @param {Object} child The child to remove
6928 * @return {Object} The child that was removed
6929 */
6930
6931/**
6932 * Removes a child from the specified index position.
6933 *
6934 * @method removeChildAt
6935 * @param {Number} index The index to get the child from
6936 * @return {Object} The child that was removed
6937 */
6938
6939//-----------------------------------------------------------------------------
6940/**
6941 * The layer which contains game windows.
6942 *
6943 * @class WindowLayer
6944 * @constructor
6945 */
6946function WindowLayer() {
6947 this.initialize.apply(this, arguments);
6948}
6949
6950WindowLayer.prototype = Object.create(PIXI.Container.prototype);
6951WindowLayer.prototype.constructor = WindowLayer;
6952
6953WindowLayer.prototype.initialize = function() {
6954 PIXI.Container.call(this);
6955 this._width = 0;
6956 this._height = 0;
6957 this._tempCanvas = null;
6958 this._translationMatrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
6959
6960 this._windowMask = new PIXI.Graphics();
6961 this._windowMask.beginFill(0xffffff, 1);
6962 this._windowMask.drawRect(0, 0, 0, 0);
6963 this._windowMask.endFill();
6964 this._windowRect = this._windowMask.graphicsData[0].shape;
6965
6966 this._renderSprite = null;
6967 this.filterArea = new PIXI.Rectangle();
6968 this.filters = [WindowLayer.voidFilter];
6969
6970 //temporary fix for memory leak bug
6971 this.on('removed', this.onRemoveAsAChild);
6972};
6973
6974WindowLayer.prototype.onRemoveAsAChild = function() {
6975 this.removeChildren();
6976}
6977
6978WindowLayer.voidFilter = new PIXI.filters.VoidFilter();
6979
6980/**
6981 * The width of the window layer in pixels.
6982 *
6983 * @property width
6984 * @type Number
6985 */
6986Object.defineProperty(WindowLayer.prototype, 'width', {
6987 get: function() {
6988 return this._width;
6989 },
6990 set: function(value) {
6991 this._width = value;
6992 },
6993 configurable: true
6994});
6995
6996/**
6997 * The height of the window layer in pixels.
6998 *
6999 * @property height
7000 * @type Number
7001 */
7002Object.defineProperty(WindowLayer.prototype, 'height', {
7003 get: function() {
7004 return this._height;
7005 },
7006 set: function(value) {
7007 this._height = value;
7008 },
7009 configurable: true
7010});
7011
7012/**
7013 * Sets the x, y, width, and height all at once.
7014 *
7015 * @method move
7016 * @param {Number} x The x coordinate of the window layer
7017 * @param {Number} y The y coordinate of the window layer
7018 * @param {Number} width The width of the window layer
7019 * @param {Number} height The height of the window layer
7020 */
7021WindowLayer.prototype.move = function(x, y, width, height) {
7022 this.x = x;
7023 this.y = y;
7024 this.width = width;
7025 this.height = height;
7026};
7027
7028/**
7029 * Updates the window layer for each frame.
7030 *
7031 * @method update
7032 */
7033WindowLayer.prototype.update = function() {
7034 this.children.forEach(function(child) {
7035 if (child.update) {
7036 child.update();
7037 }
7038 });
7039};
7040
7041/**
7042 * @method _renderCanvas
7043 * @param {Object} renderSession
7044 * @private
7045 */
7046WindowLayer.prototype.renderCanvas = function(renderer) {
7047 if (!this.visible || !this.renderable) {
7048 return;
7049 }
7050
7051 if (!this._tempCanvas) {
7052 this._tempCanvas = document.createElement('canvas');
7053 }
7054
7055 this._tempCanvas.width = Graphics.width;
7056 this._tempCanvas.height = Graphics.height;
7057
7058 var realCanvasContext = renderer.context;
7059 var context = this._tempCanvas.getContext('2d');
7060
7061 context.save();
7062 context.clearRect(0, 0, Graphics.width, Graphics.height);
7063 context.beginPath();
7064 context.rect(this.x, this.y, this.width, this.height);
7065 context.closePath();
7066 context.clip();
7067
7068 renderer.context = context;
7069
7070 for (var i = 0; i < this.children.length; i++) {
7071 var child = this.children[i];
7072 if (child._isWindow && child.visible && child.openness > 0) {
7073 this._canvasClearWindowRect(renderer, child);
7074 context.save();
7075 child.renderCanvas(renderer);
7076 context.restore();
7077 }
7078 }
7079
7080 context.restore();
7081
7082 renderer.context = realCanvasContext;
7083 renderer.context.setTransform(1, 0, 0, 1, 0, 0);
7084 renderer.context.globalCompositeOperation = 'source-over';
7085 renderer.context.globalAlpha = 1;
7086 renderer.context.drawImage(this._tempCanvas, 0, 0);
7087
7088 for (var j = 0; j < this.children.length; j++) {
7089 if (!this.children[j]._isWindow) {
7090 this.children[j].renderCanvas(renderer);
7091 }
7092 }
7093};
7094
7095/**
7096 * @method _canvasClearWindowRect
7097 * @param {Object} renderSession
7098 * @param {Window} window
7099 * @private
7100 */
7101WindowLayer.prototype._canvasClearWindowRect = function(renderSession, window) {
7102 var rx = this.x + window.x;
7103 var ry = this.y + window.y + window.height / 2 * (1 - window._openness / 255);
7104 var rw = window.width;
7105 var rh = window.height * window._openness / 255;
7106 renderSession.context.clearRect(rx, ry, rw, rh);
7107};
7108
7109/**
7110 * @method _renderWebGL
7111 * @param {Object} renderSession
7112 * @private
7113 */
7114WindowLayer.prototype.renderWebGL = function(renderer) {
7115 if (!this.visible || !this.renderable) {
7116 return;
7117 }
7118
7119 if (this.children.length==0) {
7120 return;
7121 }
7122
7123 renderer.flush();
7124 this.filterArea.copy(this);
7125 renderer.filterManager.pushFilter(this, this.filters);
7126 renderer.currentRenderer.start();
7127
7128 var shift = new PIXI.Point();
7129 var rt = renderer._activeRenderTarget;
7130 var projectionMatrix = rt.projectionMatrix;
7131 shift.x = Math.round((projectionMatrix.tx + 1) / 2 * rt.sourceFrame.width);
7132 shift.y = Math.round((projectionMatrix.ty + 1) / 2 * rt.sourceFrame.height);
7133
7134 for (var i = 0; i < this.children.length; i++) {
7135 var child = this.children[i];
7136 if (child._isWindow && child.visible && child.openness > 0) {
7137 this._maskWindow(child, shift);
7138 renderer.maskManager.pushScissorMask(this, this._windowMask);
7139 renderer.clear();
7140 renderer.maskManager.popScissorMask();
7141 renderer.currentRenderer.start();
7142 child.renderWebGL(renderer);
7143 renderer.currentRenderer.flush();
7144 }
7145 }
7146
7147 renderer.flush();
7148 renderer.filterManager.popFilter();
7149 renderer.maskManager.popScissorMask();
7150
7151 for (var j = 0; j < this.children.length; j++) {
7152 if (!this.children[j]._isWindow) {
7153 this.children[j].renderWebGL(renderer);
7154 }
7155 }
7156};
7157
7158/**
7159 * @method _maskWindow
7160 * @param {Window} window
7161 * @private
7162 */
7163WindowLayer.prototype._maskWindow = function(window, shift) {
7164 this._windowMask._currentBounds = null;
7165 this._windowMask.boundsDirty = true;
7166 var rect = this._windowRect;
7167 rect.x = this.x + shift.x + window.x;
7168 rect.y = this.x + shift.y + window.y + window.height / 2 * (1 - window._openness / 255);
7169 rect.width = window.width;
7170 rect.height = window.height * window._openness / 255;
7171};
7172
7173// The important members from Pixi.js
7174
7175/**
7176 * The x coordinate of the window layer.
7177 *
7178 * @property x
7179 * @type Number
7180 */
7181
7182/**
7183 * The y coordinate of the window layer.
7184 *
7185 * @property y
7186 * @type Number
7187 */
7188
7189/**
7190 * [read-only] The array of children of the window layer.
7191 *
7192 * @property children
7193 * @type Array
7194 */
7195
7196/**
7197 * [read-only] The object that contains the window layer.
7198 *
7199 * @property parent
7200 * @type Object
7201 */
7202
7203/**
7204 * Adds a child to the container.
7205 *
7206 * @method addChild
7207 * @param {Object} child The child to add
7208 * @return {Object} The child that was added
7209 */
7210
7211/**
7212 * Adds a child to the container at a specified index.
7213 *
7214 * @method addChildAt
7215 * @param {Object} child The child to add
7216 * @param {Number} index The index to place the child in
7217 * @return {Object} The child that was added
7218 */
7219
7220/**
7221 * Removes a child from the container.
7222 *
7223 * @method removeChild
7224 * @param {Object} child The child to remove
7225 * @return {Object} The child that was removed
7226 */
7227
7228/**
7229 * Removes a child from the specified index position.
7230 *
7231 * @method removeChildAt
7232 * @param {Number} index The index to get the child from
7233 * @return {Object} The child that was removed
7234 */
7235
7236//-----------------------------------------------------------------------------
7237/**
7238 * The weather effect which displays rain, storm, or snow.
7239 *
7240 * @class Weather
7241 * @constructor
7242 */
7243function Weather() {
7244 this.initialize.apply(this, arguments);
7245}
7246
7247Weather.prototype = Object.create(PIXI.Container.prototype);
7248Weather.prototype.constructor = Weather;
7249
7250Weather.prototype.initialize = function() {
7251 PIXI.Container.call(this);
7252
7253 this._width = Graphics.width;
7254 this._height = Graphics.height;
7255 this._sprites = [];
7256
7257 this._createBitmaps();
7258 this._createDimmer();
7259
7260 /**
7261 * The type of the weather in ['none', 'rain', 'storm', 'snow'].
7262 *
7263 * @property type
7264 * @type String
7265 */
7266 this.type = 'none';
7267
7268 /**
7269 * The power of the weather in the range (0, 9).
7270 *
7271 * @property power
7272 * @type Number
7273 */
7274 this.power = 0;
7275
7276 /**
7277 * The origin point of the weather for scrolling.
7278 *
7279 * @property origin
7280 * @type Point
7281 */
7282 this.origin = new Point();
7283};
7284
7285/**
7286 * Updates the weather for each frame.
7287 *
7288 * @method update
7289 */
7290Weather.prototype.update = function() {
7291 this._updateDimmer();
7292 this._updateAllSprites();
7293};
7294
7295/**
7296 * @method _createBitmaps
7297 * @private
7298 */
7299Weather.prototype._createBitmaps = function() {
7300 this._rainBitmap = new Bitmap(1, 60);
7301 this._rainBitmap.fillAll('white');
7302 this._stormBitmap = new Bitmap(2, 100);
7303 this._stormBitmap.fillAll('white');
7304 this._snowBitmap = new Bitmap(9, 9);
7305 this._snowBitmap.drawCircle(4, 4, 4, 'white');
7306};
7307
7308/**
7309 * @method _createDimmer
7310 * @private
7311 */
7312Weather.prototype._createDimmer = function() {
7313 this._dimmerSprite = new ScreenSprite();
7314 this._dimmerSprite.setColor(80, 80, 80);
7315 this.addChild(this._dimmerSprite);
7316};
7317
7318/**
7319 * @method _updateDimmer
7320 * @private
7321 */
7322Weather.prototype._updateDimmer = function() {
7323 this._dimmerSprite.opacity = Math.floor(this.power * 6);
7324};
7325
7326/**
7327 * @method _updateAllSprites
7328 * @private
7329 */
7330Weather.prototype._updateAllSprites = function() {
7331 var maxSprites = Math.floor(this.power * 10);
7332 while (this._sprites.length < maxSprites) {
7333 this._addSprite();
7334 }
7335 while (this._sprites.length > maxSprites) {
7336 this._removeSprite();
7337 }
7338 this._sprites.forEach(function(sprite) {
7339 this._updateSprite(sprite);
7340 sprite.x = sprite.ax - this.origin.x;
7341 sprite.y = sprite.ay - this.origin.y;
7342 }, this);
7343};
7344
7345/**
7346 * @method _addSprite
7347 * @private
7348 */
7349Weather.prototype._addSprite = function() {
7350 var sprite = new Sprite(this.viewport);
7351 sprite.opacity = 0;
7352 this._sprites.push(sprite);
7353 this.addChild(sprite);
7354};
7355
7356/**
7357 * @method _removeSprite
7358 * @private
7359 */
7360Weather.prototype._removeSprite = function() {
7361 this.removeChild(this._sprites.pop());
7362};
7363
7364/**
7365 * @method _updateSprite
7366 * @param {Sprite} sprite
7367 * @private
7368 */
7369Weather.prototype._updateSprite = function(sprite) {
7370 switch (this.type) {
7371 case 'rain':
7372 this._updateRainSprite(sprite);
7373 break;
7374 case 'storm':
7375 this._updateStormSprite(sprite);
7376 break;
7377 case 'snow':
7378 this._updateSnowSprite(sprite);
7379 break;
7380 }
7381 if (sprite.opacity < 40) {
7382 this._rebornSprite(sprite);
7383 }
7384};
7385
7386/**
7387 * @method _updateRainSprite
7388 * @param {Sprite} sprite
7389 * @private
7390 */
7391Weather.prototype._updateRainSprite = function(sprite) {
7392 sprite.bitmap = this._rainBitmap;
7393 sprite.rotation = Math.PI / 16;
7394 sprite.ax -= 6 * Math.sin(sprite.rotation);
7395 sprite.ay += 6 * Math.cos(sprite.rotation);
7396 sprite.opacity -= 6;
7397};
7398
7399/**
7400 * @method _updateStormSprite
7401 * @param {Sprite} sprite
7402 * @private
7403 */
7404Weather.prototype._updateStormSprite = function(sprite) {
7405 sprite.bitmap = this._stormBitmap;
7406 sprite.rotation = Math.PI / 8;
7407 sprite.ax -= 8 * Math.sin(sprite.rotation);
7408 sprite.ay += 8 * Math.cos(sprite.rotation);
7409 sprite.opacity -= 8;
7410};
7411
7412/**
7413 * @method _updateSnowSprite
7414 * @param {Sprite} sprite
7415 * @private
7416 */
7417Weather.prototype._updateSnowSprite = function(sprite) {
7418 sprite.bitmap = this._snowBitmap;
7419 sprite.rotation = Math.PI / 16;
7420 sprite.ax -= 3 * Math.sin(sprite.rotation);
7421 sprite.ay += 3 * Math.cos(sprite.rotation);
7422 sprite.opacity -= 3;
7423};
7424
7425/**
7426 * @method _rebornSprite
7427 * @param {Sprite} sprite
7428 * @private
7429 */
7430Weather.prototype._rebornSprite = function(sprite) {
7431 sprite.ax = Math.randomInt(Graphics.width + 100) - 100 + this.origin.x;
7432 sprite.ay = Math.randomInt(Graphics.height + 200) - 200 + this.origin.y;
7433 sprite.opacity = 160 + Math.randomInt(60);
7434};
7435
7436//-----------------------------------------------------------------------------
7437/**
7438 * The color matrix filter for WebGL.
7439 *
7440 * @class ToneFilter
7441 * @extends PIXI.Filter
7442 * @constructor
7443 */
7444function ToneFilter() {
7445 PIXI.filters.ColorMatrixFilter.call(this);
7446}
7447
7448ToneFilter.prototype = Object.create(PIXI.filters.ColorMatrixFilter.prototype);
7449ToneFilter.prototype.constructor = ToneFilter;
7450
7451/**
7452 * Changes the hue.
7453 *
7454 * @method adjustHue
7455 * @param {Number} value The hue value in the range (-360, 360)
7456 */
7457ToneFilter.prototype.adjustHue = function(value) {
7458 this.hue(value, true);
7459};
7460
7461/**
7462 * Changes the saturation.
7463 *
7464 * @method adjustSaturation
7465 * @param {Number} value The saturation value in the range (-255, 255)
7466 */
7467ToneFilter.prototype.adjustSaturation = function(value) {
7468 value = (value || 0).clamp(-255, 255) / 255;
7469 this.saturate(value, true);
7470};
7471
7472/**
7473 * Changes the tone.
7474 *
7475 * @method adjustTone
7476 * @param {Number} r The red strength in the range (-255, 255)
7477 * @param {Number} g The green strength in the range (-255, 255)
7478 * @param {Number} b The blue strength in the range (-255, 255)
7479 */
7480ToneFilter.prototype.adjustTone = function(r, g, b) {
7481 r = (r || 0).clamp(-255, 255) / 255;
7482 g = (g || 0).clamp(-255, 255) / 255;
7483 b = (b || 0).clamp(-255, 255) / 255;
7484
7485 if (r !== 0 || g !== 0 || b !== 0) {
7486 var matrix = [
7487 1, 0, 0, r, 0,
7488 0, 1, 0, g, 0,
7489 0, 0, 1, b, 0,
7490 0, 0, 0, 1, 0
7491 ];
7492
7493 this._loadMatrix(matrix, true);
7494 }
7495};
7496
7497//-----------------------------------------------------------------------------
7498/**
7499 * The sprite which changes the screen color in 2D canvas mode.
7500 *
7501 * @class ToneSprite
7502 * @constructor
7503 */
7504function ToneSprite() {
7505 this.initialize.apply(this, arguments);
7506}
7507
7508ToneSprite.prototype = Object.create(PIXI.Container.prototype);
7509ToneSprite.prototype.constructor = ToneSprite;
7510
7511ToneSprite.prototype.initialize = function() {
7512 PIXI.Container.call(this);
7513 this.clear();
7514};
7515
7516/**
7517 * Clears the tone.
7518 *
7519 * @method reset
7520 */
7521ToneSprite.prototype.clear = function() {
7522 this._red = 0;
7523 this._green = 0;
7524 this._blue = 0;
7525 this._gray = 0;
7526};
7527
7528/**
7529 * Sets the tone.
7530 *
7531 * @method setTone
7532 * @param {Number} r The red strength in the range (-255, 255)
7533 * @param {Number} g The green strength in the range (-255, 255)
7534 * @param {Number} b The blue strength in the range (-255, 255)
7535 * @param {Number} gray The grayscale level in the range (0, 255)
7536 */
7537ToneSprite.prototype.setTone = function(r, g, b, gray) {
7538 this._red = Math.round(r || 0).clamp(-255, 255);
7539 this._green = Math.round(g || 0).clamp(-255, 255);
7540 this._blue = Math.round(b || 0).clamp(-255, 255);
7541 this._gray = Math.round(gray || 0).clamp(0, 255);
7542};
7543
7544/**
7545 * @method _renderCanvas
7546 * @param {Object} renderSession
7547 * @private
7548 */
7549ToneSprite.prototype._renderCanvas = function(renderer) {
7550 if (this.visible) {
7551 var context = renderer.context;
7552 var t = this.worldTransform;
7553 var r = renderer.resolution;
7554 var width = Graphics.width;
7555 var height = Graphics.height;
7556 context.save();
7557 context.setTransform(t.a, t.b, t.c, t.d, t.tx * r, t.ty * r);
7558 if (Graphics.canUseSaturationBlend() && this._gray > 0) {
7559 context.globalCompositeOperation = 'saturation';
7560 context.globalAlpha = this._gray / 255;
7561 context.fillStyle = '#ffffff';
7562 context.fillRect(0, 0, width, height);
7563 }
7564 context.globalAlpha = 1;
7565 var r1 = Math.max(0, this._red);
7566 var g1 = Math.max(0, this._green);
7567 var b1 = Math.max(0, this._blue);
7568 if (r1 || g1 || b1) {
7569 context.globalCompositeOperation = 'lighter';
7570 context.fillStyle = Utils.rgbToCssColor(r1, g1, b1);
7571 context.fillRect(0, 0, width, height);
7572 }
7573 if (Graphics.canUseDifferenceBlend()) {
7574 var r2 = Math.max(0, -this._red);
7575 var g2 = Math.max(0, -this._green);
7576 var b2 = Math.max(0, -this._blue);
7577 if (r2 || g2 || b2) {
7578 context.globalCompositeOperation = 'difference';
7579 context.fillStyle = '#ffffff';
7580 context.fillRect(0, 0, width, height);
7581 context.globalCompositeOperation = 'lighter';
7582 context.fillStyle = Utils.rgbToCssColor(r2, g2, b2);
7583 context.fillRect(0, 0, width, height);
7584 context.globalCompositeOperation = 'difference';
7585 context.fillStyle = '#ffffff';
7586 context.fillRect(0, 0, width, height);
7587 }
7588 }
7589 context.restore();
7590 }
7591};
7592
7593/**
7594 * @method _renderWebGL
7595 * @param {Object} renderSession
7596 * @private
7597 */
7598ToneSprite.prototype._renderWebGL = function(renderer) {
7599 // Not supported
7600};
7601
7602//-----------------------------------------------------------------------------
7603/**
7604 * The root object of the display tree.
7605 *
7606 * @class Stage
7607 * @constructor
7608 */
7609function Stage() {
7610 this.initialize.apply(this, arguments);
7611}
7612
7613Stage.prototype = Object.create(PIXI.display.Stage.prototype);
7614//Stage.prototype = Object.create(PIXI.Container.prototype);
7615Stage.prototype.constructor = Stage;
7616
7617Stage.prototype.initialize = function() {
7618 //PIXI.Container.call(this);
7619 PIXI.display.Stage.call(this);
7620
7621 // The interactive flag causes a memory leak.
7622 this.interactive = false;
7623};
7624
7625/**
7626 * [read-only] The array of children of the stage.
7627 *
7628 * @property children
7629 * @type Array
7630 */
7631
7632/**
7633 * Adds a child to the container.
7634 *
7635 * @method addChild
7636 * @param {Object} child The child to add
7637 * @return {Object} The child that was added
7638 */
7639
7640/**
7641 * Adds a child to the container at a specified index.
7642 *
7643 * @method addChildAt
7644 * @param {Object} child The child to add
7645 * @param {Number} index The index to place the child in
7646 * @return {Object} The child that was added
7647 */
7648
7649/**
7650 * Removes a child from the container.
7651 *
7652 * @method removeChild
7653 * @param {Object} child The child to remove
7654 * @return {Object} The child that was removed
7655 */
7656
7657/**
7658 * Removes a child from the specified index position.
7659 *
7660 * @method removeChildAt
7661 * @param {Number} index The index to get the child from
7662 * @return {Object} The child that was removed
7663 */
7664
7665//-----------------------------------------------------------------------------
7666/**
7667 * The audio object of Web Audio API.
7668 *
7669 * @class WebAudio
7670 * @constructor
7671 * @param {String} url The url of the audio file
7672 */
7673function WebAudio() {
7674 this.initialize.apply(this, arguments);
7675}
7676
7677WebAudio._standAlone = (function(top){
7678 return !top.ResourceHandler;
7679})(this);
7680
7681WebAudio.prototype.initialize = function(url) {
7682 if (!WebAudio._initialized) {
7683 WebAudio.initialize();
7684 }
7685 this.clear();
7686
7687 if(!WebAudio._standAlone){
7688 this._loader = ResourceHandler.createLoader(url, this._load.bind(this, url), function() {
7689 this._hasError = true;
7690 }.bind(this));
7691 }
7692 this._load(url);
7693 this._url = url;
7694};
7695
7696WebAudio._masterVolume = 1;
7697WebAudio._context = null;
7698WebAudio._masterGainNode = null;
7699WebAudio._initialized = false;
7700WebAudio._unlocked = false;
7701
7702/**
7703 * Initializes the audio system.
7704 *
7705 * @static
7706 * @method initialize
7707 * @param {Boolean} noAudio Flag for the no-audio mode
7708 * @return {Boolean} True if the audio system is available
7709 */
7710WebAudio.initialize = function(noAudio) {
7711 if (!this._initialized) {
7712 if (!noAudio) {
7713 this._createContext();
7714 this._detectCodecs();
7715 this._createMasterGainNode();
7716 this._setupEventHandlers();
7717 }
7718 this._initialized = true;
7719 }
7720 return !!this._context;
7721};
7722
7723/**
7724 * Checks whether the browser can play ogg files.
7725 *
7726 * @static
7727 * @method canPlayOgg
7728 * @return {Boolean} True if the browser can play ogg files
7729 */
7730WebAudio.canPlayOgg = function() {
7731 if (!this._initialized) {
7732 this.initialize();
7733 }
7734 return !!this._canPlayOgg;
7735};
7736
7737/**
7738 * Checks whether the browser can play m4a files.
7739 *
7740 * @static
7741 * @method canPlayM4a
7742 * @return {Boolean} True if the browser can play m4a files
7743 */
7744WebAudio.canPlayM4a = function() {
7745 if (!this._initialized) {
7746 this.initialize();
7747 }
7748 return !!this._canPlayM4a;
7749};
7750
7751/**
7752 * Sets the master volume of the all audio.
7753 *
7754 * @static
7755 * @method setMasterVolume
7756 * @param {Number} value Master volume (min: 0, max: 1)
7757 */
7758WebAudio.setMasterVolume = function(value) {
7759 this._masterVolume = value;
7760 if (this._masterGainNode) {
7761 this._masterGainNode.gain.setValueAtTime(this._masterVolume, this._context.currentTime);
7762 }
7763};
7764
7765/**
7766 * @static
7767 * @method _createContext
7768 * @private
7769 */
7770WebAudio._createContext = function() {
7771 try {
7772 if (typeof AudioContext !== 'undefined') {
7773 this._context = new AudioContext();
7774 } else if (typeof webkitAudioContext !== 'undefined') {
7775 this._context = new webkitAudioContext();
7776 }
7777 } catch (e) {
7778 this._context = null;
7779 }
7780};
7781
7782/**
7783 * @static
7784 * @method _detectCodecs
7785 * @private
7786 */
7787WebAudio._detectCodecs = function() {
7788 var audio = document.createElement('audio');
7789 if (audio.canPlayType) {
7790 this._canPlayOgg = audio.canPlayType('audio/ogg');
7791 this._canPlayM4a = audio.canPlayType('audio/mp4');
7792 }
7793};
7794
7795/**
7796 * @static
7797 * @method _createMasterGainNode
7798 * @private
7799 */
7800WebAudio._createMasterGainNode = function() {
7801 var context = WebAudio._context;
7802 if (context) {
7803 this._masterGainNode = context.createGain();
7804 this._masterGainNode.gain.setValueAtTime(this._masterVolume, context.currentTime);
7805 this._masterGainNode.connect(context.destination);
7806 }
7807};
7808
7809/**
7810 * @static
7811 * @method _setupEventHandlers
7812 * @private
7813 */
7814WebAudio._setupEventHandlers = function() {
7815 var resumeHandler = function() {
7816 var context = WebAudio._context;
7817 if (context && context.state === "suspended" && typeof context.resume === "function") {
7818 context.resume().then(function() {
7819 WebAudio._onTouchStart();
7820 })
7821 } else {
7822 WebAudio._onTouchStart();
7823 }
7824 };
7825 document.addEventListener("keydown", resumeHandler);
7826 document.addEventListener("mousedown", resumeHandler);
7827 document.addEventListener("touchend", resumeHandler);
7828 document.addEventListener('touchstart', this._onTouchStart.bind(this));
7829 document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
7830};
7831
7832/**
7833 * @static
7834 * @method _onTouchStart
7835 * @private
7836 */
7837WebAudio._onTouchStart = function() {
7838 var context = WebAudio._context;
7839 if (context && !this._unlocked) {
7840 // Unlock Web Audio on iOS
7841 var node = context.createBufferSource();
7842 node.start(0);
7843 this._unlocked = true;
7844 }
7845};
7846
7847/**
7848 * @static
7849 * @method _onVisibilityChange
7850 * @private
7851 */
7852WebAudio._onVisibilityChange = function() {
7853 if (document.visibilityState === 'hidden') {
7854 this._onHide();
7855 } else {
7856 this._onShow();
7857 }
7858};
7859
7860/**
7861 * @static
7862 * @method _onHide
7863 * @private
7864 */
7865WebAudio._onHide = function() {
7866 if (this._shouldMuteOnHide()) {
7867 this._fadeOut(1);
7868 }
7869};
7870
7871/**
7872 * @static
7873 * @method _onShow
7874 * @private
7875 */
7876WebAudio._onShow = function() {
7877 if (this._shouldMuteOnHide()) {
7878 this._fadeIn(0.5);
7879 }
7880};
7881
7882/**
7883 * @static
7884 * @method _shouldMuteOnHide
7885 * @private
7886 */
7887WebAudio._shouldMuteOnHide = function() {
7888 return Utils.isMobileDevice();
7889};
7890
7891/**
7892 * @static
7893 * @method _fadeIn
7894 * @param {Number} duration
7895 * @private
7896 */
7897WebAudio._fadeIn = function(duration) {
7898 if (this._masterGainNode) {
7899 var gain = this._masterGainNode.gain;
7900 var currentTime = WebAudio._context.currentTime;
7901 gain.setValueAtTime(0, currentTime);
7902 gain.linearRampToValueAtTime(this._masterVolume, currentTime + duration);
7903 }
7904};
7905
7906/**
7907 * @static
7908 * @method _fadeOut
7909 * @param {Number} duration
7910 * @private
7911 */
7912WebAudio._fadeOut = function(duration) {
7913 if (this._masterGainNode) {
7914 var gain = this._masterGainNode.gain;
7915 var currentTime = WebAudio._context.currentTime;
7916 gain.setValueAtTime(this._masterVolume, currentTime);
7917 gain.linearRampToValueAtTime(0, currentTime + duration);
7918 }
7919};
7920
7921/**
7922 * Clears the audio data.
7923 *
7924 * @method clear
7925 */
7926WebAudio.prototype.clear = function() {
7927 this.stop();
7928 this._buffer = null;
7929 this._sourceNode = null;
7930 this._gainNode = null;
7931 this._pannerNode = null;
7932 this._totalTime = 0;
7933 this._sampleRate = 0;
7934 this._loopStart = 0;
7935 this._loopLength = 0;
7936 this._startTime = 0;
7937 this._volume = 1;
7938 this._pitch = 1;
7939 this._pan = 0;
7940 this._endTimer = null;
7941 this._loadListeners = [];
7942 this._stopListeners = [];
7943 this._hasError = false;
7944 this._autoPlay = false;
7945};
7946
7947/**
7948 * [read-only] The url of the audio file.
7949 *
7950 * @property url
7951 * @type String
7952 */
7953Object.defineProperty(WebAudio.prototype, 'url', {
7954 get: function() {
7955 return this._url;
7956 },
7957 configurable: true
7958});
7959
7960/**
7961 * The volume of the audio.
7962 *
7963 * @property volume
7964 * @type Number
7965 */
7966Object.defineProperty(WebAudio.prototype, 'volume', {
7967 get: function() {
7968 return this._volume;
7969 },
7970 set: function(value) {
7971 this._volume = value;
7972 if (this._gainNode) {
7973 this._gainNode.gain.setValueAtTime(this._volume, WebAudio._context.currentTime);
7974 }
7975 },
7976 configurable: true
7977});
7978
7979/**
7980 * The pitch of the audio.
7981 *
7982 * @property pitch
7983 * @type Number
7984 */
7985Object.defineProperty(WebAudio.prototype, 'pitch', {
7986 get: function() {
7987 return this._pitch;
7988 },
7989 set: function(value) {
7990 if (this._pitch !== value) {
7991 this._pitch = value;
7992 if (this.isPlaying()) {
7993 this.play(this._sourceNode.loop, 0);
7994 }
7995 }
7996 },
7997 configurable: true
7998});
7999
8000/**
8001 * The pan of the audio.
8002 *
8003 * @property pan
8004 * @type Number
8005 */
8006Object.defineProperty(WebAudio.prototype, 'pan', {
8007 get: function() {
8008 return this._pan;
8009 },
8010 set: function(value) {
8011 this._pan = value;
8012 this._updatePanner();
8013 },
8014 configurable: true
8015});
8016
8017/**
8018 * Checks whether the audio data is ready to play.
8019 *
8020 * @method isReady
8021 * @return {Boolean} True if the audio data is ready to play
8022 */
8023WebAudio.prototype.isReady = function() {
8024 return !!this._buffer;
8025};
8026
8027/**
8028 * Checks whether a loading error has occurred.
8029 *
8030 * @method isError
8031 * @return {Boolean} True if a loading error has occurred
8032 */
8033WebAudio.prototype.isError = function() {
8034 return this._hasError;
8035};
8036
8037/**
8038 * Checks whether the audio is playing.
8039 *
8040 * @method isPlaying
8041 * @return {Boolean} True if the audio is playing
8042 */
8043WebAudio.prototype.isPlaying = function() {
8044 return !!this._sourceNode;
8045};
8046
8047/**
8048 * Plays the audio.
8049 *
8050 * @method play
8051 * @param {Boolean} loop Whether the audio data play in a loop
8052 * @param {Number} offset The start position to play in seconds
8053 */
8054WebAudio.prototype.play = function(loop, offset) {
8055 if (this.isReady()) {
8056 offset = offset || 0;
8057 this._startPlaying(loop, offset);
8058 } else if (WebAudio._context) {
8059 this._autoPlay = true;
8060 this.addLoadListener(function() {
8061 if (this._autoPlay) {
8062 this.play(loop, offset);
8063 }
8064 }.bind(this));
8065 }
8066};
8067
8068/**
8069 * Stops the audio.
8070 *
8071 * @method stop
8072 */
8073WebAudio.prototype.stop = function() {
8074 this._autoPlay = false;
8075 this._removeEndTimer();
8076 this._removeNodes();
8077 if (this._stopListeners) {
8078 while (this._stopListeners.length > 0) {
8079 var listner = this._stopListeners.shift();
8080 listner();
8081 }
8082 }
8083};
8084
8085/**
8086 * Performs the audio fade-in.
8087 *
8088 * @method fadeIn
8089 * @param {Number} duration Fade-in time in seconds
8090 */
8091WebAudio.prototype.fadeIn = function(duration) {
8092 if (this.isReady()) {
8093 if (this._gainNode) {
8094 var gain = this._gainNode.gain;
8095 var currentTime = WebAudio._context.currentTime;
8096 gain.setValueAtTime(0, currentTime);
8097 gain.linearRampToValueAtTime(this._volume, currentTime + duration);
8098 }
8099 } else if (this._autoPlay) {
8100 this.addLoadListener(function() {
8101 this.fadeIn(duration);
8102 }.bind(this));
8103 }
8104};
8105
8106/**
8107 * Performs the audio fade-out.
8108 *
8109 * @method fadeOut
8110 * @param {Number} duration Fade-out time in seconds
8111 */
8112WebAudio.prototype.fadeOut = function(duration) {
8113 if (this._gainNode) {
8114 var gain = this._gainNode.gain;
8115 var currentTime = WebAudio._context.currentTime;
8116 gain.setValueAtTime(this._volume, currentTime);
8117 gain.linearRampToValueAtTime(0, currentTime + duration);
8118 }
8119 this._autoPlay = false;
8120};
8121
8122/**
8123 * Gets the seek position of the audio.
8124 *
8125 * @method seek
8126 */
8127WebAudio.prototype.seek = function() {
8128 if (WebAudio._context) {
8129 var pos = (WebAudio._context.currentTime - this._startTime) * this._pitch;
8130 if (this._loopLength > 0) {
8131 while (pos >= this._loopStart + this._loopLength) {
8132 pos -= this._loopLength;
8133 }
8134 }
8135 return pos;
8136 } else {
8137 return 0;
8138 }
8139};
8140
8141/**
8142 * Add a callback function that will be called when the audio data is loaded.
8143 *
8144 * @method addLoadListener
8145 * @param {Function} listner The callback function
8146 */
8147WebAudio.prototype.addLoadListener = function(listner) {
8148 this._loadListeners.push(listner);
8149};
8150
8151/**
8152 * Add a callback function that will be called when the playback is stopped.
8153 *
8154 * @method addStopListener
8155 * @param {Function} listner The callback function
8156 */
8157WebAudio.prototype.addStopListener = function(listner) {
8158 this._stopListeners.push(listner);
8159};
8160
8161/**
8162 * @method _load
8163 * @param {String} url
8164 * @private
8165 */
8166WebAudio.prototype._load = function(url) {
8167 if (WebAudio._context) {
8168 var xhr = new XMLHttpRequest();
8169 if(Decrypter.hasEncryptedAudio) url = Decrypter.extToEncryptExt(url);
8170 xhr.open('GET', url);
8171 xhr.responseType = 'arraybuffer';
8172 xhr.onload = function() {
8173 if (xhr.status < 400) {
8174 this._onXhrLoad(xhr);
8175 }
8176 }.bind(this);
8177 xhr.onerror = this._loader || function(){this._hasError = true;}.bind(this);
8178 xhr.send();
8179 }
8180};
8181
8182/**
8183 * @method _onXhrLoad
8184 * @param {XMLHttpRequest} xhr
8185 * @private
8186 */
8187WebAudio.prototype._onXhrLoad = function(xhr) {
8188 var array = xhr.response;
8189 if(Decrypter.hasEncryptedAudio) array = Decrypter.decryptArrayBuffer(array);
8190 this._readLoopComments(new Uint8Array(array));
8191 WebAudio._context.decodeAudioData(array, function(buffer) {
8192 this._buffer = buffer;
8193 this._totalTime = buffer.duration;
8194 if (this._loopLength > 0 && this._sampleRate > 0) {
8195 this._loopStart /= this._sampleRate;
8196 this._loopLength /= this._sampleRate;
8197 } else {
8198 this._loopStart = 0;
8199 this._loopLength = this._totalTime;
8200 }
8201 this._onLoad();
8202 }.bind(this));
8203};
8204
8205/**
8206 * @method _startPlaying
8207 * @param {Boolean} loop
8208 * @param {Number} offset
8209 * @private
8210 */
8211WebAudio.prototype._startPlaying = function(loop, offset) {
8212 if (this._loopLength > 0) {
8213 while (offset >= this._loopStart + this._loopLength) {
8214 offset -= this._loopLength;
8215 }
8216 }
8217 this._removeEndTimer();
8218 this._removeNodes();
8219 this._createNodes();
8220 this._connectNodes();
8221 this._sourceNode.loop = loop;
8222 this._sourceNode.start(0, offset);
8223 this._startTime = WebAudio._context.currentTime - offset / this._pitch;
8224 this._createEndTimer();
8225};
8226
8227/**
8228 * @method _createNodes
8229 * @private
8230 */
8231WebAudio.prototype._createNodes = function() {
8232 var context = WebAudio._context;
8233 this._sourceNode = context.createBufferSource();
8234 this._sourceNode.buffer = this._buffer;
8235 this._sourceNode.loopStart = this._loopStart;
8236 this._sourceNode.loopEnd = this._loopStart + this._loopLength;
8237 this._sourceNode.playbackRate.setValueAtTime(this._pitch, context.currentTime);
8238 this._gainNode = context.createGain();
8239 this._gainNode.gain.setValueAtTime(this._volume, context.currentTime);
8240 this._pannerNode = context.createPanner();
8241 this._pannerNode.panningModel = 'equalpower';
8242 this._updatePanner();
8243};
8244
8245/**
8246 * @method _connectNodes
8247 * @private
8248 */
8249WebAudio.prototype._connectNodes = function() {
8250 this._sourceNode.connect(this._gainNode);
8251 this._gainNode.connect(this._pannerNode);
8252 this._pannerNode.connect(WebAudio._masterGainNode);
8253};
8254
8255/**
8256 * @method _removeNodes
8257 * @private
8258 */
8259WebAudio.prototype._removeNodes = function() {
8260 if (this._sourceNode) {
8261 this._sourceNode.stop(0);
8262 this._sourceNode = null;
8263 this._gainNode = null;
8264 this._pannerNode = null;
8265 }
8266};
8267
8268/**
8269 * @method _createEndTimer
8270 * @private
8271 */
8272WebAudio.prototype._createEndTimer = function() {
8273 if (this._sourceNode && !this._sourceNode.loop) {
8274 var endTime = this._startTime + this._totalTime / this._pitch;
8275 var delay = endTime - WebAudio._context.currentTime;
8276 this._endTimer = setTimeout(function() {
8277 this.stop();
8278 }.bind(this), delay * 1000);
8279 }
8280};
8281
8282/**
8283 * @method _removeEndTimer
8284 * @private
8285 */
8286WebAudio.prototype._removeEndTimer = function() {
8287 if (this._endTimer) {
8288 clearTimeout(this._endTimer);
8289 this._endTimer = null;
8290 }
8291};
8292
8293/**
8294 * @method _updatePanner
8295 * @private
8296 */
8297WebAudio.prototype._updatePanner = function() {
8298 if (this._pannerNode) {
8299 var x = this._pan;
8300 var z = 1 - Math.abs(x);
8301 this._pannerNode.setPosition(x, 0, z);
8302 }
8303};
8304
8305/**
8306 * @method _onLoad
8307 * @private
8308 */
8309WebAudio.prototype._onLoad = function() {
8310 while (this._loadListeners.length > 0) {
8311 var listner = this._loadListeners.shift();
8312 listner();
8313 }
8314};
8315
8316/**
8317 * @method _readLoopComments
8318 * @param {Uint8Array} array
8319 * @private
8320 */
8321WebAudio.prototype._readLoopComments = function(array) {
8322 this._readOgg(array);
8323 this._readMp4(array);
8324};
8325
8326/**
8327 * @method _readOgg
8328 * @param {Uint8Array} array
8329 * @private
8330 */
8331WebAudio.prototype._readOgg = function(array) {
8332 var index = 0;
8333 while (index < array.length) {
8334 if (this._readFourCharacters(array, index) === 'OggS') {
8335 index += 26;
8336 var vorbisHeaderFound = false;
8337 var numSegments = array[index++];
8338 var segments = [];
8339 for (var i = 0; i < numSegments; i++) {
8340 segments.push(array[index++]);
8341 }
8342 for (i = 0; i < numSegments; i++) {
8343 if (this._readFourCharacters(array, index + 1) === 'vorb') {
8344 var headerType = array[index];
8345 if (headerType === 1) {
8346 this._sampleRate = this._readLittleEndian(array, index + 12);
8347 } else if (headerType === 3) {
8348 this._readMetaData(array, index, segments[i]);
8349 }
8350 vorbisHeaderFound = true;
8351 }
8352 index += segments[i];
8353 }
8354 if (!vorbisHeaderFound) {
8355 break;
8356 }
8357 } else {
8358 break;
8359 }
8360 }
8361};
8362
8363/**
8364 * @method _readMp4
8365 * @param {Uint8Array} array
8366 * @private
8367 */
8368WebAudio.prototype._readMp4 = function(array) {
8369 if (this._readFourCharacters(array, 4) === 'ftyp') {
8370 var index = 0;
8371 while (index < array.length) {
8372 var size = this._readBigEndian(array, index);
8373 var name = this._readFourCharacters(array, index + 4);
8374 if (name === 'moov') {
8375 index += 8;
8376 } else {
8377 if (name === 'mvhd') {
8378 this._sampleRate = this._readBigEndian(array, index + 20);
8379 }
8380 if (name === 'udta' || name === 'meta') {
8381 this._readMetaData(array, index, size);
8382 }
8383 index += size;
8384 if (size <= 1) {
8385 break;
8386 }
8387 }
8388 }
8389 }
8390};
8391
8392/**
8393 * @method _readMetaData
8394 * @param {Uint8Array} array
8395 * @param {Number} index
8396 * @param {Number} size
8397 * @private
8398 */
8399WebAudio.prototype._readMetaData = function(array, index, size) {
8400 for (var i = index; i < index + size - 10; i++) {
8401 if (this._readFourCharacters(array, i) === 'LOOP') {
8402 var text = '';
8403 while (array[i] > 0) {
8404 text += String.fromCharCode(array[i++]);
8405 }
8406 if (text.match(/LOOPSTART=([0-9]+)/)) {
8407 this._loopStart = parseInt(RegExp.$1);
8408 }
8409 if (text.match(/LOOPLENGTH=([0-9]+)/)) {
8410 this._loopLength = parseInt(RegExp.$1);
8411 }
8412 if (text == 'LOOPSTART' || text == 'LOOPLENGTH') {
8413 var text2 = '';
8414 i += 16;
8415 while (array[i] > 0) {
8416 text2 += String.fromCharCode(array[i++]);
8417 }
8418 if (text == 'LOOPSTART') {
8419 this._loopStart = parseInt(text2);
8420 } else {
8421 this._loopLength = parseInt(text2);
8422 }
8423 }
8424 }
8425 }
8426};
8427
8428/**
8429 * @method _readLittleEndian
8430 * @param {Uint8Array} array
8431 * @param {Number} index
8432 * @private
8433 */
8434WebAudio.prototype._readLittleEndian = function(array, index) {
8435 return (array[index + 3] * 0x1000000 + array[index + 2] * 0x10000 +
8436 array[index + 1] * 0x100 + array[index + 0]);
8437};
8438
8439/**
8440 * @method _readBigEndian
8441 * @param {Uint8Array} array
8442 * @param {Number} index
8443 * @private
8444 */
8445WebAudio.prototype._readBigEndian = function(array, index) {
8446 return (array[index + 0] * 0x1000000 + array[index + 1] * 0x10000 +
8447 array[index + 2] * 0x100 + array[index + 3]);
8448};
8449
8450/**
8451 * @method _readFourCharacters
8452 * @param {Uint8Array} array
8453 * @param {Number} index
8454 * @private
8455 */
8456WebAudio.prototype._readFourCharacters = function(array, index) {
8457 var string = '';
8458 for (var i = 0; i < 4; i++) {
8459 string += String.fromCharCode(array[index + i]);
8460 }
8461 return string;
8462};
8463
8464//-----------------------------------------------------------------------------
8465/**
8466 * The static class that handles HTML5 Audio.
8467 *
8468 * @class Html5Audio
8469 * @constructor
8470 */
8471function Html5Audio() {
8472 throw new Error('This is a static class');
8473}
8474
8475Html5Audio._initialized = false;
8476Html5Audio._unlocked = false;
8477Html5Audio._audioElement = null;
8478Html5Audio._gainTweenInterval = null;
8479Html5Audio._tweenGain = 0;
8480Html5Audio._tweenTargetGain = 0;
8481Html5Audio._tweenGainStep = 0;
8482Html5Audio._staticSePath = null;
8483
8484/**
8485 * Sets up the Html5 Audio.
8486 *
8487 * @static
8488 * @method setup
8489 * @param {String} url The url of the audio file
8490 */
8491Html5Audio.setup = function (url) {
8492 if (!this._initialized) {
8493 this.initialize();
8494 }
8495 this.clear();
8496
8497 if(Decrypter.hasEncryptedAudio && this._audioElement.src) {
8498 window.URL.revokeObjectURL(this._audioElement.src);
8499 }
8500 this._url = url;
8501};
8502
8503/**
8504 * Initializes the audio system.
8505 *
8506 * @static
8507 * @method initialize
8508 * @return {Boolean} True if the audio system is available
8509 */
8510Html5Audio.initialize = function () {
8511 if (!this._initialized) {
8512 if (!this._audioElement) {
8513 try {
8514 this._audioElement = new Audio();
8515 } catch (e) {
8516 this._audioElement = null;
8517 }
8518 }
8519 if (!!this._audioElement) this._setupEventHandlers();
8520 this._initialized = true;
8521 }
8522 return !!this._audioElement;
8523};
8524
8525/**
8526 * @static
8527 * @method _setupEventHandlers
8528 * @private
8529 */
8530Html5Audio._setupEventHandlers = function () {
8531 document.addEventListener('touchstart', this._onTouchStart.bind(this));
8532 document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
8533 this._audioElement.addEventListener("loadeddata", this._onLoadedData.bind(this));
8534 this._audioElement.addEventListener("error", this._onError.bind(this));
8535 this._audioElement.addEventListener("ended", this._onEnded.bind(this));
8536};
8537
8538/**
8539 * @static
8540 * @method _onTouchStart
8541 * @private
8542 */
8543Html5Audio._onTouchStart = function () {
8544 if (this._audioElement && !this._unlocked) {
8545 if (this._isLoading) {
8546 this._load(this._url);
8547 this._unlocked = true;
8548 } else {
8549 if (this._staticSePath) {
8550 this._audioElement.src = this._staticSePath;
8551 this._audioElement.volume = 0;
8552 this._audioElement.loop = false;
8553 this._audioElement.play();
8554 this._unlocked = true;
8555 }
8556 }
8557 }
8558};
8559
8560/**
8561 * @static
8562 * @method _onVisibilityChange
8563 * @private
8564 */
8565Html5Audio._onVisibilityChange = function () {
8566 if (document.visibilityState === 'hidden') {
8567 this._onHide();
8568 } else {
8569 this._onShow();
8570 }
8571};
8572
8573/**
8574 * @static
8575 * @method _onLoadedData
8576 * @private
8577 */
8578Html5Audio._onLoadedData = function () {
8579 this._buffered = true;
8580 if (this._unlocked) this._onLoad();
8581};
8582
8583/**
8584 * @static
8585 * @method _onError
8586 * @private
8587 */
8588Html5Audio._onError = function () {
8589 this._hasError = true;
8590};
8591
8592/**
8593 * @static
8594 * @method _onEnded
8595 * @private
8596 */
8597Html5Audio._onEnded = function () {
8598 if (!this._audioElement.loop) {
8599 this.stop();
8600 }
8601};
8602
8603/**
8604 * @static
8605 * @method _onHide
8606 * @private
8607 */
8608Html5Audio._onHide = function () {
8609 this._audioElement.volume = 0;
8610 this._tweenGain = 0;
8611};
8612
8613/**
8614 * @static
8615 * @method _onShow
8616 * @private
8617 */
8618Html5Audio._onShow = function () {
8619 this.fadeIn(0.5);
8620};
8621
8622/**
8623 * Clears the audio data.
8624 *
8625 * @static
8626 * @method clear
8627 */
8628Html5Audio.clear = function () {
8629 this.stop();
8630 this._volume = 1;
8631 this._loadListeners = [];
8632 this._hasError = false;
8633 this._autoPlay = false;
8634 this._isLoading = false;
8635 this._buffered = false;
8636};
8637
8638/**
8639 * Set the URL of static se.
8640 *
8641 * @static
8642 * @param {String} url
8643 */
8644Html5Audio.setStaticSe = function (url) {
8645 if (!this._initialized) {
8646 this.initialize();
8647 this.clear();
8648 }
8649 this._staticSePath = url;
8650};
8651
8652/**
8653 * [read-only] The url of the audio file.
8654 *
8655 * @property url
8656 * @type String
8657 */
8658Object.defineProperty(Html5Audio, 'url', {
8659 get: function () {
8660 return Html5Audio._url;
8661 },
8662 configurable: true
8663});
8664
8665/**
8666 * The volume of the audio.
8667 *
8668 * @property volume
8669 * @type Number
8670 */
8671Object.defineProperty(Html5Audio, 'volume', {
8672 get: function () {
8673 return Html5Audio._volume;
8674 }.bind(this),
8675 set: function (value) {
8676 Html5Audio._volume = value;
8677 if (Html5Audio._audioElement) {
8678 Html5Audio._audioElement.volume = this._volume;
8679 }
8680 },
8681 configurable: true
8682});
8683
8684/**
8685 * Checks whether the audio data is ready to play.
8686 *
8687 * @static
8688 * @method isReady
8689 * @return {Boolean} True if the audio data is ready to play
8690 */
8691Html5Audio.isReady = function () {
8692 return this._buffered;
8693};
8694
8695/**
8696 * Checks whether a loading error has occurred.
8697 *
8698 * @static
8699 * @method isError
8700 * @return {Boolean} True if a loading error has occurred
8701 */
8702Html5Audio.isError = function () {
8703 return this._hasError;
8704};
8705
8706/**
8707 * Checks whether the audio is playing.
8708 *
8709 * @static
8710 * @method isPlaying
8711 * @return {Boolean} True if the audio is playing
8712 */
8713Html5Audio.isPlaying = function () {
8714 return !this._audioElement.paused;
8715};
8716
8717/**
8718 * Plays the audio.
8719 *
8720 * @static
8721 * @method play
8722 * @param {Boolean} loop Whether the audio data play in a loop
8723 * @param {Number} offset The start position to play in seconds
8724 */
8725Html5Audio.play = function (loop, offset) {
8726 if (this.isReady()) {
8727 offset = offset || 0;
8728 this._startPlaying(loop, offset);
8729 } else if (Html5Audio._audioElement) {
8730 this._autoPlay = true;
8731 this.addLoadListener(function () {
8732 if (this._autoPlay) {
8733 this.play(loop, offset);
8734 if (this._gainTweenInterval) {
8735 clearInterval(this._gainTweenInterval);
8736 this._gainTweenInterval = null;
8737 }
8738 }
8739 }.bind(this));
8740 if (!this._isLoading) this._load(this._url);
8741 }
8742};
8743
8744/**
8745 * Stops the audio.
8746 *
8747 * @static
8748 * @method stop
8749 */
8750Html5Audio.stop = function () {
8751 if (this._audioElement) this._audioElement.pause();
8752 this._autoPlay = false;
8753 if (this._tweenInterval) {
8754 clearInterval(this._tweenInterval);
8755 this._tweenInterval = null;
8756 this._audioElement.volume = 0;
8757 }
8758};
8759
8760/**
8761 * Performs the audio fade-in.
8762 *
8763 * @static
8764 * @method fadeIn
8765 * @param {Number} duration Fade-in time in seconds
8766 */
8767Html5Audio.fadeIn = function (duration) {
8768 if (this.isReady()) {
8769 if (this._audioElement) {
8770 this._tweenTargetGain = this._volume;
8771 this._tweenGain = 0;
8772 this._startGainTween(duration);
8773 }
8774 } else if (this._autoPlay) {
8775 this.addLoadListener(function () {
8776 this.fadeIn(duration);
8777 }.bind(this));
8778 }
8779};
8780
8781/**
8782 * Performs the audio fade-out.
8783 *
8784 * @static
8785 * @method fadeOut
8786 * @param {Number} duration Fade-out time in seconds
8787 */
8788Html5Audio.fadeOut = function (duration) {
8789 if (this._audioElement) {
8790 this._tweenTargetGain = 0;
8791 this._tweenGain = this._volume;
8792 this._startGainTween(duration);
8793 }
8794};
8795
8796/**
8797 * Gets the seek position of the audio.
8798 *
8799 * @static
8800 * @method seek
8801 */
8802Html5Audio.seek = function () {
8803 if (this._audioElement) {
8804 return this._audioElement.currentTime;
8805 } else {
8806 return 0;
8807 }
8808};
8809
8810/**
8811 * Add a callback function that will be called when the audio data is loaded.
8812 *
8813 * @static
8814 * @method addLoadListener
8815 * @param {Function} listner The callback function
8816 */
8817Html5Audio.addLoadListener = function (listner) {
8818 this._loadListeners.push(listner);
8819};
8820
8821/**
8822 * @static
8823 * @method _load
8824 * @param {String} url
8825 * @private
8826 */
8827Html5Audio._load = function (url) {
8828 if (this._audioElement) {
8829 this._isLoading = true;
8830 this._audioElement.src = url;
8831 this._audioElement.load();
8832 }
8833};
8834
8835/**
8836 * @static
8837 * @method _startPlaying
8838 * @param {Boolean} loop
8839 * @param {Number} offset
8840 * @private
8841 */
8842Html5Audio._startPlaying = function (loop, offset) {
8843 this._audioElement.loop = loop;
8844 if (this._gainTweenInterval) {
8845 clearInterval(this._gainTweenInterval);
8846 this._gainTweenInterval = null;
8847 }
8848 if (this._audioElement) {
8849 this._audioElement.volume = this._volume;
8850 this._audioElement.currentTime = offset;
8851 this._audioElement.play();
8852 }
8853};
8854
8855/**
8856 * @static
8857 * @method _onLoad
8858 * @private
8859 */
8860Html5Audio._onLoad = function () {
8861 this._isLoading = false;
8862 while (this._loadListeners.length > 0) {
8863 var listener = this._loadListeners.shift();
8864 listener();
8865 }
8866};
8867
8868/**
8869 * @static
8870 * @method _startGainTween
8871 * @params {Number} duration
8872 * @private
8873 */
8874Html5Audio._startGainTween = function (duration) {
8875 this._audioElement.volume = this._tweenGain;
8876 if (this._gainTweenInterval) {
8877 clearInterval(this._gainTweenInterval);
8878 this._gainTweenInterval = null;
8879 }
8880 this._tweenGainStep = (this._tweenTargetGain - this._tweenGain) / (60 * duration);
8881 this._gainTweenInterval = setInterval(function () {
8882 Html5Audio._applyTweenValue(Html5Audio._tweenTargetGain);
8883 }, 1000 / 60);
8884};
8885
8886/**
8887 * @static
8888 * @method _applyTweenValue
8889 * @param {Number} volume
8890 * @private
8891 */
8892Html5Audio._applyTweenValue = function (volume) {
8893 Html5Audio._tweenGain += Html5Audio._tweenGainStep;
8894 if (Html5Audio._tweenGain < 0 && Html5Audio._tweenGainStep < 0) {
8895 Html5Audio._tweenGain = 0;
8896 }
8897 else if (Html5Audio._tweenGain > volume && Html5Audio._tweenGainStep > 0) {
8898 Html5Audio._tweenGain = volume;
8899 }
8900
8901 if (Math.abs(Html5Audio._tweenTargetGain - Html5Audio._tweenGain) < 0.01) {
8902 Html5Audio._tweenGain = Html5Audio._tweenTargetGain;
8903 clearInterval(Html5Audio._gainTweenInterval);
8904 Html5Audio._gainTweenInterval = null;
8905 }
8906
8907 Html5Audio._audioElement.volume = Html5Audio._tweenGain;
8908};
8909
8910//-----------------------------------------------------------------------------
8911/**
8912 * The static class that handles JSON with object information.
8913 *
8914 * @class JsonEx
8915 */
8916function JsonEx() {
8917 throw new Error('This is a static class');
8918}
8919
8920/**
8921 * The maximum depth of objects.
8922 *
8923 * @static
8924 * @property maxDepth
8925 * @type Number
8926 * @default 100
8927 */
8928JsonEx.maxDepth = 100;
8929
8930JsonEx._id = 1;
8931JsonEx._generateId = function(){
8932 return JsonEx._id++;
8933};
8934
8935/**
8936 * Converts an object to a JSON string with object information.
8937 *
8938 * @static
8939 * @method stringify
8940 * @param {Object} object The object to be converted
8941 * @return {String} The JSON string
8942 */
8943JsonEx.stringify = function(object) {
8944 var circular = [];
8945 JsonEx._id = 1;
8946 var json = JSON.stringify(this._encode(object, circular, 0));
8947 this._cleanMetadata(object);
8948 this._restoreCircularReference(circular);
8949
8950 return json;
8951};
8952
8953JsonEx._restoreCircularReference = function(circulars){
8954 circulars.forEach(function(circular){
8955 var key = circular[0];
8956 var value = circular[1];
8957 var content = circular[2];
8958
8959 value[key] = content;
8960 });
8961};
8962
8963/**
8964 * Parses a JSON string and reconstructs the corresponding object.
8965 *
8966 * @static
8967 * @method parse
8968 * @param {String} json The JSON string
8969 * @return {Object} The reconstructed object
8970 */
8971JsonEx.parse = function(json) {
8972 var circular = [];
8973 var registry = {};
8974 var contents = this._decode(JSON.parse(json), circular, registry);
8975 this._cleanMetadata(contents);
8976 this._linkCircularReference(contents, circular, registry);
8977
8978 return contents;
8979};
8980
8981JsonEx._linkCircularReference = function(contents, circulars, registry){
8982 circulars.forEach(function(circular){
8983 var key = circular[0];
8984 var value = circular[1];
8985 var id = circular[2];
8986
8987 value[key] = registry[id];
8988 });
8989};
8990
8991JsonEx._cleanMetadata = function(object){
8992 if(!object) return;
8993
8994 delete object['@'];
8995 delete object['@c'];
8996
8997 if(typeof object === 'object'){
8998 Object.keys(object).forEach(function(key){
8999 var value = object[key];
9000 if(typeof value === 'object'){
9001 JsonEx._cleanMetadata(value);
9002 }
9003 });
9004 }
9005};
9006
9007
9008/**
9009 * Makes a deep copy of the specified object.
9010 *
9011 * @static
9012 * @method makeDeepCopy
9013 * @param {Object} object The object to be copied
9014 * @return {Object} The copied object
9015 */
9016JsonEx.makeDeepCopy = function(object) {
9017 return this.parse(this.stringify(object));
9018};
9019
9020/**
9021 * @static
9022 * @method _encode
9023 * @param {Object} value
9024 * @param {Array} circular
9025 * @param {Number} depth
9026 * @return {Object}
9027 * @private
9028 */
9029JsonEx._encode = function(value, circular, depth) {
9030 depth = depth || 0;
9031 if (++depth >= this.maxDepth) {
9032 throw new Error('Object too deep');
9033 }
9034 var type = Object.prototype.toString.call(value);
9035 if (type === '[object Object]' || type === '[object Array]') {
9036 value['@c'] = JsonEx._generateId();
9037
9038 var constructorName = this._getConstructorName(value);
9039 if (constructorName !== 'Object' && constructorName !== 'Array') {
9040 value['@'] = constructorName;
9041 }
9042 for (var key in value) {
9043 if (value.hasOwnProperty(key) && !key.match(/^@./)) {
9044 if(value[key] && typeof value[key] === 'object'){
9045 if(value[key]['@c']){
9046 circular.push([key, value, value[key]]);
9047 value[key] = {'@r': value[key]['@c']};
9048 }else{
9049 value[key] = this._encode(value[key], circular, depth + 1);
9050
9051 if(value[key] instanceof Array){
9052 //wrap array
9053 circular.push([key, value, value[key]]);
9054
9055 value[key] = {
9056 '@c': value[key]['@c'],
9057 '@a': value[key]
9058 };
9059 }
9060 }
9061 }else{
9062 value[key] = this._encode(value[key], circular, depth + 1);
9063 }
9064 }
9065 }
9066 }
9067 depth--;
9068 return value;
9069};
9070
9071/**
9072 * @static
9073 * @method _decode
9074 * @param {Object} value
9075 * @param {Array} circular
9076 * @param {Object} registry
9077 * @return {Object}
9078 * @private
9079 */
9080JsonEx._decode = function(value, circular, registry) {
9081 var type = Object.prototype.toString.call(value);
9082 if (type === '[object Object]' || type === '[object Array]') {
9083 registry[value['@c']] = value;
9084
9085 if (value['@']) {
9086 var constructor = window[value['@']];
9087 if (constructor) {
9088 value = this._resetPrototype(value, constructor.prototype);
9089 }
9090 }
9091 for (var key in value) {
9092 if (value.hasOwnProperty(key)) {
9093 if(value[key] && value[key]['@a']){
9094 //object is array wrapper
9095 var body = value[key]['@a'];
9096 body['@c'] = value[key]['@c'];
9097 value[key] = body;
9098 }
9099 if(value[key] && value[key]['@r']){
9100 //object is reference
9101 circular.push([key, value, value[key]['@r']])
9102 }
9103 value[key] = this._decode(value[key], circular, registry);
9104 }
9105 }
9106 }
9107 return value;
9108};
9109
9110/**
9111 * @static
9112 * @method _getConstructorName
9113 * @param {Object} value
9114 * @return {String}
9115 * @private
9116 */
9117JsonEx._getConstructorName = function(value) {
9118 var name = value.constructor.name;
9119 if (name === undefined) {
9120 var func = /^\s*function\s*([A-Za-z0-9_$]*)/;
9121 name = func.exec(value.constructor)[1];
9122 }
9123 return name;
9124};
9125
9126/**
9127 * @static
9128 * @method _resetPrototype
9129 * @param {Object} value
9130 * @param {Object} prototype
9131 * @return {Object}
9132 * @private
9133 */
9134JsonEx._resetPrototype = function(value, prototype) {
9135 if (Object.setPrototypeOf !== undefined) {
9136 Object.setPrototypeOf(value, prototype);
9137 } else if ('__proto__' in value) {
9138 value.__proto__ = prototype;
9139 } else {
9140 var newValue = Object.create(prototype);
9141 for (var key in value) {
9142 if (value.hasOwnProperty(key)) {
9143 newValue[key] = value[key];
9144 }
9145 }
9146 value = newValue;
9147 }
9148 return value;
9149};
9150
9151
9152function Decrypter() {
9153 throw new Error('This is a static class');
9154}
9155
9156Decrypter.hasEncryptedImages = false;
9157Decrypter.hasEncryptedAudio = false;
9158Decrypter._requestImgFile = [];
9159Decrypter._headerlength = 16;
9160Decrypter._xhrOk = 400;
9161Decrypter._encryptionKey = "";
9162Decrypter._ignoreList = [
9163 "img/system/Window.png"
9164];
9165Decrypter.SIGNATURE = "5250474d56000000";
9166Decrypter.VER = "000301";
9167Decrypter.REMAIN = "0000000000";
9168
9169Decrypter.checkImgIgnore = function(url){
9170 for(var cnt = 0; cnt < this._ignoreList.length; cnt++) {
9171 if(url === this._ignoreList[cnt]) return true;
9172 }
9173 return false;
9174};
9175
9176Decrypter.decryptImg = function(url, bitmap) {
9177 url = this.extToEncryptExt(url);
9178
9179 var requestFile = new XMLHttpRequest();
9180 requestFile.open("GET", url);
9181 requestFile.responseType = "arraybuffer";
9182 requestFile.send();
9183
9184 requestFile.onload = function () {
9185 if(this.status < Decrypter._xhrOk) {
9186 var arrayBuffer = Decrypter.decryptArrayBuffer(requestFile.response);
9187 bitmap._image.src = Decrypter.createBlobUrl(arrayBuffer);
9188 bitmap._image.addEventListener('load', bitmap._loadListener = Bitmap.prototype._onLoad.bind(bitmap));
9189 bitmap._image.addEventListener('error', bitmap._errorListener = bitmap._loader || Bitmap.prototype._onError.bind(bitmap));
9190 }
9191 };
9192
9193 requestFile.onerror = function () {
9194 if (bitmap._loader) {
9195 bitmap._loader();
9196 } else {
9197 bitmap._onError();
9198 }
9199 };
9200};
9201
9202Decrypter.decryptHTML5Audio = function(url, bgm, pos) {
9203 var requestFile = new XMLHttpRequest();
9204 requestFile.open("GET", url);
9205 requestFile.responseType = "arraybuffer";
9206 requestFile.send();
9207
9208 requestFile.onload = function () {
9209 if(this.status < Decrypter._xhrOk) {
9210 var arrayBuffer = Decrypter.decryptArrayBuffer(requestFile.response);
9211 var url = Decrypter.createBlobUrl(arrayBuffer);
9212 AudioManager.createDecryptBuffer(url, bgm, pos);
9213 }
9214 };
9215};
9216
9217Decrypter.cutArrayHeader = function(arrayBuffer, length) {
9218 return arrayBuffer.slice(length);
9219};
9220
9221Decrypter.decryptArrayBuffer = function(arrayBuffer) {
9222 if (!arrayBuffer) return null;
9223 var header = new Uint8Array(arrayBuffer, 0, this._headerlength);
9224
9225 var i;
9226 var ref = this.SIGNATURE + this.VER + this.REMAIN;
9227 var refBytes = new Uint8Array(16);
9228 for (i = 0; i < this._headerlength; i++) {
9229 refBytes[i] = parseInt("0x" + ref.substr(i * 2, 2), 16);
9230 }
9231 for (i = 0; i < this._headerlength; i++) {
9232 if (header[i] !== refBytes[i]) {
9233 throw new Error("Header is wrong");
9234 }
9235 }
9236
9237 arrayBuffer = this.cutArrayHeader(arrayBuffer, Decrypter._headerlength);
9238 var view = new DataView(arrayBuffer);
9239 this.readEncryptionkey();
9240 if (arrayBuffer) {
9241 var byteArray = new Uint8Array(arrayBuffer);
9242 for (i = 0; i < this._headerlength; i++) {
9243 byteArray[i] = byteArray[i] ^ parseInt(Decrypter._encryptionKey[i], 16);
9244 view.setUint8(i, byteArray[i]);
9245 }
9246 }
9247
9248 return arrayBuffer;
9249};
9250
9251Decrypter.createBlobUrl = function(arrayBuffer){
9252 var blob = new Blob([arrayBuffer]);
9253 return window.URL.createObjectURL(blob);
9254};
9255
9256Decrypter.extToEncryptExt = function(url) {
9257 var ext = url.split('.').pop();
9258 var encryptedExt = ext;
9259
9260 if(ext === "ogg") encryptedExt = ".rpgmvo";
9261 else if(ext === "m4a") encryptedExt = ".rpgmvm";
9262 else if(ext === "png") encryptedExt = ".rpgmvp";
9263 else encryptedExt = ext;
9264
9265 return url.slice(0, url.lastIndexOf(ext) - 1) + encryptedExt;
9266};
9267
9268Decrypter.readEncryptionkey = function(){
9269 this._encryptionKey = $dataSystem.encryptionKey.split(/(.{2})/).filter(Boolean);
9270};
9271
9272//-----------------------------------------------------------------------------
9273/**
9274 * The static class that handles resource loading.
9275 *
9276 * @class ResourceHandler
9277 */
9278function ResourceHandler() {
9279 throw new Error('This is a static class');
9280}
9281
9282ResourceHandler._reloaders = [];
9283ResourceHandler._defaultRetryInterval = [500, 1000, 3000];
9284
9285ResourceHandler.createLoader = function(url, retryMethod, resignMethod, retryInterval) {
9286 retryInterval = retryInterval || this._defaultRetryInterval;
9287 var reloaders = this._reloaders;
9288 var retryCount = 0;
9289 return function() {
9290 if (retryCount < retryInterval.length) {
9291 setTimeout(retryMethod, retryInterval[retryCount]);
9292 retryCount++;
9293 } else {
9294 if (resignMethod) {
9295 resignMethod();
9296 }
9297 if (url) {
9298 if (reloaders.length === 0) {
9299 Graphics.printLoadingError(url);
9300 SceneManager.stop();
9301 }
9302 reloaders.push(function() {
9303 retryCount = 0;
9304 retryMethod();
9305 });
9306 }
9307 }
9308 };
9309};
9310
9311ResourceHandler.exists = function() {
9312 return this._reloaders.length > 0;
9313};
9314
9315ResourceHandler.retry = function() {
9316 if (this._reloaders.length > 0) {
9317 Graphics.eraseLoadingError();
9318 SceneManager.resume();
9319 this._reloaders.forEach(function(reloader) {
9320 reloader();
9321 });
9322 this._reloaders.length = 0;
9323 }
9324};