/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "hopkins/lines.h"

#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"

#include "common/system.h"
#include "common/textconsole.h"

namespace Hopkins {

LinesManager::LinesManager(HopkinsEngine *vm) {
	_vm = vm;

	for (int i = 0; i < MAX_LINES + 1; ++i)
		Common::fill((byte *)&_zoneLine[i], (byte *)&_zoneLine[i] + sizeof(LigneZoneItem), 0);

	for (int i = 0; i < MAX_LINES; ++i)
		Common::fill((byte *)&_lineItem[i], (byte *)&_lineItem[i] + sizeof(LigneItem), 0);

	for (int i = 0; i < 4000; ++i)
		Common::fill((byte *)&_smoothRoute[i], (byte *)&_smoothRoute[i] + sizeof(SmoothItem), 0);

	for (int i = 0; i < 8001; ++i)
		_bestRoute[i].set(0, 0, DIR_NONE);

	for (int i = 0; i < 101; ++i) {
		Common::fill((byte *)&_segment[i], (byte *)&_segment[i] + sizeof(SegmentItem), 0);
		Common::fill((byte *)&_squareZone[i], (byte *)&_squareZone[i] + sizeof(SquareZoneItem), 0);
	}

	for (int i = 0; i < 105; ++i) {
		_bobZone[i] = 0;
		_bobZoneFl[i] = false;
	}

	for (int i = 0; i < 106; ++i)
		Common::fill((byte *)&_zone[i], (byte *)&_zone[i] + sizeof(ZoneItem), 0);

	_linesNumb = 0;
	_newLineIdx = 0;
	_newLineDataIdx = 0;
	_newRouteIdx = 0;
	_newPosX = 0;
	_newPosY = 0;
	_smoothMoveDirection = DIR_NONE;
	_lastLine = 0;
	_maxLineIdx = 0;
	_pathFindingMaxDepth = 0;
	_testRoute0 = nullptr;
	_testRoute1 = nullptr;
	_testRoute2 = nullptr;
	_lineBuf = nullptr;
	_route = nullptr;
	_currentSegmentId = 0;
	_largeBuf = nullptr;
	_zoneSkipCount = 0;
	_hotspotTextColor = 0;
	_forceHideText = false;
	_oldMouseZoneId = 0;
	_oldMouseX = 0;
	_oldMouseY = 0;
	_oldRouteFromX = 0;
	_oldRouteFromY = 0;
	_oldRouteDestX = 0;
	_oldRouteDestY = 0;
	_oldZoneNum = 0;
}

LinesManager::~LinesManager() {
	_vm->_globals->freeMemory(_largeBuf);
	if (_testRoute0)
		delete[] _testRoute0;
	if (_testRoute1)
		delete[] _testRoute1;
	if (_testRoute2)
		delete[] _testRoute2;
}

int LigneItem::appendToRouteInc(int from, int to, RouteItem *route, int index) {
	debugC(5, kDebugPath, "appendToRouteInc(%d, %d, route, %d)", from, to, index);
	if (to == -1)
		to = _lineDataEndIdx;

	for (int i = from; i < to; ++i)
		route[index++].set(_lineData[2*i], _lineData[2*i+1], _directionRouteInc);
	return index;
}

int LigneItem::appendToRouteDec(int from, int to, RouteItem *route, int index) {
	debugC(5, kDebugPath, "appendToRouteDecc(%d, %d, route, %d)", from, to, index);
	if (from == -1)
		from = _lineDataEndIdx - 1;

	for (int i = from; i > to; --i)
		route[index++].set(_lineData[2*i], _lineData[2*i+1], _directionRouteDec);
	return index;
}

/**
 * Load lines
 */
void LinesManager::loadLines(const Common::Path &file) {
	debugC(5, kDebugPath, "loadLines(%s)", file.toString().c_str());
	resetLines();
	_linesNumb = 0;
	_lastLine = 0;
	byte *ptr = _vm->_fileIO->loadFile(file);
	for (int idx = 0; READ_LE_INT16((uint16 *)ptr + (idx * 5)) != -1; idx++) {
		addLine(idx,
		    (Directions)READ_LE_INT16((uint16 *)ptr + (idx * 5)),
		    READ_LE_INT16((uint16 *)ptr + (idx * 5) + 1),
		    READ_LE_INT16((uint16 *)ptr + (idx * 5) + 2),
		    READ_LE_INT16((uint16 *)ptr + (idx * 5) + 3),
		    READ_LE_INT16((uint16 *)ptr + (idx * 5) + 4));
	}
	initRoute();
	_vm->_globals->freeMemory(ptr);
}

/**
 * Check Hotspots in Inventory screen
 * Returns the ID of the hotspot under mouse
 */
int LinesManager::checkInventoryHotspots(int posX, int posY) {
	debugC(5, kDebugPath, "checkInventoryHotspots(%d, %d)", posX, posY);
	int hotspotId = 0;
	if (posY >= 120 && posY <= 153)
		hotspotId = checkInventoryHotspotsRow(posX, 1, false);
	if (posY >= 154 && posY <= 191)
		hotspotId = checkInventoryHotspotsRow(posX, 7, false);
	if (posY >= 192 && posY <= 229)
		hotspotId = checkInventoryHotspotsRow(posX, 13, false);
	if (posY >= 230 && posY <= 267)
		hotspotId = checkInventoryHotspotsRow(posX, 19, false);
	if (posY >= 268 && posY <= 306)
		hotspotId = checkInventoryHotspotsRow(posX, 25, true);
	if (posY >= 268 && posY <= 288 && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478)
		hotspotId = 30;
	if (posY >= 290 && posY <= 306 && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478)
		hotspotId = 31;
	if (posY < 114 || posY > 306 || posX < _vm->_graphicsMan->_scrollOffset + 152 || posX > _vm->_graphicsMan->_scrollOffset + 484)
		hotspotId = 32;

	return hotspotId;
}

/**
 * Check the hotspots in an inventory line
 * Returns the hotspot Id under the mouse, if any.
 */
int LinesManager::checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastRow) {
	debugC(5, kDebugPath, "checkInventoryHotspotsRow(%d, %d, %d)", posX, minZoneNum, lastRow ? 1 : 0);
	int result = minZoneNum;

	if (posX >= _vm->_graphicsMan->_scrollOffset + 158 && posX < _vm->_graphicsMan->_scrollOffset + 208)
		return result;

	if (posX >= _vm->_graphicsMan->_scrollOffset + 208 && posX < _vm->_graphicsMan->_scrollOffset + 266) {
		result += 1;
		return result;
	}

	if (posX >= _vm->_graphicsMan->_scrollOffset + 266 && posX < _vm->_graphicsMan->_scrollOffset + 320) {
		result += 2;
		return result;
	}

	if (posX >= _vm->_graphicsMan->_scrollOffset + 320 && posX < _vm->_graphicsMan->_scrollOffset + 370) {
		result += 3;
		return result;
	}

	if (posX >= _vm->_graphicsMan->_scrollOffset + 370 && posX < _vm->_graphicsMan->_scrollOffset + 424) {
		result += 4;
		return result;
	}

	if (!lastRow && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478) {
		result += 5;
		return result;
	}

	return 0;
}

/**
 * Add Zone Line
 */
void LinesManager::addZoneLine(int idx, int fromX, int fromY, int destX, int destY, int bobZoneIdx) {
	debugC(5, kDebugPath, "addZoneLine(%d, %d, %d, %d, %d, %d)", idx, fromX, fromY, destX, destY, bobZoneIdx);
	int16 *zoneData;

	if (fromX == fromY && fromY == destX && fromY == destY) {
		_bobZoneFl[bobZoneIdx] = true;
		_bobZone[bobZoneIdx] = fromY;
	} else {
		assert(idx < MAX_LINES + 1);
		_zoneLine[idx]._zoneData = (int16 *)_vm->_globals->freeMemory((byte *)_zoneLine[idx]._zoneData);

		int distX = abs(fromX - destX);
		int distY = abs(fromY - destY);
		int maxDist = 1;
		if (distX <= distY)
			maxDist += distY;
		else
			maxDist += distX;

		zoneData = (int16 *)_vm->_globals->allocMemory(2 * sizeof(int16) * maxDist + (4 * sizeof(int16)));
		assert(zoneData);

		_zoneLine[idx]._zoneData = zoneData;

		int16 *dataP = zoneData;
		int stepX = 1000 * distX / maxDist;
		int stepY = 1000 * distY / maxDist;
		if (destX < fromX)
			stepX = -stepX;
		if (destY < fromY)
			stepY = -stepY;
		int smoothPosX = 1000 * fromX;
		int smoothPosY = 1000 * fromY;
		for (int i = 0; i < maxDist; i++) {
			*dataP++ = smoothPosX / 1000;
			*dataP++ = smoothPosY / 1000;

			smoothPosX += stepX;
			smoothPosY += stepY;
		}
		*dataP++ = -1;
		*dataP++ = -1;

		_zoneLine[idx]._count = maxDist;
		_zoneLine[idx]._bobZoneIdx = bobZoneIdx;
	}
}

/**
 * Add Line
 */
void LinesManager::addLine(int lineIdx, Directions direction, int fromX, int fromY, int destX, int destY) {
	debugC(5, kDebugPath, "addLine(%d, %d, %d, %d, %d, %d)", lineIdx, direction, fromX, fromY, destX, destY);
	assert(lineIdx < MAX_LINES);

	if (_linesNumb < lineIdx)
		_linesNumb = lineIdx;

	_lineItem[lineIdx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[lineIdx]._lineData);
	int distX = abs(fromX - destX) + 1;
	int distY = abs(fromY - destY) + 1;
	int maxDist = distY;
	if (distX > maxDist)
		maxDist = distX;

	byte *zoneData = _vm->_globals->allocMemory(4 * maxDist + 8);
	assert(zoneData);

	Common::fill(zoneData, zoneData + 4 * maxDist + 8, 0);
	_lineItem[lineIdx]._lineData = (int16 *)zoneData;

	int16 *curLineData = _lineItem[lineIdx]._lineData;
	int stepX = 1000 * distX / (maxDist - 1);
	int stepY = 1000 * distY / (maxDist - 1);
	if (destX < fromX)
		stepX = -stepX;
	if (destY < fromY)
		stepY = -stepY;
	int dirX = (int)stepX / 1000; // -1: Left, 0: None, 1: Right
	int dirY = (int)stepY / 1000; // -1: Up, 0: None, 1: Right
	if (!dirX) {
		if (dirY == -1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_UP;
			_lineItem[lineIdx]._directionRouteDec = DIR_DOWN;
		} else if (dirY == 1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_DOWN;
			_lineItem[lineIdx]._directionRouteDec = DIR_UP;
		}
		// If dirY == 0, no move
	} else if (dirX == 1) {
		if (dirY == -1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_UP_RIGHT;
			_lineItem[lineIdx]._directionRouteDec = DIR_DOWN_LEFT;
		} else if (!dirY) {
			_lineItem[lineIdx]._directionRouteInc = DIR_RIGHT;
			_lineItem[lineIdx]._directionRouteDec = DIR_LEFT;
		} else if (dirY == 1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_DOWN_RIGHT;
			_lineItem[lineIdx]._directionRouteDec = DIR_UP_LEFT;
		}
	} else if (dirX == -1) {
		if (dirY == 1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_DOWN_LEFT;
			_lineItem[lineIdx]._directionRouteDec = DIR_UP_RIGHT;
		} else if (!dirY) {
			_lineItem[lineIdx]._directionRouteInc = DIR_LEFT;
			_lineItem[lineIdx]._directionRouteDec = DIR_RIGHT;
		} else if (dirY == -1) {
			_lineItem[lineIdx]._directionRouteInc = DIR_UP_LEFT;
			_lineItem[lineIdx]._directionRouteDec = DIR_DOWN_RIGHT;
		}
	}

	// Second pass to soften cases where dirY == 0
	if (dirX == 1) {
		if (stepY > 250 && stepY <= 999) {
			_lineItem[lineIdx]._directionRouteInc = DIR_DOWN_RIGHT;
			_lineItem[lineIdx]._directionRouteDec = DIR_UP_LEFT;
		} else if (stepY < -250 && stepY > -1000) {
			_lineItem[lineIdx]._directionRouteInc = DIR_UP_RIGHT;
			_lineItem[lineIdx]._directionRouteDec = DIR_DOWN_LEFT;
		}
	} else if (dirX == -1) {
		if (stepY > 250 && stepY <= 999) {
			_lineItem[lineIdx]._directionRouteInc = DIR_DOWN_LEFT;
			_lineItem[lineIdx]._directionRouteDec = DIR_UP_RIGHT;
		} else if (stepY < -250 && stepY > -1000) {
			// In the original code, the test was on positive values and
			// was impossible to meet.
			_lineItem[lineIdx]._directionRouteInc = DIR_UP_LEFT;
			_lineItem[lineIdx]._directionRouteDec = DIR_DOWN_RIGHT;
		}
	}

	stepX = 1000 * distX / maxDist;
	stepY = 1000 * distY / maxDist;
	if (destX < fromX)
		stepX = -stepX;
	if (destY < fromY)
		stepY = -stepY;
	int smoothPosX = 1000 * fromX;
	int smoothPosY = 1000 * fromY;
	for (int i = 0; i < maxDist - 1; i++) {
		curLineData[0] = smoothPosX / 1000;
		curLineData[1] = smoothPosY / 1000;
		curLineData += 2;

		smoothPosX += stepX;
		smoothPosY += stepY;
	}
	curLineData[0] = destX;
	curLineData[1] = destY;

	curLineData += 2;
	curLineData[0] = -1;
	curLineData[1] = -1;

	_lineItem[lineIdx]._lineDataEndIdx = maxDist;
	_lineItem[lineIdx]._direction = direction;

	++_linesNumb;
}

/**
 * Check collision line
 */
bool LinesManager::checkCollisionLine(int xp, int yp, int *foundDataIdx, int *foundLineIdx, int startLineIdx, int endLineIdx) {
	debugC(5, kDebugPath, "checkCollisionLine(%d, %d, foundDataIdx, foundLineIdx, %d, %d)", xp, yp, startLineIdx ,endLineIdx);
	int16 *lineData;

	int left = xp + 4;
	int right = xp - 4;
	int top = yp + 4;
	int bottom = yp - 4;

	*foundDataIdx = -1;
	*foundLineIdx = -1;

	for (int curLineIdx = startLineIdx; curLineIdx <= endLineIdx; curLineIdx++) {
		lineData = _lineItem[curLineIdx]._lineData;

		if (lineData == nullptr)
			continue;

		bool collisionFl = true;
		int lineStartX = lineData[0];
		int lineStartY = lineData[1];
		int lineDataIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx;
		int lineEndX = lineData[lineDataIdx - 2];
		int lineEndY = lineData[lineDataIdx - 1];
		if (lineStartX >= lineEndX) {
			if (right > lineStartX || left < lineEndX)
				collisionFl = false;
		} else { // lineStartX < lineEndX
			if (left < lineStartX || right > lineEndX)
				collisionFl = false;
		}
		if (lineStartY >= lineEndY) {
			if (bottom > lineStartY || top < lineEndY)
				collisionFl = false;
		} else { // lineStartY < lineEndY
			if (top < lineStartY || bottom > lineEndY)
				collisionFl = false;
		}

		if (!collisionFl)
			continue;

		for (int idx = 0; idx < _lineItem[curLineIdx]._lineDataEndIdx; idx++) {
			int lineX = lineData[0];
			int lineY = lineData[1];
			lineData += 2;

			if ((xp == lineX || xp + 1 == lineX) && (yp == lineY || yp + 1 == lineY)) {
				*foundDataIdx = idx;
				*foundLineIdx = curLineIdx;
				return true;
			}
		}
	}
	return false;
}

/**
 * Init route
 */
void LinesManager::initRoute() {
	debugC(5, kDebugPath, "initRoute()");
	int lineX = _lineItem[0]._lineData[0];
	int lineY = _lineItem[0]._lineData[1];

	int lineIdx = 1;
	for (;;) {
		int curDataIdx = _lineItem[lineIdx]._lineDataEndIdx;
		int16 *curLineData = _lineItem[lineIdx]._lineData;

		int curLineX = curLineData[2 * curDataIdx - 2];
		int curLineY = curLineData[2 * curDataIdx - 1];
		if (_vm->_graphicsMan->_maxX == curLineX || _vm->_graphicsMan->_maxY == curLineY ||
			_vm->_graphicsMan->_minX == curLineX || _vm->_graphicsMan->_minY == curLineY ||
			(lineX == curLineX && lineY == curLineY))
			break;
		if (lineIdx == MAX_LINES)
			error("ERROR - LAST LINE NOT FOUND");

		int16 *nextLineData = _lineItem[lineIdx + 1]._lineData;
		if (!nextLineData)
			break;
		if (nextLineData[0] != curLineX && nextLineData[1] != curLineY)
			break;
		++lineIdx;
	}

	_lastLine = lineIdx;
	for (int idx = 1; idx < MAX_LINES; idx++) {
		if ((_lineItem[idx]._lineDataEndIdx < _maxLineIdx) && (idx != _lastLine + 1)) {
			_lineItem[idx]._directionRouteInc = _lineItem[idx - 1]._directionRouteInc;
			_lineItem[idx]._directionRouteDec = _lineItem[idx - 1]._directionRouteDec;
		}
	}
}

// Avoid obstacle
int LinesManager::avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route) {
	debugC(5, kDebugPath, "avoidObstacle(%d, %d, %d, %d, %d, route)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx);
	int curLineIdx = lineIdx;
	int curLineDataIdx = lineDataIdx;
	int curRouteIdx = routeIdx;
	if (lineIdx < destLineIdx) {
		curRouteIdx = _lineItem[lineIdx].appendToRouteInc(lineDataIdx, -1, route, curRouteIdx);

		for (int i = lineIdx + 1; i < destLineIdx; i++)
			curRouteIdx = _lineItem[i].appendToRouteInc(0, -1, route, curRouteIdx);

		curLineDataIdx = 0;
		curLineIdx = destLineIdx;
	}
	if (curLineIdx > destLineIdx) {
		curRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curLineDataIdx, 0, route, curRouteIdx);
		for (int i = curLineIdx - 1; i > destLineIdx; i--)
			curRouteIdx = _lineItem[i].appendToRouteDec(-1, 0, route, curRouteIdx);
		curLineDataIdx = _lineItem[destLineIdx]._lineDataEndIdx - 1;
		curLineIdx = destLineIdx;
	}
	if (curLineIdx == destLineIdx) {
		if (destLineDataIdx >= curLineDataIdx) {
			curRouteIdx = _lineItem[destLineIdx].appendToRouteInc(curLineDataIdx, destLineDataIdx, route, curRouteIdx);
		} else {
			curRouteIdx = _lineItem[destLineIdx].appendToRouteDec(curLineDataIdx, destLineDataIdx, route, curRouteIdx);
		}
	}
	return curRouteIdx;
}

// Avoid Obstacle, taking into account start/End lind Idx
int LinesManager::avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route, int startLineIdx, int endLineIdx) {
	debugC(5, kDebugPath, "avoidObstacleOnSegment(%d, %d, %d, %d, %d, route, %d, %d)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx, startLineIdx, endLineIdx);
	int curLineIdx = lineIdx;
	int curLineDataIdx = lineDataIdx;
	int curRouteIdx = routeIdx;
	if (destLineIdx < lineIdx) {
		curRouteIdx = _lineItem[lineIdx].appendToRouteInc(lineDataIdx, -1, route, curRouteIdx);
		int wrkLineIdx = lineIdx + 1;
		if (wrkLineIdx == endLineIdx + 1)
			wrkLineIdx = startLineIdx;
		while (destLineIdx != wrkLineIdx) {
			curRouteIdx = _lineItem[wrkLineIdx].appendToRouteInc(0, -1, route, curRouteIdx);
			++wrkLineIdx;
			if (endLineIdx + 1 == wrkLineIdx)
				wrkLineIdx = startLineIdx;
		}
		curLineDataIdx = 0;
		curLineIdx = destLineIdx;
	}
	if (destLineIdx > curLineIdx) {
		curRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curLineDataIdx, 0, route, curRouteIdx);
		int wrkLineIdx = curLineIdx - 1;
		if (wrkLineIdx == startLineIdx - 1)
			wrkLineIdx = endLineIdx;
		while (destLineIdx != wrkLineIdx) {
			curRouteIdx = _lineItem[wrkLineIdx].appendToRouteDec(-1, 0, route, curRouteIdx);
			--wrkLineIdx;
			if (startLineIdx - 1 == wrkLineIdx)
				wrkLineIdx = endLineIdx;
		}
		curLineDataIdx = _lineItem[destLineIdx]._lineDataEndIdx - 1;
		curLineIdx = destLineIdx;
	}
	if (destLineIdx == curLineIdx) {
		if (destLineDataIdx >= curLineDataIdx) {
			curRouteIdx = _lineItem[destLineIdx].appendToRouteInc(curLineDataIdx, destLineDataIdx, route, curRouteIdx);
		} else {
			curRouteIdx = _lineItem[destLineIdx].appendToRouteDec(curLineDataIdx, destLineDataIdx, route, curRouteIdx);
		}
	}
	return curRouteIdx;
}

bool LinesManager::MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, int routeIdx) {
	debugC(5, kDebugPath, "MIRACLE(%d, %d, %d, %d, %d)", fromX, fromY, lineIdx, destLineIdx, routeIdx);
	int lineIdxLeft = 0;
	int lineDataIdxLeft = 0;
	int lineIdxRight = 0;
	int lineDataIdxRight = 0;
	int linesIdxUp = 0;
	int linesDataIdxUp = 0;
	int lineIdxDown = 0;
	int lineDataIdxDown = 0;

	int curX = fromX;
	int curY = fromY;
	int curLineIdx = lineIdx;
	int tmpRouteIdx = routeIdx;
	int dummyDataIdx;
	if (checkCollisionLine(fromX, fromY, &dummyDataIdx, &curLineIdx, 0, _linesNumb)) {
		switch (_lineItem[curLineIdx]._direction) {
		case DIR_UP:
			curY -= 2;
			break;
		case DIR_UP_RIGHT:
			curY -= 2;
			curX += 2;
			break;
		case DIR_RIGHT:
			curX += 2;
			break;
		case DIR_DOWN_RIGHT:
			curY += 2;
			curX += 2;
			break;
		case DIR_DOWN:
			curY += 2;
			break;
		case DIR_DOWN_LEFT:
			curY += 2;
			curX -= 2;
			break;
		case DIR_LEFT:
			curX -= 2;
			break;
		case DIR_UP_LEFT:
			curY -= 2;
			curX -= 2;
			break;
		default:
			break;
		}
	}

	int stepVertIncCount = 0;
	for (int i = curY; curY + 200 > i; i++) {
		if (checkCollisionLine(curX, i, &lineDataIdxDown, &lineIdxDown, 0, _lastLine) == 1 && lineIdxDown <= _lastLine)
			break;
		lineDataIdxDown = 0;
		lineIdxDown = -1;
		++stepVertIncCount;
	}

	int stepVertDecCount = 0;
	for (int i = curY; curY - 200 < i; i--) {
		if (checkCollisionLine(curX, i, &linesDataIdxUp, &linesIdxUp, 0, _lastLine) == 1 && linesIdxUp <= _lastLine)
			break;
		linesDataIdxUp = 0;
		linesIdxUp = -1;
		++stepVertDecCount;
	}

	int stepHoriIncCount = 0;
	for (int i = curX; curX + 200 > i; i++) {
		if (checkCollisionLine(i, curY, &lineDataIdxRight, &lineIdxRight, 0, _lastLine) == 1 && lineIdxRight <= _lastLine)
			break;
		lineDataIdxRight = 0;
		lineIdxRight = -1;
		++stepHoriIncCount;
	}

	int stepHoriDecCount = 0;
	for (int i = curX; curX - 200 < i; i--) {
		if (checkCollisionLine(i, curY, &lineDataIdxLeft, &lineIdxLeft, 0, _lastLine) == 1 && lineIdxLeft <= _lastLine)
			break;
		lineDataIdxLeft = 0;
		lineIdxLeft = -1;
		++stepHoriDecCount;
	}

	if (destLineIdx > curLineIdx) {
		if (linesIdxUp != -1 && linesIdxUp <= curLineIdx)
			linesIdxUp = -1;
		if (lineIdxRight != -1 && curLineIdx >= lineIdxRight)
			lineIdxRight = -1;
		if (lineIdxDown != -1 && curLineIdx >= lineIdxDown)
			lineIdxDown = -1;
		if (lineIdxLeft != -1 && curLineIdx >= lineIdxLeft)
			lineIdxLeft = -1;
		if (linesIdxUp != -1 && destLineIdx < linesIdxUp)
			linesIdxUp = -1;
		if (lineIdxRight != -1 && destLineIdx < lineIdxRight)
			lineIdxRight = -1;
		if (lineIdxDown != -1 && destLineIdx < lineIdxDown)
			lineIdxDown = -1;
		if (lineIdxLeft != -1 && destLineIdx < lineIdxLeft)
			lineIdxLeft = -1;
	} else if (destLineIdx < curLineIdx) {
		if (linesIdxUp != -1 && linesIdxUp >= curLineIdx)
			linesIdxUp = -1;
		if (lineIdxRight != -1 && curLineIdx <= lineIdxRight)
			lineIdxRight = -1;
		if (lineIdxDown != -1 && curLineIdx <= lineIdxDown)
			lineIdxDown = -1;
		if (lineIdxLeft != -1 && curLineIdx <= lineIdxLeft)
			lineIdxLeft = -1;
		if (linesIdxUp != -1 && destLineIdx > linesIdxUp)
			linesIdxUp = -1;
		if (lineIdxRight != -1 && destLineIdx > lineIdxRight)
			lineIdxRight = -1;
		if (lineIdxDown != -1 && destLineIdx > lineIdxDown)
			lineIdxDown = -1;
		if (lineIdxLeft != -1 && destLineIdx > lineIdxLeft)
			lineIdxLeft = -1;
	}
	if (linesIdxUp != -1 || lineIdxRight != -1 || lineIdxDown != -1 || lineIdxLeft != -1) {
		Directions newDir = DIR_NONE;
		if (destLineIdx > curLineIdx) {
			if (lineIdxDown <= linesIdxUp && lineIdxRight <= linesIdxUp && lineIdxLeft <= linesIdxUp && linesIdxUp > curLineIdx)
				newDir = DIR_UP;
			if (lineIdxDown <= lineIdxRight && linesIdxUp <= lineIdxRight && lineIdxLeft <= lineIdxRight && curLineIdx < lineIdxRight)
				newDir = DIR_RIGHT;
			if (linesIdxUp <= lineIdxDown && lineIdxRight <= lineIdxDown && lineIdxLeft <= lineIdxDown && curLineIdx < lineIdxDown)
				newDir = DIR_DOWN;
			if (lineIdxDown <= lineIdxLeft && lineIdxRight <= lineIdxLeft && linesIdxUp <= lineIdxLeft && curLineIdx < lineIdxLeft)
				newDir = DIR_LEFT;
		} else if (destLineIdx < curLineIdx) {
			if (linesIdxUp == -1)
				linesIdxUp = INVALID_LINE_VALUE;
			if (lineIdxRight == -1)
				lineIdxRight = INVALID_LINE_VALUE;
			if (lineIdxDown == -1)
				lineIdxDown = INVALID_LINE_VALUE;
			if (lineIdxLeft == -1)
				lineIdxLeft = INVALID_LINE_VALUE;
			if (linesIdxUp != INVALID_LINE_VALUE && lineIdxDown >= linesIdxUp && lineIdxRight >= linesIdxUp && lineIdxLeft >= linesIdxUp && linesIdxUp < curLineIdx)
				newDir = DIR_UP;
			if (lineIdxRight != INVALID_LINE_VALUE && lineIdxDown >= lineIdxRight && linesIdxUp >= lineIdxRight && lineIdxLeft >= lineIdxRight && curLineIdx > lineIdxRight)
				newDir = DIR_RIGHT;
			if (lineIdxDown != INVALID_LINE_VALUE && linesIdxUp >= lineIdxDown && lineIdxRight >= lineIdxDown && lineIdxLeft >= lineIdxDown && curLineIdx > lineIdxDown)
				newDir = DIR_DOWN;
			if (lineIdxLeft != INVALID_LINE_VALUE && lineIdxDown >= lineIdxLeft && lineIdxRight >= lineIdxLeft && linesIdxUp >= lineIdxLeft && curLineIdx > lineIdxLeft)
				newDir = DIR_LEFT;
		}

		int newLinesDataIdx = 0;
		int newLinesIdx = 0;
		switch(newDir) {
		case DIR_UP:
			newLinesIdx = linesIdxUp;
			newLinesDataIdx = linesDataIdxUp;
			for (int i = 0; i < stepVertDecCount; i++) {
				if (checkCollisionLine(curX, curY - i, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) {
					int tmpRouteIdxUp = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX, curY - i, curX, curY - stepVertDecCount, tmpRouteIdx, &_bestRoute[0]);
					if (tmpRouteIdxUp == -1)
						return false;
					tmpRouteIdx = tmpRouteIdxUp;
					if (_newPosY != -1)
						i = _newPosY - curY;
				}
				_bestRoute[tmpRouteIdx].set(curX, curY - i, DIR_UP);
				tmpRouteIdx++;
			}
			_newLineIdx = newLinesIdx;
			_newLineDataIdx = newLinesDataIdx;
			_newRouteIdx = tmpRouteIdx;
			return true;
			break;
		case DIR_RIGHT:
			newLinesIdx = lineIdxRight;
			newLinesDataIdx = lineDataIdxRight;
			for (int i = 0; i < stepHoriIncCount; i++) {
				if (checkCollisionLine(i + curX, curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) {
					int tmpRouteIdxRight = computeRouteIdx(linesIdxUp, linesDataIdxUp, i + curX, curY, stepHoriIncCount + curX, curY, tmpRouteIdx, &_bestRoute[0]);
					if (tmpRouteIdxRight == -1)
						return false;
					tmpRouteIdx = tmpRouteIdxRight;
					if (_newPosX != -1)
						i = _newPosX - curX;
				}
				_bestRoute[tmpRouteIdx].set(i + curX, curY, DIR_RIGHT);
				tmpRouteIdx++;
			}
			_newLineIdx = newLinesIdx;
			_newLineDataIdx = newLinesDataIdx;
			_newRouteIdx = tmpRouteIdx;
			return true;
			break;
		case DIR_DOWN:
			newLinesIdx = lineIdxDown;
			newLinesDataIdx = lineDataIdxDown;
			for (int i = 0; i < stepVertIncCount; i++) {
				if (checkCollisionLine(curX, i + curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) {
					int tmpRouteIdxDown = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX, i + curY, curX, stepVertIncCount + curY, tmpRouteIdx, &_bestRoute[0]);
					if (tmpRouteIdxDown == -1)
						return false;
					tmpRouteIdx = tmpRouteIdxDown;
					if (_newPosY != -1)
						i = curY - _newPosY;
				}
				_bestRoute[tmpRouteIdx].set(curX, i + curY, DIR_DOWN);
				tmpRouteIdx++;
			}
			_newLineIdx = newLinesIdx;
			_newLineDataIdx = newLinesDataIdx;
			_newRouteIdx = tmpRouteIdx;
			return true;
			break;
		case DIR_LEFT:
			newLinesIdx = lineIdxLeft;
			newLinesDataIdx = lineDataIdxLeft;
			for (int i = 0; i < stepHoriDecCount; i++) {
				if (checkCollisionLine(curX - i, curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) {
					int tmpRouteIdxLeft = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX - i, curY, curX - stepHoriDecCount, curY, tmpRouteIdx, &_bestRoute[0]);
					if (tmpRouteIdxLeft == -1)
						return false;
					tmpRouteIdx = tmpRouteIdxLeft;
					if (_newPosX != -1)
						i = curX - _newPosX;
				}
				_bestRoute[tmpRouteIdx].set(curX - i, curY, DIR_LEFT);
				tmpRouteIdx++;
			}
			_newLineIdx = newLinesIdx;
			_newLineDataIdx = newLinesDataIdx;
			_newRouteIdx = tmpRouteIdx;
			return true;
			break;
		default:
			break;
		}
	}
	return false;
}

int LinesManager::computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY, int destX, int destY, int routerIdx, RouteItem *route) {
	debugC(5, kDebugPath, "computeRouteIdx(%d, %d, %d, %d, %d, %d, %d)", lineIdx, dataIdx, fromX, fromY, destX, destY, routerIdx);
	int result = routerIdx;
	++_pathFindingMaxDepth;
	if (_pathFindingMaxDepth > 10) {
		warning("PathFinding - Max depth reached");
		route[routerIdx].invalidate();
		return -1;
	}
	int lineX = _lineItem[lineIdx]._lineData[0];
	int lineY = _lineItem[lineIdx]._lineData[1];
	int startLineIdx = lineIdx;

	int curLineDataEndIdx;
	bool loopCond = false;
	for (;;) {
		int curLineIdx = startLineIdx - 1;
		int endLineIdx = 2 * _lineItem[startLineIdx - 1]._lineDataEndIdx;

		int16 *lineData = _lineItem[startLineIdx - 1]._lineData;
		if (lineData == nullptr)
			break;
		while (lineData[endLineIdx - 2] != lineX || lineY != lineData[endLineIdx - 1]) {
			--curLineIdx;
			if (_lastLine - 1 != curLineIdx) {
				endLineIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx;
				lineData = _lineItem[curLineIdx]._lineData;
				if (lineData)
					continue;
			}
			loopCond = true;
			break;
		}
		if (loopCond)
			break;

		startLineIdx = curLineIdx;
		lineX = lineData[0];
		lineY = lineData[1];
	}

	int lastIdx = _lineItem[lineIdx]._lineDataEndIdx - 1;
	int lastPosX = _lineItem[lineIdx]._lineData[(2 * lastIdx)];
	int lastPosY = _lineItem[lineIdx]._lineData[(2 * lastIdx) + 1];
	int endLineIdx = lineIdx;
	int foundLineIdx, foundDataIdx;
	loopCond = false;
	for (;;) {
		int curLineIdx = endLineIdx + 1;
		int nextLineDataEndIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx;
		int16 *lineData = _lineItem[curLineIdx]._lineData;
		if (lineData == nullptr)
			break;
		for (;;) {
			curLineDataEndIdx = nextLineDataEndIdx;
			if (lineData[0] == lastPosX && lastPosY == lineData[1])
				break;

			++curLineIdx;
			if (curLineIdx != _linesNumb + 1) {
				nextLineDataEndIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx;
				lineData = _lineItem[curLineIdx]._lineData;
				if (lineData)
					continue;
			}
			loopCond = true;
			break;
		}
		if (loopCond)
			break;

		endLineIdx = curLineIdx;
		lastPosX = lineData[curLineDataEndIdx - 2];
		lastPosY = lineData[curLineDataEndIdx - 1];
	}

	int distX = abs(fromX - destX) + 1;
	int distY = abs(fromY - destY) + 1;
	int maxDist = distY;
	if (distX > distY)
		maxDist = distX;
	int stepX = 1000 * distX / maxDist;
	int stepY = 1000 * distY / maxDist;
	int smoothPosX = 1000 * fromX;
	int smoothPosY = 1000 * fromY;
	if (destX < fromX)
		stepX = -stepX;
	if (destY < fromY)
		stepY = -stepY;
	if (maxDist > 800)
		maxDist = 800;

	Common::fill(&_lineBuf[0], &_lineBuf[1000], 0);
	int bugLigIdx = 0;
	for (int i = 0; i < maxDist + 1; i++) {
		_lineBuf[bugLigIdx] = smoothPosX / 1000;
		_lineBuf[bugLigIdx + 1] = smoothPosY / 1000;
		smoothPosX += stepX;
		smoothPosY += stepY;
		bugLigIdx += 2;
	}
	bugLigIdx -= 2;
	int destDataIdx = 0;
	int destLineIdx = -1;
	int bufX = 0;
	int bufY = 0;
	for (int i = maxDist + 1; i > 0; i--) {
		if (checkCollisionLine(_lineBuf[bugLigIdx], _lineBuf[bugLigIdx + 1], &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx) && _lastLine < foundLineIdx) {
			destLineIdx = foundLineIdx;
			destDataIdx = foundDataIdx;
			bufX = _lineBuf[bugLigIdx];
			bufY = _lineBuf[bugLigIdx + 1];
			break;
		}
		bugLigIdx -= 2;
	}
	int maxLineX = 0;
	int minLineX = 0;
	int maxLineY = 0;
	int minLineY = 0;
	for (int i = startLineIdx; i <= endLineIdx; ++i) {
		int16 *lineData = _lineItem[i]._lineData;
		if (lineData == nullptr)
			error("error in genial routine");
		if (i == startLineIdx) {
			minLineY = MIN(lineData[1], lineData[2 * _lineItem[i]._lineDataEndIdx - 1]);
			maxLineY = MAX(lineData[1], lineData[2 * _lineItem[i]._lineDataEndIdx - 1]);

			minLineX = MIN(lineData[0], lineData[2 * _lineItem[i]._lineDataEndIdx - 2]);
			maxLineX = MAX(lineData[0], lineData[2 * _lineItem[i]._lineDataEndIdx - 2]);
		} else {
			if (lineData[1] < lineData[2 * _lineItem[i]._lineDataEndIdx - 1] && lineData[1] < minLineY)
				minLineY = lineData[1];
			if (lineData[2 * _lineItem[i]._lineDataEndIdx - 1] < lineData[1] && lineData[2 * _lineItem[i]._lineDataEndIdx - 1] < minLineY)
				minLineY = lineData[2 * _lineItem[i]._lineDataEndIdx - 1];
			if (lineData[1] > lineData[2 * _lineItem[i]._lineDataEndIdx - 1] && lineData[1] > maxLineY)
				maxLineY = lineData[1];
			if (lineData[2 * _lineItem[i]._lineDataEndIdx - 1] > lineData[1] && lineData[2 * _lineItem[i]._lineDataEndIdx - 1] > maxLineY)
				maxLineY = lineData[2 * _lineItem[i]._lineDataEndIdx - 1];
			if (lineData[0] < lineData[2 * _lineItem[i]._lineDataEndIdx - 2] && minLineX > lineData[0])
				minLineX = lineData[0];
			if (lineData[2 * _lineItem[i]._lineDataEndIdx - 2] < lineData[0] && minLineX > lineData[2 * _lineItem[i]._lineDataEndIdx - 2])
				minLineX = lineData[2 * _lineItem[i]._lineDataEndIdx - 2];
			if (lineData[0] > lineData[2 * _lineItem[i]._lineDataEndIdx - 2] && maxLineX < lineData[0])
				maxLineX = lineData[0];
			if (lineData[2 * _lineItem[i]._lineDataEndIdx - 2] > lineData[0] && maxLineX < lineData[2 * _lineItem[i]._lineDataEndIdx - 2])
				maxLineX = lineData[2 * _lineItem[i]._lineDataEndIdx - 2];
		}
	}

	minLineX -= 2;
	minLineY -= 2;
	maxLineX += 2;
	maxLineY += 2;
	if (destX >= minLineX && destX <= maxLineX && destY >= minLineY && destY <= maxLineY) {
		int curY = destY;
		int linesIdxUp = -1;
		do {
			--curY;
			if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
				linesIdxUp = foundLineIdx;
				break;
			}
		} while (curY && curY >= minLineY);

		curY = destY;
		int lineIdxDown = -1;
		do {
			++curY;
			if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
				lineIdxDown = foundLineIdx;
				break;
			}
		} while (curY < _vm->_globals->_characterMaxPosY && curY < maxLineY);

		int curX = destX;
		int lineIdxRight = -1;
		do {
			++curX;
			if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
				lineIdxRight = foundLineIdx;
				break;
			}
		} while (curX < _vm->_graphicsMan->_maxX && curX < maxLineX);

		curX = destX;
		int lineIdxLeft = -1;
		do {
			--curX;
			if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) {
				lineIdxLeft = foundLineIdx;
				break;
			}
		} while (curX > 0 && curX > minLineX);

		if (lineIdxRight != -1 && lineIdxLeft != -1 && linesIdxUp != -1 && lineIdxDown != -1) {
			route[routerIdx].invalidate();
			return -1;
		}
	}
	if (bufX < fromX - 1 || bufX > fromX + 1 || bufY < fromY - 1 || bufY > fromY + 1) {
		_newPosX = bufX;
		_newPosY = bufY;
		if (lineIdx < destLineIdx) {
			int stepCount = 0;
			int curLineIdx = lineIdx;
			do {
				if (curLineIdx == startLineIdx - 1)
					curLineIdx = endLineIdx;
				++stepCount;
				--curLineIdx;
				if (curLineIdx == startLineIdx - 1)
					curLineIdx = endLineIdx;
			} while (destLineIdx != curLineIdx);
			if (abs(destLineIdx - lineIdx) == stepCount) {
				if (dataIdx >  abs(_lineItem[lineIdx]._lineDataEndIdx / 2)) {
					result = avoidObstacle(lineIdx, dataIdx, routerIdx, destLineIdx, destDataIdx, route);
				} else {
					result = avoidObstacleOnSegment(lineIdx, dataIdx, routerIdx, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx);
				}
			}
			if (abs(destLineIdx - lineIdx) < stepCount)
				result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route);
			if (stepCount < abs(destLineIdx - lineIdx))
				result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx);
		}
		if (lineIdx > destLineIdx) {
			int destStepCount = abs(lineIdx - destLineIdx);
			int curLineIdx = lineIdx;
			int curStepCount = 0;
			do {
				if (curLineIdx == endLineIdx + 1)
					curLineIdx = startLineIdx;
				++curStepCount;
				++curLineIdx;
				if (curLineIdx == endLineIdx + 1)
					curLineIdx = startLineIdx;
			} while (destLineIdx != curLineIdx);
			if (destStepCount == curStepCount) {
				if (dataIdx > abs(_lineItem[lineIdx]._lineDataEndIdx / 2)) {
					result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx);
				} else {
					result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route);
				}
			}
			if (destStepCount < curStepCount)
				result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route);
			if (curStepCount < destStepCount)
				result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx);
		}
		if (lineIdx == destLineIdx)
			result = avoidObstacle(lineIdx, dataIdx, result, lineIdx, destDataIdx, route);
		for(;;) {
			if (!checkCollisionLine(_newPosX, _newPosY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb))
				break;

			switch (_lineItem[foundLineIdx]._direction) {
			case DIR_UP:
				--_newPosY;
				break;
			case DIR_UP_RIGHT:
				--_newPosY;
				++_newPosX;
				break;
			case DIR_RIGHT:
				++_newPosX;
				break;
			case DIR_DOWN_RIGHT:
				++_newPosY;
				++_newPosX;
				break;
			case DIR_DOWN:
				++_newPosY;
				break;
			case DIR_DOWN_LEFT:
				++_newPosY;
				--_newPosX;
				break;
			case DIR_LEFT:
				--_newPosX;
				break;
			case DIR_UP_LEFT:
				--_newPosY;
				--_newPosX;
				break;
			default:
				break;
			}
		}
	} else {
		_newPosX = -1;
		_newPosY = -1;
	}
	return result;
}

// Find Route from a point to the other
RouteItem *LinesManager::findRoute(int fromX, int fromY, int destX, int destY) {
	debugC(5, kDebugPath, "findRoute(%d, %d, %d, %d)", fromX, fromY, destX, destY);
	int foundLineIdx;
	int foundDataIdx;
	int curLineY = 0;
	int curLineX = 0;
	int stepArr[9];
	int deltaArr[9];
	int collLineDataIdxArr[9];
	int collLineIdxArr[9];

	int clipDestX = destX;
	int clipDestY = destY;
	int curLineIdx = 0;
	int curLineDataIdx = 0;
	int lineIdx = 0;
	int lineDataIdx = 0;
	Directions newDir = DIR_NONE;
	if (destY <= 24)
		clipDestY = 25;
	if (!_vm->_globals->_checkDistanceFl) {
		if (abs(fromX - _oldRouteFromX) <= 4 && abs(fromY - _oldRouteFromY) <= 4 &&
		    abs(_oldRouteDestX - destX) <= 4 && abs(_oldRouteDestY - clipDestY) <= 4)
			return nullptr;

		if (abs(fromX - destX) <= 4 && abs(fromY - clipDestY) <= 4)
			return nullptr;

		if (_oldZoneNum > 0 && _vm->_objectsMan->_zoneNum > 0 && _oldZoneNum == _vm->_objectsMan->_zoneNum)
			return nullptr;
	}
	_vm->_globals->_checkDistanceFl = false;
	_oldZoneNum = _vm->_objectsMan->_zoneNum;
	_oldRouteFromX = fromX;
	_oldRouteDestX = destX;
	_oldRouteFromY = fromY;
	_oldRouteDestY = clipDestY;
	_pathFindingMaxDepth = 0;
	int routeIdx = 0;
	if (destX <= 19)
		clipDestX = 20;
	if (clipDestY <= 19)
		clipDestY = 20;
	if (clipDestX > _vm->_graphicsMan->_maxX - 10)
		clipDestX = _vm->_graphicsMan->_maxX - 10;
	if (clipDestY > _vm->_globals->_characterMaxPosY)
		clipDestY = _vm->_globals->_characterMaxPosY;

	if (abs(fromX - clipDestX) <= 3 && abs(fromY - clipDestY) <= 3)
		return nullptr;

	for (int i = 0; i <= 8; ++i) {
		collLineIdxArr[i] = -1;
		collLineDataIdxArr[i] = 0;
		deltaArr[i] = INVALID_LINE_VALUE;
		stepArr[i] = INVALID_LINE_VALUE;
	}

	if (characterRoute(fromX, fromY, clipDestX, clipDestY, -1, -1, 0) == 1)
		return _bestRoute;

	int tmpDelta = 0;
	for (int tmpY = clipDestY; tmpY < _vm->_graphicsMan->_maxY; tmpY++, tmpDelta++) {
		if (checkCollisionLine(clipDestX, tmpY, &collLineDataIdxArr[DIR_DOWN], &collLineIdxArr[DIR_DOWN], 0, _lastLine) && collLineIdxArr[DIR_DOWN] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_DOWN] = 0;
		collLineIdxArr[DIR_DOWN] = -1;
	}
	deltaArr[DIR_DOWN] = tmpDelta;

	tmpDelta = 0;
	for (int tmpY = clipDestY; tmpY > _vm->_graphicsMan->_minY; tmpY--, tmpDelta++) {
		if (checkCollisionLine(clipDestX, tmpY, &collLineDataIdxArr[DIR_UP], &collLineIdxArr[DIR_UP], 0, _lastLine) && collLineIdxArr[DIR_UP] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_UP] = 0;
		collLineIdxArr[DIR_UP] = -1;
		if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1)
			break;
	}
	deltaArr[DIR_UP] = tmpDelta;

	tmpDelta = 0;
	for (int tmpX = clipDestX; tmpX < _vm->_graphicsMan->_maxX; tmpX++) {
		if (checkCollisionLine(tmpX, clipDestY, &collLineDataIdxArr[DIR_RIGHT], &collLineIdxArr[DIR_RIGHT], 0, _lastLine) && collLineIdxArr[DIR_RIGHT] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_RIGHT] = 0;
		collLineIdxArr[DIR_RIGHT] = -1;
		++tmpDelta;
		if (deltaArr[DIR_UP] < tmpDelta && collLineIdxArr[DIR_UP] != -1)
				break;
		if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1)
			break;
	}
	deltaArr[DIR_RIGHT] = tmpDelta;

	tmpDelta = 0;
	for (int tmpX = clipDestX; tmpX > _vm->_graphicsMan->_minX; tmpX--) {
		if (checkCollisionLine(tmpX, clipDestY, &collLineDataIdxArr[DIR_LEFT], &collLineIdxArr[DIR_LEFT], 0, _lastLine) && collLineIdxArr[DIR_LEFT] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_LEFT] = 0;
		collLineIdxArr[DIR_LEFT] = -1;
		++tmpDelta;
		if (deltaArr[DIR_UP] < tmpDelta && collLineIdxArr[DIR_UP] != -1)
			break;
		if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1)
			break;
		if (deltaArr[DIR_RIGHT] < tmpDelta && collLineIdxArr[DIR_RIGHT] != -1)
			break;
	}
	deltaArr[DIR_LEFT] = tmpDelta;

	if (collLineIdxArr[DIR_UP] < 0 || _lastLine < collLineIdxArr[DIR_UP])
		collLineIdxArr[DIR_UP] = -1;
	if (collLineIdxArr[DIR_RIGHT] < 0 || _lastLine < collLineIdxArr[DIR_RIGHT])
		collLineIdxArr[DIR_RIGHT] = -1;
	if (collLineIdxArr[DIR_DOWN] < 0 || _lastLine < collLineIdxArr[DIR_DOWN])
		collLineIdxArr[DIR_DOWN] = -1;
	if (collLineIdxArr[DIR_LEFT] < 0 || _lastLine < collLineIdxArr[DIR_LEFT])
		collLineIdxArr[DIR_LEFT] = -1;
	if (collLineIdxArr[DIR_UP] < 0)
		deltaArr[DIR_UP] = INVALID_LINE_VALUE;
	if (collLineIdxArr[DIR_RIGHT] < 0)
		deltaArr[DIR_RIGHT] = INVALID_LINE_VALUE;
	if (collLineIdxArr[DIR_DOWN] < 0)
		deltaArr[DIR_DOWN] = INVALID_LINE_VALUE;
	if (collLineIdxArr[DIR_LEFT] < 0)
		deltaArr[DIR_LEFT] = INVALID_LINE_VALUE;
	if (collLineIdxArr[DIR_UP] == -1 && collLineIdxArr[DIR_RIGHT] == -1 && collLineIdxArr[DIR_DOWN] == -1 && collLineIdxArr[DIR_LEFT] == -1)
		return nullptr;

	if (collLineIdxArr[DIR_DOWN] != -1 && deltaArr[DIR_UP] >= deltaArr[DIR_DOWN] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_DOWN] && deltaArr[DIR_LEFT] >= deltaArr[DIR_DOWN]) {
		curLineIdx = collLineIdxArr[DIR_DOWN];
		curLineDataIdx = collLineDataIdxArr[DIR_DOWN];
	} else if (collLineIdxArr[DIR_UP] != -1 && deltaArr[DIR_DOWN] >= deltaArr[DIR_UP] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_UP] && deltaArr[DIR_LEFT] >= deltaArr[DIR_UP]) {
		curLineIdx = collLineIdxArr[DIR_UP];
		curLineDataIdx = collLineDataIdxArr[DIR_UP];
	} else if (collLineIdxArr[DIR_RIGHT] != -1 && deltaArr[DIR_UP] >= deltaArr[DIR_RIGHT] && deltaArr[DIR_DOWN] >= deltaArr[DIR_RIGHT] && deltaArr[DIR_LEFT] >= deltaArr[DIR_RIGHT]) {
		curLineIdx = collLineIdxArr[DIR_RIGHT];
		curLineDataIdx = collLineDataIdxArr[DIR_RIGHT];
	} else if (collLineIdxArr[DIR_LEFT] != -1 && deltaArr[DIR_DOWN] >= deltaArr[DIR_LEFT] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_LEFT] && deltaArr[DIR_UP] >= deltaArr[DIR_LEFT]) {
		curLineIdx = collLineIdxArr[DIR_LEFT];
		curLineDataIdx = collLineDataIdxArr[DIR_LEFT];
	}

	for (int i = 0; i <= 8; ++i) {
		collLineIdxArr[i] = -1;
		collLineDataIdxArr[i] = 0;
		deltaArr[i] = INVALID_LINE_VALUE;
		stepArr[i] = INVALID_LINE_VALUE;
	}

	tmpDelta = 0;
	for (int tmpY = fromY; tmpY < _vm->_graphicsMan->_maxY; tmpY++, tmpDelta++) {
		if (checkCollisionLine(fromX, tmpY, &collLineDataIdxArr[DIR_DOWN], &collLineIdxArr[DIR_DOWN], 0, _lastLine) && collLineIdxArr[DIR_DOWN] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_DOWN] = 0;
		collLineIdxArr[DIR_DOWN] = -1;
	}
	deltaArr[DIR_DOWN] = tmpDelta + 1;

	tmpDelta = 0;
	for (int tmpY = fromY; tmpY > _vm->_graphicsMan->_minY; tmpY--) {
		if (checkCollisionLine(fromX, tmpY, &collLineDataIdxArr[DIR_UP], &collLineIdxArr[DIR_UP], 0, _lastLine) && collLineIdxArr[DIR_UP] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_UP] = 0;
		collLineIdxArr[DIR_UP] = -1;
		++tmpDelta;
		if (collLineIdxArr[DIR_DOWN] != -1 && tmpDelta > 80)
			break;
	}
	deltaArr[DIR_UP] = tmpDelta + 1;

	tmpDelta = 0;
	for (int tmpX = fromX; tmpX < _vm->_graphicsMan->_maxX; tmpX++) {
		if (checkCollisionLine(tmpX, fromY, &collLineDataIdxArr[DIR_RIGHT], &collLineIdxArr[DIR_RIGHT], 0, _lastLine) && collLineIdxArr[DIR_RIGHT] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_RIGHT] = 0;
		collLineIdxArr[DIR_RIGHT] = -1;
		++tmpDelta;
		if ((collLineIdxArr[DIR_DOWN] != -1 || collLineIdxArr[DIR_UP] != -1) && (tmpDelta > 100))
			break;
	}
	deltaArr[DIR_RIGHT] = tmpDelta + 1;

	tmpDelta = 0;
	for (int tmpX = fromX; tmpX > _vm->_graphicsMan->_minX; tmpX--) {
		if (checkCollisionLine(tmpX, fromY, &collLineDataIdxArr[DIR_LEFT], &collLineIdxArr[DIR_LEFT], 0, _lastLine) && collLineIdxArr[DIR_LEFT] <= _lastLine)
			break;
		collLineDataIdxArr[DIR_LEFT] = 0;
		collLineIdxArr[DIR_LEFT] = -1;
		++tmpDelta;
		if ((collLineIdxArr[DIR_DOWN] != -1 || collLineIdxArr[DIR_UP] != -1 || collLineIdxArr[DIR_RIGHT] != -1) && (tmpDelta > 100))
			break;
	}
	deltaArr[DIR_LEFT] = tmpDelta + 1;

	if (collLineIdxArr[DIR_UP] != -1)
		stepArr[DIR_UP] = abs(collLineIdxArr[DIR_UP] - curLineIdx);

	if (collLineIdxArr[DIR_RIGHT] != -1)
		stepArr[DIR_RIGHT] = abs(collLineIdxArr[DIR_RIGHT] - curLineIdx);

	if (collLineIdxArr[DIR_DOWN] != -1)
		stepArr[DIR_DOWN] = abs(collLineIdxArr[DIR_DOWN] - curLineIdx);

	if (collLineIdxArr[DIR_LEFT] != -1)
		stepArr[DIR_LEFT] = abs(collLineIdxArr[DIR_LEFT] - curLineIdx);

	if (collLineIdxArr[DIR_UP] == -1 && collLineIdxArr[DIR_RIGHT] == -1 && collLineIdxArr[DIR_DOWN] == -1 && collLineIdxArr[DIR_LEFT] == -1)
		error("Nearest point not found");

	int delta = 0;
	if (collLineIdxArr[DIR_UP] != -1 && stepArr[DIR_RIGHT] >= stepArr[DIR_UP] && stepArr[DIR_DOWN] >= stepArr[DIR_UP] && stepArr[DIR_LEFT] >= stepArr[DIR_UP]) {
		lineIdx = collLineIdxArr[DIR_UP];
		delta = deltaArr[DIR_UP];
		newDir = DIR_UP;
		lineDataIdx = collLineDataIdxArr[DIR_UP];
	} else if (collLineIdxArr[DIR_DOWN] != -1 && stepArr[DIR_RIGHT] >= stepArr[DIR_DOWN] && stepArr[DIR_UP] >= stepArr[DIR_DOWN] && stepArr[DIR_LEFT] >= stepArr[DIR_DOWN]) {
		lineIdx = collLineIdxArr[DIR_DOWN];
		delta = deltaArr[DIR_DOWN];
		newDir = DIR_DOWN;
		lineDataIdx = collLineDataIdxArr[DIR_DOWN];
	} else if (collLineIdxArr[DIR_RIGHT] != -1 && stepArr[DIR_UP] >= stepArr[DIR_RIGHT] && stepArr[DIR_DOWN] >= stepArr[DIR_RIGHT] && stepArr[DIR_LEFT] >= stepArr[DIR_RIGHT]) {
		lineIdx = collLineIdxArr[DIR_RIGHT];
		delta = deltaArr[DIR_RIGHT];
		newDir = DIR_RIGHT;
		lineDataIdx = collLineDataIdxArr[DIR_RIGHT];
	} else if (collLineIdxArr[DIR_LEFT] != -1 && stepArr[DIR_UP] >= stepArr[DIR_LEFT] && stepArr[DIR_DOWN] >= stepArr[DIR_LEFT] && stepArr[DIR_RIGHT] >= stepArr[DIR_LEFT]) {
		lineIdx = collLineIdxArr[DIR_LEFT];
		delta = deltaArr[DIR_LEFT];
		newDir = DIR_LEFT;
		lineDataIdx = collLineDataIdxArr[DIR_LEFT];
	}

	int bestRouteNum = characterRoute(fromX, fromY, clipDestX, clipDestY, lineIdx, curLineIdx, 0);

	if (bestRouteNum == 1)
		return _bestRoute;

	if (bestRouteNum == 2) {
		lineIdx = _newLineIdx;
		lineDataIdx = _newLineDataIdx;
		routeIdx = _newRouteIdx;
	} else {
		switch (newDir) {
		case DIR_UP:
			for (int deltaY = 0; deltaY < delta; deltaY++) {
				if (checkCollisionLine(fromX, fromY - deltaY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) {
					int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX, fromY - deltaY, fromX, fromY - delta, routeIdx, _bestRoute);
					if (tmpRouteIdx == -1) {
						_bestRoute[routeIdx].invalidate();
						return &_bestRoute[0];
					}
					routeIdx = tmpRouteIdx;
					if (_newPosY != -1)
						deltaY = fromY - _newPosY;
				}
				_bestRoute[routeIdx].set(fromX, fromY - deltaY, DIR_UP);
				routeIdx++;
			}
			break;
		case DIR_DOWN:
			for (int deltaY = 0; deltaY < delta; deltaY++) {
				if (checkCollisionLine(fromX, deltaY + fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb)
				        && _lastLine < foundLineIdx) {
					int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX, deltaY + fromY, fromX, delta + fromY, routeIdx, &_bestRoute[0]);
					if (tmpRouteIdx == -1) {
						_bestRoute[routeIdx].invalidate();
						return &_bestRoute[0];
					}
					routeIdx = tmpRouteIdx;
					if (_newPosY != -1)
						deltaY = _newPosY - fromY;
				}
				_bestRoute[routeIdx].set(fromX, fromY + deltaY, DIR_DOWN);
				routeIdx++;
			}
			break;
		case DIR_LEFT:
			for (int deltaX = 0; deltaX < delta; deltaX++) {
				if (checkCollisionLine(fromX - deltaX, fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) {
					int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX - deltaX, fromY, fromX - delta, fromY, routeIdx, &_bestRoute[0]);
					if (tmpRouteIdx == -1) {
						_bestRoute[routeIdx].invalidate();
						return &_bestRoute[0];
					}
					routeIdx = tmpRouteIdx;
					if (_newPosX != -1)
						deltaX = fromX - _newPosX;
				}
				_bestRoute[routeIdx].set(fromX - deltaX, fromY, DIR_LEFT);
				routeIdx++;
			}
			break;
		case DIR_RIGHT:
			for (int deltaX = 0; deltaX < delta; deltaX++) {
				if (checkCollisionLine(deltaX + fromX, fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) {
					int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, deltaX + fromX, fromY, delta + fromX, fromY, routeIdx, &_bestRoute[0]);
					if (tmpRouteIdx == -1) {
						_bestRoute[routeIdx].invalidate();
						return &_bestRoute[0];
					}
					routeIdx = tmpRouteIdx;
					if (_newPosX != -1)
						deltaX = _newPosX - fromX;
				}
				_bestRoute[routeIdx].set(fromX + deltaX, fromY, DIR_RIGHT);
				routeIdx++;
			}
			break;
		default:
			break;
		}
	}

	bool loopCond;
	do {
		loopCond = false;
		if (lineIdx < curLineIdx) {
			for (int i = lineDataIdx; _lineItem[lineIdx]._lineDataEndIdx > i; ++i) {
				curLineX = _lineItem[lineIdx]._lineData[2 * i];
				curLineY = _lineItem[lineIdx]._lineData[2 * i + 1];
				_bestRoute[routeIdx].set(_lineItem[lineIdx]._lineData[2 * i], _lineItem[lineIdx]._lineData[2 * i + 1], _lineItem[lineIdx]._directionRouteInc);
				routeIdx++;
			}
			for (int idx = lineIdx + 1; idx < curLineIdx; idx++) {
				for (int dataIdx = 0; _lineItem[idx]._lineDataEndIdx > dataIdx; dataIdx++) {
					curLineX = _lineItem[idx]._lineData[2 * dataIdx];
					curLineY = _lineItem[idx]._lineData[2 * dataIdx + 1];
					_bestRoute[routeIdx].set(_lineItem[idx]._lineData[2 * dataIdx], _lineItem[idx]._lineData[2 * dataIdx + 1], _lineItem[idx]._directionRouteInc);
					routeIdx++;
					if (_lineItem[idx]._lineDataEndIdx > 30 && dataIdx == _lineItem[idx]._lineDataEndIdx / 2) {
						bestRouteNum = characterRoute(_lineItem[idx]._lineData[2 * dataIdx], _lineItem[idx]._lineData[2 * dataIdx + 1], clipDestX, clipDestY, idx, curLineIdx, routeIdx);
						if (bestRouteNum == 1)
							return &_bestRoute[0];
						if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, idx, curLineIdx, routeIdx)) {
							lineIdx = _newLineIdx;
							lineDataIdx = _newLineDataIdx;
							routeIdx = _newRouteIdx;
							loopCond = true;
							break;
						}
					}
				}

				if (loopCond)
					break;

				bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, idx, curLineIdx, routeIdx);
				if (bestRouteNum == 1)
					return &_bestRoute[0];
				if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, idx, curLineIdx, routeIdx)) {
					lineIdx = _newLineIdx;
					lineDataIdx = _newLineDataIdx;
					routeIdx = _newRouteIdx;
					loopCond = true;
					break;
				}
			}
			if (loopCond)
				continue;

			lineDataIdx = 0;
			lineIdx = curLineIdx;
		}
		if (lineIdx > curLineIdx) {
			for (int dataIdx = lineDataIdx; dataIdx > 0; dataIdx--) {
				curLineX = _lineItem[lineIdx]._lineData[2 * dataIdx];
				curLineY = _lineItem[lineIdx]._lineData[2 * dataIdx + 1];

				_bestRoute[routeIdx].set(_lineItem[lineIdx]._lineData[2 * dataIdx], _lineItem[lineIdx]._lineData[2 * dataIdx + 1], _lineItem[lineIdx]._directionRouteDec);
				routeIdx++;
			}
			for (int i = lineIdx - 1; i > curLineIdx; i--) {
				for (int dataIdx = _lineItem[i]._lineDataEndIdx - 1; dataIdx > -1; dataIdx--) {
					curLineX = _lineItem[i]._lineData[2 * dataIdx];
					curLineY = _lineItem[i]._lineData[2 * dataIdx + 1];
					_bestRoute[routeIdx].set(_lineItem[i]._lineData[2 * dataIdx], _lineItem[i]._lineData[2 * dataIdx + 1], _lineItem[i]._directionRouteDec);
					routeIdx++;
					if (_lineItem[i]._lineDataEndIdx > 30 && dataIdx == _lineItem[i]._lineDataEndIdx / 2) {
						bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, i, curLineIdx, routeIdx);
						if (bestRouteNum == 1)
							return &_bestRoute[0];
						if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, i, curLineIdx, routeIdx)) {
							lineIdx = _newLineIdx;
							lineDataIdx = _newLineDataIdx;
							routeIdx = _newRouteIdx;
							loopCond = true;
							break;
						}
					}
				}

				if (loopCond)
					break;

				bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, i, curLineIdx, routeIdx);
				if (bestRouteNum == 1)
					return &_bestRoute[0];
				if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, i, curLineIdx, routeIdx)) {
					lineIdx = _newLineIdx;
					lineDataIdx = _newLineDataIdx;
					routeIdx = _newRouteIdx;
					loopCond = true;
					break;
				}
			}

			if (!loopCond) {
				lineDataIdx = _lineItem[curLineIdx]._lineDataEndIdx - 1;
				lineIdx = curLineIdx;
			}
		}
	} while (loopCond);

	if (lineIdx == curLineIdx) {
		if (lineDataIdx <= curLineDataIdx)
			routeIdx = _lineItem[curLineIdx].appendToRouteInc(lineDataIdx, curLineDataIdx, _bestRoute, routeIdx);
		else
			routeIdx = _lineItem[curLineIdx].appendToRouteDec(lineDataIdx, curLineDataIdx, _bestRoute, routeIdx);
	}
	if (characterRoute(_bestRoute[routeIdx - 1]._x, _bestRoute[routeIdx - 1]._y, clipDestX, clipDestY, -1, -1, routeIdx) != 1) {
		_bestRoute[routeIdx].invalidate();
	}

	return &_bestRoute[0];
}

void LinesManager::useRoute0(int idx, int curRouteIdx) {
	debugC(5, kDebugPath, "useRoute0(%d, %d)", idx, curRouteIdx);
	if (idx) {
		int i = 0;
		do {
			assert(curRouteIdx <= 8000);
			_bestRoute[curRouteIdx++] = _testRoute0[i++];
		} while (_testRoute0[i].isValid());
	}
	_bestRoute[curRouteIdx].invalidate();
}

void LinesManager::useRoute1(int idx, int curRouteIdx) {
	debugC(5, kDebugPath, "useRoute1(%d, %d)", idx, curRouteIdx);
	if (idx) {
		int i = 0;
		do {
			assert(curRouteIdx <= 8000);
			_bestRoute[curRouteIdx++] = _testRoute1[i++];
		} while (_testRoute1[i].isValid());
	}
	_bestRoute[curRouteIdx].invalidate();
}

void LinesManager::useRoute2(int idx, int curRouteIdx) {
	debugC(5, kDebugPath, "useRoute2(%d, %d)", idx, curRouteIdx);
	if (idx) {
		int i = 0;
		do {
			assert(curRouteIdx <= 8000);
			_bestRoute[curRouteIdx++] = _testRoute2[i++];
		} while (_testRoute2[i].isValid());
	}
	_bestRoute[curRouteIdx].invalidate();
}

int LinesManager::characterRoute(int fromX, int fromY, int destX, int destY, int startLineIdx, int endLineIdx, int routeIdx) {
	debugC(5, kDebugPath, "characterRoute(%d, %d, %d, %d, %d, %d, %d)", fromX, fromY, destX, destY, startLineIdx, endLineIdx, routeIdx);
	int collDataIdxRoute2 = 0;

	int curX = fromX;
	int curY = fromY;
	int curRouteIdx = routeIdx;
	bool dummyLineFl = false;
	if (startLineIdx == -1 && endLineIdx == -1)
		dummyLineFl = true;
	int foundDataIdx;
	int foundLineIdx = startLineIdx;
	if (checkCollisionLine(fromX, fromY, &foundDataIdx, &foundLineIdx, 0, _linesNumb)) {
		switch (_lineItem[foundLineIdx]._direction) {
		case DIR_UP:
			curY -= 2;
			break;
		case DIR_UP_RIGHT:
			curY -= 2;
			curX += 2;
			break;
		case DIR_RIGHT:
			curX += 2;
			break;
		case DIR_DOWN_RIGHT:
			curY += 2;
			curX += 2;
			break;
		case DIR_DOWN:
			curY += 2;
			break;
		case DIR_DOWN_LEFT:
			curY += 2;
			curX -= 2;
			break;
		case DIR_LEFT:
			curX -= 2;
			break;
		case DIR_UP_LEFT:
			curY -= 2;
			curX -= 2;
			break;
		default:
			break;
		}
	}
	int oldX = curX;
	int oldY = curY;
	int idxRoute0 = 0;
	int collLineIdxRoute0 = -1;
	int collLineIdxRoute1 = -1;
	int collLineIdxRoute2 = -1;

	int repeatFlag = 0;
	int collDataIdxRoute0 = 0;
	int collDataIdxRoute1 = 0;
	for (;;) {
		int newX = curX;
		int newY = curY;
		if (destX >= curX - 2 && destX <= curX + 2 && destY >= curY - 2 && destY <= curY + 2) {
			_testRoute0[idxRoute0].invalidate();
			useRoute0(idxRoute0, curRouteIdx);
			return 1;
		}
		int distX = abs(curX - destX) + 1;
		int distY = abs(curY - destY) + 1;
		int maxDist;
		if (distX > distY)
			maxDist = distX;
		else
			maxDist = distY;
		maxDist--;
		assert(maxDist != 0);
		int stepX = 1000 * distX / maxDist;
		int stepY = 1000 * distY / maxDist;
		if (destX < curX)
			stepX = -stepX;
		if (destY < curY)
			stepY = -stepY;
		int vertDirection = (int16)stepX / 1000;
		int horzDirection = (int16)stepY / 1000;
		Directions newDirection = DIR_NONE;
		if (horzDirection == -1 && (stepX >= 0 && stepX <= 150))
			newDirection = DIR_UP;
		if (vertDirection == 1 && (stepY >= -1 && stepY <= 150))
			newDirection = DIR_RIGHT;
		if (horzDirection == 1 && (stepX >= -150 && stepX <= 150))
			newDirection = DIR_DOWN;
		if (vertDirection == -1 && (stepY >= -150 && stepY <= 150))
			newDirection = DIR_LEFT;
		if (horzDirection == -1 && (stepX >= -150 && stepX <= 0))
			newDirection = DIR_UP;

		if (newDirection == DIR_NONE && !checkSmoothMove(curX, newY, destX, destY) && !makeSmoothMove(curX, newY, destX, destY)) {
			newDirection = _smoothMoveDirection;
			int smoothRouteIdx = 0;
			for (smoothRouteIdx = 0; _smoothRoute[smoothRouteIdx]._posX != -1 && _smoothRoute[smoothRouteIdx]._posY != -1; ++smoothRouteIdx) {
				if (checkCollisionLine(_smoothRoute[smoothRouteIdx]._posX, _smoothRoute[smoothRouteIdx]._posY, &collDataIdxRoute0, &collLineIdxRoute0, 0, _linesNumb)) {
					if (collLineIdxRoute0 > _lastLine)
						collLineIdxRoute0 = -1;
					break;
				}

				_testRoute0[idxRoute0].set(_smoothRoute[smoothRouteIdx]._posX, _smoothRoute[smoothRouteIdx]._posY, newDirection);
				idxRoute0++;

				if (repeatFlag == 1) {
					repeatFlag = 2;
					break;
				}
			}

			if (repeatFlag != 2 && _smoothRoute[smoothRouteIdx]._posX != -1 && _smoothRoute[smoothRouteIdx]._posY != -1)
				break;

			repeatFlag = 1;
			newX = _smoothRoute[smoothRouteIdx - 1]._posX;
			newY = _smoothRoute[smoothRouteIdx - 1]._posY;
		}
		int newDistX = abs(newX - destX) + 1;
		int newDistY = abs(newY - destY) + 1;
		int newMaxDist = newDistY;
		if (newDistX > newDistY)
			newMaxDist = newDistX;
		if (newMaxDist <= 10) {
			_testRoute0[idxRoute0].invalidate();
			useRoute0(idxRoute0, curRouteIdx);
			return 1;
		}
		int newStepX = 1000 * newDistX / (newMaxDist - 1);
		int newStepY = 1000 * newDistY / (newMaxDist - 1);
		if (destX < newX)
			newStepX = -newStepX;
		if (destY < newY)
			newStepY = -newStepY;
		int newVertDirection = newStepX / 1000;
		int newHorzDirection = newStepY / 1000;
		int newSmoothX = 1000 * newX;
		int newSmoothY = 1000 * newY;
		int curPosX = newSmoothX / 1000;
		int curPosY = newSmoothY / 1000;
		if (!(newStepX / 1000) && newHorzDirection == -1)
			newDirection = DIR_UP;
		if (newVertDirection == 1) {
			if (newHorzDirection == -1)
				newDirection = DIR_UP_RIGHT;
			if (!newHorzDirection)
				newDirection = DIR_RIGHT;
			if (newHorzDirection == 1)
				newDirection = DIR_DOWN_RIGHT;
		}
		if (!newVertDirection && newHorzDirection == 1)
			newDirection = DIR_DOWN;
		if ((newVertDirection != -1) && (newHorzDirection == -1)) {
			if (newStepX >= 0 && newStepX < 510)
				newDirection = DIR_UP;
			else if (newStepX >= 510 && newStepX <= 1000)
				newDirection = DIR_UP_RIGHT;
		} else {
			if (newHorzDirection == 1)
				newDirection = DIR_DOWN_LEFT;
			else if (!newHorzDirection)
				newDirection = DIR_LEFT;
			else if (newHorzDirection == -1) {
				if (newStepX >= 0 && newStepX < 510)
					newDirection = DIR_UP;
				else if (newStepX >= 510 && newStepX <= 1000)
					newDirection = DIR_UP_RIGHT;
				else
					newDirection = DIR_UP_LEFT;
			}
		}
		if (newVertDirection == 1) {
			if (newStepY >= -1000 && newStepY <= -510)
				newDirection = DIR_UP_RIGHT;
			if (newStepY >= -510 && newStepY <= 510)
				newDirection = DIR_RIGHT;
			if (newStepY >= 510 && newStepY <= 1000)
				newDirection = DIR_DOWN_RIGHT;
		}
		if (newHorzDirection == 1) {
			if (newStepX >= 510 && newStepX <= 1000)
				newDirection = DIR_DOWN_RIGHT;
			if (newStepX >= -510 && newStepX <= 510)
				newDirection = DIR_DOWN;
			if (newStepX >= -1000 && newStepX <= -510)
				newDirection = DIR_DOWN_LEFT;
		}
		if (newVertDirection == -1) {
			if (newStepY >= 510 && newStepY <= 1000)
				newDirection = DIR_DOWN_LEFT;
			if (newStepY >= -510 && newStepY <= 510)
				newDirection = DIR_LEFT;
			if (newStepY >= -1000 && newStepY <= -510)
				newDirection = DIR_UP_LEFT;
		}
		if (newHorzDirection == -1) {
			if (newStepX >= -1000 && newStepX <= -510)
				newDirection = DIR_UP_LEFT;
			if (newStepX >= -510 && newStepX <= 0)
				newDirection = DIR_UP;
		}
		if (newMaxDist + 1 <= 0) {
			_testRoute0[idxRoute0].invalidate();
			useRoute0(idxRoute0, curRouteIdx);
			return 1;
		}
		int curDist = 0;
		while (!checkCollisionLine(curPosX, curPosY, &collDataIdxRoute0, &collLineIdxRoute0, 0, _linesNumb)) {
			_testRoute0[idxRoute0].set(curPosX, curPosY, newDirection);
			newSmoothX += newStepX;
			newSmoothY += newStepY;
			curPosX = newSmoothX / 1000;
			curPosY = newSmoothY / 1000;
			idxRoute0++;
			++curDist;
			if (curDist >= newMaxDist + 1) {
				_testRoute0[idxRoute0].invalidate();
				useRoute0(idxRoute0, curRouteIdx);
				return 1;
			}
		}
		if (_lastLine >= collLineIdxRoute0)
			break;
		int tmpRouteIdx = computeRouteIdx(collLineIdxRoute0, collDataIdxRoute0, curPosX, curPosY, destX, destY, idxRoute0, _testRoute0);
		if (tmpRouteIdx == -1) {
			useRoute0(idxRoute0, curRouteIdx);
			return 1;
		}
		idxRoute0 = tmpRouteIdx;
		if (_newPosX != -1 || _newPosY != -1) {
			collLineIdxRoute0 = -1;
			break;
		}
		curX = -1;
		curY = -1;
	}

	_testRoute0[idxRoute0].invalidate();

	int idxRoute1 = 0;
	int posXRoute1 = oldX;
	int posYRoute1 = oldY;

	while (true) {

		if (destX >= posXRoute1 - 2 && destX <= posXRoute1 + 2 && destY >= posYRoute1 - 2 && destY <= posYRoute1 + 2) {
			_testRoute1[idxRoute1].invalidate();
			useRoute1(idxRoute1, curRouteIdx);
			return 1;
		}
		while (posXRoute1 != destX) {
			if (checkCollisionLine(posXRoute1, posYRoute1, &collDataIdxRoute1, &collLineIdxRoute1, 0, _linesNumb)) {
				if (collLineIdxRoute1 > _lastLine)
					collLineIdxRoute1 = -1;
				break;
			}

			if (posXRoute1 < destX)
				_testRoute1[idxRoute1++].set(posXRoute1++, posYRoute1, DIR_RIGHT);
			else
				_testRoute1[idxRoute1++].set(posXRoute1--, posYRoute1, DIR_LEFT);
		}
		if (posXRoute1 != destX)
			break;

		int curPosY = posYRoute1;
		while (curPosY != destY) {
			if (checkCollisionLine(destX, curPosY, &collDataIdxRoute1, &collLineIdxRoute1, 0, _linesNumb)) {
				if (collLineIdxRoute1 <= _lastLine)
					break;

				int tmpRouteIdx = computeRouteIdx(collLineIdxRoute1, collDataIdxRoute1, destX, curPosY, destX, destY, idxRoute1, _testRoute1);
				if (tmpRouteIdx == -1) {
					useRoute1(idxRoute1, curRouteIdx);
					return 1;
				}
				idxRoute1 = tmpRouteIdx;
				if (_newPosX != -1 && _newPosY != -1)
					break;
			}

			if (curPosY < destY)
				_testRoute1[idxRoute1++].set(destX, curPosY++, DIR_DOWN);
			else
				_testRoute1[idxRoute1++].set(destX, curPosY--, DIR_UP);
		}
		if (curPosY == destY) {
			_testRoute1[idxRoute1].invalidate();
			useRoute1(idxRoute1, curRouteIdx);
			return 1;
		}
		if (collLineIdxRoute1 <= _lastLine)
			break;
		posXRoute1 = _newPosX;
		posYRoute1 = _newPosY;
		bool colRes = checkCollisionLine(_newPosX, _newPosY, &collDataIdxRoute1, &collLineIdxRoute1, 0, _lastLine);
		if (colRes && collLineIdxRoute1 <= _lastLine)
			break;
	}

	_testRoute1[idxRoute1].invalidate();
	idxRoute1 = 0;
	int posXRoute2 = oldX;
	int posYRoute2 = oldY;
	while (true) {
		int curPosX;
		if (destX >= posXRoute2 - 2 && destX <= posXRoute2 + 2 && destY >= posYRoute2 - 2 && destY <= posYRoute2 + 2) {
			_testRoute2[idxRoute1].invalidate();
			useRoute2(idxRoute1, curRouteIdx);
			return 1;
		}

		int curPosYRoute2 = posYRoute2;
		while (curPosYRoute2 != destY) {
			if (checkCollisionLine(posXRoute2, curPosYRoute2, &collDataIdxRoute2, &collLineIdxRoute2, 0, _linesNumb)) {
				if (collLineIdxRoute2 > _lastLine)
					collLineIdxRoute2 = -1;
				break;
			}

			if (curPosYRoute2 < destY)
				_testRoute2[idxRoute1++].set(posXRoute2, curPosYRoute2++, DIR_DOWN);
			else
				_testRoute2[idxRoute1++].set(posXRoute2, curPosYRoute2--, DIR_UP);
		}
		if (curPosYRoute2 != destY)
			break;

		curPosX = posXRoute2;
		while (curPosX != destX) {
			if (checkCollisionLine(curPosX, destY, &collDataIdxRoute2, &collLineIdxRoute2, 0, _linesNumb)) {
				if (collLineIdxRoute2 <= _lastLine)
					break;

				int tmpRouteIdx = computeRouteIdx(collLineIdxRoute2, collDataIdxRoute2, curPosX, destY, destX, destY, idxRoute1, _testRoute2);
				if (tmpRouteIdx == -1) {
					useRoute2(idxRoute1, curRouteIdx);
					return 1;
				}
				idxRoute1 = tmpRouteIdx;
				if (_newPosX != -1 && _newPosY != -1)
					break;
			}

			if (curPosX < destX)
				_testRoute2[idxRoute1++].set(curPosX++, destY, DIR_RIGHT);
			else
				_testRoute2[idxRoute1++].set(curPosX--, destY, DIR_LEFT);
		}
		if (curPosX == destX) {
			collLineIdxRoute2 = -1;
			_testRoute2[idxRoute1].invalidate();
			useRoute2(idxRoute1, curRouteIdx);
			return 1;
		}
		if (collLineIdxRoute2 <= _lastLine)
			break;

		posXRoute2 = _newPosX;
		posYRoute2 = _newPosY;
		bool colResult = checkCollisionLine(_newPosX, _newPosY, &collDataIdxRoute2, &collLineIdxRoute2, 0, _lastLine);
		if (colResult && collLineIdxRoute2 <= _lastLine)
			break;
	}

	_testRoute2[idxRoute1].invalidate();

	if (!dummyLineFl) {
		if (endLineIdx > foundLineIdx) {
			if (_testRoute0[0]._x != -1 && collLineIdxRoute0 > foundLineIdx && collLineIdxRoute1 <= collLineIdxRoute0 && collLineIdxRoute2 <= collLineIdxRoute0 && endLineIdx >= collLineIdxRoute0) {
				_newLineIdx = collLineIdxRoute0;
				_newLineDataIdx = collDataIdxRoute0;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute0[i++];
				} while (_testRoute0[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}
			if (_testRoute1[0]._x != -1 && foundLineIdx < collLineIdxRoute1 && collLineIdxRoute2 <= collLineIdxRoute1 && collLineIdxRoute0 <= collLineIdxRoute1 && endLineIdx >= collLineIdxRoute1) {
				_newLineIdx = collLineIdxRoute1;
				_newLineDataIdx = collDataIdxRoute1;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute1[i++];
				} while (_testRoute1[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}
			if (_testRoute2[0]._x != -1 && foundLineIdx < collLineIdxRoute2 && collLineIdxRoute1 < collLineIdxRoute2 && collLineIdxRoute0 < collLineIdxRoute2 && endLineIdx >= collLineIdxRoute2) {
				_newLineIdx = collLineIdxRoute2;
				_newLineDataIdx = collDataIdxRoute2;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute2[i++];
				} while (_testRoute2[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}
		}
		if (endLineIdx < foundLineIdx) {
			if (collLineIdxRoute0 == -1)
				collLineIdxRoute0 = INVALID_LINE_VALUE;
			if (collLineIdxRoute1 == -1)
				collLineIdxRoute0 = INVALID_LINE_VALUE;
			if (collLineIdxRoute2 == -1)
				collLineIdxRoute0 = INVALID_LINE_VALUE;
			if (_testRoute1[0]._x != -1 && collLineIdxRoute1 < foundLineIdx && collLineIdxRoute2 >= collLineIdxRoute1 && collLineIdxRoute0 >= collLineIdxRoute1 && endLineIdx <= collLineIdxRoute1) {
				_newLineIdx = collLineIdxRoute1;
				_newLineDataIdx = collDataIdxRoute1;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute1[i++];
				} while (_testRoute1[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}
			if (_testRoute2[0]._x != -1 && foundLineIdx > collLineIdxRoute2 && collLineIdxRoute1 >= collLineIdxRoute2 && collLineIdxRoute0 >= collLineIdxRoute2 && endLineIdx <= collLineIdxRoute2) {
				_newLineIdx = collLineIdxRoute2;
				_newLineDataIdx = collDataIdxRoute2;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute2[i++];
				} while (_testRoute2[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}

			if (_testRoute0[0]._x != -1 && foundLineIdx > collLineIdxRoute0 && collLineIdxRoute1 >= collLineIdxRoute0 && collLineIdxRoute2 >= collLineIdxRoute0 && endLineIdx <= collLineIdxRoute0) {
				_newLineIdx = collLineIdxRoute0;
				_newLineDataIdx = collDataIdxRoute0;
				int i = 0;
				do {
					assert(curRouteIdx <= 8000);
					_bestRoute[curRouteIdx++] = _testRoute0[i++];
				} while (_testRoute0[i].isValid());
				_newRouteIdx = curRouteIdx;
				return 2;
			}
		}
	}
	return 0;
}

RouteItem *LinesManager::cityMapCarRoute(int x1, int y1, int x2, int y2) {
	debugC(5, kDebugPath, "cityMapCarRoute(%d, %d, %d, %d)", x1, y1, x2, y2);
	RouteItem *result;
	int arrDelta[10];
	int arrDataIdx[10];
	int arrLineIdx[10];

	int clipX2 = x2;
	int clipY2 = y2;
	if (x2 <= 14)
		clipX2 = 15;
	if (y2 <= 14)
		clipY2 = 15;
	if (clipX2 > _vm->_graphicsMan->_maxX - 10)
		clipX2 = _vm->_graphicsMan->_maxX - 10;
	if (clipY2 > 445)
		clipY2 = 440;

	int delta = 0;
	for (delta = 0; clipY2 + delta < _vm->_graphicsMan->_maxY; delta++) {
		if (checkCollisionLine(clipX2, clipY2 + delta, &arrDataIdx[DIR_DOWN], &arrLineIdx[DIR_DOWN], 0, _lastLine) && arrLineIdx[DIR_DOWN] <= _lastLine)
			break;
		arrDataIdx[DIR_DOWN] = 0;
		arrLineIdx[DIR_DOWN] = -1;
	}
	arrDelta[DIR_DOWN] = delta;

	for (delta = 0; clipY2 - delta > _vm->_graphicsMan->_minY; delta++) {
		if (checkCollisionLine(clipX2, clipY2 - delta , &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine) && arrLineIdx[DIR_UP] <= _lastLine)
			break;
		arrDataIdx[DIR_UP] = 0;
		arrLineIdx[DIR_UP] = -1;
		if (arrDelta[DIR_DOWN] < delta && arrLineIdx[DIR_DOWN] != -1)
			break;
	}
	arrDelta[DIR_UP] = delta;

	for (delta = 0; clipX2 + delta < _vm->_graphicsMan->_maxX; delta++) {
		if (checkCollisionLine(clipX2 + delta, clipY2, &arrDataIdx[DIR_RIGHT], &arrLineIdx[DIR_RIGHT], 0, _lastLine) && arrLineIdx[DIR_RIGHT] <= _lastLine)
			break;
		arrDataIdx[DIR_RIGHT] = 0;
		arrLineIdx[DIR_RIGHT] = -1;
		if ((arrDelta[DIR_UP] <= delta && arrLineIdx[DIR_UP] != -1) || (arrDelta[DIR_DOWN] <= delta && arrLineIdx[DIR_DOWN] != -1))
			break;
	}
	arrDelta[DIR_RIGHT] = delta;

	for (delta = 0; clipX2 - delta > _vm->_graphicsMan->_minX; delta++) {
		if (checkCollisionLine(clipX2 - delta, clipY2, &arrDataIdx[DIR_LEFT], &arrLineIdx[DIR_LEFT], 0, _lastLine) && arrLineIdx[DIR_LEFT] <= _lastLine)
			break;
		arrDataIdx[DIR_LEFT] = 0;
		arrLineIdx[DIR_LEFT] = -1;
		if ((arrDelta[DIR_UP] <= delta && arrLineIdx[DIR_UP] != -1) || (arrDelta[DIR_RIGHT] <= delta && arrLineIdx[DIR_RIGHT] != -1) || (arrDelta[DIR_DOWN] <= delta && arrLineIdx[DIR_DOWN] != -1))
			break;
	}
	arrDelta[DIR_LEFT] = delta;

	if (arrLineIdx[DIR_UP] == -1)
		arrDelta[DIR_UP] = INVALID_LINE_VALUE;
	if (arrLineIdx[DIR_RIGHT] == -1)
		arrDelta[DIR_RIGHT] = INVALID_LINE_VALUE;
	if (arrLineIdx[DIR_DOWN] == -1)
		arrDelta[DIR_DOWN] = INVALID_LINE_VALUE;
	if (arrLineIdx[DIR_LEFT] == -1)
		arrDelta[DIR_LEFT] = INVALID_LINE_VALUE;
	if (arrLineIdx[DIR_UP] != -1 || arrLineIdx[DIR_RIGHT] != -1 || arrLineIdx[DIR_DOWN] != -1 || arrLineIdx[DIR_LEFT] != -1) {
		int curLineDataIdx = 0;
		int curLineIdx = 0;
		if (arrLineIdx[DIR_DOWN] != -1 && arrDelta[DIR_UP] >= arrDelta[DIR_DOWN] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_DOWN] && arrDelta[DIR_LEFT] >= arrDelta[DIR_DOWN]) {
			curLineIdx = arrLineIdx[DIR_DOWN];
			curLineDataIdx = arrDataIdx[DIR_DOWN];
		} else if (arrLineIdx[DIR_UP] != -1 && arrDelta[DIR_DOWN] >= arrDelta[DIR_UP] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_UP] && arrDelta[DIR_LEFT] >= arrDelta[DIR_UP]) {
			curLineIdx = arrLineIdx[DIR_UP];
			curLineDataIdx = arrDataIdx[DIR_UP];
		} else if (arrLineIdx[DIR_RIGHT] != -1 && arrDelta[DIR_UP] >= arrDelta[DIR_RIGHT] && arrDelta[DIR_DOWN] >= arrDelta[DIR_RIGHT] && arrDelta[DIR_LEFT] >= arrDelta[DIR_RIGHT]) {
			curLineIdx = arrLineIdx[DIR_RIGHT];
			curLineDataIdx = arrDataIdx[DIR_RIGHT];
		} else if (arrLineIdx[DIR_LEFT] != -1 && arrDelta[DIR_DOWN] >= arrDelta[DIR_LEFT] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_LEFT] && arrDelta[DIR_UP] >= arrDelta[DIR_LEFT]) {
			curLineIdx = arrLineIdx[DIR_LEFT];
			curLineDataIdx = arrDataIdx[DIR_LEFT];
		}

		for (int i = 0; i <= 8; i++) {
			arrLineIdx[i] = -1;
			arrDataIdx[i] = 0;
			arrDelta[i] = INVALID_LINE_VALUE;
		}

		int superRouteIdx = 0;
		int curRouteDataIdx = 0;
		int curRouteLineIdx = 0;
		if (checkCollisionLine(x1, y1, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine)) {
			curRouteLineIdx = arrLineIdx[DIR_UP];
			curRouteDataIdx = arrDataIdx[DIR_UP];
		} else if (checkCollisionLine(x1, y1, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _linesNumb)) {
			int curRouteIdx = 0;
			int curRouteX;
			for (;;) {
				curRouteX = _testRoute2[curRouteIdx]._x;
				int curRouteY = _testRoute2[curRouteIdx]._y;
				Directions curRouteDir = _testRoute2[curRouteIdx]._dir;
				curRouteIdx++;

				if (checkCollisionLine(curRouteX, curRouteY, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine))
					break;

				_bestRoute[superRouteIdx].set(curRouteX, curRouteY, curRouteDir);

				_testRoute0[superRouteIdx].set(curRouteX, curRouteY, curRouteDir);
				superRouteIdx++;
				if (curRouteX == -1)
					break;
			}
			if (curRouteX != -1) {
				curRouteLineIdx = arrLineIdx[DIR_UP];
				curRouteDataIdx = arrDataIdx[DIR_UP];
			}
		} else {
			curRouteLineIdx = 1;
			curRouteDataIdx = 1;
			superRouteIdx = 0;
		}
		bool loopFl = true;
		while (loopFl) {
			loopFl = false;
			if (curRouteLineIdx < curLineIdx) {
				superRouteIdx = _lineItem[curRouteLineIdx].appendToRouteInc(curRouteDataIdx, _lineItem[curRouteLineIdx]._lineDataEndIdx - 2, _bestRoute, superRouteIdx);
				for (int j = curRouteLineIdx + 1; j < curLineIdx; ++j) {
					if (PLAN_TEST(_lineItem[j]._lineData[0], _lineItem[j]._lineData[1], superRouteIdx, j, curLineIdx)) {
						curRouteLineIdx = _newLineIdx;
						curRouteDataIdx = _newLineDataIdx;
						superRouteIdx = _newRouteIdx;
						loopFl = true;
						break;
					}
					if (_lineItem[j]._lineDataEndIdx - 2 > 0) {
						superRouteIdx = _lineItem[j].appendToRouteInc(0, _lineItem[j]._lineDataEndIdx - 2, _bestRoute, superRouteIdx);
					}
				}
				if (loopFl)
					continue;
				curRouteDataIdx = 0;
				curRouteLineIdx = curLineIdx;
			}
			if (curRouteLineIdx > curLineIdx) {
				superRouteIdx = _lineItem[curRouteLineIdx].appendToRouteDec(curRouteDataIdx, 0, _bestRoute, superRouteIdx);
				for (int l = curRouteLineIdx - 1; l > curLineIdx; --l) {
					if (PLAN_TEST(_lineItem[l]._lineData[2 * _lineItem[l]._lineDataEndIdx - 2], _lineItem[l]._lineData[2 * _lineItem[l]._lineDataEndIdx - 1], superRouteIdx, l, curLineIdx)) {
						curRouteLineIdx = _newLineIdx;
						curRouteDataIdx = _newLineDataIdx;
						superRouteIdx = _newRouteIdx;
						loopFl = true;
						break;
					}

					superRouteIdx = _lineItem[l].appendToRouteDec(_lineItem[l]._lineDataEndIdx - 2, 0, _bestRoute, superRouteIdx);
				}
				if (loopFl)
					continue;

				curRouteDataIdx = _lineItem[curLineIdx]._lineDataEndIdx - 1;
				curRouteLineIdx = curLineIdx;
			}
			if (curRouteLineIdx == curLineIdx) {
				if (curRouteDataIdx <= curLineDataIdx) {
					superRouteIdx = _lineItem[curLineIdx].appendToRouteInc(curRouteDataIdx, curLineDataIdx, _bestRoute, superRouteIdx);
				} else {
					superRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curRouteDataIdx, curLineDataIdx, _bestRoute, superRouteIdx);
				}
			}
		}
		_bestRoute[superRouteIdx].invalidate();
		result = &_bestRoute[0];
	} else {
		result = nullptr;
	}
	return result;
}

bool LinesManager::checkSmoothMove(int fromX, int fromY, int destX, int destY) {
	debugC(5, kDebugPath, "checkSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY);
	int distX = abs(fromX - destX) + 1;
	int distY = abs(fromY - destY) + 1;
	if (distX > distY)
		distY = distX;
	if (distY <= 10)
		return true;

	int stepX = 1000 * distX / (distY - 1);
	int stepY = 1000 * distY / (distY - 1);
	if (destX < fromX)
		stepX = -stepX;
	if (destY < fromY)
		stepY = -stepY;

	int smoothPosX = 1000 * fromX;
	int smoothPosY = 1000 * fromY;
	int newPosX = fromX;
	int newPosY = fromY;

	if (distY + 1 > 0) {
		int stepCount = 0;
		int foundLineIdx;
		int foundDataIdx;
		while (!checkCollisionLine(newPosX, newPosY, &foundDataIdx, &foundLineIdx, 0, _linesNumb) || foundLineIdx > _lastLine) {
			smoothPosX += stepX;
			smoothPosY += stepY;
			newPosX = smoothPosX / 1000;
			newPosY = smoothPosY / 1000;
			++stepCount;
			if (stepCount >= distY + 1)
				return false;
		}
		return true;
	}
	return false;
}

bool LinesManager::makeSmoothMove(int fromX, int fromY, int destX, int destY) {
	debugC(5, kDebugPath, "makeSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY);
	int curX = fromX;
	int curY = fromY;
	if (fromX > destX && destY > fromY) {
		int hopkinsIdx = 36;
		int smoothIdx = 0;
		int stepCount = 0;
		while (curX > destX && destY > curY) {
			int realSpeedX = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedX;
			int realSpeedY = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedY;
			int spriteSize = _vm->_globals->_spriteSize[curY];
			if (spriteSize < 0) {
				realSpeedX = _vm->_graphicsMan->zoomOut(realSpeedX, -spriteSize);
				realSpeedY = _vm->_graphicsMan->zoomOut(realSpeedY, -spriteSize);
			} else if (spriteSize > 0) {
				realSpeedX = _vm->_graphicsMan->zoomIn(realSpeedX, spriteSize);
				realSpeedY = _vm->_graphicsMan->zoomIn(realSpeedY, spriteSize);
			}
			int oldY = curY;
			for (int i = 0; i < realSpeedX; i++) {
				--curX;
				_smoothRoute[smoothIdx]._posX = curX;
				if (curY != oldY + realSpeedY)
					curY++;
				_smoothRoute[smoothIdx]._posY = curY;
				smoothIdx++;
			}
			++hopkinsIdx;
			if (hopkinsIdx == 48)
				hopkinsIdx = 36;
			++stepCount;
		}
		if (stepCount > 5) {
			_smoothRoute[smoothIdx]._posX = -1;
			_smoothRoute[smoothIdx]._posY = -1;
			_smoothMoveDirection = DIR_DOWN_LEFT;
			return false;
		}
	} else if (fromX < destX && destY > fromY) {
		int hopkinsIdx = 36;
		int smoothIdx = 0;
		int stepCount = 0;
		while (curX < destX && destY > curY) {
			int realSpeedX = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedX;
			int realSpeedY = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedY;
			int spriteSize = _vm->_globals->_spriteSize[curY];
			if (spriteSize < 0) {
				realSpeedX = _vm->_graphicsMan->zoomOut(realSpeedX, -spriteSize);
				realSpeedY = _vm->_graphicsMan->zoomOut(realSpeedY, -spriteSize);
			} else if (spriteSize > 0) {
				realSpeedX = _vm->_graphicsMan->zoomIn(realSpeedX, spriteSize);
				realSpeedY = _vm->_graphicsMan->zoomIn(realSpeedY, spriteSize);
			}
			int oldY = curY;
			for (int i = 0; i < realSpeedX; i++) {
				++curX;
				_smoothRoute[smoothIdx]._posX = curX;
				if (curY != oldY + realSpeedY)
					curY++;
				_smoothRoute[smoothIdx]._posY = curY;
				smoothIdx++;
			}
			++hopkinsIdx;
			if (hopkinsIdx == 48)
				hopkinsIdx = 36;
			++stepCount;
		}
		if (stepCount > 5) {
			_smoothRoute[smoothIdx]._posX = -1;
			_smoothRoute[smoothIdx]._posY = -1;
			_smoothMoveDirection = DIR_DOWN_RIGHT;
			return false;
		}
	} else if (fromX > destX && destY < fromY) {
		int hopkinsIdx = 12;
		int smoothIdx = 0;
		int stepCount = 0;
		while (curX > destX && destY < curY) {
			int realSpeedX = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedX, 25);
			int realSpeedY = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedY, 25);
			int oldY = curY;
			for (int i = 0; i < realSpeedX; i++) {
				--curX;
				_smoothRoute[smoothIdx]._posX = curX;
				if ((uint16)curY != (uint16)oldY + realSpeedY)
					curY--;
				_smoothRoute[smoothIdx]._posY = curY;
				smoothIdx++;
			}
			++hopkinsIdx;
			if (hopkinsIdx == 24)
				hopkinsIdx = 12;
			++stepCount;
		}
		if (stepCount > 5) {
			_smoothRoute[smoothIdx]._posX = -1;
			_smoothRoute[smoothIdx]._posY = -1;
			_smoothMoveDirection = DIR_UP_LEFT;
			return false;
		}
	} else if (fromX < destX && destY < fromY) {
		int hopkinsIdx = 12;
		int smoothIdx = 0;
		int stepCount = 0;
		while (curX < destX && destY < curY) {
			int oldY = curY;
			int realSpeedX = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedX, 25);
			int realSpeedY = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedY, 25);
			for (int i = 0; i < realSpeedX; i++) {
				++curX;
				_smoothRoute[smoothIdx]._posX = curX;
				if ((uint16)curY != (uint16)oldY + realSpeedY)
					curY--;
				_smoothRoute[smoothIdx]._posY = curY;
				smoothIdx++;
			}
			++hopkinsIdx;
			if (hopkinsIdx == 24)
				hopkinsIdx = 12;
			++stepCount;
		}

		if (stepCount > 5) {
			_smoothRoute[smoothIdx]._posX = -1;
			_smoothRoute[smoothIdx]._posY = -1;
			_smoothMoveDirection = DIR_UP_RIGHT;
			return false;
		}
	}
	return true;
}

bool LinesManager::PLAN_TEST(int paramX, int paramY, int superRouteIdx, int paramStartLineIdx, int paramEndLineIdx) {
	debugC(5, kDebugPath, "PLAN_TEST(%d, %d, %d, %d, %d)", paramX, paramY, superRouteIdx, paramStartLineIdx, paramEndLineIdx);
	int sideTestUp;
	int sideTestDown;
	int sideTestLeft;
	int sideTestRight;
	int lineIdxTestUp;
	int lineIdxTestDown;
	int lineIdxTestLeft;
	int lineIdxTestRight;
	int dataIdxTestUp;
	int dataIdxTestDown;
	int dataIdxTestLeft;
	int dataIdxTestRight;

	int idxTestUp = testLine(paramX, paramY - 2, &sideTestUp, &lineIdxTestUp, &dataIdxTestUp);
	int idxTestDown = testLine(paramX, paramY + 2, &sideTestDown, &lineIdxTestDown, &dataIdxTestDown);
	int idxTestLeft = testLine(paramX - 2, paramY, &sideTestLeft, &lineIdxTestLeft, &dataIdxTestLeft);
	int idxTestRight = testLine(paramX + 2, paramY, &sideTestRight, &lineIdxTestRight, &dataIdxTestRight);
	if (idxTestUp == -1 && idxTestDown == -1 && idxTestLeft == -1 && idxTestRight == -1)
		return false;

	// Direction: 1 = Up, 2 = Down, 3 = Left, 4 = Right
	int direction;
	if (paramStartLineIdx == -1 || paramEndLineIdx == -1) {
		if (idxTestUp != -1)
			direction = 1;
		else if (idxTestDown != -1)
			direction = 2;
		else if (idxTestLeft != -1)
			direction = 3;
		else if (idxTestRight != -1)
			direction = 4;
		else
			return false;
	} else {
		int stepCountUp = 100;
		int stepCountDown = 100;
		int stepCountLeft = 100;
		int stepCountRight = 100;
		int paramStepCount = abs(paramStartLineIdx - paramEndLineIdx);
		if (idxTestUp != -1) {
			stepCountUp = abs(lineIdxTestUp - paramEndLineIdx);
		}
		if (idxTestDown != -1) {
			stepCountDown = abs(lineIdxTestDown - paramEndLineIdx);
		}
		if (idxTestLeft != -1) {
			stepCountLeft = abs(lineIdxTestLeft - paramEndLineIdx);
		}
		if (idxTestRight != -1) {
			stepCountRight = abs(lineIdxTestRight - paramEndLineIdx);
		}

		if (stepCountUp < paramStepCount && stepCountUp <= stepCountDown && stepCountUp <= stepCountLeft && stepCountUp <= stepCountRight)
			direction = 1;
		else if (paramStepCount > stepCountDown && stepCountUp >= stepCountDown && stepCountLeft >= stepCountDown && stepCountRight >= stepCountDown)
			direction = 2;
		else if (stepCountLeft < paramStepCount && stepCountLeft <= stepCountUp && stepCountLeft <= stepCountDown && stepCountLeft <= stepCountRight)
			direction = 3;
		else if (stepCountRight < paramStepCount && stepCountRight <= stepCountUp && stepCountRight <= stepCountDown && stepCountRight <= stepCountLeft)
			direction = 4;
		else
			return false;
	}

	int sideTest = 0;
	int idxTest = 0;
	if (direction == 1) {
		idxTest = idxTestUp;
		sideTest = sideTestUp;
		_newLineIdx = lineIdxTestUp;
		_newLineDataIdx = dataIdxTestUp;
	} else if (direction == 2) {
		idxTest = idxTestDown;
		sideTest = sideTestDown;
		_newLineIdx = lineIdxTestDown;
		_newLineDataIdx = dataIdxTestDown;
	} else if (direction == 3) {
		idxTest = idxTestLeft;
		sideTest = sideTestLeft;
		_newLineIdx = lineIdxTestLeft;
		_newLineDataIdx = dataIdxTestLeft;
	} else if (direction == 4) {
		idxTest = idxTestRight;
		sideTest = sideTestRight;
		_newLineIdx = lineIdxTestRight;
		_newLineDataIdx = dataIdxTestRight;
	}

	int routeIdx = superRouteIdx;
	if (sideTest == 1) {
		routeIdx = _lineItem[idxTest].appendToRouteInc(0, -1, _bestRoute, routeIdx);
	} else if (sideTest == 2) {
		routeIdx = _lineItem[idxTest].appendToRouteDec(-1, -1, _bestRoute, routeIdx);
	}
	_newRouteIdx = routeIdx;
	return true;
}

// Test line
int LinesManager::testLine(int paramX, int paramY, int *testValue, int *foundLineIdx, int *foundDataIdx) {
	debugC(5, kDebugPath, "testLine(%d, %d, testValue, foundLineIdx, foundDataIdx)", paramX, paramY);
	int16 *lineData;
	int collLineIdx;
	int collDataIdx;

	for (int idx = _lastLine + 1; idx < _linesNumb + 1; idx++) {
		lineData = _lineItem[idx]._lineData;
		int lineDataEndIdx = _lineItem[idx]._lineDataEndIdx;
		if (!lineData)
			continue;

		if (lineData[0] == paramX && lineData[1] == paramY) {
			*testValue = 1;
			int posX = lineData[2 * (lineDataEndIdx - 1)];
			int posY = lineData[2 * (lineDataEndIdx - 1) + 1];
			if (_lineItem[idx]._directionRouteInc == DIR_DOWN || _lineItem[idx]._directionRouteInc == DIR_UP)
				posY += 2;
			if (_lineItem[idx]._directionRouteInc == DIR_RIGHT || _lineItem[idx]._directionRouteDec == DIR_LEFT)
				posX += 2;
			if (!checkCollisionLine(posX, posY, &collDataIdx, &collLineIdx, 0, _lastLine))
				error("Error in test line");
			*foundLineIdx = collLineIdx;
			*foundDataIdx = collDataIdx;
			return idx;
		}

		if (lineDataEndIdx > 0) {
			if (lineData[2 * (lineDataEndIdx - 1)] == paramX && lineData[2 * (lineDataEndIdx - 1) + 1] == paramY) {
				*testValue = 2;
				int posX = lineData[0];
				int posY = lineData[1];
				if (_lineItem[idx]._directionRouteInc == DIR_DOWN || _lineItem[idx]._directionRouteInc == DIR_UP)
					posY -= 2;
				if (_lineItem[idx]._directionRouteInc == DIR_RIGHT || _lineItem[idx]._directionRouteDec == DIR_LEFT)
					posX -= 2;
				if (!checkCollisionLine(posX, posY, &collDataIdx, &collLineIdx, 0, _lastLine))
					error("Error in test line");
				*foundLineIdx = collLineIdx;
				*foundDataIdx = collDataIdx;
				return idx;
			}
		}
	}
	return -1;
}

int LinesManager::computeYSteps(int idx) {
	debugC(5, kDebugPath, "computeYSteps(%d)", idx);
	int zoomPct = _vm->_globals->_spriteSize[idx];
	if (_vm->_globals->_characterType == CHARACTER_HOPKINS_CLONE) {
		if (zoomPct < 0)
			zoomPct = -zoomPct;
		zoomPct = 20 * (5 * zoomPct - 100) / -80;
	} else if (_vm->_globals->_characterType == CHARACTER_SAMANTHA) {
		if (zoomPct < 0)
			zoomPct = -zoomPct;
		zoomPct = 20 * (5 * zoomPct - 165) / -67;
	}

	int retVal = 25;
	if (zoomPct < 0)
		retVal = _vm->_graphicsMan->zoomOut(25, -zoomPct);
	else if (zoomPct > 0)
		retVal = _vm->_graphicsMan->zoomIn(25, zoomPct);

	return retVal;
}

void LinesManager::optimizeRoute(RouteItem *route) {
	debugC(5, kDebugPath, "optimizeRoute(route)");
	if (route[0]._x == -1 && route[0]._y == -1)
		return;

	int routeIdx = 0;
	Directions oldDir = DIR_NONE;
	int route0Y = route[0]._y;
	Directions curDir = route[0]._dir;

	for (;;) {
		if (oldDir != DIR_NONE && curDir != oldDir) {
			int oldRouteIdx = routeIdx;
			int routeCount = 0;
			int yStep = computeYSteps(route0Y);
			int curRouteX = route[routeIdx]._x;
			int curRouteY = route[routeIdx]._y;
			while (curRouteX != -1 || curRouteY != -1) {
				int idx = routeIdx;
				++routeIdx;
				++routeCount;
				if (route[idx]._dir != curDir)
					break;
				curRouteX = route[routeIdx]._x;
				curRouteY = route[routeIdx]._y;
			}
			if (routeCount < yStep) {
				int idx = oldRouteIdx;
				for (int i = 0; i < routeCount; i++) {
					route[idx]._dir = oldDir;
					idx++;
				}
				curDir = oldDir;
			}
			routeIdx = oldRouteIdx;
			if (curRouteX == -1 && curRouteY == -1)
				break;
		}
		routeIdx++;
		oldDir = curDir;
		route0Y = route[routeIdx]._y;
		curDir = route[routeIdx]._dir;
		if (route[routeIdx]._x == -1 && route0Y == -1)
			break;
	}
}

int LinesManager::getMouseZone() {
	debugC(9, kDebugPath, "getMouseZone()");
	int result;

	int xp = _vm->_events->_mousePos.x + _vm->_events->_mouseOffset.x;
	int yp = _vm->_events->_mousePos.y + _vm->_events->_mouseOffset.y;
	if ((_vm->_events->_mousePos.y + _vm->_events->_mouseOffset.y) > 19) {
		for (int bobZoneId = 0; bobZoneId <= 48; bobZoneId++) {
			int bobId = _bobZone[bobZoneId];
			if (bobId && _bobZoneFl[bobZoneId] && _vm->_objectsMan->_bob[bobId]._bobMode && _vm->_objectsMan->_bob[bobId]._frameIndex != 250 &&
				!_vm->_objectsMan->_bob[bobId]._disabledAnimationFl && xp > _vm->_objectsMan->_bob[bobId]._oldX &&
				xp < _vm->_objectsMan->_bob[bobId]._oldWidth + _vm->_objectsMan->_bob[bobId]._oldX && yp > _vm->_objectsMan->_bob[bobId]._oldY) {
					if (yp < _vm->_objectsMan->_bob[bobId]._oldHeight + _vm->_objectsMan->_bob[bobId]._oldY) {
						if (_zone[bobZoneId]._spriteIndex == -1) {
							_zone[bobZoneId]._destX = 0;
							_zone[bobZoneId]._destY = 0;
						}
						if (!_zone[bobZoneId]._destX && !_zone[bobZoneId]._destY) {
							_zone[bobZoneId]._destX = _vm->_objectsMan->_bob[bobId]._oldWidth + _vm->_objectsMan->_bob[bobId]._oldX;
							_zone[bobZoneId]._destY = _vm->_objectsMan->_bob[bobId]._oldHeight + _vm->_objectsMan->_bob[bobId]._oldY + 6;
							_zone[bobZoneId]._spriteIndex = -1;
						}

						// WORKAROUND: Avoid allowing hotspots that should remain non-interactive
						if (bobZoneId == 24 && _vm->_globals->_curRoomNum == 14)
							continue;

						return bobZoneId;
					}
			}
		}
		_currentSegmentId = 0;
		for (int squareZoneId = 0; squareZoneId <= 99; squareZoneId++) {
			if (_zone[squareZoneId]._enabledFl && _squareZone[squareZoneId]._enabledFl
				&& _squareZone[squareZoneId]._left <= xp && _squareZone[squareZoneId]._right >= xp
				&& _squareZone[squareZoneId]._top <= yp && _squareZone[squareZoneId]._bottom >= yp) {
					if (_squareZone[squareZoneId]._squareZoneFl)
						return _zoneLine[_squareZone[squareZoneId]._minZoneLineIdx]._bobZoneIdx;

					_segment[_currentSegmentId]._minZoneLineIdx = _squareZone[squareZoneId]._minZoneLineIdx;
					_segment[_currentSegmentId]._maxZoneLineIdx = _squareZone[squareZoneId]._maxZoneLineIdx;
					++_currentSegmentId;
			}
		}
		if (!_currentSegmentId)
			return -1;


		int colRes1 = 0;
		for (int yCurrent = yp; yCurrent >= 0; --yCurrent) {
			colRes1 = checkCollision(xp, yCurrent);
			if (colRes1 != -1 && _zone[colRes1]._enabledFl)
				break;
		}

		if (colRes1 == -1)
			return -1;

		int colRes2 = 0;
		for (int j = yp; j < _vm->_graphicsMan->_maxY; ++j) {
			colRes2 = checkCollision(xp, j);
			if (colRes2 != -1 && _zone[colRes1]._enabledFl)
				break;
		}

		if (colRes2 == -1)
			return -1;

		int colRes3 = 0;
		for (int k = xp; k >= 0; --k) {
			colRes3 = checkCollision(k, yp);
			if (colRes3 != -1 && _zone[colRes1]._enabledFl)
				break;
		}
		if (colRes3 == -1)
			return -1;

		int colRes4 = 0;
		for (int xCurrent = xp; _vm->_graphicsMan->_maxX > xCurrent; ++xCurrent) {
			colRes4 = checkCollision(xCurrent, yp);
			if (colRes4 != -1 && _zone[colRes1]._enabledFl)
				break;
		}
		if (colRes1 == colRes2 && colRes1 == colRes3 && colRes1 == colRes4)
			result = colRes1;
		else
			result = -1;

	} else {
		result = 0;
	}
	return result;
}

int LinesManager::checkCollision(int xp, int yp) {
	debugC(7, kDebugPath, "checkCollision(%d, %d)", xp, yp);
	if (_currentSegmentId <= 0)
		return -1;

	int xMax = xp + 4;
	int xMin = xp - 4;

	for (int idx = 0; idx <= _currentSegmentId; ++idx) {
		int curZoneLineIdx = _segment[idx]._minZoneLineIdx;
		if (_segment[idx]._maxZoneLineIdx < curZoneLineIdx)
			continue;

		int yMax = yp + 4;
		int yMin = yp - 4;

		do {
			LigneZoneItem *curZoneLine = &_zoneLine[curZoneLineIdx];
			int16 *dataP = curZoneLine->_zoneData;
			if (dataP) {
				int count = curZoneLine->_count;
				int startX = dataP[0];
				int startY = dataP[1];
				int destX = dataP[count * 2 - 2];
				int destY = dataP[count * 2 - 1];

				bool flag = true;
				if ((startX < destX && (xMax < startX || xMin > destX))  ||
				    (startX >= destX && (xMin > startX || xMax < destX)) ||
				    (startY < destY && (yMax < startY || yMin > destY))  ||
				    (startY >= destY && (yMin > startY || yMax < destY)))
					flag = false;

				if (flag && curZoneLine->_count > 0) {
					for (int i = 0; i < count; ++i) {
						int xCheck = *dataP++;
						int yCheck = *dataP++;

						if ((xp == xCheck || (xp + 1) == xCheck) && (yp == yCheck))
							return curZoneLine->_bobZoneIdx;
					}
				}
			}
		} while (++curZoneLineIdx <= _segment[idx]._maxZoneLineIdx);
	}

	return -1;
}

// Square Zone
void LinesManager::initSquareZones() {
	debugC(5, kDebugPath, "initSquareZones()");
	for (int idx = 0; idx < 100; ++idx) {
		SquareZoneItem *curZone = &_squareZone[idx];
		curZone->_enabledFl = false;
		curZone->_squareZoneFl = false;
		curZone->_left = 1280;
		curZone->_right = 0;
		curZone->_top = 460;
		curZone->_bottom = 0;
		curZone->_minZoneLineIdx = 401;
		curZone->_maxZoneLineIdx = 0;
	}

	for (int idx = 0; idx < MAX_LINES + 1; ++idx) {
		int16 *dataP = _zoneLine[idx]._zoneData;
		if (dataP == nullptr)
			continue;

		SquareZoneItem *curZone = &_squareZone[_zoneLine[idx]._bobZoneIdx];
		curZone->_enabledFl = true;
		curZone->_maxZoneLineIdx = MAX(curZone->_maxZoneLineIdx, idx);
		curZone->_minZoneLineIdx = MIN(curZone->_minZoneLineIdx, idx);

		for (int i = 0; i < _zoneLine[idx]._count; i++) {
			int zoneX = *dataP++;
			int zoneY = *dataP++;

			curZone->_left = MIN(curZone->_left, zoneX);
			curZone->_right = MAX(curZone->_right, zoneX);
			curZone->_top = MIN(curZone->_top, zoneY);
			curZone->_bottom = MAX(curZone->_bottom, zoneY);
		}
	}

	for (int idx = 0; idx < 100; idx++) {
		int zoneWidth = abs(_squareZone[idx]._left - _squareZone[idx]._right);
		int zoneHeight = abs(_squareZone[idx]._top - _squareZone[idx]._bottom);
		if (zoneWidth == zoneHeight)
			_squareZone[idx]._squareZoneFl = true;
	}
}

void LinesManager::clearAll() {
	debugC(5, kDebugPath, "clearAll()");
	for (int idx = 0; idx < 105; ++idx) {
		_zone[idx]._destX = 0;
		_zone[idx]._destY = 0;
		_zone[idx]._spriteIndex = 0;
	}

	_testRoute0 = nullptr;
	_testRoute1 = nullptr;
	_testRoute2 = nullptr;
	_lineBuf = nullptr;
	_route = nullptr;

	for (int idx = 0; idx < MAX_LINES; ++idx) {
		_lineItem[idx]._lineDataEndIdx = 0;
		_lineItem[idx]._direction = DIR_NONE;
		_lineItem[idx]._directionRouteInc = DIR_NONE;
		_lineItem[idx]._directionRouteDec = DIR_NONE;
		_lineItem[idx]._lineData = nullptr;

		_zoneLine[idx]._count = 0;
		_zoneLine[idx]._bobZoneIdx = 0;
		_zoneLine[idx]._zoneData = nullptr;
	}

	for (int idx = 0; idx < 100; ++idx)
		_squareZone[idx]._enabledFl = false;

	_testRoute0 = new RouteItem[8334];
	_testRoute1 = new RouteItem[8334];
	_testRoute2 = new RouteItem[8334];
	if (!_testRoute0)
		_testRoute0 = nullptr;
	if (!_testRoute1)
		_testRoute1 = nullptr;
	if (!_testRoute2)
		_testRoute2 = nullptr;

	_largeBuf = _vm->_globals->allocMemory(10000);
	_lineBuf = (int16 *)(_largeBuf);
}

/**
 * Clear all zones and reset nextLine
 */
void LinesManager::clearAllZones() {
	debugC(5, kDebugPath, "clearAllZones()");
	for (int idx = 0; idx < MAX_LINES; ++idx)
		removeZoneLine(idx);
}

/**
 * Remove Zone Line
 */
void LinesManager::removeZoneLine(int idx) {
	debugC(5, kDebugPath, "removeZoneLine(%d)", idx);
	assert(idx < MAX_LINES + 1);
	_zoneLine[idx]._zoneData = (int16 *)_vm->_globals->freeMemory((byte *)_zoneLine[idx]._zoneData);
}

void LinesManager::resetLines() {
	debugC(5, kDebugPath, "resetLines()");
	for (int idx = 0; idx < MAX_LINES; ++idx) {
		_lineItem[idx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[idx]._lineData);
		_lineItem[idx]._lineDataEndIdx = 0;
		_lineItem[idx]._lineData = nullptr;
	}
}

void LinesManager::setMaxLineIdx(int idx) {
	debugC(5, kDebugPath, "setMaxLineIdx(%d)", idx);
	_maxLineIdx = idx;
}

void LinesManager::resetLastLine() {
	debugC(5, kDebugPath, "resetLastLine()");
	_lastLine = 0;
}

void LinesManager::resetLinesNumb() {
	debugC(5, kDebugPath, "resetLinesNumb()");
	_linesNumb = 0;
}

void LinesManager::enableZone(int idx) {
	debugC(5, kDebugPath, "enableZone(%d)", idx);
	if (_bobZone[idx]) {
		_bobZoneFl[idx] = true;
	} else {
		_zone[idx]._enabledFl = true;
	}
}

void LinesManager::disableZone(int idx) {
	debugC(5, kDebugPath, "disableZone(%d)", idx);
	if (_bobZone[idx]) {
		_bobZoneFl[idx] = false;
	} else {
		_zone[idx]._enabledFl = false;
	}
}

void LinesManager::checkZone() {
	debugC(9, kDebugPath, "checkZone()");
	int mouseX = _vm->_events->getMouseX();
	int mouseY = _vm->_events->getMouseY();
	int oldMouseY = mouseY;
	if (_vm->_globals->_cityMapEnabledFl
		|| _vm->_events->_startPos.x >= mouseX
		|| (mouseY = _vm->_graphicsMan->_scrollOffset + 54, mouseX >= mouseY)
		|| (mouseY = oldMouseY - 1, mouseY < 0 || mouseY > 59)) {
			if (_vm->_objectsMan->_visibleFl)
				_vm->_objectsMan->_eraseVisibleCounter = 4;
			_vm->_objectsMan->_visibleFl = false;
	} else {
		_vm->_objectsMan->_visibleFl = true;
	}
	if (_vm->_objectsMan->_forceZoneFl) {
		_zoneSkipCount = 100;
		_oldMouseZoneId = -1;
		_oldMouseX = -200;
		_oldMouseY = -220;
		_vm->_objectsMan->_forceZoneFl = false;
	}

	_zoneSkipCount++;
	if (_zoneSkipCount <= 1)
		return;

	if (_vm->_globals->_freezeCharacterFl || (_route == nullptr) || _zoneSkipCount > 4) {
		_zoneSkipCount = 0;
		int zoneId;
		if (_oldMouseX != mouseX || _oldMouseY != oldMouseY) {
			zoneId = getMouseZone();

			// WORKAROUND: Incorrect hotspot zones in the guard's control room
			if (_vm->_globals->_curRoomNum == 71 && (zoneId == 14 || zoneId == 12 || zoneId == 17))
				zoneId = _oldMouseZoneId;
		} else {
			zoneId = _oldMouseZoneId;
		}
		if (_oldMouseZoneId != zoneId) {
			_vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100);
			_vm->_events->_mouseCursorId = 4;
			_vm->_events->changeMouseCursor(4);
			if (_forceHideText) {
				_vm->_fontMan->hideText(5);
				_forceHideText = false;
				return;
			}
		}
		if (zoneId != -1) {
			if (_zone[zoneId]._verbFl1 || _zone[zoneId]._verbFl2 ||
				_zone[zoneId]._verbFl3 || _zone[zoneId]._verbFl4 ||
				_zone[zoneId]._verbFl5 || _zone[zoneId]._verbFl6 ||
				_zone[zoneId]._verbFl7 || _zone[zoneId]._verbFl8 ||
				_zone[zoneId]._verbFl9 || _zone[zoneId]._verbFl10) {
					if (_oldMouseZoneId != zoneId) {
						_vm->_fontMan->initTextBuffers(5, _zone[zoneId]._messageId, _vm->_globals->_zoneFilename, 0, 430, 0, 0, 252);
						_vm->_fontMan->showText(5);
						_forceHideText = true;
					}
					_hotspotTextColor += 25;
					if (_hotspotTextColor > 100)
						_hotspotTextColor = 0;
					_vm->_graphicsMan->setColorPercentage2(251, _hotspotTextColor, _hotspotTextColor, _hotspotTextColor);
					if (_vm->_events->_mouseCursorId == 4) {
						if (_zone[zoneId]._verbFl1 == 2) {
							_vm->_events->changeMouseCursor(16);
							_vm->_events->_mouseCursorId = 16;
							_vm->_objectsMan->setVerb(16);
						}
					}
			} else {
				_vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100);
				_vm->_events->_mouseCursorId = 4;
				_vm->_events->changeMouseCursor(4);
			}
		}
		_vm->_objectsMan->_zoneNum = zoneId;
		_oldMouseX = mouseX;
		_oldMouseY = oldMouseY;
		_oldMouseZoneId = zoneId;
		if (_vm->_globals->_freezeCharacterFl && (_vm->_events->_mouseCursorId == 4)) {
			if (zoneId != -1 && zoneId != 0)
				_vm->_objectsMan->handleRightButton();
		}
		if ((_vm->_globals->_cityMapEnabledFl && zoneId == -1) || !zoneId) {
			_vm->_objectsMan->setVerb(0);
			_vm->_events->_mouseCursorId = 0;
			_vm->_events->changeMouseCursor(0);
		}
	}
}

} // End of namespace Hopkins
