diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..8fafdb309 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,84 @@ +# Code of Conduct + +## 1. Purpose + +A primary goal of Phaser is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). + +This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. + +We invite all those who participate in Phaser to help us create safe and positive experiences for everyone. + +## 2. Open Source Citizenship + +A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. + +Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. + +If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. + +## 3. Expected Behavior + +The following behaviors are expected and requested of all community members: + +* Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. +* Exercise consideration and respect in your speech and actions. +* Attempt collaboration before conflict. +* Refrain from demeaning, discriminatory, or harassing behavior and speech. +* Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. +* Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. + +## 4. Unacceptable Behavior + +The following behaviors are considered harassment and are unacceptable within our community: + +* Violence, threats of violence or violent language directed against another person. +* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. +* Posting or displaying sexually explicit or violent material. +* Posting or threatening to post other people’s personally identifying information ("doxing"). +* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. +* Inappropriate photography or recording. +* Inappropriate physical contact. You should have someone’s consent before touching them. +* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. +* Deliberate intimidation, stalking or following (online or in person). +* Advocating for, or encouraging, any of the above behavior. +* Sustained disruption of community events, including talks and presentations. + +## 5. Consequences of Unacceptable Behavior + +Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. + +Anyone asked to stop unacceptable behavior is expected to comply immediately. + +If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). + +## 6. Reporting Guidelines + +If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. support@phaser.io. + + + +Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. + +## 7. Addressing Grievances + +If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Photon Storm Ltd with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. + + + +## 8. Scope + +We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business. + +This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. + +## 9. Contact info + +support@phaser.io + +## 10. License and attribution + +This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). + +Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). + +Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) diff --git a/src/boot/Config.js b/src/boot/Config.js index 3fdce34d9..d02961058 100644 --- a/src/boot/Config.js +++ b/src/boot/Config.js @@ -13,7 +13,7 @@ var ValueToColor = require('../display/color/ValueToColor'); /** * @typedef {object} FPSConfig - * + * * @property {integer} [min=10] - [description] * @property {integer} [target=60] - [description] * @property {boolean} [forceSetTimeOut=false] - [description] @@ -25,7 +25,7 @@ var ValueToColor = require('../display/color/ValueToColor'); * @typedef {object} GameConfig * * @todo Add Physics Config - * + * * @property {integer|string} [width=1024] - [description] * @property {integer|string} [height=768] - [description] * @property {number} [zoom=1] - [description] @@ -81,7 +81,7 @@ var Config = new Class({ * @since 3.0.0 * * @param {object} [GameConfig] - The configuration object for your Phaser Game instance. - * + * */ function Config (config) { @@ -135,6 +135,8 @@ var Config = new Class({ this.disableContextMenu = GetValue(config, 'disableContextMenu', false); + this.audio = GetValue(config, 'audio'); + // If you do: { banner: false } it won't display any banner at all this.hideBanner = (GetValue(config, 'banner', null) === false); @@ -146,7 +148,7 @@ var Config = new Class({ { this.hideBanner = true; } - + // Frame Rate config // fps: { // min: 10, diff --git a/src/boot/DebugHeader.js b/src/boot/DebugHeader.js index 763af002f..eb7a45ceb 100644 --- a/src/boot/DebugHeader.js +++ b/src/boot/DebugHeader.js @@ -20,6 +20,24 @@ var DebugHeader = function (game) var renderType = (config.renderType === CONST.CANVAS) ? 'Canvas' : 'WebGL'; + var audioConfig = game.config.audio; + var deviceAudio = game.device.Audio; + + var audioType; + + if(deviceAudio.webAudio && !(audioConfig && audioConfig.disableWebAudio)) + { + audioType = 'Web Audio'; + } + else if((audioConfig && audioConfig.noAudio) || (!deviceAudio.webAudio && !deviceAudio.audioData)) + { + audioType = 'No Audio'; + } + else + { + audioType = 'HTML5 Audio'; + } + var ie = false; if (!ie) @@ -71,8 +89,7 @@ var DebugHeader = function (game) if (!config.hidePhaser) { - // TODO add audio - c = c.concat('Phaser v' + CONST.VERSION + ' (' + renderType + ')'); + c = c.concat('Phaser v' + CONST.VERSION + ' (' + renderType + ' | ' + audioType + ')'); } c = c.concat(' %c ' + config.gameURL); diff --git a/src/boot/Game.js b/src/boot/Game.js index ce10eaf6d..3e5d43aeb 100644 --- a/src/boot/Game.js +++ b/src/boot/Game.js @@ -11,7 +11,7 @@ var VisibilityHandler = require('./VisibilityHandler'); var AnimationManager = require('../animations/manager/AnimationManager'); var CreateRenderer = require('./CreateRenderer'); -var Data = require('../scene/plugins/Data'); +var Data = require('../data/Data'); var GlobalCache = require('../cache/GlobalCache'); var GlobalInputManager = require('../input/global/GlobalInputManager'); var GlobalSceneManager = require('../scene/global/GlobalSceneManager'); diff --git a/src/scene/plugins/Data.js b/src/data/Data.js similarity index 97% rename from src/scene/plugins/Data.js rename to src/data/Data.js index f66c9390e..a38483b25 100644 --- a/src/scene/plugins/Data.js +++ b/src/data/Data.js @@ -1,6 +1,6 @@ -var Class = require('../../utils/Class'); -var Event = require('../../events/Event'); -var EventDispatcher = require('../../events/EventDispatcher'); +var Class = require('../utils/Class'); +var Event = require('../events/Event'); +var EventDispatcher = require('../events/EventDispatcher'); /** * The Data Component features a means to store pieces of data specific to a Game Object, diff --git a/src/scene/plugins/DataStore.js b/src/data/DataStore.js similarity index 98% rename from src/scene/plugins/DataStore.js rename to src/data/DataStore.js index 4cf0209ac..8f44835c2 100644 --- a/src/scene/plugins/DataStore.js +++ b/src/data/DataStore.js @@ -1,4 +1,4 @@ -var Class = require('../../utils/Class'); +var Class = require('../utils/Class'); var Data = require('./Data'); var DataStore = new Class({ diff --git a/src/gameobjects/tilemap/components/WeightedRandomize.js b/src/gameobjects/tilemap/components/WeightedRandomize.js index b786b0f40..185109056 100644 --- a/src/gameobjects/tilemap/components/WeightedRandomize.js +++ b/src/gameobjects/tilemap/components/WeightedRandomize.js @@ -20,7 +20,8 @@ var GetTilesWithin = require('./GetTilesWithin'); * @param {integer} [width=max width based on tileX] - [description] * @param {integer} [height=max height based on tileY] - [description] * @param {object[]} [weightedIndexes] - An array of objects to randomly draw from during - * randomization. They should be in the form: { index: 0, weight: 4 }. + * randomization. They should be in the form: { index: 0, weight: 4 } or + * { index: [0, 1], weight: 4 } if you wish to draw from multiple tile indexes. * @param {LayerData} layer - [description] */ var WeightedRandomize = function (tileX, tileY, width, height, weightedIndexes, layer) @@ -48,7 +49,10 @@ var WeightedRandomize = function (tileX, tileY, width, height, weightedIndexes, sum += weightedIndexes[j].weight; if (rand <= sum) { - randomIndex = weightedIndexes[j].index; + var chosen = weightedIndexes[j].index; + randomIndex = Array.isArray(chosen) + ? chosen[Math.floor(Math.random() * chosen.length)] + : chosen; break; } } diff --git a/src/geom/point/Normalize.js b/src/geom/point/Normalize.js index 196e96e3c..193ece471 100644 --- a/src/geom/point/Normalize.js +++ b/src/geom/point/Normalize.js @@ -12,7 +12,7 @@ var GetMagnitude = require('./GetMagnitude'); */ var Normalize = function (point) { - if (point.x !== 0 && point.y !== 0) + if (point.x !== 0 || point.y !== 0) { var m = GetMagnitude(point); diff --git a/src/loader/BaseLoader.js b/src/loader/BaseLoader.js index ee86e65d9..ccf06ca71 100644 --- a/src/loader/BaseLoader.js +++ b/src/loader/BaseLoader.js @@ -194,6 +194,11 @@ var BaseLoader = new Class({ { // console.log('---> BaseLoader.finishedLoading PROCESSING', this.queue.size, 'files'); + if(this.state === CONST.LOADER_PROCESSING) + { + return; + } + this.state = CONST.LOADER_PROCESSING; this.storage.clear(); diff --git a/src/loader/filetypes/AudioFile.js b/src/loader/filetypes/AudioFile.js index fdf2033c3..4e1dd28bd 100644 --- a/src/loader/filetypes/AudioFile.js +++ b/src/loader/filetypes/AudioFile.js @@ -2,6 +2,7 @@ var Class = require('../../utils/Class'); var File = require('../File'); var GetFastValue = require('../../utils/object/GetFastValue'); var CONST = require('../../const'); +var HTML5AudioFile = require('./HTML5AudioFile'); // Phaser.Loader.FileTypes.AudioFile @@ -46,7 +47,6 @@ var AudioFile = new Class({ }, function (e) { - // TODO properly log decoding error console.error('Error with decoding audio data for \'' + this.key + '\':', e.message); _this.state = CONST.FILE_ERRORED; @@ -66,7 +66,6 @@ AudioFile.create = function (loader, key, urls, config, xhrSettings) if ((audioConfig && audioConfig.noAudio) || (!deviceAudio.webAudio && !deviceAudio.audioData)) { - // TODO log not loading audio because sounds are disabled console.info('Skipping loading audio \'' + key + '\' since sounds are disabled.'); return null; } @@ -75,7 +74,6 @@ AudioFile.create = function (loader, key, urls, config, xhrSettings) if (!url) { - // TODO log no supported types console.warn('No supported url provided for audio \'' + key + '\'!'); return null; } @@ -85,8 +83,7 @@ AudioFile.create = function (loader, key, urls, config, xhrSettings) return new AudioFile(key, url, loader.path, xhrSettings, game.sound.context); } - // TODO handle loading audio tags - return null; + return new HTML5AudioFile(key, url, loader.path, config); }; // this.load.audio('sound', 'assets/audio/booom.ogg', config, xhrSettings); diff --git a/src/loader/filetypes/HTML5AudioFile.js b/src/loader/filetypes/HTML5AudioFile.js new file mode 100644 index 000000000..8c0751ce0 --- /dev/null +++ b/src/loader/filetypes/HTML5AudioFile.js @@ -0,0 +1,96 @@ +var Class = require('../../utils/Class'); +var File = require('../File'); +var GetFastValue = require('../../utils/object/GetFastValue'); +var GetURL = require('../GetURL'); + +// Phaser.Loader.FileTypes.HTML5AudioFile + +var HTML5AudioFile = new Class({ + + Extends: File, + + initialize: + + function HTML5AudioFile (key, url, path, config) + { + var fileConfig = { + type: 'audio', + extension: GetFastValue(url, 'type', ''), + key: key, + url: GetFastValue(url, 'uri', url), + path: path, + config: config + }; + + File.call(this, fileConfig); + }, + + onLoad: function () + { + this.callback(this, true); + }, + + onError: function (event) + { + for (var i = 0; i < this.data.length; i++) + { + var audio = this.data[i]; + audio.oncanplaythrough = null; + audio.onerror = null; + } + + this.callback(this, false); + }, + + onProgress: function (event) + { + var audio = event.target; + audio.oncanplaythrough = null; + audio.onerror = null; + + if(++this.filesLoaded === this.filesTotal) + { + this.onLoad(); + } + + this.percentComplete = Math.min((this.filesLoaded / this.filesTotal), 1); + }, + + // Called by the Loader, starts the actual file downloading + load: function (callback, baseURL) + { + this.callback = callback; + + this.data = []; + + var instances = (this.config && this.config.instances) || 1; + + this.filesTotal = instances; + this.filesLoaded = 0; + this.percentComplete = 0; + + for(var i = 0; i < instances; i++) + { + var audio = new Audio(); + audio.name = this.key; + audio.preload = 'auto'; + + // TODO check if ios is locked + + audio.oncanplaythrough = this.onProgress.bind(this); + audio.onerror = this.onError.bind(this); + + this.data.push(audio); + } + + for (i = 0; i < this.data.length; i++) + { + audio = this.data[i]; + audio.src = GetURL(this, baseURL || ''); + audio.load(); + } + } + +}); + +module.exports = HTML5AudioFile; diff --git a/src/scene/local/Systems.js b/src/scene/local/Systems.js index 74e6c804b..d40596bf7 100644 --- a/src/scene/local/Systems.js +++ b/src/scene/local/Systems.js @@ -1,8 +1,8 @@ var CameraManager = require('../../camera/local/CameraManager'); var Class = require('../../utils/Class'); var Clock = require('../../time/Clock'); -var Data = require('../plugins/Data'); -var DataStore = require('../plugins/DataStore'); +var Data = require('../../data/Data'); +var DataStore = require('../../data/DataStore'); var DisplayList = require('../plugins/DisplayList'); var EventDispatcher = require('../../events/EventDispatcher'); var GameObjectCreator = require('../plugins/GameObjectCreator'); @@ -16,6 +16,8 @@ var StableSort = require('../../utils/array/StableSort'); var TweenManager = require('../../tweens/manager/TweenManager'); var UpdateList = require('../plugins/UpdateList'); +var PluginManager = require('../../plugins/PluginManager'); + var Systems = new Class({ initialize: @@ -27,8 +29,6 @@ var Systems = new Class({ this.config = config; this.settings = Settings.create(config); - this.renderList = []; - this.sortChildrenFlag = false; // Set by the GlobalSceneManager @@ -59,6 +59,8 @@ var Systems = new Class({ this.time; this.tweens; this.updateList; + + this.plugins; }, init: function (game) @@ -74,6 +76,8 @@ var Systems = new Class({ this.registry = game.registry; this.textures = game.textures; + this.plugins = new PluginManager(scene); + // Scene specific managers (Factory, Tweens, Loader, Physics, etc) this.add = new GameObjectFactory(scene); diff --git a/src/sound/BaseSoundManager.js b/src/sound/BaseSoundManager.js index 4d4cd0509..81fc21214 100644 --- a/src/sound/BaseSoundManager.js +++ b/src/sound/BaseSoundManager.js @@ -303,6 +303,7 @@ var BaseSoundManager = new Class({ this.forEachActiveSound(function (sound) { sound.destroy(); }); + this.sounds.length = 0; this.sounds = null; }, /** @@ -336,7 +337,7 @@ Object.defineProperty(BaseSoundManager.prototype, 'rate', { this._rate = value; this.forEachActiveSound(function (sound) { sound.setRate(); - }, this); + }); this.events.dispatch(new SoundValueEvent(this, 'SOUND_RATE', value)); } }); @@ -354,7 +355,7 @@ Object.defineProperty(BaseSoundManager.prototype, 'detune', { this._detune = value; this.forEachActiveSound(function (sound) { sound.setRate(); - }, this); + }); this.events.dispatch(new SoundValueEvent(this, 'SOUND_DETUNE', value)); } }); diff --git a/src/sound/html5/HTML5AudioSound.js b/src/sound/html5/HTML5AudioSound.js new file mode 100644 index 000000000..230ae9304 --- /dev/null +++ b/src/sound/html5/HTML5AudioSound.js @@ -0,0 +1,18 @@ +var Class = require('../../utils/Class'); +var BaseSound = require('../BaseSound'); +var HTML5AudioSound = new Class({ + Extends: BaseSound, + initialize: function HTML5AudioSound(manager, key, config) { + if (config === void 0) { config = {}; } + /** + * Reference to HTML5 Audio tag used for playing sound. + * + * @private + * @property {HTMLAudioElement} audio + * @default null + */ + this.audio = null; + BaseSound.call(this, manager, key, config); + } +}); +module.exports = HTML5AudioSound; diff --git a/src/sound/html5/HTML5AudioSoundManager.js b/src/sound/html5/HTML5AudioSoundManager.js new file mode 100644 index 000000000..cc68446f2 --- /dev/null +++ b/src/sound/html5/HTML5AudioSoundManager.js @@ -0,0 +1,95 @@ +var Class = require('../../utils/Class'); +var BaseSoundManager = require('../BaseSoundManager'); +var HTML5AudioSound = require('./HTML5AudioSound'); +var HTML5AudioSoundManager = new Class({ + Extends: BaseSoundManager, + initialize: function HTML5AudioSoundManager(game) { + /** + * An array for keeping track of all the sounds + * that were paused when game lost focus. + * + * @private + * @property {Phaser.Sound.HTML5AudioSound[]} onBlurPausedSounds + * @default [] + */ + this.onBlurPausedSounds = []; + /** + * Property that actually holds the value of global mute + * for HTML5 Audio sound manager implementation. + * + * @private + * @property {boolean} _mute + * @default false + */ + this._mute = false; + /** + * Property that actually holds the value of global volume + * for HTML5 Audio sound manager implementation. + * + * @private + * @property {boolean} _volume + * @default 1 + */ + this._volume = 1; + BaseSoundManager.call(this, game); + }, + add: function (key, config) { + var sound = new HTML5AudioSound(this, key, config); + this.sounds.push(sound); + return sound; + }, + onBlur: function () { + this.forEachActiveSound(function (sound) { + if (sound.isPlaying) { + this.onBlurPausedSounds.push(sound); + sound.pause(); + } + }); + }, + onFocus: function () { + this.onBlurPausedSounds.forEach(function (sound) { + sound.resume(); + }); + this.onBlurPausedSounds.length = 0; + }, + destroy: function () { + BaseSoundManager.prototype.destroy.call(this); + this.onBlurPausedSounds.length = 0; + this.onBlurPausedSounds = null; + } +}); +/** + * Global mute setting. + * + * @name Phaser.Sound.HTML5AudioSoundManager#mute + * @property {boolean} mute + */ +Object.defineProperty(HTML5AudioSoundManager.prototype, 'mute', { + get: function () { + return this._mute; + }, + set: function (value) { + this._mute = value; + this.forEachActiveSound(function (sound) { + sound.setMute(); + }); + } +}); +/** + * Global volume setting. + * + * @name Phaser.Sound.HTML5AudioSoundManager#volume + * @property {number} volume + */ +Object.defineProperty(HTML5AudioSoundManager.prototype, 'volume', { + get: function () { + return this._volume; + }, + set: function (value) { + this._volume = value; + this.forEachActiveSound(function (sound) { + sound.setVolume(); + }); + } +}); +module.exports = HTML5AudioSoundManager; diff --git a/src/sound/index.js b/src/sound/index.js index 2dfa87786..bc4cab78c 100644 --- a/src/sound/index.js +++ b/src/sound/index.js @@ -10,6 +10,9 @@ module.exports = { WebAudioSound: require('./webaudio/WebAudioSound'), WebAudioSoundManager: require('./webaudio/WebAudioSoundManager'), + HTML5AudioSound: require('./html5/HTML5AudioSound'), + HTML5AudioSoundManager: require('./html5/HTML5AudioSoundManager'), + SoundEvent: require('./SoundEvent'), SoundValueEvent: require('./SoundValueEvent') }; diff --git a/src/sound/webaudio/WebAudioSound.js b/src/sound/webaudio/WebAudioSound.js index b1d40b87b..44bb2331f 100644 --- a/src/sound/webaudio/WebAudioSound.js +++ b/src/sound/webaudio/WebAudioSound.js @@ -336,6 +336,7 @@ var WebAudioSound = new Class({ this.muteNode = null; this.volumeNode.disconnect(); this.volumeNode = null; + this.rateUpdates.length = 0; this.rateUpdates = null; }, /** @@ -491,12 +492,12 @@ Object.defineProperty(WebAudioSound.prototype, 'seek', { if (this.isPlaying || this.isPaused) { value = Math.min(Math.max(0, value), this.duration); this.currentConfig.seek = value; + if (this.isPlaying) { + this.stopAndRemoveBufferSource(); + this.createAndStartBufferSource(); + } this.events.dispatch(new SoundValueEvent(this, 'SOUND_SEEK', value)); } - if (this.isPlaying) { - this.stopAndRemoveBufferSource(); - this.createAndStartBufferSource(); - } } }); /**