Game/Game.js

/**
* Модуль, работающий с движком игры и инициализирующий все остальные модули
* @param {string} [parent] id DOM элемента, в который будет добавлен canvas элемент игры
* @param {number} [speed=1] скорость игры
* @param {number} [inDebugMode=false] находится ли игра в дебаг режиме
* @class
* @extends {Phaser.Game}
* @listens document.resize
* @listens document.orientationchange
* @listens document.visibilitychange
* @see {@link http://phaser.io/docs/2.6.2/Phaser.Game.html}
*/

var Game = function(parent, speed, inDebugMode){

	/**
	* Скорость игры.
	* @type {number}
	* @default 1
	*/
	this.speed = speed || 1;

	/**
	* Находится ли игры в дебаг режиме.
	* @type {boolean}
	* @default false
	*/
	this.inDebugMode = inDebugMode || false;

	/**
	* Инициализирована ли игра.
	* @type {Boolean}
	*/
	this.initialized = false;

	/**
	* Была ли игра остановлена из-за потери видимости окна.
	* @type {Boolean}
	*/
	this.pausedByViewChange = false;

	this.pausedAt = 0;

	/**
	* Находится ли игра в горизонтальном положении, 
	* рассчитывается только по размеру экрана.
	* @type {Boolean}
	*/
	this.isRawLandscape = true;

	Phaser.Game.call(
		this,
		{
			width: this.screenWidth,
 			height: this.screenHeight, 
			renderer: Number(gameOptions.get('system_renderer')), 
			parent: parent,
			transparent: true
		}
	);

	this._dimensionsUpdateTimeout = null;
	this._hiddenValue = null;

};

extend(Game, Phaser.Game);

/**
* Инициализирет игру.
*/
Game.prototype.initialize = function(){

	// Устанавливаем размер игры
	this.scale.updateGameSize();

	// Отключаем контекстное меню
	this.canvas.oncontextmenu = function (e) {e.preventDefault();};

	// Добавляем листенеры
	this._addVisibilityChangeListener();
	window.addEventListener('resize', this._updateCoordinatesDebounce.bind(this));
	window.addEventListener('orientationchange', this._updateCoordinatesDebounce.bind(this));

	// Антиалиасинг
	// Phaser.Canvas.setImageRenderingCrisp(game.canvas);
	
	/**
	* Менеджер полей
	* @type {FieldManager}
	* @global
	*/
	fieldManager = new FieldManager(gameOptions.get('debug_fields'));

	/**
	* Менеджер карт
	* @type {CardManager}
	* @global
	*/
	cardManager = new CardManager(gameOptions.get('debug_cards'));

	/**
	* Эмиттер карт
	* @type {CardEmitter}
	* @global
	*/
	cardEmitter = new CardEmitter();

	// Инициализация модулей
	cardControl.initialize();
	ui.initialize();
	connection.initialize();

	/* Дебаг */
	this.scale.drawDebugGrid();

	this.onPause.add(function(){
		if(this.inDebugMode){
			console.log('Game: paused internally');
		}
	}, this);

	this.onResume.add(function(){
		if(this.inDebugMode){
			console.log('Game: unpaused internally');
		}
	}, this);
	/********/
	
	this.initialized = true;
};

/**
* Корректирует размеры игры в соответствии с размером окна.
*/
Game.prototype.updateCoordinates = function(){
	if(PhaserInput.KeyboardOpen && !game.scale.isFullScreen){
		return;
	}
	this.scale.updateGameSize();
	this.scale.drawDebugGrid();
	var state = this.state.getCurrent();
	state.postResize();
	this._dimensionsUpdateTimeout = null;
};

/**
* Применяет скин ко всем элементам игры
*/
Game.prototype.applySkin = function(){
	this.scale.updateGameSize();
	this.scale.drawDebugGrid();
	var state = this.state.getCurrent();
	state.applySkin();
};

/**
* Запускает дебаунс корректировки размеров игры.
*/
Game.prototype._updateCoordinatesDebounce = function(){
	if(PhaserInput.KeyboardOpen && !game.scale.isFullScreen){
		clearTimeout(this._dimensionsUpdateTimeout);
		document.getElementById('loading').style.display = 'none';
		return;
	}
	if(this._dimensionsUpdateTimeout){
		clearTimeout(this._dimensionsUpdateTimeout);
	}
	else if(!this.scale.fullScreenModeChanged && !this.inDebugMode){
		document.getElementById('loading').style.display = 'block';
	}
	var timeout = (this.scale.fullScreenModeChanged || this.inDebugMode) ? 10 : 500;
	this._dimensionsUpdateTimeout = setTimeout(this.updateCoordinates.bind(this), timeout);
};

/** Остонавливает симуляцию. */
Game.prototype.pause = function(){
	this.paused = true;	
	this.pausedByViewChange = true;
	this.pausedAt = Date.now();
	if(this.inDebugMode){
		console.log('Game: paused by visibility change');
	}
};

/** Запускает симуляцию. */
Game.prototype.unpause = function(){
	this.paused = false;
	this.pausedByViewChange = false;
	if(this.inDebugMode){
		console.log('Game: unpaused by visibility change');
	}

	var state = this.state.getCurrent();
	setTimeout(state.postResumed.bind(state), 1000);

	if(Date.now() - this.pausedAt > 5000){
		actionHandler.sequencer.finish();
	}
	this.pausedAt = 0;
};

/**
* Ставит и снимает игру с паузы в зависимости от видимости окна,
* корректирует элементы игры после снятия паузы.
*/
Game.prototype._visibilityChangeListener = function(){
	if (!document[this._hiddenValue]) {
		this.unpause();
	}
	else{
		this.pause();
	}
};

/**
* Добавляет листенер изменения видимости вкладки в зависимости от браузера.
*/
Game.prototype._addVisibilityChangeListener = function(){
	var visibilityChange; 
	if (typeof document.hidden !== "undefined") {
		this._hiddenValue = "hidden";
		visibilityChange = "visibilitychange";
	} else if (typeof document.msHidden !== "undefined") {
		this._hiddenValue = "msHidden";
		visibilityChange = "msvisibilitychange";
	} else if (typeof document.webkitHidden !== "undefined") {
		this._hiddenValue = "webkitHidden";
		visibilityChange = "webkitvisibilitychange";
	}
	document.addEventListener(visibilityChange, this._visibilityChangeListener.bind(this), false);
};

/** Переключает дебаг всех элементов игры. */
Game.prototype.toggleAllDebugModes = function(){

	this.toggleDebugMode();

	if(connection.inDebugMode != this.inDebugMode){
		connection.toggleDebugMode();
	}

	if(this.scale.inDebugMode != this.inDebugMode){
		this.scale.toggleDebugMode();
	}

	if(cardControl.inDebugMode != this.inDebugMode){
		cardControl.toggleDebugMode();
	}

	if(fieldManager.inDebugMode != this.inDebugMode){
		fieldManager.toggleDebugMode();
	}

	if(cardManager.inDebugMode != this.inDebugMode){
		cardManager.toggleDebugMode();	
	}
};

/** Переключает дебаг игры */
Game.prototype.toggleDebugMode = function(){
	this.inDebugMode = !this.inDebugMode;
	this.time.advancedTiming = this.inDebugMode;
	gameOptions.set('debug_game', this.inDebugMode);
	gameOptions.save();
	ui.setDebugButtonText('game', 'Game', this.inDebugMode);
};

/** Выводит FPS. */
Game.prototype.updateDebug = function(){
	if(!this.inDebugMode){
		return;
	}
	this.debug.text(this.time.fps, 2, 14, "#00ff00");
};

/** Снимает игру с паузы, если она была поставлена на паузу по неверной причине. */
Game.prototype.fixPause = function(){
	if(this.stage.disableVisibilityChange && this.paused && !this.pausedByViewChange){
		this.paused = false;
		if(this.inDebugMode){
			console.log('Game: unpaused forced');
		}
	}
};

/**
* Трясет фон, поля, карты и кнопку действия.
* @param  {number} distance дальность тряски
* @param  {number} duration время тряски
* @param  {number} sin      аргумент для Math.sin
* @param  {number} cos      аргумент для Math.cos
*/
Game.prototype.shake = function(distance, duration, sin, cos){	

	var elements = [
		ui.background,
		fieldManager,
		cardManager,
		ui.actionButtons
	];

	var position;

	function restorePosition(el){
		el.position = position;
	}

	for(var i = 0; i < elements.length; i++){
		var el = elements[i];
		position = {x: el.position.x, y: el.position.y};
		var tween = game.add.tween(el.position);
		tween.to(
			{
				x: el.position.x + distance,
				y: el.position.y + distance
			},
			duration,
			Phaser.Easing.Wiggle.bind(null, sin, cos)
		);
		tween.onComplete.add(restorePosition.bind(null, el));
		tween.start();
	}
};

Game.prototype.clearLocationHash = function(){
	history.replaceState(null, null, location.href.replace(location.hash, ''));
};

//@include:GameOverride