Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Tsunami
Path: blob/main/public/games/files/algaes-escapade/js/gamejs/gamejs.min.js
1037 views
1
/* This file has been generated by yabbler.js */
2
require.define({
3
"gamejs/mask": function(require, exports, module) {
4
var gamejs = require('../gamejs');
5
var objects = require('./utils/objects');
6
7
/**
8
* @fileoverview Image masks. Usefull for pixel perfect collision detection.
9
*/
10
11
/**
12
* Creates an image mask from the given Surface. The alpha of each pixel is checked
13
* to see if it is greater than the given threshold. If it is greater then
14
* that pixel is set as non-colliding.
15
*
16
* @param {gamejs.Surface} surface
17
* @param {Number} threshold 0 to 255. defaults to: 255, fully transparent
18
*/
19
exports.fromSurface = function(surface, threshold) {
20
threshold = threshold && (255 - threshold) || 255;
21
var imgData = surface.getImageData().data;
22
var dims = surface.getSize();
23
var mask = new Mask(dims);
24
for (var i=0;i<imgData.length;i += 4) {
25
// y: pixel # / width
26
var y = parseInt((i / 4) / dims[0], 10);
27
// x: pixel # % width
28
var x = parseInt((i / 4) % dims[0], 10);
29
var alpha = imgData[i+3];
30
if (alpha >= threshold) {
31
mask.setAt(x, y);
32
}
33
}
34
return mask;
35
};
36
37
/**
38
* Image Mask
39
* @param {Array} dimensions [width, height]
40
*
41
*/
42
var Mask = exports.Mask = function(dims) {
43
/**
44
* @ignore
45
*/
46
this.width = dims[0];
47
/**
48
* @ignore
49
*/
50
this.height = dims[1];
51
/**
52
* @ignore
53
*/
54
this._bits = [];
55
for (var i=0;i<this.width;i++) {
56
this._bits[i] = [];
57
for (var j=0;j<this.height;j++) {
58
this._bits[i][j] = false;
59
}
60
}
61
return;
62
};
63
64
/**
65
* @param {gamejs.mask.Mask} otherMask
66
* @param {Array} offset [x,y]
67
* @returns the overlapping rectangle or null if there is no overlap;
68
*/
69
Mask.prototype.overlapRect = function(otherMask, offset) {
70
var arect = this.rect;
71
var brect = otherMask.rect;
72
if (offset) {
73
brect.moveIp(offset);
74
}
75
// bounding box intersect
76
if (!brect.collideRect(arect)) {
77
return null;
78
}
79
var xStart = Math.max(arect.left, brect.left);
80
var xEnd = Math.min(arect.right, brect.right);
81
82
var yStart = Math.max(arect.top, brect.top);
83
var yEnd = Math.min(arect.bottom, brect.bottom);
84
85
return new gamejs.Rect([xStart, yStart], [xEnd - xStart, yEnd - yStart]);
86
};
87
88
/**
89
*
90
* @returns True if the otherMask overlaps with this map.
91
* @param {Mask} otherMask
92
* @param {Array} offset
93
*/
94
Mask.prototype.overlap = function(otherMask, offset) {
95
var overlapRect = this.overlapRect(otherMask, offset);
96
if (overlapRect === null) {
97
return false;
98
}
99
100
var arect = this.rect;
101
var brect = otherMask.rect;
102
if (offset) {
103
brect.moveIp(offset);
104
}
105
106
var count = 0;
107
for (var y=overlapRect.top; y<=overlapRect.bottom; y++) {
108
for (var x=overlapRect.left; x<=overlapRect.right; x++) {
109
if (this.getAt(x - arect.left, y - arect.top) &&
110
otherMask.getAt(x - brect.left, y - brect.top)) {
111
return true;
112
}
113
}
114
}
115
// NOTE this should not happen because either we bailed out
116
// long ago because the rects do not overlap or there is an
117
// overlap and we should not have gotten this far.
118
// throw new Error("Maks.overlap: overlap detected but could not create mask for it.");
119
return false;
120
};
121
122
/**
123
* @param {gamejs.mask.Mask} otherMask
124
* @param {Array} offset [x,y]
125
* @returns the number of overlapping pixels
126
*/
127
Mask.prototype.overlapArea = function(otherMask, offset) {
128
var overlapRect = this.overlapRect(otherMask, offset);
129
if (overlapRect === null) {
130
return 0;
131
}
132
133
var arect = this.rect;
134
var brect = otherMask.rect;
135
if (offset) {
136
brect.moveIp(offset);
137
}
138
139
var count = 0;
140
for (var y=overlapRect.top; y<=overlapRect.bottom; y++) {
141
for (var x=overlapRect.left; x<=overlapRect.right; x++) {
142
if (this.getAt(x - arect.left, y - arect.top) &&
143
otherMask.getAt(x - brect.left, y - brect.top)) {
144
count++;
145
}
146
}
147
}
148
return count;
149
};
150
151
/**
152
* @param {gamejs.mask.Mask} otherMask
153
* @param {Array} offset [x,y]
154
* @returns a mask of the overlapping pixels
155
*/
156
Mask.prototype.overlapMask = function(otherMask, offset) {
157
var overlapRect = this.overlapRect(otherMask, offset);
158
if (overlapRect === null) {
159
return 0;
160
}
161
162
var arect = this.rect;
163
var brect = otherMask.rect;
164
if (offset) {
165
brect.moveIp(offset);
166
}
167
168
var mask = new Mask([overlapRect.width, overlapRect.height]);
169
for (var y=overlapRect.top; y<=overlapRect.bottom; y++) {
170
for (var x=overlapRect.left; x<=overlapRect.right; x++) {
171
if (this.getAt(x - arect.left, y - arect.top) &&
172
otherMask.getAt(x - brect.left, y - brect.top)) {
173
mask.setAt(x, y);
174
}
175
}
176
}
177
return mask;
178
};
179
180
/**
181
* Set bit at position.
182
* @param {Number} x
183
* @param {Number} y
184
*/
185
Mask.prototype.setAt = function(x, y) {
186
this._bits[x][y] = true;
187
};
188
189
/**
190
* Get bit at position.
191
*
192
* @param {Number} x
193
* @param {Number} y
194
*/
195
Mask.prototype.getAt = function(x, y) {
196
x = parseInt(x, 10);
197
y = parseInt(y, 10);
198
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
199
return false;
200
}
201
return this._bits[x][y];
202
};
203
204
205
/**
206
* Flip the bits in this map.
207
*/
208
Mask.prototype.invert = function() {
209
this._bits = this._bits.map(function(row) {
210
return row.map(function(b) {
211
return !b;
212
});
213
});
214
};
215
216
/**
217
* @returns {Array} the dimensions of the map
218
*/
219
Mask.prototype.getSize = function() {
220
return [this.width, this.height];
221
};
222
223
objects.accessors(Mask.prototype, {
224
/**
225
* Rect of this Mask.
226
*/
227
'rect': {
228
get: function() {
229
return new gamejs.Rect([0, 0], [this.width, this.height]);
230
}
231
},
232
/**
233
* @returns {Number} number of set pixels in this mask.
234
*/
235
'length': {
236
get: function() {
237
var c = 0;
238
this._bits.forEach(function(row) {
239
row.forEach(function(b) {
240
if (b) {
241
c++;
242
}
243
});
244
});
245
return c;
246
}
247
}
248
});
249
250
}}, ["gamejs", "gamejs/utils/objects"]);/* This file has been generated by yabbler.js */
251
require.define({
252
"gamejs/http": function(require, exports, module) {
253
/**
254
* @fileoverview Make synchronous http requests to your game's serverside component.
255
*
256
* If you configure a ajax base URL you can make http requests to your
257
* server using those functions.
258
259
* The most high-level functions are `load()` and `save()` which take
260
* and return a JavaScript object, which they will send to / recieve from
261
* the server-side in JSON format.
262
*
263
* @example
264
*
265
* <script>
266
* // Same Origin policy applies! You can only make requests
267
* // to the server from which the html page is served.
268
* var $g = {
269
* ajaxBaseHref: "http://the-same-server.com/ajax/"
270
* };
271
* </script>
272
* <script src="./public/gamejs-wrapped.js"></script>
273
* ....
274
* typeof gamejs.load('userdata/') === 'object'
275
* typeof gamejs.get('userdata/') === 'string'
276
* ...
277
*
278
*/
279
280
/**
281
* Response object returned by http functions `get` and `post`. This
282
* class is not instantiable.
283
*
284
* @param{String} responseText
285
* @param {String} responseXML
286
* @param {Number} status
287
* @param {String} statusText
288
*/
289
exports.Response = function() {
290
/**
291
* @param {String} header
292
*/
293
this.getResponseHeader = function(header) {
294
};
295
throw new Error('response class not instantiable');
296
};
297
298
/**
299
* Make http request to server-side
300
* @param {String} method http method
301
* @param {String} url
302
* @param {String|Object} data
303
* @param {String|Object} type "Accept" header value
304
* @return {Response} response
305
*/
306
var ajax = exports.ajax = function(method, url, data, type) {
307
data = data || null;
308
var response = new XMLHttpRequest();
309
response.open(method, url, false);
310
if (type) {
311
response.setRequestHeader("Accept", type);
312
}
313
if (data instanceof Object) {
314
data = JSON.stringify(data);
315
response.setRequestHeader('Content-Type', 'application/json');
316
}
317
response.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
318
response.send(data);
319
return response;
320
};
321
322
/**
323
* Make http GET request to server-side
324
* @param {String} url
325
*/
326
var get = exports.get = function(url) {
327
return ajax('GET', url);
328
};
329
330
/**
331
* Make http POST request to server-side
332
* @param {String} url
333
* @param {String|Object} data
334
* @param {String|Object} type "Accept" header value
335
* @returns {Response}
336
*/
337
var post = exports.post = function(url, data, type) {
338
return ajax('POST', url, data, type);
339
};
340
341
function stringify(response) {
342
// eval is evil
343
return eval('(' + response.responseText + ')');
344
}
345
346
function ajaxBaseHref() {
347
return (window.$g && window.$g.ajaxBaseHref) || './';
348
}
349
350
/**
351
* Load an object from the server-side.
352
* @param {String} url
353
* @return {Object} the object loaded from the server
354
*/
355
exports.load = function(url) {
356
return stringify(get(ajaxBaseHref() + url));
357
};
358
359
/**
360
* Send an object to a server-side function.
361
* @param {String} url
362
* @param {String|Object} data
363
* @param {String|Object} type "Accept" header value
364
* @returns {Object} the response object
365
*/
366
exports.save = function(url, data, type) {
367
return stringify(post(ajaxBaseHref() + url, {payload: data}, type));
368
};
369
370
}}, []);/* This file has been generated by yabbler.js */
371
require.define({
372
"gamejs/mixer": function(require, exports, module) {
373
var gamejs = require('../gamejs');
374
375
/**
376
* @fileoverview Playing sounds with the html5 audio tag. Audio files must be preloaded
377
* with the usual `gamejs.preload()` function. Ogg, wav and webm supported.
378
*
379
* Sounds & Images are loaded relative to './'.
380
*/
381
382
var CACHE = {};
383
384
/**
385
* need to export preloading status for require
386
* @ignore
387
*/
388
var _PRELOADING = false;
389
390
/**
391
* @ignore
392
*/
393
var NUM_CHANNELS = 8;
394
395
/**
396
* Sets the number of available channels for the mixer. The default value is 8.
397
*/
398
exports.setNumChannels = function(count) {
399
NUM_CHANNELS = parseInt(count, 10) || NUM_CHANNELS;
400
};
401
402
exports.getNumChannels = function() {
403
return NUM_CHANNELS;
404
};
405
406
/**
407
* put all audios on page in cache
408
* if same domain as current page, remove common href-prefix
409
* @ignore
410
*/
411
exports.init = function() {
412
var audios = Array.prototype.slice.call(document.getElementsByTagName("audio"), 0);
413
addToCache(audios);
414
return;
415
};
416
417
/**
418
* Preload the audios into cache
419
* @param {String[]} List of audio URIs to load
420
* @returns {Function} which returns 0-1 for preload progress
421
* @ignore
422
*/
423
exports.preload = function(audioUrls, showProgressOrImage) {
424
var countTotal = 0;
425
var countLoaded = 0;
426
427
function incrementLoaded() {
428
countLoaded++;
429
if (countLoaded == countTotal) {
430
_PRELOADING = false;
431
}
432
}
433
434
function getProgress() {
435
return countTotal > 0 ? countLoaded / countTotal : 1;
436
}
437
438
function successHandler() {
439
addToCache(this);
440
incrementLoaded();
441
}
442
function errorHandler() {
443
incrementLoaded();
444
throw new Error('Error loading ' + this.src);
445
}
446
447
for (var key in audioUrls) {
448
if (key.indexOf('wav') == -1 && key.indexOf('ogg') == -1 && key.indexOf('webm') == -1) {
449
continue;
450
}
451
countTotal++;
452
var audio = new Audio();
453
audio.addEventListener('canplay', successHandler, true);
454
audio.addEventListener('error', errorHandler, true);
455
audio.src = audioUrls[key];
456
audio.gamejsKey = key;
457
audio.load();
458
}
459
if (countTotal > 0) {
460
_PRELOADING = true;
461
}
462
return getProgress;
463
};
464
465
/**
466
* @ignore
467
*/
468
exports.isPreloading = function() {
469
return _PRELOADING;
470
};
471
472
/**
473
* @param {dom.ImgElement} audios the <audio> elements to put into cache
474
* @ignore
475
*/
476
function addToCache(audios) {
477
if (!(audios instanceof Array)) {
478
audios = [audios];
479
}
480
481
var docLoc = document.location.href;
482
audios.forEach(function(audio) {
483
CACHE[audio.gamejsKey] = audio;
484
});
485
return;
486
}
487
488
/**
489
* Sounds can be played back.
490
* @constructor
491
* @param {String|dom.AudioElement} uriOrAudio the uri of <audio> dom element
492
* of the sound
493
*/
494
exports.Sound = function Sound(uriOrAudio) {
495
var cachedAudio;
496
if (typeof uriOrAudio === 'string') {
497
cachedAudio = CACHE[uriOrAudio];
498
} else {
499
cachedAudio = uriOrAudio;
500
}
501
if (!cachedAudio) {
502
// TODO sync audio loading
503
throw new Error('Missing "' + uriOrAudio + '", gamejs.preload() all audio files before loading');
504
}
505
506
var channels = [];
507
var i = NUM_CHANNELS;
508
while (i-->0) {
509
var audio = new Audio();
510
audio.preload = "auto";
511
audio.loop = false;
512
audio.src = cachedAudio.src;
513
channels.push(audio);
514
}
515
/**
516
* start the sound
517
* @param {Boolean} loop whether the audio should loop for ever or not
518
*/
519
this.play = function(loop) {
520
channels.some(function(audio) {
521
if (audio.ended || audio.paused) {
522
audio.loop = !!loop;
523
audio.play();
524
return true;
525
}
526
return false;
527
});
528
};
529
530
/**
531
* Stop the sound.
532
* This will stop the playback of this Sound on any active Channels.
533
*/
534
this.stop = function() {
535
channels.forEach(function(audio) {
536
audio.stop();
537
});
538
};
539
540
/**
541
* Set volume of this sound
542
* @param {Number} value volume from 0 to 1
543
*/
544
this.setVolume = function(value) {
545
channels.forEach(function(audio) {
546
audio.volume = value;
547
});
548
};
549
550
/**
551
* @returns {Number} the sound's volume from 0 to 1
552
*/
553
this.getVolume = function() {
554
return channels[0].volume;
555
};
556
557
/**
558
* @returns {Number} Duration of this sound in seconds
559
*/
560
this.getLength = function() {
561
return channels[0].duration;
562
};
563
564
return this;
565
};
566
567
}}, ["gamejs"]);/* This file has been generated by yabbler.js */
568
require.define({
569
"gamejs/tmx": function(require, exports, module) {
570
var gamejs = require('gamejs');
571
var objects = require('gamejs/utils/objects');
572
var xml = require('gamejs/xml');
573
var base64 = require('gamejs/base64');
574
var uri = require('gamejs/utils/uri');
575
576
/**
577
* @fileoverview
578
* This is a loader for the general purpose tile map editor "Tiled".
579
*
580
* This module can load all ".tmx" files even if additionally base64 encoded
581
* (can be configured in Tiled).
582
*
583
* This module loads the whole map definition, including the TileSets with
584
* all necessary images. For an example on how to render a map loaded with
585
* this module, see `examples/tiledmap`.
586
*
587
* You will typically create a Map instance with `Map(url)` and deal
588
* with the layers, tilesets, etc. through the Map instance
589
* instead of loading & creating them yourself.
590
*
591
* Only orthogonol maps are supported (no isometric maps).
592
*
593
* @see http://www.mapeditor.org/
594
* @see https://github.com/bjorn/tiled/wiki/TMX-Map-Format
595
*/
596
597
/**
598
* My code is inspired by:
599
* * https://bitbucket.org/maikg/tiled2cocos/
600
* * https://github.com/obiot/melonJS/
601
*
602
*/
603
604
/**
605
* A Tiled Map holds all layers defined in the tmx file as well
606
* as the necessary tiles to render the map.
607
* @param {String} url Relative or absolute URL to the tmx file
608
*/
609
var Map = exports.Map = function(url) {
610
611
var url = uri.resolve(document.location.href, url);
612
var xmlDoc = xml.Document.fromURL(url);
613
var mapNode = xmlDoc.element('map');
614
615
/**
616
* Width of a single tile in pixels
617
* @type Number
618
*/
619
this.tileWidth = mapNode.attribute('tilewidth');
620
/**
621
* Height of a single tile in pixels
622
* @type Number
623
*/
624
this.tileHeight = mapNode.attribute('tileheight');
625
/**
626
* Width of the map in tiles
627
* @type Number
628
*/
629
this.width = mapNode.attribute('width');
630
/**
631
* Height of the map in tiles
632
* @type Number
633
*/
634
this.height = mapNode.attribute('height');
635
636
var orientation = mapNode.attribute('orientation');
637
if (orientation !== 'orthogonal') {
638
throw new Error('only orthogonol maps supported');
639
}
640
641
/**
642
* Custom properties of the map
643
*/
644
this.properties = {};
645
setProperties(this.properties, mapNode);
646
647
/**
648
* All tiles of this map.
649
* @type TileSets
650
*/
651
this.tiles = new TileSets(mapNode, url);
652
this.layers = loadLayers(mapNode);
653
return this;
654
};
655
656
/**
657
* A Tile. Can not be instantiated. Get a Tile by calling `getTile(gid)`
658
* on a `TileSets` instance.
659
*/
660
var Tile = exports.Tile = function() {
661
throw new Error('Can not be instantiated.')
662
/**
663
* @type {gamejs.Surface} this tile's Surface
664
*/
665
this.surface = null;
666
/**
667
* @type {Object} custom properties attach for this tile
668
*/
669
this.properties = null;
670
return;
671
}
672
673
/**
674
* A TileSets instance holds all tilesets of a map. This class
675
* makes it easy to get the image for a certain tile ID. You usually
676
* don't care about in which specific TileSet an image is so this
677
* class holds them all and deals with the lookup.
678
*
679
* You don't usually create a `TileSets` instance yourself, instead
680
* it is automatically created and attached to a `Map`.
681
*/
682
var TileSets = exports.TileSets = function(mapNode, mapUrl) {
683
var tileSets = [];
684
685
/**
686
* Retrieve the image for a tile ID (gid).
687
*
688
* @param {Number} gid global tile id to retrieve
689
* @returns {gamejs.Surface} the Surface for the gid
690
*/
691
this.getSurface = function(gid) {
692
var tile = this.getTile(gid);
693
return tile && tile.surface || null;
694
};
695
696
/**
697
* @param {Number} gid global tile id
698
* @returns {Object} the custom properties of this tile
699
*/
700
this.getProperties = function(gid) {
701
var tile = this.getTile(gid);
702
return tile && tile.properties || {};
703
};
704
705
/**
706
* @param {Number} gid global tile id
707
* @returns {Object} the Tile object for this gid
708
*/
709
this.getTile = function(gid) {
710
var tile = null;
711
tileSets.some(function(tileSet, idx) {
712
if (tileSet.firstGid <= gid) {
713
tile = tileSet.tiles[gid - tileSet.firstGid];
714
return true;
715
}
716
return false;
717
}, this);
718
return tile;
719
};
720
721
var loadTileSet = function(tileSetNode) {
722
var tiles = [];
723
var tileWidth = tileSetNode.attribute('tilewidth');
724
var tileHeight = tileSetNode.attribute('tileheight');
725
var spacing = tileSetNode.attribute('spacing') || 0;
726
// broken in tiled?
727
var margin = 0;
728
729
var imageNode = tileSetNode.element('image');
730
var imageAtlasFile = imageNode.attribute('source');
731
var imageUrl = uri.makeRelative(uri.resolve(mapUrl, imageAtlasFile));
732
var atlas = gamejs.image.load(imageUrl);
733
// FIXME set transparency if imageNode.attribute('trans') is set
734
735
var tileNodes = tileSetNode.elements('tile')
736
var dims = atlas.getSize();
737
var imgSize = new gamejs.Rect([0,0], [tileWidth, tileHeight]);
738
var idx = 0;
739
var y = 0;
740
while (y + tileHeight <= dims[1]) {
741
x = 0;
742
while (x + tileWidth <= dims[0]) {
743
var tileImage = new gamejs.Surface(tileWidth, tileHeight);
744
var rect = new gamejs.Rect([x, y], [tileWidth, tileHeight]);
745
tileImage.blit(atlas, imgSize, rect);
746
var tileProperties = {};
747
tileNodes.some(function(tileNode) {
748
if (tileNode.attribute('id') === idx) {
749
setProperties(tileProperties, tileNode);
750
return true;
751
}
752
}, this);
753
tiles.push({
754
surface: tileImage,
755
properties: tileProperties
756
});
757
x += tileWidth + spacing;
758
idx++;
759
}
760
y += tileHeight + spacing;
761
}
762
return tiles;
763
}
764
765
/**
766
*
767
* constructor
768
**/
769
mapNode.elements('tileset').forEach(function(tileSetNode) {
770
var firstGid = tileSetNode.attribute('firstgid');
771
var externalSource = tileSetNode.attribute('source');
772
if (externalSource) {
773
var tileSetDocument = xml.Document.fromURL(uri.resolve(mapUrl, externalSource));
774
tileSetNode = tileSetDocument.element('tileset');
775
}
776
tileSets.push({
777
tiles: loadTileSet(tileSetNode),
778
firstGid: firstGid
779
});
780
});
781
tileSets.reverse();
782
783
return this;
784
};
785
786
/**
787
* loadLayers
788
*/
789
var H_FLIP = 0x80000000;
790
var V_FLIP = 0x40000000;
791
var loadLayers = function(mapNode) {
792
var layers = [];
793
794
var getGids = function(layerNode) {
795
var dataNode = layerNode.element('data');
796
var encoding = dataNode.attribute('encoding');
797
var compression = dataNode.attribute('compression')
798
var data = "";
799
dataNode.children().forEach(function(textNode) {
800
data += textNode.value();
801
});
802
var byteData = [];
803
if (encoding === 'base64') {
804
if (compression) {
805
throw new Error('Compression of map data unsupported');
806
}
807
byteData = base64.decodeAsArray(data, 4);
808
} else if (encoding === 'csv') {
809
data.trim().split('\n').forEach(function(row) {
810
row.split(',', width).forEach(function(entry) {
811
byteData.push(parseInt(entry, 10));
812
});
813
});
814
} else {
815
// FIXME individual XML tile elements
816
throw new Error('individual tile format not supported');
817
}
818
return byteData;
819
};
820
821
var width = mapNode.attribute('width');
822
var height = mapNode.attribute('height');
823
mapNode.elements('layer').forEach(function(layerNode) {
824
// create empty gid matrix
825
var gidMatrix = [];
826
var i = height;
827
while (i-->0) {
828
var j = width;
829
gidMatrix[i] = [];
830
while (j-->0) {
831
gidMatrix[i][j] = 0;
832
}
833
}
834
835
getGids(layerNode).forEach(function(gid, idx) {
836
// FIXME flipX/Y currently ignored
837
var flipX = gid & H_FLIP;
838
var flipY = gid & V_FLIP;
839
// clear flags
840
gid &= ~(H_FLIP | V_FLIP);
841
gidMatrix[parseInt(idx / width, 10)][parseInt(idx % width, 10)] = gid;
842
});
843
layers.push({
844
gids: gidMatrix,
845
opacity: layerNode.attribute('opacity'),
846
visible: layerNode.attribute('visible'),
847
properties: setProperties({}, layerNode)
848
});
849
});
850
return layers;
851
}
852
853
/**
854
* set generic <properties><property name="" value="">... on given object
855
*/
856
var setProperties = function(object, node) {
857
var props = node.element('properties');
858
if (!props) {
859
return;
860
}
861
props.elements('property').forEach(function(propertyNode) {
862
var name = propertyNode.attribute('name');
863
var value = propertyNode.attribute('value');
864
object[name] = value;
865
});
866
return object;
867
};
868
869
}}, ["gamejs", "gamejs/utils/objects", "gamejs/xml", "gamejs/base64", "gamejs/utils/uri"]);/* This file has been generated by yabbler.js */
870
require.define({
871
"gamejs/time": function(require, exports, module) {
872
/**
873
* @fileoverview
874
* Provides tools for game time managment.
875
*
876
* This is very different from how PyGame works. We can not
877
* pause the execution of the script in Browser JavaScript, so what
878
* we do you do is write a main function which contains the code
879
* you would put into your main loop and pass that to `fpsCallback()`:
880
*
881
* @example
882
* function main() {
883
* // update models
884
* // draw to screen
885
* };
886
* gamejs.time.fpsCallback(main, this, 30);
887
* ;
888
* function aiUpdate() {
889
* // do stuff that needs low update rates
890
* }
891
* gamejs.time.fpsCallback(aiUpdate, this, 10);
892
*
893
*
894
*/
895
896
897
var TIMER_LASTCALL = null;
898
var CALLBACKS = {};
899
var CALLBACKS_LASTCALL = {};
900
var TIMER = null;
901
var STARTTIME = null;
902
903
/**
904
* @ignore
905
*/
906
exports.init = function() {
907
STARTTIME = Date.now();
908
TIMER = setInterval(perInterval, 10);
909
return;
910
};
911
912
/**
913
* @param {Function} fn the function to call back
914
* @param {Object} thisObj `this` will be set to that object when executing the function
915
* @param {Number} fps specify the framerate by which you want the callback to be called. (e.g. 30 = 30 times per seconds). default: 30
916
*/
917
exports.fpsCallback = function(fn, thisObj, fps) {
918
if ( fps === undefined ) {
919
fps = 30;
920
}
921
922
fps = parseInt(1000/fps, 10);
923
CALLBACKS[fps] = CALLBACKS[fps] || [];
924
CALLBACKS_LASTCALL[fps] = CALLBACKS_LASTCALL[fps] || 0;
925
926
CALLBACKS[fps].push({
927
'rawFn': fn,
928
'callback': function(msWaited) {
929
fn.apply(thisObj, [msWaited]);
930
}
931
});
932
return;
933
};
934
935
/**
936
* @param {Function} callback the function delete
937
* @param {Number} fps
938
*/
939
exports.deleteCallback = function(callback, fps) {
940
fps = parseInt(1000/fps, 10);
941
var callbacks = CALLBACKS[fps];
942
if (!callbacks) {
943
return;
944
}
945
946
CALLBACKS[fps] = callbacks.filter(function(fnInfo, idx) {
947
if (fnInfo.rawFn !== callback) {
948
return true;
949
}
950
return false;
951
});
952
return;
953
};
954
955
var perInterval = function() {
956
var msNow = Date.now();
957
var lastCalls = CALLBACKS_LASTCALL;
958
function callbackWrapper(fnInfo) {
959
fnInfo.callback(msWaited);
960
}
961
for (var fpsKey in lastCalls) {
962
if (!lastCalls[fpsKey]) {
963
CALLBACKS_LASTCALL[fpsKey] = msNow;
964
}
965
var msWaited = msNow - lastCalls[fpsKey];
966
if (fpsKey <= msWaited) {
967
CALLBACKS_LASTCALL[fpsKey] = msNow;
968
CALLBACKS[fpsKey].forEach(callbackWrapper, this);
969
}
970
}
971
return;
972
};
973
974
}}, []);/* This file has been generated by yabbler.js */
975
require.define({
976
"gamejs/draw": function(require, exports, module) {
977
/**
978
* @fileoverview Utilities for drawing geometrical objects to Surfaces. If you want to put images on
979
* the screen see `gamejs.image`.
980
*
981
* ### Colors
982
* There are several ways to specify colors. Whenever the docs says "valid #RGB string"
983
* you can pass in any of the following formats.
984
*
985
*
986
* @example
987
* "#ff00ff"
988
* "rgb(255, 0, 255)"
989
* "rgba(255,0, 255, 1)"
990
*/
991
992
// FIXME all draw functions must return a minimal rect containing the drawn shape
993
994
/**
995
* @param {gamejs.Surface} surface the Surface to draw on
996
* @param {String} color valid #RGB string, e.g., "#ff0000"
997
* @param {Array} startPos [x, y] position of line start
998
* @param {Array} endPos [x, y] position of line end
999
* @param {Number} width of the line, defaults to 1
1000
*/
1001
exports.line = function(surface, color, startPos, endPos, width) {
1002
var ctx = surface.context;
1003
ctx.save();
1004
ctx.beginPath();
1005
ctx.strokeStyle = color;
1006
ctx.lineWidth = width || 1;
1007
ctx.moveTo(startPos[0], startPos[1]);
1008
ctx.lineTo(endPos[0], endPos[1]);
1009
ctx.stroke();
1010
ctx.restore();
1011
return;
1012
};
1013
1014
/**
1015
* Draw connected lines. Use this instead of indiviudal line() calls for
1016
* better performance
1017
*
1018
* @param {gamejs.Surface} surface the Surface to draw on
1019
* @param {String} color a valid #RGB string, "#ff0000"
1020
* @param {Boolean} closed if true the last and first point are connected
1021
* @param {Array} pointlist holding array [x,y] arrays of points
1022
* @param {Number} width width of the lines, defaults to 1
1023
*/
1024
exports.lines = function(surface, color, closed, pointlist, width) {
1025
closed = closed || false;
1026
var ctx = surface.context;
1027
ctx.save();
1028
ctx.beginPath();
1029
ctx.strokeStyle = ctx.fillStyle = color;
1030
ctx.lineWidth = width || 1;
1031
pointlist.forEach(function(point, idx) {
1032
if (idx === 0) {
1033
ctx.moveTo(point[0], point[1]);
1034
} else {
1035
ctx.lineTo(point[0], point[1]);
1036
}
1037
});
1038
if (closed) {
1039
ctx.lineTo(pointlist[0][0], pointlist[0][1]);
1040
}
1041
ctx.stroke();
1042
ctx.restore();
1043
return;
1044
};
1045
1046
/**
1047
* Draw a circle on Surface
1048
*
1049
* @param {gamejs.Surface} surface the Surface to draw on
1050
* @param {String} color a valid #RGB String, #ff00cc
1051
* @param {Array} pos [x, y] position of the circle center
1052
* @param {Number} radius of the circle
1053
* @param {Number} width width of the circle, if not given or 0 the circle is filled
1054
*/
1055
exports.circle = function(surface, color, pos, radius, width) {
1056
if (!radius) {
1057
throw new Error('[circle] radius required argument');
1058
}
1059
if (!pos || !(pos instanceof Array)) {
1060
throw new Error('[circle] pos must be given & array' + pos);
1061
}
1062
1063
var ctx = surface.context;
1064
ctx.save();
1065
ctx.beginPath();
1066
ctx.strokeStyle = ctx.fillStyle = color;
1067
ctx.lineWidth = width || 1;
1068
ctx.arc(pos[0], pos[1], radius, 0, 2*Math.PI, true);
1069
if (width === undefined || width === 0) {
1070
ctx.fill();
1071
} else {
1072
ctx.stroke();
1073
}
1074
ctx.restore();
1075
return;
1076
};
1077
1078
/**
1079
* @param {gamejs.Surface} surface the Surface to draw on
1080
* @param {String} color a valid #RGB String, #ff00cc
1081
* @param {gamejs.Rect} rect the position and dimension attributes of this Rect will be used
1082
* @param {Number} width the width of line drawing the Rect, if 0 or not given the Rect is filled.
1083
*/
1084
exports.rect = function(surface, color, rect, width) {
1085
var ctx =surface.context;
1086
ctx.save();
1087
ctx.beginPath();
1088
ctx.strokeStyle = ctx.fillStyle = color;
1089
if (isNaN(width) || width === 0) {
1090
ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
1091
} else {
1092
ctx.lineWidth = width || 1;
1093
ctx.strokeRect(rect.left, rect.top, rect.width, rect.height);
1094
}
1095
ctx.restore();
1096
};
1097
1098
/**
1099
* @param {gamejs.Surface} surface the Surface to draw on
1100
* @param {String} color a valid #RGB String, #ff00cc
1101
* @param {gamejs.Rect} rect the position and dimension attributes of this Rect will be used
1102
* @param {Number} startAngle
1103
* @param {Number} stopAngle
1104
* @param {Number} width the width of line, if 0 or not given the arc is filled.
1105
*/
1106
exports.arc= function(surface, color, rect, startAngle, stopAngle, width) {
1107
var ctx = surface.context;
1108
ctx.save();
1109
ctx.beginPath();
1110
ctx.strokeStyle = ctx.fillStyle = color;
1111
ctx.arc(rect.center[0], rect.center[1],
1112
rect.width/2,
1113
startAngle * (Math.PI/180), stopAngle * (Math.PI/180),
1114
false
1115
);
1116
if (isNaN(width) || width === 0) {
1117
ctx.fill();
1118
} else {
1119
ctx.lineWidth = width || 1;
1120
ctx.stroke();
1121
}
1122
ctx.restore();
1123
};
1124
1125
/**
1126
* Draw a polygon on the surface. The pointlist argument are the vertices
1127
* for the polygon.
1128
*
1129
* @param {gamejs.Surface} surface the Surface to draw on
1130
* @param {String} color a valid #RGB String, #ff00cc
1131
* @param {Array} pointlist array of vertices [x, y] of the polygon
1132
* @param {Number} width the width of line, if 0 or not given the polygon is filled.
1133
*/
1134
exports.polygon = function(surface, color, pointlist, width) {
1135
var ctx = surface.context;
1136
ctx.save();
1137
ctx.fillStyle = ctx.strokeStyle = color;
1138
ctx.beginPath();
1139
pointlist.forEach(function(point, idx) {
1140
if (idx == 0) {
1141
ctx.moveTo(point[0], point[1]);
1142
} else {
1143
ctx.lineTo(point[0], point[1]);
1144
}
1145
});
1146
ctx.closePath();
1147
if (isNaN(width) || width === 0) {
1148
ctx.fill();
1149
} else {
1150
ctx.lineWidth = width || 1;
1151
ctx.stroke();
1152
}
1153
ctx.restore();
1154
};
1155
1156
}}, []);/* This file has been generated by yabbler.js */
1157
require.define({
1158
"gamejs/worker": function(require, exports, module) {
1159
var gamejs = require('gamejs');
1160
var uri = require('gamejs/utils/uri');
1161
1162
/**
1163
* @fileoverview
1164
* Workers are useful to relieve your GameJs application from code which
1165
* might take long to run. Either expensive algorithms, which might get called
1166
* every now and then (e.g., path-finding) or another logic being run continously
1167
* within the rendering loop (e.g., physics engine).
1168
*
1169
* A Worker is like a seperate GameJs application being executed - another `main.js`
1170
* with its own `gamejs.ready()`. The Worker's most important feature is that
1171
* code executing within it does not block the rendering code. The Worker's
1172
* greatest limitation is that you can only communicate with it through text
1173
* messages.
1174
*
1175
* See the `examples/workers` directory for a running example.
1176
*
1177
* @example
1178
* // Create a worker with the main module "./test"
1179
* var fooWorker = new Worker('./test');
1180
* // Send a message to your worker.
1181
* // The Message doesn't have to be a string but it must be `JSON.stringify()`-able
1182
* fooWorker.post("foobar");
1183
*
1184
* // The result of the worker will be accessible
1185
* // in the main application via the gamejs.event queue
1186
* if (event.type === gamejs.event.WORKER_RESULT) {
1187
* gamejs.log('Worker #' + event.worker.id + ' returned ' + event.data);
1188
* }
1189
*
1190
* // In the worker module, we can send results back to the main application
1191
* // by posting them to the gamejs event queue as type `gamejs.event.WORKER_RESULT`
1192
* gamejs.event.post({
1193
* type: gamejs.event.WORKER_RESULT,
1194
* data: "zarzar"
1195
* });
1196
*
1197
*/
1198
1199
/**
1200
* true if this GameJs instance is being executed within a WebWorker
1201
* @type Boolean
1202
*/
1203
exports.inWorker = (this.importScripts !== undefined);
1204
1205
/**
1206
* Executed in scope of worker after user's main module
1207
* @ignore
1208
*/
1209
exports._ready = function () {
1210
var gamejs = require('gamejs');
1211
self.onmessage = function(event) {
1212
gamejs.event.post(event.data)
1213
};
1214
self.postMessage({
1215
type: gamejs.event.WORKER_ALIVE
1216
});
1217
};
1218
1219
/**
1220
* Send message to main context for logging
1221
* @ignore
1222
**/
1223
exports._logMessage = function(arguments) {
1224
self.postMessage({
1225
type: gamejs.event.WORKER_LOGMESSAGE,
1226
arguments: Array.prototype.slice.apply(arguments)
1227
});
1228
};
1229
1230
/**
1231
* Send result message to main context
1232
* @ignore
1233
*/
1234
exports._messageMain = function(event) {
1235
self.postMessage({
1236
type: gamejs.event.WORKER_RESULT,
1237
data: event.data
1238
});
1239
};
1240
1241
/**
1242
* executed in scope of worker before user's main module
1243
* @ignore
1244
*/
1245
var workerPrefix = function workerPrefix() {
1246
__scripts.forEach(function(script) {
1247
try {
1248
importScripts(script)
1249
} catch (e) {
1250
// can't help the worker
1251
}
1252
});
1253
};
1254
1255
/**
1256
* Setup a worker which has `require()` defined
1257
* @ignore
1258
**/
1259
var create = function(workerModuleId) {
1260
var moduleRoot = uri.resolve(document.location.href, window.require.getModuleRoot());
1261
var initialScripts = [];
1262
Array.prototype.slice.apply(document.getElementsByTagName('script'), [0]).forEach(function(script) {
1263
if (script.src) {
1264
initialScripts.push(script.src);
1265
}
1266
});
1267
1268
var URL = window.URL || window.webkitURL;
1269
var prefixString = workerPrefix.toString();
1270
// don't be afraid...
1271
prefixString = prefixString.substring(prefixString.indexOf("{") + 1, prefixString.lastIndexOf("}"));
1272
var blob = new Blob([
1273
'var __scripts = ["' + initialScripts.join('","') + '"];',
1274
prefixString,
1275
'self.require.setModuleRoot("' + moduleRoot + '");',
1276
'self.require.run("'+ workerModuleId +'");'
1277
]);
1278
1279
var blobURL = URL.createObjectURL(blob);
1280
return new Worker(blobURL);
1281
};
1282
1283
/**
1284
* The `Worker` constructor takes only one argument: a module id. This module
1285
* will be executed inside the newly created Worker. It is effectively the
1286
* main module of the Worker.
1287
*
1288
* Inside a Worker, you can use `require()` to import other scripts or
1289
* GameJs modules.
1290
*
1291
* **Note:** A Worker does not have access to the browser's `document`. So
1292
* a lot of GameJs modules - everything related to drawing to the canvas -
1293
* do not work in the Worker.
1294
*
1295
* You can use `gamejs.time.*`, `gamejs.utils.*`, `gamejs.event.*` and probably others
1296
* (as well as any module you write yourself for this purpose, of course).
1297
*
1298
* @param {String} moduleId The Worker's main module id. The main module will be executed in the worker
1299
*/
1300
exports.Worker = function(moduleId) {
1301
// FIXME id should be unchangeable
1302
/**
1303
* Unique id of this worker
1304
* @property {Number}
1305
*/
1306
var id = this.id = guid(moduleId);
1307
var worker = create(moduleId);
1308
var deadQueue = [];
1309
var alive = false;
1310
var self = this;
1311
1312
worker.onmessage = function(event) {
1313
if (event.data.type === gamejs.event.WORKER_ALIVE) {
1314
alive = true;
1315
deadQueue.forEach(function(data) {
1316
self.post(data);
1317
});
1318
} else if (event.data.type === gamejs.event.WORKER_LOGMESSAGE) {
1319
gamejs.log.apply(null, [id].concat(event.data.arguments));
1320
} else {
1321
gamejs.event.post({
1322
type: gamejs.event.WORKER_RESULT,
1323
data: event.data.data,
1324
worker: self,
1325
event: event,
1326
})
1327
}
1328
};
1329
worker.onerror = function(event) {
1330
gamejs.error('Error in worker "' + id + '" line ' + event.lineno + ': ', event.message)
1331
gamejs.event.post({
1332
type: gamejs.event.WORKER_ERROR,
1333
data: event.data,
1334
worker: self,
1335
event: event,
1336
})
1337
};
1338
1339
/**
1340
* Send a message to the worker
1341
*
1342
* @param {Object} data Payload object which gets sent to the Worker
1343
*/
1344
this.post = function(data) {
1345
if (alive) {
1346
worker.postMessage({
1347
type: gamejs.event.WORKER,
1348
data: data
1349
});
1350
} else {
1351
deadQueue.push(data);
1352
}
1353
};
1354
return this;
1355
}
1356
1357
/**
1358
* not a real GUID
1359
* @ignore
1360
*/
1361
function guid(moduleId) {
1362
var S4 = function() {
1363
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
1364
};
1365
return moduleId + '@' + (S4()+S4());
1366
}
1367
1368
}}, ["gamejs", "gamejs/utils/uri"]);/* This file has been generated by yabbler.js */
1369
require.define({
1370
"gamejs/event": function(require, exports, module) {
1371
var display = require('./display');
1372
var gamejs = require('../gamejs');
1373
/**
1374
* @fileoverview Methods for polling mouse and keyboard.
1375
*
1376
* Call `gamejs.event.get()` in your main loop to get a list of events that happend
1377
* since your last call.
1378
*
1379
* Note that some events, which would trigger a default browser action, are prevented
1380
* from triggering their default behaviour.
1381
*
1382
* All events have a type identifier. This event type is in between the values
1383
* of NOEVENT and NUMEVENTS. Each event has a constant in `gamejs.event.*`
1384
* All user defined events can have the value of USEREVENT or higher.
1385
* Make sure your custom event ids* follow this system.
1386
*
1387
* A pattern for using the event loop: your main game function (tick in this example)
1388
* is being called by [gamejs.time.fpsCallback()](../time/#fpsCallback) 25 times per second.
1389
* Inside tick we call [gamejs.event.get()](#get) for a list of events that happened since the last
1390
* tick and we loop over each event and act on the event properties.
1391
*
1392
* @example
1393
* var events = gamejs.event.get()
1394
* events.forEach(function(event) {
1395
* if (event.type === gamejs.event.MOUSE_UP) {
1396
* gamejs.log(event.pos, event.button);
1397
* } else if (event.type === gamejs.event.KEY_UP) {
1398
* gamejs.log(event.key);
1399
* }
1400
* });
1401
*
1402
*/
1403
// key constants
1404
exports.K_UP = 38;
1405
exports.K_DOWN = 40;
1406
exports.K_RIGHT = 39;
1407
exports.K_LEFT = 37;
1408
1409
exports.K_SPACE = 32;
1410
exports.K_BACKSPACE = 8;
1411
exports.K_TAB = 9;
1412
exports.K_ENTER = 13;
1413
exports.K_SHIFT = 16;
1414
exports.K_CTRL = 17;
1415
exports.K_ALT = 18;
1416
exports.K_ESC = 27;
1417
1418
exports.K_0 = 48;
1419
exports.K_1 = 49;
1420
exports.K_2 = 50;
1421
exports.K_3 = 51;
1422
exports.K_4 = 52;
1423
exports.K_5 = 53;
1424
exports.K_6 = 54;
1425
exports.K_7 = 55;
1426
exports.K_8 = 56;
1427
exports.K_9 = 57;
1428
exports.K_a = 65;
1429
exports.K_b = 66;
1430
exports.K_c = 67;
1431
exports.K_d = 68;
1432
exports.K_e = 69;
1433
exports.K_f = 70;
1434
exports.K_g = 71;
1435
exports.K_h = 72;
1436
exports.K_i = 73;
1437
exports.K_j = 74;
1438
exports.K_k = 75;
1439
exports.K_l = 76;
1440
exports.K_m = 77;
1441
exports.K_n = 78;
1442
exports.K_o = 79;
1443
exports.K_p = 80;
1444
exports.K_q = 81;
1445
exports.K_r = 82;
1446
exports.K_s = 83;
1447
exports.K_t = 84;
1448
exports.K_u = 85;
1449
exports.K_v = 86;
1450
exports.K_w = 87;
1451
exports.K_x = 88;
1452
exports.K_y = 89;
1453
exports.K_z = 90;
1454
1455
exports.K_KP1 = 97;
1456
exports.K_KP2 = 98;
1457
exports.K_KP3 = 99;
1458
exports.K_KP4 = 100;
1459
exports.K_KP5 = 101;
1460
exports.K_KP6 = 102;
1461
exports.K_KP7 = 103;
1462
exports.K_KP8 = 104;
1463
exports.K_KP9 = 105;
1464
1465
// event type constants
1466
exports.NOEVENT = 0
1467
exports.NUMEVENTS = 32000
1468
1469
exports.QUIT = 0;
1470
exports.KEY_DOWN = 1;
1471
exports.KEY_UP = 2;
1472
exports.MOUSE_MOTION = 3;
1473
exports.MOUSE_UP = 4;
1474
exports.MOUSE_DOWN = 5;
1475
exports.MOUSE_WHEEL = 6;
1476
exports.USEREVENT = 2000;
1477
1478
exports.WORKER = 1000;
1479
exports.WORKER_RESULT = 1001;
1480
/** @ignore **/
1481
exports.WORKER_ERROR = 1002;
1482
/** @ignore **/
1483
exports.WORKER_ALIVE = 1003;
1484
/** @ignore **/
1485
exports.WORKER_LOG = 1004;
1486
1487
var QUEUE = [];
1488
1489
/**
1490
* Get all events from the event queue
1491
* @returns {Array}
1492
*/
1493
exports.get = function() {
1494
return QUEUE.splice(0, QUEUE.length);
1495
};
1496
1497
/**
1498
* Get the newest event of the event queue
1499
* @returns {gamejs.event.Event}
1500
*/
1501
exports.poll = function() {
1502
return QUEUE.pop();
1503
};
1504
1505
/**
1506
* Post an event to the event queue.
1507
* @param {gamejs.event.Event} userEvent the event to post to the queue
1508
*/
1509
exports.post = function(userEvent) {
1510
if (userEvent.type === exports.WORKER_RESULT && gamejs.worker.inWorker === true) {
1511
gamejs.worker._messageMain(userEvent);
1512
} else if (userEvent.type === exports.WORKER && gamejs.worker.inWorker === false) {
1513
if (!userEvent.worker || !userEvent.worker.post) {
1514
throw new Error('Missing "worker" property on event');
1515
}
1516
userEvent.worker.post(userEvent.data);
1517
} else {
1518
QUEUE.push(userEvent);
1519
}
1520
return;
1521
};
1522
1523
/**
1524
* Remove all events from the queue
1525
*/
1526
exports.clear = function() {
1527
QUEUE = [];
1528
};
1529
1530
/**
1531
* Holds all information about an event.
1532
* @class
1533
*/
1534
1535
exports.Event = function() {
1536
/**
1537
* The type of the event. e.g., gamejs.event.QUIT, KEYDOWN, MOUSEUP.
1538
*/
1539
this.type = null;
1540
/**
1541
* key the keyCode of the key. compare with gamejs.event.K_a, gamejs.event.K_b,...
1542
*/
1543
this.key = null;
1544
/**
1545
* relative movement for a mousemove event
1546
*/
1547
this.rel = null;
1548
/**
1549
* the number of the mousebutton pressed
1550
*/
1551
this.button = null;
1552
/**
1553
* pos the position of the event for mouse events
1554
*/
1555
this.pos = null;
1556
};
1557
1558
/**
1559
* @ignore
1560
*/
1561
exports.init = function() {
1562
1563
var lastPos = [];
1564
1565
// anonymous functions as event handlers = memory leak, see MDC:elementAddEventListener
1566
1567
function onMouseDown (ev) {
1568
var canvasOffset = display._getCanvasOffset();
1569
QUEUE.push({
1570
'type': gamejs.event.MOUSE_DOWN,
1571
'pos': [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]],
1572
'button': ev.button,
1573
'shiftKey': ev.shiftKey,
1574
'ctrlKey': ev.ctrlKey,
1575
'metaKey': ev.metaKey
1576
});
1577
}
1578
1579
function onMouseUp (ev) {
1580
var canvasOffset = display._getCanvasOffset();
1581
QUEUE.push({
1582
'type':gamejs.event.MOUSE_UP,
1583
'pos': [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]],
1584
'button': ev.button,
1585
'shiftKey': ev.shiftKey,
1586
'ctrlKey': ev.ctrlKey,
1587
'metaKey': ev.metaKey
1588
});
1589
}
1590
1591
function onKeyDown (ev) {
1592
var key = ev.keyCode || ev.which;
1593
QUEUE.push({
1594
'type': gamejs.event.KEY_DOWN,
1595
'key': key,
1596
'shiftKey': ev.shiftKey,
1597
'ctrlKey': ev.ctrlKey,
1598
'metaKey': ev.metaKey
1599
});
1600
1601
if ((!ev.ctrlKey && !ev.metaKey &&
1602
((key >= exports.K_LEFT && key <= exports.K_DOWN) ||
1603
(key >= exports.K_0 && key <= exports.K_z) ||
1604
(key >= exports.K_KP1 && key <= exports.K_KP9) ||
1605
key === exports.K_SPACE ||
1606
key === exports.K_TAB ||
1607
key === exports.K_ENTER)) ||
1608
key === exports.K_ALT ||
1609
key === exports.K_BACKSPACE) {
1610
ev.preventDefault();
1611
}
1612
}
1613
1614
function onKeyUp (ev) {
1615
QUEUE.push({
1616
'type': gamejs.event.KEY_UP,
1617
'key': ev.keyCode,
1618
'shiftKey': ev.shiftKey,
1619
'ctrlKey': ev.ctrlKey,
1620
'metaKey': ev.metaKey
1621
});
1622
}
1623
1624
function onMouseMove (ev) {
1625
var canvasOffset = display._getCanvasOffset();
1626
var currentPos = [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]];
1627
var relativePos = [];
1628
if (lastPos.length) {
1629
relativePos = [
1630
lastPos[0] - currentPos[0],
1631
lastPos[1] - currentPos[1]
1632
];
1633
}
1634
QUEUE.push({
1635
'type': gamejs.event.MOUSE_MOTION,
1636
'pos': currentPos,
1637
'rel': relativePos,
1638
'buttons': null, // FIXME, fixable?
1639
'timestamp': ev.timeStamp
1640
});
1641
lastPos = currentPos;
1642
return;
1643
}
1644
1645
function onMouseScroll(ev) {
1646
var canvasOffset = display._getCanvasOffset();
1647
var currentPos = [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]];
1648
QUEUE.push({
1649
type: gamejs.event.MOUSE_WHEEL,
1650
pos: currentPos,
1651
delta: ev.detail || (- ev.wheelDeltaY / 40)
1652
});
1653
return;
1654
}
1655
1656
function onBeforeUnload (ev) {
1657
QUEUE.push({
1658
'type': gamejs.event.QUIT
1659
});
1660
return;
1661
}
1662
1663
// IEFIX does not support addEventListener on document itself
1664
// MOZFIX but in moz & opera events don't reach body if mouse outside window or on menubar
1665
var canvas = display.getSurface()._canvas;
1666
canvas.addEventListener('mousedown', onMouseDown, false);
1667
canvas.addEventListener('mouseup', onMouseUp, false);
1668
document.addEventListener('keydown', onKeyDown, false);
1669
document.addEventListener('keyup', onKeyUp, false);
1670
canvas.addEventListener('mousemove', onMouseMove, false);
1671
canvas.addEventListener('mousewheel', onMouseScroll, false);
1672
// MOZFIX
1673
// https://developer.mozilla.org/en/Code_snippets/Miscellaneous#Detecting_mouse_wheel_events
1674
canvas.addEventListener('DOMMouseScroll', onMouseScroll, false);
1675
canvas.addEventListener('beforeunload', onBeforeUnload, false);
1676
1677
};
1678
1679
}}, ["gamejs/display", "gamejs"]);/* This file has been generated by yabbler.js */
1680
require.define({
1681
"gamejs/transform": function(require, exports, module) {
1682
var Surface = require('../gamejs').Surface;
1683
var matrix = require('./utils/matrix');
1684
var math = require('./utils/math');
1685
var vectors = require('./utils/vectors');
1686
1687
/**
1688
* @fileoverview Rotate and scale Surfaces.
1689
*/
1690
1691
/**
1692
* Returns a new surface which holds the original surface rotate by angle degrees.
1693
* @param {Surface} surface
1694
* @param {angel} angle Clockwise angle by which to rotate
1695
* @returns {Surface} new, rotated surface
1696
*/
1697
exports.rotate = function (surface, angle) {
1698
var origSize = surface.getSize();
1699
var radians = (angle * Math.PI / 180);
1700
var newSize = origSize;
1701
// find new bounding box
1702
if (angle % 360 !== 0) {
1703
var rect = surface.getRect();
1704
var points = [
1705
[-rect.width/2, rect.height/2],
1706
[rect.width/2, rect.height/2],
1707
[-rect.width/2, -rect.height/2],
1708
[rect.width/2, -rect.height/2]
1709
];
1710
var rotPoints = points.map(function(p) {
1711
return vectors.rotate(p, radians);
1712
});
1713
var xs = rotPoints.map(function(p) { return p[0]; });
1714
var ys = rotPoints.map(function(p) { return p[1]; });
1715
var left = Math.min.apply(Math, xs);
1716
var right = Math.max.apply(Math, xs);
1717
var bottom = Math.min.apply(Math, ys);
1718
var top = Math.max.apply(Math, ys);
1719
newSize = [right-left, top-bottom];
1720
}
1721
var newSurface = new Surface(newSize);
1722
var oldMatrix = surface._matrix;
1723
surface._matrix = matrix.translate(surface._matrix, origSize[0]/2, origSize[1]/2);
1724
surface._matrix = matrix.rotate(surface._matrix, radians);
1725
surface._matrix = matrix.translate(surface._matrix, -origSize[0]/2, -origSize[1]/2);
1726
var offset = [(newSize[0] - origSize[0]) / 2, (newSize[1] - origSize[1]) / 2];
1727
newSurface.blit(surface, offset);
1728
surface._matrix = oldMatrix;
1729
return newSurface;
1730
};
1731
1732
/**
1733
* Returns a new surface holding the scaled surface.
1734
* @param {Surface} surface
1735
* @param {Array} dimensions new [width, height] of surface after scaling
1736
* @returns {Surface} new, scaled surface
1737
*/
1738
exports.scale = function(surface, dims) {
1739
var width = dims[0];
1740
var height = dims[1];
1741
if (width <= 0 || height <= 0) {
1742
throw new Error('[gamejs.transform.scale] Invalid arguments for height and width', [width, height]);
1743
}
1744
var oldDims = surface.getSize();
1745
var ws = width / oldDims[0];
1746
var hs = height / oldDims[1];
1747
var newSurface = new Surface([width, height]);
1748
var originalMatrix = surface._matrix.slice(0);
1749
surface._matrix = matrix.scale(surface._matrix, [ws, hs]);
1750
newSurface.blit(surface);
1751
surface._matrix = originalMatrix;
1752
return newSurface;
1753
};
1754
1755
/**
1756
* Flip a Surface either vertically, horizontally or both. This returns
1757
* a new Surface (i.e: nondestructive).
1758
* @param {gamejs.Surface} surface
1759
* @param {Boolean} flipHorizontal
1760
* @param {Boolean} flipVertical
1761
* @returns {Surface} new, flipped surface
1762
*/
1763
exports.flip = function(surface, flipHorizontal, flipVertical) {
1764
var dims = surface.getSize();
1765
var newSurface = new Surface(dims);
1766
var scaleX = 1;
1767
var scaleY = 1;
1768
var xPos = 0;
1769
var yPos = 0;
1770
if (flipHorizontal === true) {
1771
scaleX = -1;
1772
xPos = -dims[0];
1773
}
1774
if (flipVertical === true) {
1775
scaleY = -1;
1776
yPos = -dims[1];
1777
}
1778
newSurface.context.save();
1779
newSurface.context.scale(scaleX, scaleY);
1780
newSurface.context.drawImage(surface.canvas, xPos, yPos);
1781
newSurface.context.restore();
1782
return newSurface;
1783
};
1784
1785
}}, ["gamejs", "gamejs/utils/matrix", "gamejs/utils/math", "gamejs/utils/vectors"]);/* This file has been generated by yabbler.js */
1786
require.define({
1787
"gamejs/xml": function(require, exports, module) {
1788
/**
1789
* @fileoverview
1790
*
1791
* Provides facilities for parsing an xml String.
1792
*
1793
* You will typically get a `gamejs.xml.Document` instance
1794
* by loading the data with one of the two static
1795
* `Document.fromString(string)` or `Document.fromUrl(url)`.
1796
1797
* Querying for `elements(name)` or `children()` will return a
1798
* new `gamejs.xml.Document` matching your result (or null).
1799
*
1800
* Use `attributes(name)` and `value()` to get the data stored
1801
* in the XML Document.
1802
*/
1803
1804
/**
1805
* XMLParser
1806
*/
1807
var Parser = exports.Parser = function() {
1808
1809
var xmlDoc = null;
1810
var parser = new DOMParser();
1811
1812
this.parseFromString = function(xmlString) {
1813
xmlDoc = parser.parseFromString(xmlString, 'text/xml');
1814
return xmlDoc;
1815
};
1816
1817
return this;
1818
};
1819
1820
/**
1821
* Instantiate with the static functions `Document.fromString()` and `fromURL()`.
1822
*/
1823
var Document = exports.Document = function(xmlDocument) {
1824
if (!xmlDocument || (!xmlDocument instanceof XMLDocument) ) {
1825
throw new Error('Need a valid xmlDocument.');
1826
}
1827
/** @ignore **/
1828
this._xmlDocument = xmlDocument;
1829
return this;
1830
};
1831
1832
/**
1833
* Returns the first element in the current document whose tag-name matches
1834
* the given 'name'.
1835
* @returns gamejs.xml.Document
1836
*/
1837
Document.prototype.element = function(name) {
1838
var elem = this._xmlDocument.getElementsByTagName(name)[0];
1839
return elem && new Document(elem) || null;
1840
};
1841
1842
/**
1843
* Returns all elements in the current document whose tag-name matches
1844
* the given 'name'.
1845
* @returns an Array of gamejs.xml.Document
1846
*/
1847
Document.prototype.elements = function(name) {
1848
var elems = this._xmlDocument.getElementsByTagName(name);
1849
return Array.prototype.slice.apply(elems, [0]).map(function(elem) {
1850
return new Document(elem);
1851
});
1852
};
1853
1854
/**
1855
* Returns the attribute value of this document.
1856
*
1857
* @returns String
1858
*/
1859
Document.prototype.attribute = function(name) {
1860
var attributeValue = this._xmlDocument.getAttribute(name);
1861
attributeValue = attributeValue ? attributeValue.trim() : null;
1862
if (attributeValue === null) {
1863
return null;
1864
}
1865
if (attributeValue.toLowerCase() === 'true') {
1866
return true;
1867
}
1868
if (attributeValue.toLowerCase() === 'false') {
1869
return false;
1870
}
1871
var attributeIntValue = parseInt(attributeValue, 10);
1872
var attributeFloatValue = parseFloat(attributeValue, 10);
1873
if (!isNaN(attributeIntValue)) {
1874
if (attributeFloatValue !== attributeIntValue) {
1875
return attributeFloatValue;
1876
}
1877
return attributeIntValue;
1878
}
1879
return attributeValue;
1880
};
1881
1882
/**
1883
* Returns the nodevalue of the current xml document
1884
* @returns String
1885
*/
1886
Document.prototype.value = function() {
1887
return this._xmlDocument.nodeValue;
1888
};
1889
1890
/**
1891
* Returns all children of this xml document
1892
* @returns Array of gamejs.xml.Document
1893
*/
1894
Document.prototype.children = function() {
1895
return Array.prototype.slice.apply(this._xmlDocument.childNodes, [0]).map(function(cNode) {
1896
return new Document(cNode);
1897
});
1898
};
1899
1900
/**
1901
* @returns gamejs.xml.Document
1902
*/
1903
Document.fromString = function(xmlString) {
1904
var parser = new DOMParser();
1905
var xmlDoc = parser.parseFromString(xmlString, 'text/xml');
1906
return new Document(xmlDoc);
1907
};
1908
1909
/**
1910
* @returns gamejs.xml.Document
1911
*/
1912
Document.fromURL = function(url) {
1913
var response = new XMLHttpRequest();
1914
response.open('GET', url, false);
1915
response.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1916
response.setRequestHeader('Content-Type', 'text/xml');
1917
response.overrideMimeType('text/xml');
1918
response.send();
1919
return new Document(response.responseXML);
1920
};
1921
1922
}}, []);/* This file has been generated by yabbler.js */
1923
require.define({
1924
"gamejs/sprite": function(require, exports, module) {
1925
var gamejs = require('../gamejs');
1926
var arrays = require('./utils/arrays');
1927
var $o = require('./utils/objects');
1928
var $v = require('./utils/vectors');
1929
1930
/**
1931
* @fileoverview Provides `Sprite` the basic building block for any game and
1932
* `SpriteGroups`, which are an efficient
1933
* way for doing collision detection between groups as well as drawing layered
1934
* groups of objects on the screen.
1935
*
1936
*/
1937
1938
/**
1939
* Your visible game objects will typically subclass Sprite. By setting it's image
1940
* and rect attributes you can control its appeareance. Those attributes control
1941
* where and what `Sprite.draw(surface)` will blit on the the surface.
1942
*
1943
* Your subclass should overwrite `update(msDuration)` with its own implementation.
1944
* This function is called once every game tick, it is typically used to update
1945
* the status of that object.
1946
* @constructor
1947
*/
1948
var Sprite = exports.Sprite = function() {
1949
/** @ignore */
1950
this._groups = [];
1951
/** @ignore */
1952
this._alive = true;
1953
1954
/**
1955
* Image to be rendered for this Sprite.
1956
* @type gamejs.Surface
1957
*/
1958
this.image = null;
1959
/**
1960
* Rect describing the position of this sprite on the display.
1961
* @type gamejs.Rect
1962
*/
1963
this.rect = null;
1964
1965
/**
1966
* List of all groups that contain this sprite.
1967
*/
1968
$o.accessor(this, 'groups', function() {
1969
return this._groups;
1970
});
1971
1972
return this;
1973
};
1974
1975
/**
1976
* Kill this sprite. This removes the sprite from all associated groups and
1977
* makes future calls to `Sprite.isDead()` return `true`
1978
*/
1979
Sprite.prototype.kill = function() {
1980
this._alive = false;
1981
this._groups.forEach(function(group) {
1982
group.remove(this);
1983
}, this);
1984
return;
1985
};
1986
1987
/**
1988
* Remove the sprite from the passed groups
1989
* @param {Array|gamejs.sprite.Group} groups One or more `gamejs.Group`
1990
* instances
1991
*/
1992
Sprite.prototype.remove = function(groups) {
1993
if (!(groups instanceof Array)) {
1994
groups = [groups];
1995
}
1996
1997
groups.forEach(function(group) {
1998
group.remove(this);
1999
}, this);
2000
return;
2001
};
2002
2003
/**
2004
* Add the sprite to the passed groups
2005
* @param {Array|gamejs.sprite.Group} groups One or more `gamejs.sprite.Group`
2006
* instances
2007
*/
2008
Sprite.prototype.add = function(groups) {
2009
if (!(groups instanceof Array)) {
2010
groups = [groups];
2011
}
2012
2013
groups.forEach(function(group) {
2014
group.add(this);
2015
}, this);
2016
return;
2017
};
2018
2019
/**
2020
* Draw this sprite onto the given surface. The position is defined by this
2021
* sprite's rect.
2022
* @param {gamejs.Surface} surface The surface to draw on
2023
*/
2024
Sprite.prototype.draw = function(surface) {
2025
surface.blit(this.image, this.rect);
2026
return;
2027
};
2028
2029
/**
2030
* Update this sprite. You **should** override this method with your own to
2031
* update the position, status, etc.
2032
*/
2033
Sprite.prototype.update = function() {};
2034
2035
/**
2036
* @returns {Boolean} True if this sprite has had `Sprite.kill()` called on it
2037
* previously, otherwise false
2038
*/
2039
Sprite.prototype.isDead = function() {
2040
return !this._alive;
2041
};
2042
2043
/**
2044
* Sprites are often grouped. That makes collision detection more efficient and
2045
* improves rendering performance. It also allows you to easly keep track of layers
2046
* of objects which are rendered to the screen in a particular order.
2047
*
2048
* `Group.update()` calls `update()` on all the contained sprites; the same is true for `draw()`.
2049
* @constructor
2050
*/
2051
var Group = exports.Group = function() {
2052
/** @ignore */
2053
this._sprites = [];
2054
2055
2056
if (arguments[0] instanceof Sprite ||
2057
(arguments[0] instanceof Array &&
2058
arguments[0].length &&
2059
arguments[0][0] instanceof Sprite
2060
)) {
2061
this.add(arguments[0]);
2062
}
2063
return this;
2064
};
2065
2066
/**
2067
* Update all the sprites in this group. This is equivalent to calling the
2068
* update method on each sprite in this group.
2069
*/
2070
Group.prototype.update = function() {
2071
var updateArgs = arguments;
2072
2073
this._sprites.forEach(function(sp) {
2074
sp.update.apply(sp, updateArgs);
2075
}, this);
2076
return;
2077
};
2078
2079
/**
2080
* Add one or more sprites to this group
2081
* @param {Array|gamejs.sprite.Sprite} sprites One or more
2082
* `gamejs.sprite.Sprite` instances
2083
*/
2084
Group.prototype.add = function(sprites) {
2085
if (!(sprites instanceof Array)) {
2086
sprites = [sprites];
2087
}
2088
2089
sprites.forEach(function(sprite) {
2090
this._sprites.push(sprite);
2091
sprite._groups.push(this);
2092
}, this);
2093
return;
2094
};
2095
2096
/**
2097
* Remove one or more sprites from this group
2098
* @param {Array|gamejs.sprite.Sprite} sprites One or more
2099
* `gamejs.sprite.Sprite` instances
2100
*/
2101
Group.prototype.remove = function(sprites) {
2102
if (!(sprites instanceof Array)) {
2103
sprites = [sprites];
2104
}
2105
2106
sprites.forEach(function(sp) {
2107
arrays.remove(sp, this._sprites);
2108
arrays.remove(this, sp._groups);
2109
}, this);
2110
return;
2111
};
2112
2113
/**
2114
* Check for the existence of one or more sprites within a group
2115
* @param {Array|gamejs.sprite.Sprite} sprites One or more
2116
* `gamejs.sprite.Sprite` instances
2117
* @returns {Boolean} True if every sprite is in this group, false otherwise
2118
*/
2119
Group.prototype.has = function(sprites) {
2120
if (!(sprites instanceof Array)) {
2121
sprites = [sprites];
2122
}
2123
2124
return sprites.every(function(sp) {
2125
return this._sprites.indexOf(sp) !== -1;
2126
}, this);
2127
};
2128
2129
/**
2130
* Get the sprites in this group
2131
* @returns {Array} An array of `gamejs.sprite.Sprite` instances
2132
*/
2133
Group.prototype.sprites = function() {
2134
return this._sprites;
2135
};
2136
2137
/**
2138
* Draw all the sprites in this group. This is equivalent to calling each
2139
* sprite's draw method.
2140
*/
2141
Group.prototype.draw = function() {
2142
var args = arguments;
2143
this._sprites.forEach(function(sprite) {
2144
sprite.draw.apply(sprite, args);
2145
}, this);
2146
return;
2147
};
2148
2149
/**
2150
* Draw background (`source` argument) over each sprite in the group
2151
* on the `destination` surface.
2152
*
2153
* This can, for example, be used to clear the
2154
* display surface to a a static background image in all the places
2155
* occupied by the sprites of all group.
2156
*
2157
* @param {gamejs.Surface} destination the surface to draw on
2158
* @param {gamejs.Surface} source surface
2159
*/
2160
Group.prototype.clear = function(destination, source) {
2161
this._sprites.forEach(function(sprite) {
2162
destination.blit(source, sprite.rect);
2163
}, this);
2164
};
2165
2166
/**
2167
* Remove all sprites from this group
2168
*/
2169
Group.prototype.empty = function() {
2170
this._sprites = [];
2171
return;
2172
};
2173
2174
/**
2175
* @returns {Array} of sprites colliding with the point
2176
*/
2177
Group.prototype.collidePoint = function() {
2178
var args = Array.prototype.slice.apply(arguments);
2179
return this._sprites.filter(function(sprite) {
2180
return sprite.rect.collidePoint.apply(sprite.rect, args);
2181
}, this);
2182
};
2183
2184
/**
2185
* Loop over each sprite in this group. This is a shortcut for
2186
* `group.sprites().forEach(...)`.
2187
*/
2188
Group.prototype.forEach = function(callback, thisArg) {
2189
return this._sprites.forEach(callback, thisArg);
2190
};
2191
2192
/**
2193
* Check whether some sprite in this group passes a test. This is a shortcut
2194
* for `group.sprites().some(...)`.
2195
*/
2196
Group.prototype.some = function(callback, thisArg) {
2197
return this._sprites.some(callback, thisArg);
2198
};
2199
2200
/**
2201
* Find sprites in a group that intersect another sprite
2202
* @param {gamejs.sprite.Sprite} sprite The sprite to check
2203
* @param {gamejs.sprite.Group} group The group to check
2204
* @param {Boolean} doKill If true, kill sprites in the group when collided
2205
* @param {function} collided Collision function to use, defaults to `gamejs.sprite.collideRect`
2206
* @returns {Array} An array of `gamejs.sprite.Sprite` instances that collided
2207
*/
2208
exports.spriteCollide = function(sprite, group, doKill, collided) {
2209
collided = collided || collideRect;
2210
doKill = doKill || false;
2211
2212
var collidingSprites = [];
2213
group.sprites().forEach(function(groupSprite) {
2214
if (collided(sprite, groupSprite)) {
2215
if (doKill) {
2216
groupSprite.kill();
2217
}
2218
collidingSprites.push(groupSprite);
2219
}
2220
});
2221
return collidingSprites;
2222
};
2223
2224
/**
2225
* Find all Sprites that collide between two Groups.
2226
*
2227
* @example
2228
* groupCollide(group1, group2).forEach(function (collision) {
2229
* var group1Sprite = collision.a;
2230
* var group2Sprite = collision.b;
2231
* // Do processing here!
2232
* });
2233
*
2234
* @param {gamejs.sprite.Group} groupA First group to check
2235
* @param {gamejs.sprite.Group} groupB Second group to check
2236
* @param {Boolean} doKillA If true, kill sprites in the first group when
2237
* collided
2238
* @param {Boolean} doKillB If true, kill sprites in the second group when
2239
* collided
2240
* @param {function} collided Collision function to use, defaults to `gamejs.sprite.collideRect`
2241
* @returns {Array} A list of objects where properties 'a' and 'b' that
2242
* correspond with objects from the first and second groups
2243
*/
2244
exports.groupCollide = function(groupA, groupB, doKillA, doKillB, collided) {
2245
doKillA = doKillA || false;
2246
doKillB = doKillB || false;
2247
2248
var collideList = [];
2249
var collideFn = collided || collideRect;
2250
groupA.sprites().forEach(function(groupSpriteA) {
2251
groupB.sprites().forEach(function(groupSpriteB) {
2252
if (collideFn(groupSpriteA, groupSpriteB)) {
2253
if (doKillA) {
2254
groupSpriteA.kill();
2255
}
2256
if (doKillB) {
2257
groupSpriteB.kill();
2258
}
2259
2260
collideList.push({
2261
'a': groupSpriteA,
2262
'b': groupSpriteB
2263
});
2264
}
2265
});
2266
});
2267
2268
return collideList;
2269
};
2270
2271
/**
2272
* Check for collisions between two sprites using their rects.
2273
*
2274
* @param {gamejs.sprite.Sprite} spriteA First sprite to check
2275
* @param {gamejs.sprite.Sprite} spriteB Second sprite to check
2276
* @returns {Boolean} True if they collide, false otherwise
2277
*/
2278
var collideRect = exports.collideRect = function (spriteA, spriteB) {
2279
return spriteA.rect.collideRect(spriteB.rect);
2280
};
2281
2282
/**
2283
* Collision detection between two sprites utilizing the optional `mask`
2284
* attribute on the sprites. Beware: expensive operation.
2285
*
2286
* @param {gamejs.sprite.Sprite} spriteA Sprite with 'mask' property set to a `gamejs.mask.Mask`
2287
* @param {gamejs.sprite.Sprite} spriteB Sprite with 'mask' property set to a `gamejs.mask.Mask`
2288
* @returns {Boolean} True if any mask pixels collide, false otherwise
2289
*/
2290
exports.collideMask = function(spriteA, spriteB) {
2291
if (!spriteA.mask || !spriteB.mask) {
2292
throw new Error("Both sprites must have 'mask' attribute set to an gamejs.mask.Mask");
2293
}
2294
var offset = [
2295
spriteB.rect.left - spriteA.rect.left,
2296
spriteB.rect.top - spriteA.rect.top
2297
];
2298
return spriteA.mask.overlap(spriteB.mask, offset);
2299
};
2300
2301
/**
2302
* Collision detection between two sprites using circles at centers.
2303
* There sprite property `radius` is used if present, otherwise derived from bounding rect.
2304
* @param {gamejs.sprite.Sprite} spriteA First sprite to check
2305
* @param {gamejs.sprite.Sprite} spriteB Second sprite to check
2306
* @returns {Boolean} True if they collide, false otherwise
2307
*/
2308
exports.collideCircle = function(spriteA, spriteB) {
2309
var rA = spriteA.radius || Math.max(spriteA.rect.width, spriteA.rect.height);
2310
var rB = spriteB.radius || Math.max(spriteB.rect.width, spriteB.rect.height);
2311
return $v.distance(spriteA.rect.center, spriteB.rect.center) <= rA + rB;
2312
};
2313
2314
}}, ["gamejs", "gamejs/utils/arrays", "gamejs/utils/objects", "gamejs/utils/vectors"]);/* This file has been generated by yabbler.js */
2315
require.define({
2316
"gamejs/display": function(require, exports, module) {
2317
var Surface = require('../gamejs').Surface;
2318
2319
/**
2320
* @fileoverview Methods to create, access and manipulate the display Surface.
2321
*
2322
* @example
2323
* var display = gamejs.display.setMode([800, 600]);
2324
* // blit sunflower picture in top left corner of display
2325
* var sunflower = gamejs.image.load("images/sunflower");
2326
* display.blit(sunflower);
2327
*
2328
*/
2329
2330
var CANVAS_ID = "gjs-canvas";
2331
var LOADER_ID = "gjs-loader";
2332
var SURFACE = null;
2333
2334
/**
2335
* @returns {document.Element} the canvas dom element
2336
*/
2337
var getCanvas = function() {
2338
var jsGameCanvas = null;
2339
var canvasList = Array.prototype.slice.call(document.getElementsByTagName("canvas"));
2340
canvasList.every(function(canvas) {
2341
if (canvas.getAttribute("id") == CANVAS_ID) {
2342
jsGameCanvas = canvas;
2343
return false;
2344
}
2345
return true;
2346
});
2347
return jsGameCanvas;
2348
};
2349
2350
/**
2351
* Create the master Canvas plane.
2352
* @ignore
2353
*/
2354
exports.init = function() {
2355
// create canvas element if not yet present
2356
var jsGameCanvas = null;
2357
if ((jsGameCanvas = getCanvas()) === null) {
2358
jsGameCanvas = document.createElement("canvas");
2359
jsGameCanvas.setAttribute("id", CANVAS_ID);
2360
document.body.appendChild(jsGameCanvas);
2361
}
2362
// remove loader if any;
2363
var $loader = document.getElementById('gjs-loader');
2364
if ($loader) {
2365
$loader.style.display = "none";
2366
}
2367
return;
2368
};
2369
2370
/**
2371
* Set the width and height of the Display. Conviniently this will
2372
* return the actual display Surface - the same as calling [gamejs.display.getSurface()](#getSurface))
2373
* later on.
2374
* @param {Array} dimensions [width, height] of the display surface
2375
*/
2376
exports.setMode = function(dimensions) {
2377
var canvas = getCanvas();
2378
canvas.width = dimensions[0];
2379
canvas.height = dimensions[1];
2380
return getSurface();
2381
};
2382
2383
/**
2384
* Set the Caption of the Display (document.title)
2385
* @param {String} title the title of the app
2386
* @param {gamejs.Image} icon FIXME implement favicon support
2387
*/
2388
exports.setCaption = function(title, icon) {
2389
document.title = title;
2390
};
2391
2392
2393
/**
2394
* The Display (the canvas element) is most likely not in the top left corner
2395
* of the browser due to CSS styling. To calculate the mouseposition within the
2396
* canvas we need this offset.
2397
* @see {gamejs.event}
2398
* @ignore
2399
*
2400
* @returns {Array} [x, y] offset of the canvas
2401
*/
2402
2403
exports._getCanvasOffset = function() {
2404
var boundRect = getCanvas().getBoundingClientRect();
2405
return [boundRect.left, boundRect.top];
2406
};
2407
2408
/**
2409
* Drawing on the Surface returned by `getSurface()` will draw on the screen.
2410
* @returns {gamejs.Surface} the display Surface
2411
*/
2412
var getSurface = exports.getSurface = function() {
2413
if (SURFACE === null) {
2414
var canvas = getCanvas();
2415
SURFACE = new Surface([canvas.clientWidth, canvas.clientHeight]);
2416
SURFACE._canvas = canvas;
2417
SURFACE._context = canvas.getContext('2d');
2418
SURFACE._smooth();
2419
}
2420
return SURFACE;
2421
};
2422
2423
}}, ["gamejs"]);/* This file has been generated by yabbler.js */
2424
require.define({
2425
"gamejs/pathfinding/astar": function(require, exports, module) {
2426
/**
2427
* @fileoverview
2428
* AStar Path finding algorithm
2429
*
2430
* Use the `findRoute(map, from, to, [timeout])` function to get the linked list
2431
* leading `from` a point `to` another on the given `map`.
2432
*
2433
* The map must implement interface `gamejs.pathfinding.Map`. This
2434
* class really holds an example implementation & data for you to study. If you
2435
* understand what this calls provides, you understand this module.
2436
*
2437
* Optionally, the search is canceld after `timeout` in millseconds.
2438
*
2439
* If there is no route `null` is returned.
2440
*
2441
* @see http://eloquentjavascript.net/chapter7.html
2442
*/
2443
var BinaryHeap = require('../utils/binaryheap').BinaryHeap;
2444
2445
/**
2446
* helper function for A*
2447
*/
2448
function ReachedList(hashFn) {
2449
var list = {};
2450
2451
this.store = function(point, route) {
2452
list[hashFn(point)] = route;
2453
return;
2454
};
2455
2456
this.find = function(point) {
2457
return list[hashFn(point)];
2458
};
2459
return this;
2460
}
2461
2462
2463
/** A* search function.
2464
*
2465
* This function expects a `Map` implementation and the origin and destination
2466
* points given. If there is a path between the two it will return the optimal
2467
* path as a linked list. If there is no path it will return null.
2468
*
2469
* The linked list is in reverse order: the first item is the destination and
2470
* the path to the origin follows.
2471
*
2472
* @param {Map} map map instance, must follow interface defined in {Map}
2473
* @param {Array} origin
2474
* @param {Array} destination
2475
* @param {Number} timeout milliseconds after which search should be canceled
2476
* @returns {Object} the linked list leading from `to` to `from` (sic!).
2477
**/
2478
exports.findRoute = function(map, from, to, timeout) {
2479
var open = new BinaryHeap(routeScore);
2480
var hashFn = typeof map.hash === 'function' ? map.hash : defaultHash;
2481
var reached = new ReachedList(hashFn);
2482
2483
function routeScore(route) {
2484
if (route.score === undefined) {
2485
route.score = map.estimatedDistance(route.point, to) + route.length;
2486
}
2487
return route.score;
2488
}
2489
function addOpenRoute(route) {
2490
open.push(route);
2491
reached.store(route.point, route);
2492
}
2493
2494
function processNewPoints(direction) {
2495
var known = reached.find(direction);
2496
var newLength = route.length + map.actualDistance(route.point, direction);
2497
if (!known || known.length > newLength){
2498
if (known) {
2499
open.remove(known);
2500
}
2501
addOpenRoute({
2502
point: direction,
2503
from: route,
2504
length: newLength
2505
});
2506
}
2507
}
2508
var startMs = Date.now();
2509
var route = null;
2510
addOpenRoute({
2511
point: from,
2512
from: null,
2513
length: 0
2514
});
2515
var equalsFn = typeof map.equals === 'function' ? map.equals : defaultEquals;
2516
while (open.size() > 0 && (!timeout || Date.now() - startMs < timeout)) {
2517
route = open.pop();
2518
if (equalsFn(to, route.point)) {
2519
return route;
2520
}
2521
map.adjacent(route.point).forEach(processNewPoints);
2522
} // end while
2523
return null;
2524
};
2525
2526
var defaultEquals = function(a, b) {
2527
return a[0] === b[0] && a[1] === b[1];
2528
};
2529
2530
var defaultHash = function(a) {
2531
return a[0] + '-' + a[1];
2532
};
2533
2534
/**
2535
* This is the interface for a Map that can be passed to the `findRoute()`
2536
* function. `Map` is not instantiable - see the unit tests for an example
2537
* implementation of Map.
2538
*/
2539
var Map = exports.Map = function() {
2540
throw new Error('not instantiable, this is an interface');
2541
};
2542
2543
/**
2544
* @param {Array} origin
2545
* @returns {Array} list of points accessible from given Point
2546
*/
2547
Map.prototype.adjacent = function(origin) {
2548
};
2549
2550
/**
2551
* @param {Object} a one of the points ot test for equality
2552
* @param {Object} b ... the other point
2553
* @returns Wheter the two points are equal.
2554
*/
2555
Map.prototype.equals = defaultEquals;
2556
2557
/**
2558
* @param {Object} a point
2559
* @returns {String} hash for the point
2560
*/
2561
Map.prototype.hash = defaultHash;
2562
2563
/**
2564
* Estimated lower bound distance between two, adjacent points.
2565
* @param {Object} pointA
2566
* @param {Object} pointB
2567
* @returns {Number} the estimated distance between two points
2568
*/
2569
Map.prototype.estimatedDistance = function(pointA, pointB) {
2570
return 1;
2571
};
2572
2573
/**
2574
* Actual distance between the two given points.
2575
* @param {Object} pointA
2576
* @param {Object} pointB
2577
* @returns {Number} the actual distance between two points
2578
*/
2579
Map.prototype.actualDistance = function(pointA, pointB) {
2580
return 1;
2581
};
2582
2583
}}, ["gamejs/utils/binaryheap"]);/* This file has been generated by yabbler.js */
2584
require.define({
2585
"gamejs/surfacearray": function(require, exports, module) {
2586
var gamejs = require('../gamejs');
2587
var accessors = require('./utils/objects').accessors;
2588
/**
2589
* @fileoverview Fast pixel access.
2590
*
2591
* @example
2592
*
2593
* // create array from display surface
2594
* var srfArray = new SurfaceArray(display);
2595
* // direct pixel access
2596
* srfArray.set(50, 100, [255, 0, 0, 100]);
2597
* console.log(srfArray.get(30, 50));
2598
* // blit modified array back to display surface
2599
* blitArray(display, srfArray);
2600
*/
2601
2602
/**
2603
* Directly copy values from an array into a Surface.
2604
*
2605
* This is faster than blitting the `surface` property on a SurfaceArray
2606
*
2607
* The array must be the same dimensions as the Surface and will completely
2608
* replace all pixel values.
2609
* @param {gamejs.Surface} surface
2610
* @param {gamejs.surfacearray.SurfaceArray} surfaceArray
2611
*/
2612
exports.blitArray = function(surface, surfaceArray) {
2613
surface.context.putImageData(surfaceArray.imageData, 0, 0);
2614
return;
2615
};
2616
2617
/**
2618
* The SurfaceArray can be constructed with a surface whose values
2619
* are then used to initialize the pixel array.
2620
*
2621
* The surface passed as argument is not modified by the SurfaceArray.
2622
*
2623
* If an array is used to construct SurfaceArray, the array must describe
2624
* the dimensions of the SurfaceArray [width, height].
2625
*
2626
* @param {gamejs.Surface|Array} surfaceOrDimensions
2627
* @see http://dev.w3.org/html5/2dcontext/#pixel-manipulation
2628
*/
2629
var SurfaceArray = exports.SurfaceArray = function(surfaceOrDimensions) {
2630
var size = null;
2631
var data = null;
2632
var imageData = null;
2633
2634
/**
2635
* Set rgba value at position x, y.
2636
*
2637
* For performance reasons this function has only one signature
2638
* being Number, Number, Array[4].
2639
*
2640
* @param {Number} x x position of pixel
2641
* @param {Number} y y position of pixel
2642
* @param {Array} rgba [red, green, blue, alpha] values [255, 255, 255, 255] (alpha, the last argument defaults to 255)
2643
* @throws Error if x, y out of range
2644
*/
2645
this.set = function(x, y, rgba) {
2646
var offset = (x * 4) + (y * size[0] * 4);
2647
/** faster without
2648
if (offset + 3 >= data.length || x < 0 || y < 0) {
2649
throw new Error('x, y out of range', x, y);
2650
}
2651
**/
2652
data[offset] = rgba[0];
2653
data[offset+1] = rgba[1];
2654
data[offset+2] = rgba[2];
2655
data[offset+3] = rgba[3] === undefined ? 255 : rgba[3];
2656
return;
2657
};
2658
2659
/**
2660
* Get rgba value at position xy,
2661
* @param {Number} x
2662
* @param {Number} y
2663
* @returns {Array} [red, green, blue, alpha]
2664
*/
2665
this.get = function(x, y) {
2666
var offset = (x * 4) + (y * size[0] * 4);
2667
return [
2668
data[offset],
2669
data[offset+1],
2670
data[offset+2],
2671
data[offset+3]
2672
];
2673
};
2674
2675
/**
2676
* a new gamejs.Surface on every access, representing
2677
* the current state of the SurfaceArray.
2678
* @type {gamejs.Surface}
2679
*/
2680
// for jsdoc only
2681
this.surface = null;
2682
2683
accessors(this, {
2684
surface: {
2685
get: function() {
2686
var s = new gamejs.Surface(size);
2687
s.context.putImageData(imageData, 0, 0);
2688
return s;
2689
}
2690
},
2691
imageData: {
2692
get: function() {
2693
return imageData;
2694
}
2695
}
2696
});
2697
2698
this.getSize = function() {
2699
return size;
2700
};
2701
2702
/**
2703
* constructor
2704
*/
2705
if (surfaceOrDimensions instanceof Array) {
2706
size = surfaceOrDimensions;
2707
imageData = gamejs.display.getSurface().context.createImageData(size[0], size[1]);
2708
data = imageData.data;
2709
} else {
2710
size = surfaceOrDimensions.getSize();
2711
imageData = surfaceOrDimensions.getImageData(0, 0, size[0], size[1]);
2712
data = imageData.data;
2713
}
2714
return this;
2715
};
2716
2717
}}, ["gamejs", "gamejs/utils/objects"]);/* This file has been generated by yabbler.js */
2718
require.define({
2719
"gamejs/base64": function(require, exports, module) {
2720
/**
2721
* @fileoverview
2722
* Base64 encode / decode
2723
* @author http://www.webtoolkit.info
2724
*/
2725
2726
2727
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2728
2729
/**
2730
* Decodes a base64 encoded string to a string.
2731
*/
2732
var decode = exports.decode = function(input) {
2733
var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
2734
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
2735
2736
while (i < input.length) {
2737
enc1 = keyStr.indexOf(input.charAt(i++));
2738
enc2 = keyStr.indexOf(input.charAt(i++));
2739
enc3 = keyStr.indexOf(input.charAt(i++));
2740
enc4 = keyStr.indexOf(input.charAt(i++));
2741
2742
chr1 = (enc1 << 2) | (enc2 >> 4);
2743
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2744
chr3 = ((enc3 & 3) << 6) | enc4;
2745
2746
output.push(String.fromCharCode(chr1));
2747
2748
if (enc3 != 64) {
2749
output.push(String.fromCharCode(chr2));
2750
}
2751
if (enc4 != 64) {
2752
output.push(String.fromCharCode(chr3));
2753
}
2754
}
2755
2756
output = output.join('');
2757
return output;
2758
};
2759
2760
/**
2761
* Decodes a base64 encoded string into a byte array
2762
* @param {String} input
2763
* @param {Array} bytes bytes per character, defaults to 1
2764
*/
2765
exports.decodeAsArray = function(input, bytes) {
2766
bytes = bytes || 1;
2767
var decoded = decode(input);
2768
var len = decoded.length / bytes;
2769
var array = [];
2770
for (var i=0; i< len; i++) {
2771
array[i] = 0;
2772
for (var j = bytes - 1; j >=0; --j) {
2773
array[i] += decoded.charCodeAt((i * bytes) + j) << (j <<3 )
2774
}
2775
}
2776
return array;
2777
}
2778
2779
}}, []);/* This file has been generated by yabbler.js */
2780
require.define({
2781
"gamejs/image": function(require, exports, module) {
2782
var gamejs = require('../gamejs');
2783
2784
/**
2785
* @fileoverview Load images as Surfaces.
2786
*
2787
* Sounds & Images are loaded relative to your game's html page
2788
* (the html which includes the GameJs code) or relative to the
2789
* property `window.$g.resourceBaseHref`
2790
* if it is set.
2791
*
2792
*
2793
*/
2794
2795
var CACHE = {};
2796
2797
/**
2798
* need to export preloading status for require
2799
* @ignore
2800
*/
2801
var _PRELOADING = false;
2802
2803
/**
2804
* Load image and return it on a Surface.
2805
*
2806
* All images must be preloaded before they can be used.
2807
* @example
2808
2809
* gamejs.preload(["./images/ship.png", "./images/sunflower.png"]);
2810
* // ...later...
2811
* display.blit(gamejs.load('images/ship.png'))
2812
*
2813
* @param {String|dom.Image} uriOrImage resource uri for image
2814
* @returns {gamejs.Surface} surface with the image on it.
2815
*/
2816
exports.load = function(key) {
2817
var img;
2818
if (typeof key === 'string') {
2819
img = CACHE[key];
2820
if (!img) {
2821
// TODO sync image loading
2822
throw new Error('Missing "' + key + '", gamejs.preload() all images before trying to load them.');
2823
}
2824
} else {
2825
img = key;
2826
}
2827
var canvas = document.createElement('canvas');
2828
// IEFIX missing html5 feature naturalWidth/Height
2829
canvas.width = img.naturalWidth || img.width;
2830
canvas.height = img.naturalHeight || img.height;
2831
var context = canvas.getContext('2d');
2832
context.drawImage(img, 0, 0);
2833
img.getSize = function() { return [img.naturalWidth, img.naturalHeight]; };
2834
var surface = new gamejs.Surface(img.getSize());
2835
// NOTE hack setting protected _canvas directly
2836
surface._canvas = canvas;
2837
surface._context = context;
2838
return surface;
2839
};
2840
2841
2842
/**
2843
* add all images on the currrent page into cache
2844
* @ignore
2845
*/
2846
exports.init = function() {
2847
return;
2848
};
2849
2850
/**
2851
* preload the given img URIs
2852
* @returns {Function} which returns 0-1 for preload progress
2853
* @ignore
2854
*/
2855
exports.preload = function(imgIdents) {
2856
2857
var countLoaded = 0;
2858
var countTotal = 0;
2859
2860
function incrementLoaded() {
2861
countLoaded++;
2862
if (countLoaded == countTotal) {
2863
_PRELOADING = false;
2864
}
2865
if (countLoaded % 10 === 0) {
2866
gamejs.log('gamejs.image: preloaded ' + countLoaded + ' of ' + countTotal);
2867
}
2868
}
2869
2870
function getProgress() {
2871
return countTotal > 0 ? countLoaded / countTotal : 1;
2872
}
2873
2874
function successHandler() {
2875
addToCache(this);
2876
incrementLoaded();
2877
}
2878
function errorHandler() {
2879
incrementLoaded();
2880
throw new Error('Error loading ' + this.src);
2881
}
2882
2883
for (var key in imgIdents) {
2884
var lowerKey = key.toLowerCase();
2885
if (lowerKey.indexOf('.png') == -1 &&
2886
lowerKey.indexOf('.jpg') == -1 &&
2887
lowerKey.indexOf('.jpeg') == -1 &&
2888
lowerKey.indexOf('.svg') == -1 &&
2889
lowerKey.indexOf('.gif') == -1) {
2890
continue;
2891
}
2892
var img = new Image();
2893
img.addEventListener('load', successHandler, true);
2894
img.addEventListener('error', errorHandler, true);
2895
img.src = imgIdents[key];
2896
img.gamejsKey = key;
2897
countTotal++;
2898
}
2899
if (countTotal > 0) {
2900
_PRELOADING = true;
2901
}
2902
return getProgress;
2903
};
2904
2905
/**
2906
* add the given <img> dom elements into the cache.
2907
* @private
2908
*/
2909
var addToCache = function(img) {
2910
CACHE[img.gamejsKey] = img;
2911
return;
2912
};
2913
2914
}}, ["gamejs"]);/* This file has been generated by yabbler.js */
2915
require.define({
2916
"gamejs/utils/objects": function(require, exports, module) {
2917
/**
2918
* @fileoverview Utility functions for working with Objects
2919
*/
2920
2921
/**
2922
* Put a prototype into the prototype chain of another prototype.
2923
* @param {Object} subClass
2924
* @param {Object} superClass
2925
*/
2926
exports.extend = function(subClass, superClass) {
2927
if (subClass === undefined) {
2928
throw new Error('unknown subClass');
2929
}
2930
if (superClass === undefined) {
2931
throw new Error('unknown superClass');
2932
}
2933
// new Function() is evil
2934
var f = new Function();
2935
f.prototype = superClass.prototype;
2936
2937
subClass.prototype = new f();
2938
subClass.prototype.constructor = subClass;
2939
subClass.superClass = superClass.prototype;
2940
subClass.superConstructor = superClass;
2941
return;
2942
};
2943
2944
/**
2945
* Creates a new object as the as the keywise union of the provided objects.
2946
* Whenever a key exists in a later object that already existed in an earlier
2947
* object, the according value of the earlier object takes precedence.
2948
* @param {Object} obj... The objects to merge
2949
*/
2950
exports.merge = function() {
2951
var result = {};
2952
for (var i = arguments.length; i > 0; --i) {
2953
var obj = arguments[i - 1];
2954
for (var property in obj) {
2955
result[property] = obj[property];
2956
}
2957
}
2958
return result;
2959
};
2960
2961
/**
2962
* fallback for Object.keys
2963
* @param {Object} obj
2964
* @returns {Array} list of own properties
2965
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
2966
*/
2967
var keys = exports.keys = function(obj) {
2968
if (Object.keys) {
2969
return Object.keys(obj);
2970
}
2971
2972
var ret=[],p;
2973
for (p in obj) {
2974
if(Object.prototype.hasOwnProperty.call(obj, p)) {
2975
ret.push(p);
2976
}
2977
}
2978
return ret;
2979
};
2980
2981
/**
2982
* Create object accessors
2983
* @param {Object} object The object on which to define the property
2984
* @param {String} name name of the property
2985
* @param {Function} get
2986
* @param {Function} set
2987
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
2988
*/
2989
var accessor = exports.accessor = function(object, name, get, set) {
2990
// ECMA5
2991
if (Object.defineProperty !== undefined) {
2992
Object.defineProperty(object, name, {
2993
get: get,
2994
set: set
2995
});
2996
// non-standard
2997
} else if (Object.prototype.__defineGetter__ !== undefined) {
2998
object.__defineGetter__(name, get);
2999
if (set) {
3000
object.__defineSetter__(name, set);
3001
}
3002
}
3003
return;
3004
};
3005
3006
/**
3007
* @param {Object} object The object on which to define or modify properties.
3008
* @param {Object} props An object whose own enumerable properties constitute descriptors for the properties to be defined or modified.
3009
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties
3010
*/
3011
exports.accessors = function(object, props) {
3012
keys(props).forEach(function(propKey) {
3013
accessor(object, propKey, props[propKey].get, props[propKey].set);
3014
});
3015
return;
3016
};
3017
3018
}}, []);/* This file has been generated by yabbler.js */
3019
require.define({
3020
"gamejs/utils/uri": function(require, exports, module) {
3021
/**
3022
* @fileoverview Utilies for URI handling.
3023
*
3024
*/
3025
3026
var URI_REGEX = new RegExp(
3027
'^' +
3028
'(?:' +
3029
'([^:/?#.]+)' + // scheme - ignore special characters
3030
// used by other URL parts such as :,
3031
// ?, /, #, and .
3032
':)?' +
3033
'(?://' +
3034
'(?:([^/?#]*)@)?' + // userInfo
3035
'([\\w\\d\\-\\u0100-\\uffff.%]*)' + // domain - restrict to letters,
3036
// digits, dashes, dots, percent
3037
// escapes, and unicode characters.
3038
'(?::([0-9]+))?' + // port
3039
')?' +
3040
'([^?#]+)?' + // path
3041
'(?:\\?([^#]*))?' + // query
3042
'(?:#(.*))?' + // fragment
3043
'$');
3044
3045
/**
3046
* Resolve path against URI.
3047
*
3048
* @param {String} uri
3049
* @param {String} path to resolve
3050
*/
3051
var resolve = exports.resolve = function(uri, path) {
3052
var m = match(uri);
3053
var host = m[1] + '://' + m[3];
3054
if (m[4]) {
3055
host = host + ":" + m[4];
3056
}
3057
var absolutePath = m[5];
3058
if (path.charAt(0) !== '/') {
3059
var lastSlashIndex = absolutePath.lastIndexOf('/');
3060
absolutePath = absolutePath.substr(0, lastSlashIndex + 1) + path;
3061
} else {
3062
absolutePath = path;
3063
}
3064
return host + removeDotSegments(absolutePath);
3065
3066
};
3067
3068
/**
3069
* Try to match an URI against a regex returning the following
3070
* capture groups:
3071
* $1 = http scheme
3072
* $2 = <undefined> userInfo -\
3073
* $3 = www.ics.uci.edu domain | authority
3074
* $4 = <undefined> port -/
3075
* $5 = /pub/ietf/uri/ path
3076
* $6 = <undefined> query without ?
3077
* $7 = Related fragment without #
3078
*
3079
* @param {String} uri
3080
*/
3081
var match = exports.match = function(uri) {
3082
return uri.match(URI_REGEX);
3083
}
3084
3085
/**
3086
* Make an absolute URI relative to document.location.href
3087
* @param {String} uri
3088
* @returns The relative URI or the unchanged URI if it's not
3089
* possible to make it relative to the path of document.location.href.
3090
*/
3091
var makeRelative = exports.makeRelative = function(uri) {
3092
var docLocPath = resolve(document.location.href, './');
3093
if (uri.indexOf(docLocPath) == 0) {
3094
uri = './' + uri.substring(docLocPath.length);
3095
}
3096
return uri;
3097
};
3098
3099
/**
3100
* Removes dot segments in given path component
3101
*/
3102
var removeDotSegments = function(path) {
3103
if (path == '..' || path == '.') {
3104
return '';
3105
}
3106
var leadingSlash = path.indexOf('/') > -1;
3107
3108
var segments = path.split('/');
3109
var out = [];
3110
3111
for (var pos = 0; pos < segments.length; ) {
3112
var segment = segments[pos++];
3113
3114
if (segment == '.') {
3115
if (leadingSlash && pos == segments.length) {
3116
out.push('');
3117
}
3118
} else if (segment == '..') {
3119
if (out.length > 1 || out.length == 1 && out[0] != '') {
3120
out.pop();
3121
}
3122
if (leadingSlash && pos == segments.length) {
3123
out.push('');
3124
}
3125
} else {
3126
out.push(segment);
3127
leadingSlash = true;
3128
}
3129
}
3130
return out.join('/');
3131
};
3132
3133
}}, []);/* This file has been generated by yabbler.js */
3134
require.define({
3135
"gamejs/utils/strings": function(require, exports, module) {
3136
/**
3137
* Get the longest common segment that two strings
3138
* have in common, starting at the beginning of the string
3139
* @param {String} str1 a string
3140
* @param {String} str2 another string
3141
* @returns {String} the longest common segment
3142
*/
3143
exports.getCommonPrefix = function getCommonPrefix(str1, str2) {
3144
if (str1 == null || str2 == null) {
3145
return null;
3146
} else if (str1.length > str2.length && str1.indexOf(str2) == 0) {
3147
return str2;
3148
} else if (str2.length > str1.length && str2.indexOf(str1) == 0) {
3149
return str1;
3150
}
3151
var length = Math.min(str1.length, str2.length);
3152
for (var i = 0; i < length; i++) {
3153
if (str1[i] != str2[i]) {
3154
return str1.slice(0, i);
3155
}
3156
}
3157
return str1.slice(0, length);
3158
}
3159
3160
}}, []);/* This file has been generated by yabbler.js */
3161
require.define({
3162
"gamejs/utils/matrix": function(require, exports, module) {
3163
/**
3164
* @fileoverview Matrix manipulation, used by GameJs itself. You
3165
* probably do not need this unless you manipulate a Context's transformation
3166
* matrix yourself.
3167
*/
3168
3169
// correct way to do scale, rotate, translate
3170
// * gamejs.utils.matrix will be used in gamejs.transforms, modifing the surfaces.matrix
3171
// * this matrix must be applied to the context in Surface.draw()
3172
3173
/**
3174
* @returns {Array} [1, 0, 0, 1, 0, 0]
3175
*/
3176
var identiy = exports.identity = function () {
3177
return [1, 0, 0, 1, 0, 0];
3178
};
3179
3180
/**
3181
* @param {Array} matrix
3182
* @param {Array} matrix
3183
* @returns {Array} matrix sum
3184
*/
3185
var add = exports.add = function(m1, m2) {
3186
return [
3187
m1[0] + m2[0],
3188
m1[1] + m2[1],
3189
m1[2] + m2[2],
3190
m1[3] + m2[3],
3191
m1[4] + m2[4],
3192
m1[5] + m2[5],
3193
m1[6] + m2[6]
3194
];
3195
};
3196
3197
/**
3198
* @param {Array} matrix A
3199
* @param {Array} matrix B
3200
* @returns {Array} matrix product
3201
*/
3202
var multiply = exports.multiply = function(m1, m2) {
3203
return [
3204
m1[0] * m2[0] + m1[2] * m2[1],
3205
m1[1] * m2[0] + m1[3] * m2[1],
3206
m1[0] * m2[2] + m1[2] * m2[3],
3207
m1[1] * m2[2] + m1[3] * m2[3],
3208
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
3209
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
3210
];
3211
};
3212
3213
/**
3214
* @param {Array} matrix
3215
* @param {Number} dx
3216
* @param {Number} dy
3217
* @returns {Array} translated matrix
3218
*/
3219
var translate = exports.translate = function(m1, dx, dy) {
3220
return multiply(m1, [1, 0, 0, 1, dx, dy]);
3221
};
3222
3223
/**
3224
* @param {Array} matrix
3225
* @param {Number} angle in radians
3226
* @returns {Array} rotated matrix
3227
*/
3228
var rotate = exports.rotate = function(m1, angle) {
3229
// radians
3230
var sin = Math.sin(angle);
3231
var cos = Math.cos(angle);
3232
return multiply(m1, [cos, sin, -sin, cos, 0, 0]);
3233
};
3234
3235
/**
3236
* @param {Array} matrix
3237
* @returns {Number} rotation in radians
3238
*/
3239
var rotation = exports.rotation = function(m1) {
3240
return Math.atan2(m1[1], m1[0]);
3241
};
3242
3243
/**
3244
* @param {Array} matrix
3245
* @param {Array} vector [a, b]
3246
* @returns {Array} scaled matrix
3247
*/
3248
var scale = exports.scale = function(m1, svec) {
3249
var sx = svec[0];
3250
var sy = svec[1];
3251
return multiply(m1, [sx, 0, 0, sy, 0, 0]);
3252
};
3253
3254
}}, []);/* This file has been generated by yabbler.js */
3255
require.define({
3256
"gamejs/utils/math": function(require, exports, module) {
3257
/**
3258
*
3259
* absolute angle to relative angle, in degrees
3260
* @param {Number} absolute angle in degrees
3261
* @returns {Number} relative angle in degrees
3262
*/
3263
exports.normaliseDegrees=function(degrees){
3264
degrees=degrees % 360;
3265
if(degrees<0) {
3266
degrees+=360;
3267
}
3268
return degrees;
3269
};
3270
3271
/**
3272
*
3273
* absolute angle to relative angle, in radians
3274
* @param {Number} absolute angle in radians
3275
* @returns {Number} relative angle in radians
3276
*/
3277
exports.normaliseRadians=function(radians){
3278
radians=radians % (2*Math.PI);
3279
if(radians<0) {
3280
radians+=(2*Math.PI);
3281
}
3282
return radians;
3283
};
3284
3285
/**
3286
*
3287
* convert radians to degrees
3288
* @param {Number} radians
3289
* @returns {Number} degrees
3290
*/
3291
exports.degrees=function(radians) {
3292
return radians*(180/Math.PI);
3293
};
3294
3295
/**
3296
*
3297
* convert degrees to radians
3298
* @param {Number} degrees
3299
* @returns {Number} radians
3300
*/
3301
exports.radians=function(degrees) {
3302
return degrees*(Math.PI/180);
3303
};
3304
3305
/**
3306
* @returns the center of multipled 2d points
3307
* @param {Array} first point
3308
* @param {Array} second point
3309
* @param {Array} ...
3310
*/
3311
exports.centroid = function() {
3312
var args = Array.prototype.slice.apply(arguments, [0]);
3313
var c = [0,0];
3314
args.forEach(function(p) {
3315
c[0] += parseInt(p[0], 10);
3316
c[1] += parseInt(p[1], 10);
3317
});
3318
var len = args.length;
3319
return [
3320
c[0] / len,
3321
c[1] / len
3322
];
3323
};
3324
3325
}}, []);/* This file has been generated by yabbler.js */
3326
require.define({
3327
"gamejs/utils/vectors": function(require, exports, module) {
3328
var math=require('./math');
3329
3330
/**
3331
* @param {Array} origin point [b0, b1]
3332
* @param {Array} target point [b0, b1]
3333
* @returns {Number} distance between two points
3334
*/
3335
exports.distance = function(a, b) {
3336
return len(subtract(a, b));
3337
};
3338
3339
/**
3340
* subtracts vectors [a0, a1] - [a0, a1]
3341
* @param {Array} a
3342
* @param {Array} b
3343
* @returns {Array} vector
3344
*/
3345
var subtract = exports.subtract = function(a, b) {
3346
return [a[0] - b[0], a[1] - b[1]];
3347
};
3348
3349
/**
3350
* adds vectors [a0, a1] - [a0, a1]
3351
* @param {Array} a vector
3352
* @param {Array} b vector
3353
* @returns {Array} vector
3354
*/
3355
var add = exports.add = function(a, b) {
3356
return [a[0] + b[0], a[1] + b[1]];
3357
};
3358
3359
/**
3360
* multiply vector with scalar or other vector
3361
* @param {Array} vector [v0, v1]
3362
* @param {Number|Array} vector or number
3363
* @returns {Number|Array} result
3364
*/
3365
var multiply = exports.multiply = function(a, s) {
3366
if (typeof s === 'number') {
3367
return [a[0] * s, a[1] * s];
3368
}
3369
3370
return [a[0] * s[0], a[1] * s[1]];
3371
};
3372
3373
/**
3374
* @param {Array} a vector
3375
* @param {Number} s
3376
*/
3377
exports.divide = function(a, s) {
3378
if (typeof s === 'number') {
3379
return [a[0] / s, a[1] / s];
3380
}
3381
throw new Error('only divide by scalar supported');
3382
};
3383
3384
/**
3385
* @param {Array} vector [v0, v1]
3386
* @returns {Number} length of vector
3387
*/
3388
var len = exports.len = function(v) {
3389
return Math.sqrt(v[0]*v[0] + v[1]*v[1]);
3390
};
3391
3392
/**
3393
*
3394
* normalize vector to unit vector
3395
* @param {Array} vector [v0, v1]
3396
* @returns {Array} unit vector [v0, v1]
3397
*/
3398
var unit = exports.unit = function(v) {
3399
var l = len(v);
3400
if(l) return [v[0] / l, v[1] / l];
3401
return [0, 0];
3402
};
3403
3404
/**
3405
*
3406
* rotate vector
3407
* @param {Array} vector [v0, v1]
3408
* @param {Number} angle to rotate vector by, radians. can be negative
3409
* @returns {Array} rotated vector [v0, v1]
3410
*/
3411
exports.rotate=function(v, angle){
3412
angle=math.normaliseRadians(angle);
3413
return [v[0]* Math.cos(angle)-v[1]*Math.sin(angle),
3414
v[0]* Math.sin(angle)+v[1]*Math.cos(angle)];
3415
3416
};
3417
3418
/**
3419
*
3420
* calculate vector dot product
3421
* @param {Array} vector [v0, v1]
3422
* @param {Array} vector [v0, v1]
3423
* @returns {Number} dot product of v1 and v2
3424
*/
3425
var dot = exports.dot=function(v1, v2){
3426
return (v1[0] * v2[0]) + (v1[1] * v2[1]);
3427
};
3428
3429
/**
3430
*
3431
* calculate angle between vectors
3432
* @param {Array} vector [v0, v1]
3433
* @param {Array} vector [v0, v1]
3434
* @returns {Number} angle between v1 and v2 in radians
3435
*/
3436
exports.angle=function(v1, v2){
3437
var a1 = Math.atan2(v1[0], v1[1]);
3438
var a2 = Math.atan2(v2[0], v2[1]);
3439
var rel = a1 - a2;
3440
return rel - Math.floor((rel + Math.PI) / (2 * Math.PI)) * (2 * Math.PI) - (2 * Math.PI);
3441
};
3442
3443
/**
3444
* @returns {Array} vector with max length as specified.
3445
*/
3446
exports.truncate = function(v, maxLength) {
3447
if (len(v) > maxLength) {
3448
return multiply(unit(v), maxLength);
3449
};
3450
return v;
3451
};
3452
3453
}}, ["gamejs/utils/math"]);/* This file has been generated by yabbler.js */
3454
require.define({
3455
"gamejs/utils/arrays": function(require, exports, module) {
3456
/**
3457
* @fileoverview Utility functions for working with Obiects
3458
* @param {Object} item
3459
* @param {Array} array
3460
* @param {Object} returns removed item or null
3461
*/
3462
3463
exports.remove = function(item, array) {
3464
var index = array.indexOf(item);
3465
if (index !== -1) {
3466
return array.splice(array.indexOf(item), 1);
3467
}
3468
return null;
3469
};
3470
3471
/**
3472
* Shuffles the array *in place*.
3473
* @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
3474
*/
3475
exports.shuffle = function(array) {
3476
var len = array.length -1;
3477
for (i = len; i > 0; i--) {
3478
var idx = parseInt(Math.random() * (i + 1));
3479
var item = array[i];
3480
array[i] = array[idx];
3481
array[idx] = item;
3482
}
3483
return array;
3484
};
3485
3486
}}, []);/* This file has been generated by yabbler.js */
3487
require.define({
3488
"gamejs/utils/binaryheap": function(require, exports, module) {
3489
/**
3490
* Binary Heap
3491
*
3492
* @see http://eloquentjavascript.net/appendix2.html
3493
*/
3494
var BinaryHeap = exports.BinaryHeap = function(scoreFunction){
3495
/**
3496
* @ignore
3497
*/
3498
this.content = [];
3499
/**
3500
* @ignore
3501
*/
3502
this.scoreFunction = scoreFunction;
3503
return this;
3504
};
3505
3506
/**
3507
* Add element to heap.
3508
* @param {Object} element
3509
*/
3510
BinaryHeap.prototype.push = function(element) {
3511
this.content.push(element);
3512
this.sinkDown(this.content.length - 1);
3513
return;
3514
};
3515
3516
/**
3517
* Return first element from heap.
3518
* @param {Object} element
3519
* @returns {Object} element
3520
*/
3521
BinaryHeap.prototype.pop = function() {
3522
// Store the first element so we can return it later.
3523
var result = this.content[0];
3524
// Get the element at the end of the array.
3525
var end = this.content.pop();
3526
// If there are any elements left, put the end element at the
3527
// start, and let it bubble up.
3528
if (this.content.length > 0) {
3529
this.content[0] = end;
3530
this.bubbleUp(0);
3531
}
3532
return result;
3533
};
3534
3535
/**
3536
* Remove the given element from the heap.
3537
* @param {Object} element
3538
* @throws {Error} if node not found
3539
*/
3540
BinaryHeap.prototype.remove = function(node) {
3541
// To remove a value, we must search through the array to find
3542
// it.
3543
var isFound = this.content.some(function(cNode, idx) {
3544
if (cNode == node) {
3545
var end = this.content.pop();
3546
if (idx != this.content.length) {
3547
this.content[idx] = end;
3548
if (this.scoreFunction(end) < this.scoreFunction(node)) {
3549
this.sinkDown(idx);
3550
} else {
3551
this.bubbleUp(idx);
3552
}
3553
}
3554
return true;
3555
}
3556
return false;
3557
}, this);
3558
if (!isFound) {
3559
//throw new Error("Node not found.");
3560
}
3561
return;
3562
};
3563
3564
/**
3565
* Number of elements in heap.
3566
*/
3567
BinaryHeap.prototype.size = function() {
3568
return this.content.length;
3569
};
3570
3571
/**
3572
* @ignore
3573
*/
3574
BinaryHeap.prototype.sinkDown = function(idx) {
3575
// Fetch the element that has to be sunk
3576
var element = this.content[idx];
3577
// When at 0, an element can not sink any further.
3578
while (idx > 0) {
3579
// Compute the parent element's index, and fetch it.
3580
var parentIdx = Math.floor((idx + 1) / 2) - 1;
3581
var parent = this.content[parentIdx];
3582
// Swap the elements if the parent is greater.
3583
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
3584
this.content[parentIdx] = element;
3585
this.content[idx] = parent;
3586
// Update 'n' to continue at the new position.
3587
idx = parentIdx;
3588
// Found a parent that is less, no need to sink any further.
3589
} else {
3590
break;
3591
}
3592
}
3593
return;
3594
};
3595
3596
/**
3597
* @ignore
3598
*/
3599
BinaryHeap.prototype.bubbleUp = function(idx) {
3600
// Look up the target element and its score.
3601
var length = this.content.length;
3602
var element = this.content[idx];
3603
var elemScore = this.scoreFunction(element);
3604
3605
while(true) {
3606
// Compute the indices of the child elements.
3607
var child2Idx = (idx + 1) * 2;
3608
var child1Idx= child2Idx - 1;
3609
// This is used to store the new position of the element,
3610
// if any.
3611
var swapIdx = null;
3612
// If the first child exists (is inside the array)...
3613
if (child1Idx < length) {
3614
// Look it up and compute its score.
3615
var child1 = this.content[child1Idx];
3616
var child1Score = this.scoreFunction(child1);
3617
// If the score is less than our element's, we need to swap.
3618
if (child1Score < elemScore) {
3619
swapIdx = child1Idx;
3620
}
3621
}
3622
// Do the same checks for the other child.
3623
if (child2Idx < length) {
3624
var child2 = this.content[child2Idx];
3625
var child2Score = this.scoreFunction(child2);
3626
if (child2Score < (swapIdx === null ? elemScore : child1Score)) {
3627
swapIdx = child2Idx;
3628
}
3629
}
3630
3631
// If the element needs to be moved, swap it, and continue.
3632
if (swapIdx !== null) {
3633
this.content[idx] = this.content[swapIdx];
3634
this.content[swapIdx] = element;
3635
idx = swapIdx;
3636
// Otherwise, we are done.
3637
} else {
3638
break;
3639
}
3640
}
3641
return;
3642
};
3643
3644
}}, []);/* This file has been generated by yabbler.js */
3645
require.define({
3646
"gamejs/font": function(require, exports, module) {
3647
var Surface = require('../gamejs').Surface;
3648
var objects = require('./utils/objects');
3649
3650
/**
3651
* @fileoverview Methods for creating Font objects which can render text
3652
* to a Surface.
3653
*
3654
* @example
3655
* // create a font
3656
* var font = new Font('20px monospace');
3657
* // render text - this returns a surface with the text written on it.
3658
* var helloSurface = font.render('Hello World')
3659
*/
3660
3661
/**
3662
* Create a Font to draw on the screen. The Font allows you to
3663
* `render()` text. Rendering text returns a Surface which
3664
* in turn can be put on screen.
3665
*
3666
* @constructor
3667
* @property {Number} fontHeight the line height of this Font
3668
*
3669
* @param {String} fontSettings a css font definition, e.g., "20px monospace"
3670
* @param {STring} backgroundColor valid #rgb string, "#ff00cc"
3671
*/
3672
var Font = exports.Font = function(fontSettings, backgroundColor) {
3673
/**
3674
* @ignore
3675
*/
3676
this.sampleSurface = new Surface([10,10]);
3677
this.sampleSurface.context.font = fontSettings;
3678
this.sampleSurface.context.textAlign = 'start';
3679
// http://diveintohtml5.org/canvas.html#text
3680
this.sampleSurface.context.textBaseline = 'bottom';
3681
this.backgroundColor = backgroundColor || false;
3682
return this;
3683
};
3684
3685
/**
3686
* Returns a Surface with the given text on it.
3687
* @param {String} text the text to render
3688
* @param {String} color a valid #RGB String, "#ffcc00"
3689
* @returns {gamejs.Surface} Surface with the rendered text on it.
3690
*/
3691
Font.prototype.render = function(text, color) {
3692
var dims = this.size(text);
3693
var surface = new Surface(dims);
3694
var ctx = surface.context;
3695
ctx.save();
3696
if ( this.backgroundColor ) {
3697
ctx.fillStyle = this.backgroundColor;
3698
ctx.fillRect(0, 0, surface.rect.width, surface.rect.height);
3699
}
3700
ctx.font = this.sampleSurface.context.font;
3701
ctx.textBaseline = this.sampleSurface.context.textBaseline;
3702
ctx.textAlign = this.sampleSurface.context.textAlign;
3703
ctx.fillStyle = ctx.strokeStyle = color || "#000000";
3704
ctx.fillText(text, 0, surface.rect.height, surface.rect.width);
3705
ctx.restore();
3706
return surface;
3707
};
3708
3709
/**
3710
* Determine the width and height of the given text if rendered
3711
* with this Font.
3712
* @param {String} text the text to measure
3713
* @returns {Array} the [width, height] of the text if rendered with this Font
3714
*/
3715
Font.prototype.size = function(text) {
3716
var metrics = this.sampleSurface.context.measureText(text);
3717
// FIXME measuretext is buggy, make extra wide
3718
return [metrics.width, this.fontHeight];
3719
};
3720
3721
/**
3722
* Height of the font in pixels.
3723
*/
3724
objects.accessors(Font.prototype, {
3725
'fontHeight': {
3726
get: function() {
3727
// Returns an approximate line height of the text
3728
// »This version of the specification does not provide a way to obtain
3729
// the bounding box dimensions of the text.«
3730
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-measuretext
3731
return this.sampleSurface.context.measureText('M').width * 1.5;
3732
}
3733
}
3734
3735
});
3736
3737
}}, ["gamejs", "gamejs/utils/objects"]);/* This file has been generated by yabbler.js */
3738
require.define({
3739
"gamejs": function(require, exports, module) {
3740
var matrix = require('./gamejs/utils/matrix');
3741
var objects = require('./gamejs/utils/objects');
3742
3743
/**
3744
* @fileoverview This module holds the essential `Rect` and `Surface` classes as
3745
* well as static methods for preloading assets. `gamejs.ready()` is maybe
3746
* the most important as it kickstarts your app.
3747
*
3748
*/
3749
3750
var DEBUG_LEVELS = ['info', 'warn', 'error', 'fatal'];
3751
var debugLevel = 2;
3752
3753
/**
3754
* set logLevel as string or number
3755
* * 0 = info
3756
* * 1 = warn
3757
* * 2 = error
3758
* * 3 = fatal
3759
*
3760
* @example
3761
* gamejs.setLogLevel(0); // debug
3762
* gamejs.setLogLevel('error'); // equal to setLogLevel(2)
3763
*/
3764
exports.setLogLevel = function(logLevel) {
3765
if (typeof logLevel === 'string' && DEBUG_LEVELS.indexOf(logLevel)) {
3766
debugLevel = DEBUG_LEVELS.indexOf(logLevel);
3767
} else if (typeof logLevel === 'number') {
3768
debugLevel = logLevel;
3769
} else {
3770
throw new Error('invalid logLevel ', logLevel, ' Must be one of: ', DEBUG_LEVELS);
3771
}
3772
return debugLevel;
3773
};
3774
/**
3775
* Log a msg to the console if console is enable
3776
* @param {String} msg the msg to log
3777
*/
3778
var log = exports.log = function() {
3779
3780
if (gamejs.worker.inWorker === true) {
3781
gamejs.worker._logMessage(arguments);
3782
return;
3783
}
3784
3785
// IEFIX can't call apply on console
3786
var args = Array.prototype.slice.apply(arguments, [0]);
3787
args.unshift(Date.now());
3788
if (window.console !== undefined && console.log.apply) {
3789
console.log.apply(console, args);
3790
}
3791
};
3792
exports.info = function() {
3793
if (debugLevel <= DEBUG_LEVELS.indexOf('info')) {
3794
log.apply(this, arguments);
3795
}
3796
};
3797
exports.warn = function() {
3798
if (debugLevel <= DEBUG_LEVELS.indexOf('warn')) {
3799
log.apply(this, arguments);
3800
}
3801
};
3802
exports.error = function() {
3803
if (debugLevel <= DEBUG_LEVELS.indexOf('error')) {
3804
log.apply(this, arguments);
3805
}
3806
};
3807
exports.fatal = function() {
3808
if (debugLevel <= DEBUG_LEVELS.indexOf('fatal')) {
3809
log.apply(this, arguments);
3810
}
3811
};
3812
3813
/**
3814
* Normalize various ways to specify a Rect into {left, top, width, height} form.
3815
*
3816
*/
3817
function normalizeRectArguments() {
3818
var left = 0;
3819
var top = 0;
3820
var width = 0;
3821
var height = 0;
3822
3823
if (arguments.length === 2) {
3824
if (arguments[0] instanceof Array && arguments[1] instanceof Array) {
3825
left = arguments[0][0];
3826
top = arguments[0][1];
3827
width = arguments[1][0];
3828
height = arguments[1][1];
3829
} else {
3830
left = arguments[0];
3831
top = arguments[1];
3832
}
3833
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
3834
left = arguments[0][0];
3835
top = arguments[0][1];
3836
width = arguments[0][2];
3837
height = arguments[0][3];
3838
} else if (arguments.length === 1 && arguments[0] instanceof Rect) {
3839
left = arguments[0].left;
3840
top = arguments[0].top;
3841
width = arguments[0].width;
3842
height = arguments[0].height;
3843
} else if (arguments.length === 4) {
3844
left = arguments[0];
3845
top = arguments[1];
3846
width = arguments[2];
3847
height = arguments[3];
3848
} else {
3849
throw new Error('not a valid rectangle specification');
3850
}
3851
return {left: left || 0, top: top || 0, width: width || 0, height: height || 0};
3852
}
3853
3854
/**
3855
* Creates a Rect. Rects are used to hold rectangular areas. There are a couple
3856
* of convinient ways to create Rects with different arguments and defaults.
3857
*
3858
* Any function that requires a `gamejs.Rect` argument also accepts any of the
3859
* constructor value combinations `Rect` accepts.
3860
*
3861
* Rects are used a lot. They are good for collision detection, specifying
3862
* an area on the screen (for blitting) or just to hold an objects position.
3863
*
3864
* The Rect object has several virtual attributes which can be used to move and align the Rect:
3865
*
3866
* top, left, bottom, right
3867
* topleft, bottomleft, topright, bottomright
3868
* center
3869
* width, height
3870
* w,h
3871
*
3872
* All of these attributes can be assigned to.
3873
* Assigning to width or height changes the dimensions of the rectangle; all other
3874
* assignments move the rectangle without resizing it. Notice that some attributes
3875
* are Numbers and others are pairs of Numbers.
3876
*
3877
* @example
3878
* new Rect([left, top]) // width & height default to 0
3879
* new Rect(left, top) // width & height default to 0
3880
* new Rect(left, top, width, height)
3881
* new Rect([left, top], [width, height])
3882
* new Rect(oldRect) // clone of oldRect is created
3883
*
3884
* @property {Number} right
3885
* @property {Number} bottom
3886
* @property {Number} center
3887
*
3888
* @param {Array|gamejs.Rect} position Array holding left and top coordinates
3889
* @param {Array} dimensions Array holding width and height
3890
*/
3891
var Rect = exports.Rect = function() {
3892
3893
var args = normalizeRectArguments.apply(this, arguments);
3894
3895
/**
3896
* Left, X coordinate
3897
* @type Number
3898
*/
3899
this.left = args.left;
3900
3901
/**
3902
* Top, Y coordinate
3903
* @type Number
3904
*/
3905
this.top = args.top;
3906
3907
/**
3908
* Width of rectangle
3909
* @type Number
3910
*/
3911
this.width = args.width;
3912
3913
/**
3914
* Height of rectangle
3915
* @type Number
3916
*/
3917
this.height = args.height;
3918
3919
return this;
3920
};
3921
3922
objects.accessors(Rect.prototype, {
3923
/**
3924
* Bottom, Y coordinate
3925
* @name Rect.prototype.bottom
3926
* @type Number
3927
*/
3928
'bottom': {
3929
get: function() {
3930
return this.top + this.height;
3931
},
3932
set: function(newValue) {
3933
this.top = newValue - this.height;
3934
return;
3935
}
3936
},
3937
/**
3938
* Right, X coordinate
3939
* @name Rect.prototype.right
3940
* @type Number
3941
*/
3942
'right': {
3943
get: function() {
3944
return this.left + this.width;
3945
},
3946
set: function(newValue) {
3947
this.left = newValue - this.width;
3948
}
3949
},
3950
/**
3951
* Center Position. You can assign a rectangle form.
3952
* @name Rect.prototype.center
3953
* @type Array
3954
*/
3955
'center': {
3956
get: function() {
3957
return [this.left + (this.width / 2) | 0,
3958
this.top + (this.height / 2) | 0
3959
];
3960
},
3961
set: function() {
3962
var args = normalizeRectArguments.apply(this, arguments);
3963
this.left = args.left - (this.width / 2) | 0;
3964
this.top = args.top - (this.height / 2) | 0;
3965
return;
3966
}
3967
},
3968
/**
3969
* Top-left Position. You can assign a rectangle form.
3970
* @name Rect.prototype.topleft
3971
* @type Array
3972
*/
3973
'topleft': {
3974
get: function() {
3975
return [this.left, this.top];
3976
},
3977
set: function() {
3978
var args = normalizeRectArguments.apply(this, arguments);
3979
this.left = args.left;
3980
this.top = args.top;
3981
return;
3982
}
3983
},
3984
/**
3985
* Bottom-left Position. You can assign a rectangle form.
3986
* @name Rect.prototype.bottomleft
3987
* @type Array
3988
*/
3989
'bottomleft': {
3990
get: function() {
3991
return [this.left, this.bottom];
3992
},
3993
set: function() {
3994
var args = normalizeRectArguments.apply(this, arguments);
3995
this.left = args.left;
3996
this.bottom = args.bottom;
3997
return;
3998
}
3999
},
4000
/**
4001
* Top-right Position. You can assign a rectangle form.
4002
* @name Rect.prototype.topright
4003
* @type Array
4004
*/
4005
'topright': {
4006
get: function() {
4007
return [this.right, this.top];
4008
},
4009
set: function() {
4010
var args = normalizeRectArguments.apply(this, arguments);
4011
this.right = args.right;
4012
this.top = args.top;
4013
return;
4014
}
4015
},
4016
/**
4017
* Bottom-right Position. You can assign a rectangle form.
4018
* @name Rect.prototype.bottomright
4019
* @type Array
4020
*/
4021
'bottomright': {
4022
get: function() {
4023
return [this.right, this.bottom];
4024
},
4025
set: function() {
4026
var args = normalizeRectArguments.apply(this, arguments);
4027
this.right = args.right;
4028
this.bottom = args.bottom;
4029
return;
4030
}
4031
},
4032
/**
4033
* Position x value, alias for `left`.
4034
* @name Rect.prototype.y
4035
* @type Array
4036
*/
4037
'x': {
4038
get: function() {
4039
return this.left;
4040
},
4041
set: function(newValue) {
4042
this.left = newValue;
4043
return;
4044
}
4045
},
4046
/**
4047
* Position y value, alias for `top`.
4048
* @name Rect.prototype.y
4049
* @type Array
4050
*/
4051
'y': {
4052
get: function() {
4053
return this.top;
4054
},
4055
set: function(newValue) {
4056
this.top = newValue;
4057
return;
4058
}
4059
}
4060
});
4061
4062
/**
4063
* Move returns a new Rect, which is a version of this Rect
4064
* moved by the given amounts. Accepts any rectangle form.
4065
* as argument.
4066
*
4067
* @param {Number|gamejs.Rect} x amount to move on x axis
4068
* @param {Number} y amount to move on y axis
4069
*/
4070
Rect.prototype.move = function() {
4071
var args = normalizeRectArguments.apply(this, arguments);
4072
return new Rect(this.left + args.left, this.top + args.top, this.width, this.height);
4073
};
4074
4075
/**
4076
* Move this Rect in place - not returning a new Rect like `move(x, y)` would.
4077
*
4078
* `moveIp(x,y)` or `moveIp([x,y])`
4079
*
4080
* @param {Number|gamejs.Rect} x amount to move on x axis
4081
* @param {Number} y amount to move on y axis
4082
*/
4083
Rect.prototype.moveIp = function() {
4084
var args = normalizeRectArguments.apply(this, arguments);
4085
this.left += args.left;
4086
this.top += args.top;
4087
return;
4088
};
4089
4090
/**
4091
* Return the area in which this Rect and argument Rect overlap.
4092
*
4093
* @param {gamejs.Rect} Rect to clip this one into
4094
* @returns {gamejs.Rect} new Rect which is completely inside the argument Rect,
4095
* zero sized Rect if the two rectangles do not overlap
4096
*/
4097
Rect.prototype.clip = function(rect) {
4098
if(!this.collideRect(rect)) {
4099
return new Rect(0,0,0,0);
4100
}
4101
4102
var x, y, width, height;
4103
4104
// Left
4105
if ((this.left >= rect.left) && (this.left < rect.right)) {
4106
x = this.left;
4107
} else if ((rect.left >= this.left) && (rect.left < this.right)) {
4108
x = rect.left;
4109
}
4110
4111
// Right
4112
if ((this.right > rect.left) && (this.right <= rect.right)) {
4113
width = this.right - x;
4114
} else if ((rect.right > this.left) && (rect.right <= this.right)) {
4115
width = rect.right - x;
4116
}
4117
4118
// Top
4119
if ((this.top >= rect.top) && (this.top < rect.bottom)) {
4120
y = this.top;
4121
} else if ((rect.top >= this.top) && (rect.top < this.bottom)) {
4122
y = rect.top;
4123
}
4124
4125
// Bottom
4126
if ((this.bottom > rect.top) && (this.bottom <= rect.bottom)) {
4127
height = this.bottom - y;
4128
} else if ((rect.bottom > this.top) && (rect.bottom <= this.bottom)) {
4129
height = rect.bottom - y;
4130
}
4131
return new Rect(x, y, width, height);
4132
};
4133
4134
/**
4135
* Join two rectangles
4136
*
4137
* @param {gamejs.Rect} union with this rectangle
4138
* @returns {gamejs.Rect} rectangle containing area of both rectangles
4139
*/
4140
Rect.prototype.union = function(rect) {
4141
var x, y, width, height;
4142
4143
x = Math.min(this.left, rect.left);
4144
y = Math.min(this.top, rect.top);
4145
width = Math.max(this.right, rect.right) - x;
4146
height = Math.max(this.bottom, rect.bottom) - y;
4147
return new Rect(x, y, width, height);
4148
};
4149
4150
/**
4151
* Grow or shrink the rectangle size
4152
*
4153
* @param {Number} amount to change in the width
4154
* @param {Number} amount to change in the height
4155
* @returns {gamejs.Rect} inflated rectangle centered on the original rectangle's center
4156
*/
4157
Rect.prototype.inflate = function(x, y) {
4158
var copy = this.clone();
4159
4160
copy.inflateIp(x, y);
4161
4162
return copy;
4163
}
4164
4165
/**
4166
* Grow or shrink this Rect in place - not returning a new Rect like `inflate(x, y)` would.
4167
*
4168
* @param {Number} amount to change in the width
4169
* @param {Number} amount to change in the height
4170
*/
4171
Rect.prototype.inflateIp = function(x, y) {
4172
// Use Math.floor here to deal with rounding of negative numbers the
4173
// way this relies on.
4174
this.left -= Math.floor(x / 2);
4175
this.top -= Math.floor(y / 2);
4176
this.width += x;
4177
this.height += y;
4178
}
4179
4180
/**
4181
* Check for collision with a point.
4182
*
4183
* `collidePoint(x,y)` or `collidePoint([x,y])` or `collidePoint(new Rect(x,y))`
4184
*
4185
* @param {Array|gamejs.Rect} point the x and y coordinates of the point to test for collision
4186
* @returns {Boolean} true if the point collides with this Rect
4187
*/
4188
Rect.prototype.collidePoint = function() {
4189
var args = normalizeRectArguments.apply(this, arguments);
4190
return (this.left <= args.left && args.left <= this.right) &&
4191
(this.top <= args.top && args.top <= this.bottom);
4192
};
4193
4194
/**
4195
* Check for collision with a Rect.
4196
* @param {gamejs.Rect} rect the Rect to test check for collision
4197
* @returns {Boolean} true if the given Rect collides with this Rect
4198
*/
4199
Rect.prototype.collideRect = function(rect) {
4200
return !(this.left > rect.right || this.right < rect.left ||
4201
this.top > rect.bottom || this.bottom < rect.top);
4202
};
4203
4204
/**
4205
* @param {Array} pointA start point of the line
4206
* @param {Array} pointB end point of the line
4207
* @returns true if the line intersects with the rectangle
4208
* @see http://stackoverflow.com/questions/99353/how-to-test-if-a-line-segment-intersects-an-axis-aligned-rectange-in-2d/293052#293052
4209
*
4210
*/
4211
Rect.prototype.collideLine = function(p1, p2) {
4212
var x1 = p1[0];
4213
var y1 = p1[1];
4214
var x2 = p2[0];
4215
var y2 = p2[1];
4216
4217
function linePosition(point) {
4218
var x = point[0];
4219
var y = point[1];
4220
return (y2 - y1) * x + (x1 - x2) * y + (x2 * y1 - x1 * y2);
4221
}
4222
4223
var relPoses = [[this.left, this.top],
4224
[this.left, this.bottom],
4225
[this.right, this.top],
4226
[this.right, this.bottom]
4227
].map(linePosition);
4228
4229
var noNegative = true;
4230
var noPositive = true;
4231
var noZero = true;
4232
relPoses.forEach(function(relPos) {
4233
if (relPos > 0) {
4234
noPositive = false;
4235
} else if (relPos < 0) {
4236
noNegative = false;
4237
} else if (relPos === 0) {
4238
noZero = false;
4239
}
4240
}, this);
4241
4242
if ( (noNegative || noPositive) && noZero) {
4243
return false;
4244
}
4245
return !((x1 > this.right && x2 > this.right) ||
4246
(x1 < this.left && x2 < this.left) ||
4247
(y1 < this.top && y2 < this.top) ||
4248
(y1 > this.bottom && y2 > this.bottom)
4249
);
4250
};
4251
4252
/**
4253
* @returns {String} Like "[x, y][w, h]"
4254
*/
4255
Rect.prototype.toString = function() {
4256
return ["[", this.left, ",", this.top, "]"," [",this.width, ",", this.height, "]"].join("");
4257
};
4258
4259
/**
4260
* @returns {gamejs.Rect} A new copy of this rect
4261
*/
4262
Rect.prototype.clone = function() {
4263
return new Rect(this);
4264
};
4265
4266
/**
4267
* A Surface represents a bitmap image with a fixed width and height. The
4268
* most important feature of a Surface is that they can be `blitted`
4269
* onto each other.
4270
*
4271
* @example
4272
* new gamejs.Surface([width, height]);
4273
* new gamejs.Surface(width, height);
4274
* new gamejs.Surface(rect);
4275
* @constructor
4276
*
4277
* @param {Array} dimensions Array holding width and height
4278
*/
4279
var Surface = exports.Surface = function() {
4280
var args = normalizeRectArguments.apply(this, arguments);
4281
var width = args.left;
4282
var height = args.top;
4283
// unless argument is rect:
4284
if (arguments.length == 1 && arguments[0] instanceof Rect) {
4285
width = args.width;
4286
height = args.height;
4287
}
4288
// only for rotatation & scale
4289
/** @ignore */
4290
this._matrix = matrix.identity();
4291
/** @ignore */
4292
this._canvas = document.createElement("canvas");
4293
this._canvas.width = width;
4294
this._canvas.height = height;
4295
/** @ignore */
4296
this._blitAlpha = 1.0;
4297
4298
/** @ignore */
4299
this._context = this._canvas.getContext('2d')
4300
this._smooth();
4301
return this;
4302
};
4303
4304
/** @ignore */
4305
Surface.prototype._noSmooth = function() {
4306
// disable image scaling
4307
// see https://developer.mozilla.org/en/Canvas_tutorial/Using_images#Controlling_image_scaling_behavior
4308
// and https://github.com/jbuck/processing-js/commit/65de16a8340c694cee471a2db7634733370b941c
4309
this.context.mozImageSmoothingEnabled = false;
4310
this.canvas.style.setProperty("image-rendering", "optimizeSpeed", "important");
4311
this.canvas.style.setProperty("image-rendering", "-moz-crisp-edges", "important");
4312
this.canvas.style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
4313
this.canvas.style.setProperty("image-rendering", "optimize-contrast", "important");
4314
this.canvas.style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
4315
return;
4316
};
4317
/** @ignore */
4318
Surface.prototype._smooth = function() {
4319
this.canvas.style.setProperty("image-rendering", "optimizeQuality", "important");
4320
this.canvas.style.setProperty("-ms-interpolation-mode", "bicubic", "important");
4321
this.context.mozImageSmoothingEnabled = true;
4322
};
4323
4324
/**
4325
* Blits another Surface on this Surface. The destination where to blit to
4326
* can be given (or it defaults to the top left corner) as well as the
4327
* Area from the Surface which should be blitted (e.g., for cutting out parts of
4328
* a Surface).
4329
*
4330
* @example
4331
* // blit flower in top left corner of display
4332
* displaySurface.blit(flowerSurface);
4333
*
4334
* // position flower at 10/10 of display
4335
* displaySurface.blit(flowerSurface, [10, 10])
4336
*
4337
* // ... `dest` can also be a rect whose topleft position is taken:
4338
* displaySurface.blit(flowerSurface, new gamejs.Rect([10, 10]);
4339
*
4340
* // only blit half of the flower onto the display
4341
* var flowerRect = flowerSurface.rect;
4342
* flowerRect = new gamejs.Rect([0,0], [flowerRect.width/2, flowerRect.height/2])
4343
* displaySurface.blit(flowerSurface, [0,0], flowerRect);
4344
*
4345
* @param {gamejs.Surface} src The Surface which will be blitted onto this one
4346
* @param {gamejs.Rect|Array} dst the Destination x, y position in this Surface.
4347
* If a Rect is given, it's top and left values are taken. If this argument
4348
* is not supplied the blit happens at [0,0].
4349
* @param {gamesjs.Rect|Array} area the Area from the passed Surface which
4350
* should be blitted onto this Surface.
4351
* @param {Number} compositionOperation how the source and target surfaces are composited together; one of: source-atop, source-in, source-out, source-over (default), destination-atop, destination-in, destination-out, destination-over, lighter, copy, xor; for an explanation of these values see: http://dev.w3.org/html5/2dcontext/#dom-context-2d-globalcompositeoperation
4352
* @returns {gamejs.Rect} Rect actually repainted FIXME actually return something?
4353
*/
4354
Surface.prototype.blit = function(src, dest, area, compositeOperation) {
4355
4356
var rDest, rArea;
4357
4358
if (dest instanceof Rect) {
4359
rDest = dest.clone();
4360
var srcSize = src.getSize();
4361
if (!rDest.width) {
4362
rDest.width = srcSize[0];
4363
}
4364
if (!rDest.height) {
4365
rDest.height = srcSize[1];
4366
}
4367
} else if (dest && dest instanceof Array && dest.length == 2) {
4368
rDest = new Rect(dest, src.getSize());
4369
} else {
4370
rDest = new Rect([0,0], src.getSize());
4371
}
4372
compositeOperation = compositeOperation || 'source-over';
4373
4374
// area within src to be drawn
4375
if (area instanceof Rect) {
4376
rArea = area;
4377
} else if (area && area instanceof Array && area.length == 2) {
4378
var size = src.getSize();
4379
rArea = new Rect(area, [size[0] - area[0], size[1] - area[1]]);
4380
} else {
4381
rArea = new Rect([0,0], src.getSize());
4382
}
4383
4384
if (isNaN(rDest.left) || isNaN(rDest.top) || isNaN(rDest.width) || isNaN(rDest.height)) {
4385
throw new Error('[blit] bad parameters, destination is ' + rDest);
4386
}
4387
4388
this.context.save();
4389
this.context.globalCompositeOperation = compositeOperation;
4390
// first translate, then rotate
4391
var m = matrix.translate(matrix.identity(), rDest.left, rDest.top);
4392
m = matrix.multiply(m, src._matrix);
4393
this.context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
4394
// drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
4395
this.context.globalAlpha = src._blitAlpha;
4396
this.context.drawImage(src.canvas, rArea.left, rArea.top, rArea.width, rArea.height, 0, 0, rDest.width, rDest.height);
4397
this.context.restore();
4398
return;
4399
};
4400
4401
/**
4402
* @returns {Number[]} the width and height of the Surface
4403
*/
4404
Surface.prototype.getSize = function() {
4405
return [this.canvas.width, this.canvas.height];
4406
};
4407
4408
/**
4409
* Obsolte, only here for compatibility.
4410
* @deprecated
4411
* @ignore
4412
* @returns {gamejs.Rect} a Rect of the size of this Surface
4413
*/
4414
Surface.prototype.getRect = function() {
4415
return new Rect([0,0], this.getSize());
4416
};
4417
4418
/**
4419
* Fills the whole Surface with a color. Usefull for erasing a Surface.
4420
* @param {String} CSS color string, e.g. '#0d120a' or '#0f0' or 'rgba(255, 0, 0, 0.5)'
4421
* @param {gamejs.Rect} a Rect of the area to fill (defauts to entire surface if not specified)
4422
*/
4423
Surface.prototype.fill = function(color, rect) {
4424
this.context.save();
4425
this.context.fillStyle = color || "#000000";
4426
if ( rect === undefined )
4427
rect = new Rect(0, 0, this.canvas.width, this.canvas.height);
4428
4429
this.context.fillRect(rect.left, rect.top, rect.width, rect.height);
4430
this.context.restore();
4431
return;
4432
};
4433
4434
/**
4435
* Clear the surface.
4436
*/
4437
Surface.prototype.clear = function(rect) {
4438
var size = this.getSize();
4439
rect = rect || new Rect(0, 0, size[0], size[1]);
4440
this.context.clearRect(rect.left, rect.top, rect.width, rect.height);
4441
return;
4442
};
4443
4444
/**
4445
* Crop the surface by a given amount
4446
*/
4447
Surface.prototype.crop = function ( rect )
4448
{
4449
if ( typeof(this._originalImg) == 'undefined' )
4450
{
4451
var _size = this.getSize();
4452
this._originalImg = new gamejs.Surface([_size[0], _size[1]]);
4453
4454
var originalSize = new gamejs.Rect(0, 0, _size[0], _size[1]);
4455
this._originalImg.blit(this, originalSize, originalSize);
4456
}
4457
4458
this.clear();
4459
4460
this.canvas.width = rect.width;
4461
this.canvas.height = rect.height;
4462
4463
var newPos = new gamejs.Rect([0,0], [rect.width, rect.height]);
4464
this.blit( this._originalImg, newPos, rect);
4465
}
4466
4467
objects.accessors(Surface.prototype, {
4468
/**
4469
* @type gamejs.Rect
4470
*/
4471
'rect': {
4472
get: function() {
4473
return this.getRect();
4474
}
4475
},
4476
/**
4477
* @ignore
4478
*/
4479
'context': {
4480
get: function() {
4481
return this._context;
4482
}
4483
},
4484
'canvas': {
4485
get: function() {
4486
return this._canvas;
4487
}
4488
}
4489
});
4490
4491
/**
4492
* @returns {gamejs.Surface} a clone of this surface
4493
*/
4494
Surface.prototype.clone = function() {
4495
var newSurface = new Surface(this.getRect());
4496
newSurface.blit(this);
4497
return newSurface;
4498
};
4499
4500
/**
4501
* @returns {Number} current alpha value
4502
*/
4503
Surface.prototype.getAlpha = function() {
4504
return (1 - this._blitAlpha);
4505
};
4506
4507
/**
4508
* Set the alpha value for the whole Surface. When blitting the Surface on
4509
* a destination, the pixels will be drawn slightly transparent.
4510
* @param {Number} alpha value in range 0.0 - 1.0
4511
* @returns {Number} current alpha value
4512
*/
4513
Surface.prototype.setAlpha = function(alpha) {
4514
if (isNaN(alpha) || alpha < 0 || alpha > 1) {
4515
return;
4516
}
4517
4518
this._blitAlpha = (1 - alpha);
4519
return (1 - this._blitAlpha);
4520
};
4521
4522
/**
4523
* The data must be represented in left-to-right order, row by row top to bottom,
4524
* starting with the top left, with each pixel's red, green, blue, and alpha components
4525
* being given in that order for each pixel.
4526
* @see http://dev.w3.org/html5/2dcontext/#canvaspixelarray
4527
* @returns {ImageData} an object holding the pixel image data {data, width, height}
4528
*/
4529
Surface.prototype.getImageData = function() {
4530
var size = this.getSize();
4531
return this.context.getImageData(0, 0, size[0], size[1]);
4532
};
4533
4534
/**
4535
* @ignore
4536
*/
4537
exports.display = require('./gamejs/display');
4538
/**
4539
* @ignore
4540
*/
4541
exports.draw = require('./gamejs/draw');
4542
/**
4543
* @ignore
4544
*/
4545
exports.event = require('./gamejs/event');
4546
/**
4547
* @ignore
4548
*/
4549
exports.font = require('./gamejs/font');
4550
/**
4551
* @ignore
4552
*/
4553
exports.http = require('./gamejs/http');
4554
/**
4555
* @ignore
4556
*/
4557
exports.image = require('./gamejs/image');
4558
/**
4559
* @ignore
4560
*/
4561
exports.mask = require('./gamejs/mask');
4562
/**
4563
* @ignore
4564
*/
4565
exports.mixer = require('./gamejs/mixer');
4566
/**
4567
* @ignore
4568
*/
4569
exports.sprite = require('./gamejs/sprite');
4570
/**
4571
* @ignore
4572
*/
4573
exports.surfacearray = require('./gamejs/surfacearray');
4574
/**
4575
* @ignore
4576
*/
4577
exports.time = require('./gamejs/time');
4578
/**
4579
* @ignore
4580
*/
4581
exports.transform = require('./gamejs/transform');
4582
4583
/**
4584
* @ignore
4585
*/
4586
exports.utils = {
4587
arrays: require('./gamejs/utils/arrays'),
4588
objects: require('./gamejs/utils/objects'),
4589
matrix: require('./gamejs/utils/matrix'),
4590
vectors: require('./gamejs/utils/vectors'),
4591
math: require('./gamejs/utils/math'),
4592
uri: require('./gamejs/utils/uri')
4593
};
4594
4595
/**
4596
* @ignore
4597
*/
4598
exports.pathfinding = {
4599
astar: require('./gamejs/pathfinding/astar')
4600
};
4601
4602
/**
4603
* @ignore
4604
*/
4605
exports.worker = require('./gamejs/worker');
4606
4607
/**
4608
* @ignore
4609
*/
4610
exports.base64 = require('./gamejs/base64');
4611
4612
/**
4613
* @ignore
4614
*/
4615
exports.xml = require('./gamejs/xml');
4616
4617
/**
4618
* @ignore
4619
*/
4620
exports.tmx = require('./gamejs/tmx');
4621
4622
// preloading stuff
4623
var gamejs = exports;
4624
var RESOURCES = {};
4625
4626
/**
4627
* ReadyFn is called once all modules and assets are loaded.
4628
* @param {Function} readyFn the function to be called once gamejs finished loading
4629
* @name ready
4630
*/
4631
if (gamejs.worker.inWorker === true) {
4632
exports.ready = function(readyFn) {
4633
gamejs.worker._ready();
4634
gamejs.init();
4635
readyFn();
4636
}
4637
} else {
4638
exports.ready = function(readyFn) {
4639
4640
var getMixerProgress = null;
4641
var getImageProgress = null;
4642
4643
// init time instantly - we need it for preloaders
4644
gamejs.time.init();
4645
4646
// 2.
4647
function _ready() {
4648
if (!document.body) {
4649
return window.setTimeout(_ready, 50);
4650
}
4651
getImageProgress = gamejs.image.preload(RESOURCES);
4652
try {
4653
getMixerProgress = gamejs.mixer.preload(RESOURCES);
4654
} catch (e) {
4655
gamejs.debug('Error loading audio files ', e);
4656
}
4657
window.setTimeout(_readyResources, 50);
4658
}
4659
4660
// 3.
4661
function _readyResources() {
4662
if (getImageProgress() < 1 || getMixerProgress() < 1) {
4663
return window.setTimeout(_readyResources, 100);
4664
}
4665
gamejs.display.init();
4666
gamejs.image.init();
4667
gamejs.mixer.init();
4668
gamejs.event.init();
4669
readyFn();
4670
}
4671
4672
// 1.
4673
window.setTimeout(_ready, 13);
4674
4675
function getLoadProgress() {
4676
if (getImageProgress) {
4677
return (0.5 * getImageProgress()) + (0.5 * getMixerProgress());
4678
}
4679
return 0.1;
4680
}
4681
4682
return getLoadProgress;
4683
};
4684
}
4685
4686
/**
4687
* Initialize all gamejs modules. This is automatically called
4688
* by `gamejs.ready()`.
4689
* @returns {Object} the properties of this objecte are the moduleIds that failed, they value are the exceptions
4690
* @ignore
4691
*/
4692
exports.init = function() {
4693
var errorModules = {};
4694
['time', 'display', 'image', 'mixer', 'event'].forEach(function(moduleName) {
4695
try {
4696
gamejs[moduleName].init();
4697
} catch (e) {
4698
errorModules[moduleName] = e.toString();
4699
}
4700
});
4701
return errorModules;
4702
}
4703
4704
function resourceBaseHref() {
4705
return (window.$g && window.$g.resourceBaseHref) || document.location.href;
4706
}
4707
4708
/**
4709
* Preload resources.
4710
* @param {Array} resources list of resources paths
4711
* @name preload
4712
*/
4713
var preload = exports.preload = function(resources) {
4714
var uri = require('./gamejs/utils/uri');
4715
var baseHref = resourceBaseHref();
4716
resources.forEach(function(res) {
4717
RESOURCES[res] = uri.resolve(baseHref, res);
4718
}, this);
4719
return;
4720
};
4721
4722
}}, ["gamejs/utils/matrix", "gamejs/utils/objects", "gamejs/display", "gamejs/draw", "gamejs/event", "gamejs/font", "gamejs/http", "gamejs/image", "gamejs/mask", "gamejs/mixer", "gamejs/sprite", "gamejs/surfacearray", "gamejs/time", "gamejs/transform", "gamejs/utils/arrays", "gamejs/utils/vectors", "gamejs/utils/math", "gamejs/utils/uri", "gamejs/pathfinding/astar", "gamejs/worker", "gamejs/base64", "gamejs/xml", "gamejs/tmx"]);
4723