Field/FieldBuilder/FieldBuilder.js

/**
* Модуль, создающий поля для {@link FieldManager}.
* @param {FieldManager} manager Менеджер полей, для которого рассчитываются размеры.
* @class
*/

var FieldBuilder = function(manager){

	/**
	* Ссылка на менеджер полей
	* @type {FieldManager}
	*/
	this.manager = manager;

	/**
	* Отступы полей
	* @type {Object<number>}
	*/
	this.offsets = {};

	/**
	* Минимальные ширины для размещения карт
	* @type {Object<number>}
	*/
	this.minActiveSpaces = {};

	/**
	* Рассчитанные позиции полей
	* @type {Object<Object>}
	*/
	this.positions = {};

	/**
	* Рассчитанные размеры полей
	* @type {Object<Object>}
	*/
	this.dimensions = {};

	/**
	* Опции полей противников.
	* @type {Object}
	*/
	this.options = {};

	/**
	* Стили полей противников.
	* @type {Object}
	*/
	this.styles = {};

	/**
	* Стили плашек противников.
	* @type {Object}
	*/
	this.badgeStyles = {};

	/**
	* Стандартный минимальный размер для размещения карт
	* @type {Number}
	*/
	this.minActiveSpace = 10;

	/**
	* Возможные последовательности полей.
	* @type {Object}
	*/
	this._possibleTableOrders = {	
		1: [4, 2, 0, 1, 3, 5],		// 1x6
		2: [2, 3, 0, 1, 4, 5],		// 2x3
		3: [3, 1, 4, 2, 0, 5],		// 3x2
		6: [4, 2, 0, 1, 3, 5]		// 6x1
	};

	/**
	* Последовательность полей стола
	* @type {array}
	*/
	this.tableOrder = null;

	/**
	* Количество полей стола
	* @type {Number}
	*/
	this.tableAmount = 6;

	/**
	* Кол-во полей стола в строке
	* @type {Number}
	*/
	this.tablesInRow = 0;

	/**
	* Отступ между полями стола
	* @type {Number}
	*/
	this._tableOffset = 0;

	/**
	* Кол-во клеток, занимаемые полями стола
	* @type {Number}
	*/
	this._tableCells = 0;
	
	/**
	* Количество противников в трех позициях - левой, верхней и правой.  
	* `[left, top, right]`
	* @type {number[]}
	*/
	this._opponentPlacement = null;

	/**
	* Отступы позиций полей оппонентов.
	* @type {number[]}
	*/
	this._opponentsOffset = null;	

	// Magic numbers
	/**
	* Ниже этого отношения клеток сетки игры ({@link ScaleManager#cellRelation}) 
	* колода и стопка сброса сдвигаются ближе к краям.
	* @type {Number}
	*/
	this._increaseTopOpponentsSpaceRelation = 1.78;
	/**
	* Ниже этого отношения клеток сетки игры ({@link ScaleManager#cellRelation}) 
	* больше оппонентов помещается по горизотали чем по вертикали.
	* @type {Number}
	*/
	this._reduceTopOpponentsNumberRelation = 1.13;

	/**
	* Есть место для поля оппонента сверху.
	* Если нет, колода и стопка сброса будут сдвинуты.
	* @type {Boolean}
	*/
	this._topOpponentFits = true;

	/**
	* Противники не будут располагаться вверху экрана.
	* @type {Boolean}
	*/
	this._noTopOpponents = false;
};

/** Создает поля */
FieldBuilder.prototype.createFieldNetwork = function(lockedFields){

	var manager = this.manager;

	manager.table.length = 0;
	manager.opponents.length = 0;

	this.calcSizes();

	this._buildDeckField();
	this._buildDiscardField();
	this._buildPlayerField();
	this._buildOpponentFields();
	this._buildTableFields(lockedFields);

	manager.networkCreated = true;
};

/** Правит поля */
FieldBuilder.prototype.adjustFieldNetwork = function(lockedFields){

	var manager = this.manager;

	if(!manager.networkCreated){
		this.createFieldNetwork();
		return;
	}

	manager.table.length = 0;
	manager.opponents.length = 0;

	this.calcSizes();
	this.manager.resizeFields();
	this._buildOpponentFields();
	//this._buildDiscardField();
	this._buildTableFields(lockedFields);

};

/**
* Расчитывает размеры и позиции полей.
*/
FieldBuilder.prototype.calcSizes = function(){
	var numPlayers = gameInfo.players.length;
	this._topOpponentFits = game.scale.cellRelation > this._increaseTopOpponentsSpaceRelation;
	this._noTopOpponents = game.scale.cellRelation < this._reduceTopOpponentsNumberRelation && numPlayers == 3;
	this._opponentPlacement = this._countOpponentPlacement(numPlayers - 1);
	this._calcGenSizes();
	this._calcSpecSizes();
};

/**
* Обобщенные (General) размеры.
*/
FieldBuilder.prototype._calcGenSizes = function(){

	// Игрок
	this._calcGenPlayerSizes();

	// Противники
	this._calcGenOpponentSizes();

	// Стол
	var counter = this.tableAmount;
	do{
		this.tablesInRow = counter;
		this._calcGenTableSizes(counter);
		counter = Math.ceil(counter/2);
	}
	while(this._notEnoughSpace(null, 'table', null, true, true) && counter > 1);
};

/**
* Размеры для каждого поля (Specific).
*/
FieldBuilder.prototype._calcSpecSizes = function(){
	this._calcDeckDiscardSizes();
	this._calcSpecTableSizes();
	this._calcSpecPlayerSizes();
	this._calcSpecOpponentSizes();
};

//@include:FieldBuilderBuild
//@include:FieldBuilderTable
//@include:FieldBuilderPlayer
//@include:FieldBuilderOpponent

/**
* Размеры для колоды и стопки сброса.
*/
FieldBuilder.prototype._calcDeckDiscardSizes = function(){
	var numOfCards = cardManager.numOfCards,
		halfDensity = Math.floor(game.scale.density / 2);

	var addedCells;
	if(this._topOpponentFits){
		addedCells = 3;
	}
	else if(this._noTopOpponents){		
		addedCells = 6;
	}
	else{
		addedCells = 0;
	}

	this.offsets.DECK = 15;
	this.minActiveSpaces.DECK = numOfCards/2;
	this.dimensions.DECK = {};
	this.positions.DECK = game.scale.cellAt(
		game.scale.density + addedCells,
		-halfDensity,
		-this.offsets.DECK,
		-this.offsets.DECK
	);

	this.offsets.DISCARD_PILE = 15;
	this.minActiveSpaces.DISCARD_PILE = numOfCards/2;
	this.dimensions.DISCARD_PILE = {};
	this.positions.DISCARD_PILE = game.scale.cellAt(
		game.scale.numCols - game.scale.density - addedCells,
		-halfDensity,
		-this.offsets.DISCARD_PILE,
		-this.offsets.DISCARD_PILE
	);

	this.positions.DECK.x -= skinManager.skin.height;
};

/**
* Выводит предупреждение в консоль, если ширина/высота меньше ширины одной карты.
* @param {string}  [id]          id поля (для вывода в консоль)
* @param {string}  ref           id поля или обобщенное название, по которому будут найдены размеры ('TABLE', 'player', etc.)
* @param {number}  [index]       индекс позиции, если поле противника
* @param {boolean} [silent=true] выключает вывод предупреждения в консоль
* @param {boolean} [noHeight]    убирает проверку высоты
* @param {boolean} [noWidth]     убирает проверку ширины
* @return {boolean} Меньше ли ширина\высота.
*/
FieldBuilder.prototype._notEnoughSpace = function(id, ref, index, silent, noHeight, noWidth){
	var arrayExists = typeof index == 'number',
		width = arrayExists ? this.dimensions[ref][index].width : this.dimensions[ref].width,
		height = arrayExists ? this.dimensions[ref][index].height : this.dimensions[ref].height,
		minActiveSpace = arrayExists ? this.minActiveSpaces[ref][index] : this.minActiveSpaces[ref],
		requiredWidth = skinManager.skin.width + minActiveSpace,
		requiredHeight = skinManager.skin.height + minActiveSpace,
		str = null;

	if(!this.manager.inDebugMode){
		silent = true;
	}

	if(!noWidth && (width || width === 0) && width < requiredWidth){
		str = ['Field builder: Not enough space (width) for field', id, '(', width, '<', requiredWidth, ')\n'];
	}
	else if(!noHeight && (height || height === 0) && height < requiredHeight){
		str = ['Field builder: Not enough space (height) for field', id, '(', height, '<', requiredHeight, ')\n'];
	}
	
	if(str){
		if(!silent){
			str.push(this.manager.fields[id]);
			console.warn.apply(console, str);
		}
		return true;
	}
	return false;
};