//-----------------------------------------------------------------------------
//  Galv's Diagonal Movement
//-----------------------------------------------------------------------------
//  For: RPGMAKER MV
//  GALV_DiagonalMovement.js
//-----------------------------------------------------------------------------
//  2016-05-17 - Version 1.2 - diagonal block option added to prevent moving
//                           - diagonally past blocking tiles.
//                           - added ability to disable diagonal movement
//                           - during game
//  2016-02-04 - Version 1.1 - added option to slow diagonal move speed
//  2015-12-12 - Version 1.0 - release
//-----------------------------------------------------------------------------
//  Terms can be found at:
//  galvs-scripts.com
//-----------------------------------------------------------------------------

var Imported = Imported || {};
Imported.Galv_DiagonalMovement = true;

var Galv = Galv || {};
Galv.DM = Galv.DM || {};

//-----------------------------------------------------------------------------
/*:
 * @plugindesc Just a basic diagonal movement plugin. Written for compatibility with other Galv plugins.
 * 
 * @author Galv - galvs-scripts.com
 *
 * @param Diagonal Mouse
 * @desc true or false for diagonal pathfinding movement on mouse click. true may conflict with pathfinding plugins.
 * @default true
 *
 * @param Diagonal Charset
 * @desc true or false if you want to use diagonal charactersets (see help for more)
 * @default true
 *
 * @param Diagonal Speed
 * @desc % of move speed characters move while travelling diagonally
 * @default 90
 *
 * @param Block Diagonal
 * @desc true or false - if diagonal movement is blocked when moving past an impassable tile or not.
 * @default false
 *
 * @help
 *   Galv's Diagonal Movement
 * ----------------------------------------------------------------------------
 * Plug and play. If this doesn't play nice with other plugins, try putting it
 * at the top of the plugin list. It overwrites the default diagonal function.
 *
 * If this conflicts with other pathfinding plugins you might have, change
 * 'Diagonal Mouse' setting to false.
 *
 * When 'Diagonal Charsets' is true, the plugin will change the sprite if the
 * character is on a diagonal. The new sprite used will be in the position
 * directly below the selected character graphic. This means that only sprites
 * on the top of a character sheet will be able to have diagonal graphics.
 *
 * Sprites on the bottom will not have diagonal graphics.
 * ----------------------------------------------------------------------------
 *   SCRIPT CALL
 * ----------------------------------------------------------------------------
 *
 *   $gameSystem.disableVert = true;   // DISABLE diagonal movement
 *
 *   $gameSystem.disableVert = false;  // ENABLE diagonal movement
 *
 * ----------------------------------------------------------------------------
 */


//-----------------------------------------------------------------------------
//  CODE STUFFS
//-----------------------------------------------------------------------------



(function() {
Galv.DM.mouseMove = PluginManager.parameters('Galv_DiagonalMovement')["Diagonal Mouse"].toLowerCase() == 'true';
Galv.DM.diagGraphic = PluginManager.parameters('Galv_DiagonalMovement')["Diagonal Charset"].toLowerCase() == 'true';
Galv.DM.diagMod = Number(PluginManager.parameters('Galv_DiagonalMovement')["Diagonal Speed"]) * 0.01;	
Galv.DM.diagBlocked = PluginManager.parameters('Galv_DiagonalMovement')["Block Diagonal"].toLowerCase() == 'true';


Game_CharacterBase.prototype._cframes = 3;

var Galv_Game_CharacterBase_initMembers = Game_CharacterBase.prototype.initMembers;
Game_CharacterBase.prototype.initMembers = function() {
    Galv_Game_CharacterBase_initMembers.call(this);
	
    this.setDiagMoveSpeed(this._moveSpeed);
};

var Galv_Game_CharacterBase_setMoveSpeed = Game_CharacterBase.prototype.setMoveSpeed;
Game_CharacterBase.prototype.setMoveSpeed = function(moveSpeed) {
	Galv_Game_CharacterBase_setMoveSpeed.call(this,moveSpeed);
    this.setDiagMoveSpeed(moveSpeed);
};

Game_CharacterBase.prototype.setDiagMoveSpeed = function(moveSpeed) {
	this._defaultMoveSpeed = moveSpeed;
	this._diagMoveSpeed = moveSpeed * Galv.DM.diagMod;
};

	
Galv.DM.getHorzVertDirs = function(direction) {
	switch (direction) {
		case 1: return [4,2];
		case 3: return [6,2];
		case 7: return [4,8];
		case 9: return [6,8];
		default: return [0,0];
	};
};

Galv.DM.getDir = function(horz,vert) {
	if (horz == 4 && vert == 2) return 1;
	if (horz == 6 && vert == 2) return 3;
	if (horz == 4 && vert == 8) return 7;
	if (horz == 6 && vert == 8) return 9;
	return 0;
};

Galv.DM.diagRow = {
	3: 0,
	1: 1,
	9: 2,
	7: 3
};

var Galv_Game_CharacterBase_moveStraight = Game_CharacterBase.prototype.moveStraight;
Game_CharacterBase.prototype.moveStraight = function(d) {
	this._diagDir = false;
	this._moveSpeed = this._defaultMoveSpeed;
	Galv_Game_CharacterBase_moveStraight.call(this,d);
};


if (Galv.DM.diagBlocked) {
	Game_Player.prototype.canPassDiagonally = function(x, y, horz, vert) {
		var x2 = $gameMap.roundXWithDirection(x, horz);
		var y2 = $gameMap.roundYWithDirection(y, vert);
		if (this.canPass(x, y, vert) && this.canPass(x, y2, horz) && this.canPass(x, y, horz) && this.canPass(x2, y, vert)) {
			return true;
		};
		return false;
	};
};



Galv.DM.Game_Player_canPassDiagonally = Game_Player.prototype.canPassDiagonally;
Game_Player.prototype.canPassDiagonally = function(x, y, horz, vert) {
	if ($gameSystem.disableVert) return false;
	return Galv.DM.Game_Player_canPassDiagonally.call(this,x,y,horz,vert);
};

// OVERWRITE
Game_CharacterBase.prototype.moveDiagonally = function(horz, vert) {
	var diag = this.canPassDiagonally(this._x, this._y, horz, vert);
    var norm = this.canPass(this._x, this._y, horz) || this.canPass(this._x, this._y, vert);
	
	if (diag) {
		this._moveSpeed = this._diagMoveSpeed;
		this._diagDir = Galv.DM.getDir(horz,vert);
        this._x = $gameMap.roundXWithDirection(this._x, horz);
        this._y = $gameMap.roundYWithDirection(this._y, vert);
        this._realX = $gameMap.xWithDirection(this._x, this.reverseDir(horz));
        this._realY = $gameMap.yWithDirection(this._y, this.reverseDir(vert));
        this.increaseSteps();
	} else if (norm) {
		this._diagDir = false;
		this._moveSpeed = this._defaultMoveSpeed;
		this.moveStraight(this.getOtherdir(horz,vert));
    };
	
    if (this._direction === this.reverseDir(horz)) this.setDirection(horz);
    if (this._direction === this.reverseDir(vert)) this.setDirection(vert);

};

Game_CharacterBase.prototype.getOtherdir = function(horz, vert) {
    return this.canPass(this._x, this._y, horz) ? horz : vert;
};

// OVERWRITE
Game_Player.prototype.getInputDirection = function() {
    return Input.dir8;
};

var Galv_Game_Player_executeMove = Game_Player.prototype.executeMove;
Game_Player.prototype.executeMove = function(direction) {
	if (direction % 2 == 0) {
    	Galv_Game_Player_executeMove.call(this,direction);
	} else if (Math.abs(direction % 2) == 1) {
		var dirArray = Galv.DM.getHorzVertDirs(direction);
		this.moveDiagonally(dirArray[0],dirArray[1]);
	};
};






// If using Diaonal Charset

if (Galv.DM.diagGraphic) {
var Galv_Sprite_Character_characterPatternY = Sprite_Character.prototype.characterPatternY;
Sprite_Character.prototype.characterPatternY = function() {
	if (!this._isBigCharacter && this._character._diagDir && this._character.characterIndex() < 4) {
		return Galv.DM.diagRow[this._character._diagDir];
	} else {
    	return Galv_Sprite_Character_characterPatternY.call(this);
	};
};


var Galv_Sprite_Character_characterBlockX = Sprite_Character.prototype.characterBlockX;
Sprite_Character.prototype.characterBlockX = function() {
	if (!this._isBigCharacter && this._character._diagDir && this._character.characterIndex() < 4) {
		var index = this._character.characterIndex() + 4;
        return index % 4 * this._character._cframes;
	} else {	
	    return Galv_Sprite_Character_characterBlockX.call(this);
	};

};

var Galv_Sprite_Character_characterBlockY = Sprite_Character.prototype.characterBlockY;
Sprite_Character.prototype.characterBlockY = function() {
	if (!this._isBigCharacter && this._character._diagDir && this._character.characterIndex() < 4) {
		var index = this._character.characterIndex() + 4;
        return Math.floor(index / 4) * 4;
	} else {	
	    return Galv_Sprite_Character_characterBlockY.call(this);
	};

};
};


// If using diagonal mouse movement:

if (Galv.DM.mouseMove) {
Galv.DM.Game_Character_findDirectionTo = Game_Character.prototype.findDirectionTo;
// OVERWRITE
Game_Character.prototype.findDirectionTo = function(goalX, goalY) {
	if ($gameSystem.disableVert) {
		return Galv.DM.Game_Character_findDirectionTo.call(this,goalX,goalY);
	} else {
		var searchLimit = this.searchLimit();
		var mapWidth = $gameMap.width();
		var nodeList = [];
		var openList = [];
		var closedList = [];
		var start = {};
		var best = start;
	
		if (this.x === goalX && this.y === goalY) {
			return 0;
		}
	
		start.parent = null;
		start.x = this.x;
		start.y = this.y;
		start.g = 0;
		start.f = $gameMap.distance(start.x, start.y, goalX, goalY);
		nodeList.push(start);
		openList.push(start.y * mapWidth + start.x);
	
		while (nodeList.length > 0) {
			var bestIndex = 0;
			for (var i = 0; i < nodeList.length; i++) {
				if (nodeList[i].f < nodeList[bestIndex].f) {
					bestIndex = i;
				}
			}
	
			var current = nodeList[bestIndex];
			var x1 = current.x;
			var y1 = current.y;
			var pos1 = y1 * mapWidth + x1;
			var g1 = current.g;
	
			nodeList.splice(bestIndex, 1);
			openList.splice(openList.indexOf(pos1), 1);
			closedList.push(pos1);
	
			if (current.x === goalX && current.y === goalY) {
				best = current;
				goaled = true;
				break;
			}
	
			if (g1 >= searchLimit) {
				continue;
			}
	
	
			for (var j = 0; j < 9; j++) {
				var direction = 1 + j;
				
				if (direction === 5) continue;
				
				var diag = Math.abs(direction % 2) == 1;
				var dirs = Galv.DM.getHorzVertDirs(direction);
				var horz = dirs[0];
				var vert = dirs[1];
				
				if (diag && this.canPassDiagonally(x1, y1, horz, vert) && (this.canPass(x1, y1, horz) || this.canPass(x1, y1, vert))) {
					// If can go diagonally and a horizontal dir isn't blocking
					var x2 = $gameMap.roundXWithDirection(x1, horz);
					var y2 = $gameMap.roundYWithDirection(y1, vert);
				} else if (this.canPass(x1, y1, direction)) {
					var x2 = $gameMap.roundXWithDirection(x1, direction);
					var y2 = $gameMap.roundYWithDirection(y1, direction);
				} else {
					continue;
				};
	
				var pos2 = y2 * mapWidth + x2;
	
				if (closedList.contains(pos2)) {
					continue;
				}
	
				var g2 = g1 + 1;
				var index2 = openList.indexOf(pos2);
	
				if (index2 < 0 || g2 < nodeList[index2].g) {
					var neighbor;
					if (index2 >= 0) {
						neighbor = nodeList[index2];
					} else {
						neighbor = {};
						nodeList.push(neighbor);
						openList.push(pos2);
					}
					neighbor.parent = current;
					neighbor.x = x2;
					neighbor.y = y2;
					neighbor.g = g2;
					neighbor.f = g2 + $gameMap.distance(x2, y2, goalX, goalY);
					if (!best || neighbor.f - neighbor.g < best.f - best.g) {
						best = neighbor;
					}
				}
			}
		}
	
		var node = best;
		while (node.parent && node.parent !== start) {
			node = node.parent;
		}
	
		var deltaX1 = $gameMap.deltaX(node.x, start.x);
		var deltaY1 = $gameMap.deltaY(node.y, start.y);
		
		
		if (deltaY1 > 0 && deltaX1 > 0) {
			return 3;
		} else if (deltaY1 > 0 && deltaX1 < 0) {
			return 1;
		} else if (deltaY1 < 0 && deltaX1 < 0) {
			return 7;
		} else if (deltaY1 < 0 && deltaX1 > 0) {
			return 9;
		};
		
		
		if (deltaY1 > 0) {
			return 2;
		} else if (deltaX1 < 0) {
			return 4;
		} else if (deltaX1 > 0) {
			return 6;
		} else if (deltaY1 < 0) {
			return 8;
		}
	
		var deltaX2 = this.deltaXFrom(goalX);
		var deltaY2 = this.deltaYFrom(goalY);
		
		
		if (Math.abs(deltaX2) > Math.abs(deltaY2)) {
			return deltaX2 > 0 ? 4 : 6;
		} else if (deltaY2 !== 0) {
			return deltaY2 > 0 ? 8 : 2;
		}
	
		return 0;
	};
};
};

})();