/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #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 = NULL; _testRoute1 = NULL; _testRoute2 = NULL; _lineBuf = NULL; _route = NULL; _currentSegmentId = 0; _largeBuf = NULL; _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::String &file) { debugC(5, kDebugPath, "loadLines(%s)", file.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 == NULL) 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 == NULL) 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 == NULL) 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 == NULL) 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 NULL; if (abs(fromX - destX) <= 4 && abs(fromY - clipDestY) <= 4) return NULL; if (_oldZoneNum > 0 && _vm->_objectsMan->_zoneNum > 0 && _oldZoneNum == _vm->_objectsMan->_zoneNum) return NULL; } _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 NULL; 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 NULL; 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 = NULL; } 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 == NULL) 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 = NULL; _testRoute1 = NULL; _testRoute2 = NULL; _lineBuf = NULL; _route = NULL; 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 = NULL; _zoneLine[idx]._count = 0; _zoneLine[idx]._bobZoneIdx = 0; _zoneLine[idx]._zoneData = NULL; } 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 = NULL; if (!_testRoute1) _testRoute1 = NULL; if (!_testRoute2) _testRoute2 = NULL; _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 = NULL; } } 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 == NULL) || _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