SkinManager/SkinManager.js

/**
* Модуль, управляющий внешним видом карт
* @param {string} skinToSet скин с этим именем будет установлен после его добавления
* @class
*/

var SkinManager = function(skinToSet){

	/**
	* Добавленные скины.
	* @type {Object}
	*/
	this.skins = {};

	/**
	* Текущий скин. Далее указаны свойства, которые полезны за пределами этого модуля.
	* @type {Object}
	* @prop {string}  name            название скина
	* @prop {number}  width           ширина карты
	* @prop {number}  height          высота карты
	* @prop {number}  scale           масштаб карты
	* @prop {number}  firstValueFrame кадр, с которого начинаются карты со значениями
	* @prop {number}  cardbackFrame   кадр рубашки карт
	* @prop {number}  trumpOffset     сдвиг для отображения масти карты
	* @prop {number}  color           цвет, соответствующий скину
	*
	* @prop {boolean} hasSuits        есть ли графика отображения козырной масти
	*
	* @prop {string}  sheetName       имя текстуры карт
	* @prop {string}  glowSheetName   имя текстуры свечения карт
	* @prop {string}  trailName       имя текстуры хвоста карт
	* @prop {string}  suitsName       имя текстуры отображения козырной масти
	*/
	this.skin = null;

	/**
	* Скин с этим именем будет установлен после его добавления.
	* @type {string}
	*/
	this.skinToSet = skinToSet || null;
};

/**
* Добавляет скины из массива.
* @param {array} skins массив скинов
*/
SkinManager.prototype.addSkins = function(skins){
	for(var i = 0; i < skins.length; i++){
		this.addSkin(skins[i]);
	}
};

/**
* Добавляет скин, загружает графику.
* @param {object}   options                                           опции скина
*
* @param {string}   options.name                                      имя скина, должно соотвтествтовать папке с графикой скина в `assets/skins/`
*                                                                     под этим именем скин будет сохранен в {@link SkinManager#skins}
*                                                                     и это имя нужно использовать для установки скина
*                                                                     графика карт - `cards.png`
*                                                                     графика свечения карт - `glow.png`
*                      		                                          графика хвоста карт - `trails.png`
*                      		                                          графика отображения козырной масти - `suits.png`
* @param {number}   options.width                                     реальная ширина карты (и ширина кадра графики карты)
* @param {number}   options.height                                    реальная высота карты (и высота кадра графики карты)
*
* @param {number}   options.glowWidth                                 реальная ширина свечения карты (и ширина кадра графики свечения)
* @param {number}   options.glowHeight                                реальная высота свечения карты (и высота кадра графики свечения)
*
* @param {number}   options.trailWidth                                ширина хвоста карты (и ширина кадра графики хвоста)
* @param {number}   options.trailHeight                               высота хвоста карты (и высота кадра графики хвоста)
*
* @param {number}   [options.scale=1]                                 масштаб скина
*
* @param {number}   [options.numOfFrames=53]                          количество кадров в графике карты
* @param {number}   [options.firstValueFrame=0]                       кадр графики карт, с которого начинаются карты со значениями
*
* @param {number[]} [options.cardbackPossibleFrames=<52>]             возможные кадры рубашки карт из графики карт
* @param {number}   [options.cardbackFrame=cardbackPossibleFrames<0>] текущий кадр рубашки карт
*
* @param {number}   [options.trumpOffset=0]                           сдвиг для отображения масти карты
*
* @param {boolean}  [options.hasSuits=true]                           есть ли графика отображения козырной масти
* @param {string}   [options.background='blue']                       какой фон соответствует этому скину
* @param {number}   [options.color=ui.colors.lightBlue]               цвет, соответствующий скину
*
* @param {boolean} [options.uiVignette=true]
*/
SkinManager.prototype.addSkin = function(options){

	if(!options || !options.name){
		return;
	}

	var skin = {};

	skin.background 	 = options.background || 'blue';
	skin.color 			 = options.color === undefined ? ui.colors.lightBlue : options.color;

	skin.frameWidth 	 = options.width || 0;
	skin.frameHeight 	 = options.height || 0;

	skin.scale 			 = options.scale || 1;

	skin.width 			 = skin.frameWidth*skin.scale;
	skin.height 		 = skin.frameHeight*skin.scale;

	skin.name 			 = options.name;
	skin.friendlyName	 = options.friendlyName || options.name;
	skin.sheetName 		 = skin.name + 'Cards';
	skin.sheetPath 		 = 'assets/skins/' + options.name + '/cards.png';

	skin.numOfFrames 	 = options.numOfFrames || 53;
	skin.firstValueFrame = options.firstValueFrame || 0;
	skin.cardbackPossibleNames = [];
	skin.cardbackPossibleFrames = [];
	skin.cardbackFrame 	 = (options.cardbackFrame || options.cardbackFrame === 0) ? options.cardbackFrame : skin.cardbackPossibleFrames[0];

	skin.trumpOffset 	 = options.trumpOffset || 0;

	skin.glowPath 		 = 'assets/skins/' + options.name + '/glow.png';
	skin.glowSheetName 	 = options.name + 'Glow';
	skin.glowRealWidth 	 = options.glowWidth || 0;
	skin.glowRealHeight  = options.glowHeight || 0;
	skin.glowWidth 		 = skin.glowRealWidth*skin.scale;
	skin.glowHeight 	 = skin.glowRealHeight*skin.scale;

	skin.trailWidth 	 = options.trailWidth || 0;
	skin.trailHeight 	 = options.trailHeight || 0;
	skin.trailPath 		 = 'assets/skins/' + options.name + '/trails.png';
	skin.trailName 		 = options.name + 'Trail';

	skin.hasSuits 		 = options.hasSuits === undefined ? true : options.hasSuits;
	skin.suitsPath 		 = 'assets/skins/' + options.name + '/suits.png';
	skin.suitsName 		 = options.name + 'Suits';

	skin.uiVignette    = options.uiVignette !== false;

	if(options.cardbackPossibleFrames){
		options.cardbackPossibleFrames.forEach(function(frame){
			skin.cardbackPossibleNames.push(frame[0]);
			skin.cardbackPossibleFrames.push(frame[1]);
		})
	}
	else{
		skin.cardbackPossibleFrames = [52];
		skin.cardbackPossibleNames = ['Default'];
	}

	skin.loaded = false;

	this.skins[skin.name] = skin;
	if(this.skinToSet == skin.name){
		var cardback = gameOptions.get('appearance_cardback');
		if(cardback !== null && cardback < skin.cardbackPossibleFrames.length){
			skin.cardbackFrame = skin.cardbackPossibleFrames[cardback];
		}
		this.skin = skin;
		this.skinToSet = null;
		this.loadSkin(skin.name, false);
	}
};

/**
* Загружает ассеты скина.
* @param {string}  skinName название скина
* @param {boolean} [apply]  нужно ли применять скин после загрузки
*/
SkinManager.prototype.loadSkin = function(skinName, apply){
	var skin = this.skins[skinName];

	if(!skin || skin.loaded){
		return;
	}

	skin.loaded = true;

	game.load.spritesheet(
		skin.sheetName,
		skin.sheetPath,
		skin.frameWidth,
		skin.frameHeight,
		skin.numOfFrames
	);
	game.load.image(skin.glowSheetName, skin.glowPath);
	game.load.spritesheet(
		skin.trailName,
		skin.trailPath,
		skin.trailWidth,
		skin.trailHeight,
		4
	);
	if(skin.hasSuits){
		game.load.spritesheet(
			skin.suitsName,
			skin.suitsPath,
			skin.frameWidth,
			skin.frameHeight,
			4
		);
	}
	if(apply){
		game.load.onLoadComplete.addOnce(this.applySkin, this);
	}
	if(game.initialized){
		var loadText = ui.feed.newMessage('Loading skin...', 'system');
		game.load.onLoadComplete.addOnce(ui.feed.removeMessage.bind(ui.feed, loadText), this);
	}

	game.load.start();
};

/**
* Устанавливает скин.
* @param {string} skinName название скина
*/
SkinManager.prototype.setSkin = function(skinName){
	if(!this.skins[skinName]){
		return;
	}
	if(this.skinToSet){
		this.skinToSet = null;
	}
	this.skin = this.skins[skinName];
	gameOptions.set('appearance_skin', skinName);
	gameOptions.save();
	gameOptions.set('appearance_cardback', this.getCurrentCardbackIndex());
	gameOptions.set('ui_vignette', this.skin.uiVignette);
	gameOptions.save();
	if(!this.skin.loaded){
		this.loadSkin(skinName, true);
	}
	else{
		this.applySkin();
	}
};

/** Применяет текущий установленный {@link SkinManager#skin} скин. */
SkinManager.prototype.applySkin = function(){
	if(!game.initialized){
		return;
	}
	game.applySkin();
	ui.menus.options.getElementByName('cardback').setChoices(
		this.getCardbacks(),
		this.getCurrentCardbackIndex()
	);
};

/**
* Устанавливает рубашку карт.
* @param {number} i индекс рубашки в `cardbackPossibleFrames` текущего скина
*/
SkinManager.prototype.setCardback = function(i){
	if(isNaN(i) || i >= this.skin.cardbackPossibleFrames.length || i < 0){
		console.log('SkinManager: Invalid cardback index', i);
		return;
	}
	this.skin.cardbackFrame = this.skin.cardbackPossibleFrames[i];
	gameOptions.set('appearance_cardback', this.getCurrentCardbackIndex());
	gameOptions.save();
	for(var ci in cardManager.cards){
		if(cardManager.cards.hasOwnProperty(ci)){
			cardManager.cards[ci].applyCardback();
		}
	}
};

SkinManager.prototype.getCardbacks = function(){
	return this.skin.cardbackPossibleNames.map(function(name, i){
		return [i, name];
	}, this)
};

SkinManager.prototype.getCurrentCardbackIndex = function(){
	return this.skin.cardbackPossibleFrames.indexOf(this.skin.cardbackFrame);
};

SkinManager.prototype.getSkinNames = function(){
	var skins = [];
	for(var k in this.skins){
		if(this.skins.hasOwnProperty(k)){
			skins.push([this.skins[k].name, this.skins[k].friendlyName]);
		}
	}
	return skins;
}

//@include:skins