/**
* Создает и управляет "слоями" интерфейса. Слоем может быть любой `{@link DisplayObject}`.
* Обновляет z-index и позиции элементов слоев, загружает текст кнопок после загрузки шрифтов.
* Все слои добавляются в `game.world.children`. Существующие слои должны быть там же.
* z-index - индекс элемента в `game.world.children`.
* z-index >= 0 - начиная с низа
* z-index < 0 - начиная с верха (-1 - самый верхний слой)
* @class
*/
UI.Layers = function(){
/**
* Слои интерфейса.
* @type {Object<DisplayObject>}
*/
this.byName = {};
/**
* Слои, отсортированные по вертикали.
* @type {DisplayObject[]}
*/
this.positions = [];
/**
* Индекс модального слоя (слоя, блокирующего клики по элементам за ним).
* @type {Number}
*/
this.modalLayerIndex = -1;
};
/**
* Определяет реальные индексы слоев для позиционирования.
* Исправляет повторяющиеся индексы.
* Сортировка слоев нестабильная, т.е. слои с одинаковым индексом могут поменять последовательность после сортировки.
*/
UI.Layers.prototype._sortPositions = function(){
this.positions.sort(function(a, b){
a = a.index;
b = b.index;
if(a < 0 && b >= 0){
return 1;
}
if(a >= 0 && b < 0){
return -1;
}
return a - b;
});
//console.log(this.positions.map(l => l.index))
};
/**
* Создает новую `Phaser.Group` группу и добавляет ее как слой.
* @param {number} i index слоя
* @param {string} name имя слоя, должно быть уникальным
*
* @return {external:Phaser.Group} Созданный слой.
*/
UI.Layers.prototype.addLayer = function(i, name){
if(this.byName[name]){
console.error('UI.Layers: Layer name must be unique', name);
return null;
}
var layer = game.add.group();
layer.index = i;
layer.name = name;
this.byName[name] = layer;
this.positions.push(layer);
return layer;
};
/**
* Добавляет существующий элемент игры, как слой.
* @param {DisplayObject} layer добавляемый элемент игры
* @param {number} i index слоя
*
* @return {DisplayObject} Добавленный слой.
*/
UI.Layers.prototype.addExistingLayer = function(layer, i){
if(this.byName[layer.name]){
console.error('UI.Layers: Layer name must be unique', layer.name);
return null;
}
layer.parent = game.world;
layer.index = i;
this.byName[layer.name] = layer;
this.positions.push(layer);
return layer;
};
/**
* Добавляет существующие элементы игры как слои из массива.
* @param {array} layers Слои `[layer, i]`
*/
UI.Layers.prototype.addExistingLayers = function(layers){
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
this.addExistingLayer(layer[0], layer[1]);
}
};
/**
* Прячет элементы слоя у которых есть метод `hide`.
* Опционально отключает элементы слоя, у которых есть метод `disable`.
* @param {DisplayObject} layer слой
* @param {boolean} shouldDisable нужно ли отключать элементы
*/
UI.Layers.prototype.hideLayer = function(layer, shouldDisable){
layer.forEach(function(el){
if(el.hide){
el.hide();
}
if(shouldDisable && el.disable){
el.disable();
}
});
};
/**
* Показывает элементы слоя у которых есть метод `show`.
* Опционально отключает элементы слоя, у которых есть метод `disable`.
* @param {DisplayObject} layer слой
* @param {boolean} shouldDisable нужно ли отключать элементы
*/
UI.Layers.prototype.showLayer = function(layer, shouldDisable){
layer.forEach(function(el){
if(el.show){
el.show();
}
if(shouldDisable && el.disable){
el.disable();
}
});
this._positionElementsInLayer(layer);
};
/**
* Меняет z-index слоя.
* @param {DisplayObject} layer слой
* @param {number} i index слоя
*/
UI.Layers.prototype.setLayerIndex = function(layer, i){
layer.index = i;
this.positionLayers();
};
/**
* Позиционирует все слои по вертикали.
*/
UI.Layers.prototype.positionLayers = function(){
this._sortPositions();
game.world.children = this.positions.slice();
};
/**
* Вызывает `updatePosition` у всех элементов слоя.
* @param {DisplayObject} layer слой
*/
UI.Layers.prototype._positionElementsInLayer = function(layer){
layer.forEach(function(el){
if(el.updatePosition){
el.updatePosition();
}
});
};
/**
* Вызывает `updatePosition` у всех элементов всех слоев, которые относятся к `Phaser.Group`.
*/
UI.Layers.prototype.positionElements = function(){
for(var pname in this.byName){
if(!this.byName.hasOwnProperty(pname)){
continue;
}
var layer = this.byName[pname];
if(layer.updatePosition){
layer.updatePosition();
continue;
}
if(layer instanceof Phaser.Group){
this._positionElementsInLayer(layer);
}
}
};
/**
* Перезагружает текст всех элементов всех слоев, относящихся к `Phaser.Group`, у которых есть `label` и `label.isText`.
*/
UI.Layers.prototype.loadLabels = function(){
for(var pname in this.byName){
if(!this.byName.hasOwnProperty(pname)){
continue;
}
var layer = this.byName[pname];
if(layer.loadLabels){
layer.loadLabels();
continue;
}
if(layer instanceof Phaser.Text){
layer.setText(layer.text, true);
continue;
}
if(!(layer instanceof Phaser.Group)){
continue;
}
layer.forEach(function(el){
if(el.loadLabels){
el.loadLabels();
return;
}
if(el instanceof Phaser.Text){
el.setText(el.text, true);
}
if(el.label && el.label.isText){
el.label.setText(el.label.text, true);
}
});
}
};
/**
* Дебаг функция для получения списка слоев.
* @return {object} Возвращает `{world: [], layers: []}`.
* `world` содержит имена `{@link DisplayObject}` в `game.world.children`.
* `layers` содержит соответствующие слои (`{@link DisplayObject}`), если они есть.
* @see {@link printLayers}
*/
UI.Layers.prototype.getOrder = function(){
var arr = {
world: game.world.children.map(function(c){
return c.name;
}),
layers: game.world.children.map(function(c){
return this.byName[c.name];
}, this)
};
return arr;
};
/**
* Вызывается из {@link UI.ModalManager} и обновляет индекс модального слоя.
* @param {DisplayObject} modalLayer слой, который стал модальным
*/
UI.Layers.prototype.updateModalIndex = function(modalLayer){
if(!modalLayer){
this.modalLayerIndex = -1;
return;
}
var i = this.positions.indexOf(modalLayer);
if(!~i){
this.modalLayerIndex = -1;
return;
}
this.modalLayerIndex = i;
};
/**
* Вызывается элементами игры и проверяет, не заблокирован ли элемент
* над которым находится курсор модальным слоем, и соответственно обновляет курсор.
* @param {DisplayObject} el объект над которым находится курсор
*/
UI.Layers.prototype.updateCursorOverlap = function(el){
var parent = el.parent;
if(!parent){
return;
}
var i = this.positions.indexOf(parent);
if(!~i){
return;
}
var m = this.modalLayerIndex;
if(!~m || i >= m){
ui.cursor.updateOverlap(el);
}
};