/* 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 . * */ #include "common/math.h" #include "petka/walk.h" #include "petka/petka.h" #include "petka/q_manager.h" #include "petka/q_system.h" #include "petka/objects/heroes.h" namespace Petka { const char *const wayPrefixes[] = {"-w-n.", "-w-ne.", "-w-e.", "-w-se.", "-w-s.", "-w-sw.", "-w-w.", "-w-nw."}; const double kPI = M_PI; const double k2PI = M_PI * 2; const double kHalfPI = M_PI_2; const double kPiArray[] = {M_PI_2, M_PI_4, 0.0, -M_PI_4, -M_PI_2, -3 * M_PI_4, M_PI, 3 * M_PI_4}; Walk::Walk(int id) { // CHECKED QManager *mgr = g_vm->resMgr(); Common::String res = mgr->findResourceName(id); res.toLowercase(); res = res.substr(0, res.find(wayPrefixes[0])); for (int i = 0; i < 8; ++i) { waysSizes[i] = readWayFile(res + Common::String(wayPrefixes[i]) + "off", &_off1[i], &_off2[i]) - 1; readWayFile(res + Common::String(wayPrefixes[i]) + "leg", &_leg1[i], &_leg2[i]); } for (int i = 0; i < 8; ++i) { _offleg1[i] = new int[waysSizes[i] + 1]; _offleg2[i] = new int[waysSizes[i] + 1]; for (int j = 1; j <= waysSizes[i]; ++j) { _offleg1[i][j] = _leg1[i][j] + _off1[i][j] - _leg1[i][j - 1]; _offleg2[i][j] = _leg2[i][j] + _off2[i][j] - _leg2[i][j - 1]; } _offleg1[i][0] = _offleg1[i][waysSizes[i]]; _offleg2[i][0] = _offleg2[i][waysSizes[i]]; } for (int i = 0; i < 8; ++i) { int v16 = 0; int v18 = 0; int idx = 1; for (int j = 0; j < 150; ++j) { v16 += _offleg1[i][idx]; v18 += _offleg2[i][idx]; idx = (idx + 1) % waysSizes[i]; } field_D0[i] = (double)v18 / v16; } currX = 0.0; currY = 0.0; _bkg3Count = 0; _edgesCount = 0; field_134 = 0; _verticesCount = 0; _bkg3_1 = nullptr; _bkg3_2 = nullptr; _edges = nullptr; _bkg3_3 = nullptr; _bkg3_4 = nullptr; _bkg3_5 = nullptr; _vertices = nullptr; field_190 = 0; resId = 0; field_170 = field_178 = 0.0; field_180 = field_181 = field_182 = field_183 = field_184 = field_185 = field_186 = field_187 = field_188 = field_189 = field_18A = field_18B = field_18C = field_18D = field_18E = field_18F = 0; field_194 = 0; field_198 = 0.0; destX = destY = 0; field_1C4 = field_1C5 = field_1C6 = field_1C7 = 0; } Walk::~Walk() { // CHECKED clearBackground(); reset(); for (int i = 0; i < 8; ++i) { delete[] _leg1[i]; delete[] _leg2[i]; delete[] _off1[i]; delete[] _off2[i]; delete[] _offleg1[i]; delete[] _offleg2[i]; _leg1[i] = nullptr; _leg2[i] = nullptr; _off1[i] = nullptr; _off2[i] = nullptr; _offleg1[i] = nullptr; _offleg2[i] = nullptr; waysSizes[i] = 0; } } void Walk::init(Point start, Point end) { reset(); field_134 = 1; int v5 = sub_424230(&end) ? moveInside(&end) : sub_423600(end); _bkg3_4[0] = sub_424230(&start) ? moveInside(&start) : sub_423600(start); destX = end.x; destY = end.y; currX = end.x; currY = end.y; if (start == end) return; if (sub_424160(&start, &end) || v5 != _bkg3_4[0]) { for (int i = 0; i < _bkg3Count; ++i) _bkg3_5[i] = -1; field_134 = 1; if (_bkg3_4[0] != v5) { do { _bkg3_4[field_134] = sub_423970(_bkg3_4[field_134 - 1], _bkg3_5[field_134 - 1]); if (_bkg3_4[field_134] >= 0) { _bkg3_5[field_134 - 1] = _bkg3_4[field_134]; if (field_134 <= 1 || _bkg3_4[field_134 - 2] != _bkg3_4[field_134]) field_134++; } else { field_134--; _bkg3_4[field_134] = -1; _bkg3_5[field_134] = -1; } } while (_bkg3_4[field_134 - 1] != v5); } int v20 = 1; int v21 = 4; int v22 = 1; int a2 = 4; _bkg3_3[0] = start; if (field_134 > 1) { do { int v23 = sub_423A30(_bkg3_4[(v21 - 4) / 4], _bkg3_4[v21 / 4]); _bkg3_3[v22].x = (_vertices[_edges[v23].x].x + _vertices[_edges[v23].y].x) / 2; _bkg3_3[v22].y = (_vertices[_edges[v23].x].y + _vertices[_edges[v23].y].y) / 2; if (v22 > 1 && !sub_424160(&_bkg3_3[v22 - 2], &_bkg3_3[v22])) { v20--; _bkg3_3[v22 - 1] = _bkg3_3[v22]; v22--; field_134--; } v21 = a2 + 4; v20++; v22++; a2 += 4; } while (v20 < field_134); } _bkg3_3[v20] = end; if (v20 > 1 && !sub_424160(_bkg3_3 + v20 - 2, _bkg3_3 + v20)) { _bkg3_3[v20 - 1] = _bkg3_3[v20]; field_134--; } field_134++; } else { _bkg3_3[0] = start; _bkg3_3[1] = end; field_134 = 2; } sub_422EA0(_bkg3_3[0], _bkg3_3[1]); field_14C = 1; } void Walk::clearBackground() { // CHECKED delete[] _vertices; _vertices = nullptr; _verticesCount = 0; if (_bkg3_1) { if (_bkg3_2) { for (int i = 0; i < _bkg3Count; ++i) { delete[] _bkg3_2[i]; _bkg3_2[i] = nullptr; } delete[] _bkg3_2; _bkg3_2 = nullptr; } delete[] _bkg3_1; _bkg3_1 = nullptr; _bkg3Count = 0; } delete[] _edges; _edges = nullptr; _edgesCount = 0; delete[] _bkg3_3; _bkg3_3 = nullptr; delete[] _bkg3_4; _bkg3_4 = nullptr; delete[] _bkg3_5; _bkg3_5 = nullptr; } void Walk::setBackground(Common::String name) { // CHECKED clearBackground(); name.toLowercase(); name.replace(name.size() - 3, 3, "cvx"); Common::SeekableReadStream *stream = g_vm->openFile(name, false); if (!stream) return; _verticesCount = stream->readUint32LE(); _vertices = new Point[_verticesCount]; for (int i = 0; i < _verticesCount; ++i) { _vertices[i].x = stream->readUint32LE(); _vertices[i].y = stream->readUint32LE(); } _edgesCount = stream->readUint32LE(); _edges = new Point[_edgesCount]; for (int i = 0; i < _edgesCount; ++i) { _edges[i].x = stream->readUint32LE(); _edges[i].y = stream->readUint32LE(); } _bkg3Count = stream->readUint32LE(); _bkg3_1 = new int[_bkg3Count]; stream->read(_bkg3_1, 4 * _bkg3Count); _bkg3_2 = new int*[_bkg3Count]; for (int i = 0; i < _bkg3Count; ++i) { _bkg3_2[i] = new int[_bkg3_1[i]]; stream->read(_bkg3_2[i], 4 * _bkg3_1[i]); } delete stream; _bkg3_3 = new Point[_bkg3Count + 1]; _bkg3_4 = new int[_bkg3Count + 1]; _bkg3_5 = new int[_bkg3Count + 1]; } void Walk::reset() { // CHECKED field_140 = 0.0; field_138 = 0.0; currX = 0; currY = 0; resId = 0; field_14C = 0; field_190 = 0; field_194 = 0; } Common::Point Walk::currPos() { // CHECKED return Common::Point(currX, currY); } int Walk::getSpriteId() { // CHECKED return resId; } int Walk::commonPoint(int idx1, int idx2) { // CHECKED if (_edges[idx1].x == _edges[idx2].x || _edges[idx1].x == _edges[idx2].y) return _edges[idx1].x; if (_edges[idx1].y == _edges[idx2].x || _edges[idx1].y == _edges[idx2].y) return _edges[idx1].y; return 0; } int Walk::readWayFile(const Common::String &name, int **p1, int **p2) { // CHECKED Common::SeekableReadStream *stream = g_vm->openFile(name, false); if (!stream) { p1 = nullptr; p2 = nullptr; return 0; } const uint items = (uint)stream->size() / 8; *p1 = new int[items]; *p2 = new int[items]; stream->skip(4); for (uint i = 0; i < items; ++i) { stream->read(&(*p1)[i], 4); stream->read(&(*p2)[i], 4); } delete stream; return items; } int Walk::sub_422EA0(Point p1, Point p2) { if (p1 == p2) return 0; Point p = p1; p.x += 150; double v5 = angle(p1, p, p2); double v6; if (v5 >= 0.0) v6 = k2PI - v5; else v6 = v5 + k2PI; double v30 = 4.0; for (uint i = 0; i < ARRAYSIZE(kPiArray); ++i) { double v9 = v5 - kPiArray[i]; if (v9 < 0.0) v9 = -v9; if (v9 < v30) { v30 = v9; resId = i; } double v10 = v6 - kPiArray[i]; if (v10 < 0.0) v10 = -v10; if (v10 < v30) { v30 = v10; resId = i; } } double v28 = p2.x - p1.x; double v26 = p2.y - p1.y; double v12 = Common::hypotenuse(p2.x - p1.x, p2.y - p1.y); double v39 = 1.0 / sqrt(field_D0[resId] * field_D0[resId] - -1.0); if (v39 == 0.0) field_140 = v28 / v12; else field_140 = (field_D0[resId] - -1.0 / (v26 / v28)) * (v26 / v12) * v39; DBLPoint a1; DBLPoint a2; DBLPoint a3; a1.x = p1.x; a1.y = p1.y; a2.x = p2.x; a2.y = p2.y; a3.x = p2.x; a3.y = field_D0[resId] * v28 + p1.y; double v13 = angle(a1, a2, a3); field_140 = cos(v13); field_138 = sin(v13); double v16 = v13; if (v13 < -kHalfPI) v16 = v13 + kPI; if (v13 > kHalfPI) v16 = v13 - kPI; field_140 = cos(v16); field_138 = sin(v16); int v32 = 1; double v34 = 0.0; double v35 = 0.0; double v36 = p1.y; v39 = v28 * v28 + v26 * v26 - -1.0; int j = 0; for (int i = 0; i < 10;) { double k = g_vm->getQSystem()->getPetka()->calcPerspective(v36); v34 += _offleg1[resId][v32] * k; v35 += _offleg2[resId][v32] * k; j++; v32 = (v32 + 1) % waysSizes[resId]; v36 = v35 * field_140 + v34 * field_138 + p1.y; double v22 = v34 * field_140 - v35 * field_138 + p1.x - p2.x; double v38 = v36 - p2.y; double v23 = v22 * v22 + v38 * v38; if (v23 >= v39) { i++; } else { v39 = v23; field_194 = j; i = 0; } } field_170 = 0; field_178 = 0; currX = p1.x; currY = p1.y; field_150 = p1.x; field_158 = p1.y; field_190 = 0; field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(p1.y); return resId; } int Walk::sub_423350() { // CHECKED field_190 = (field_190 + 1) % waysSizes[resId]; --field_194; if (field_194 < 0) { field_14C++; if (field_14C < field_134) { int t = field_190; int id = resId; if (id == sub_422EA0(_bkg3_3[field_14C - 1], _bkg3_3[field_14C])) { field_190 = t; return 1; } return 2; } return 0; } field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(currY); field_170 = _offleg1[resId][field_190] * field_198 + field_170; field_178 = _offleg2[resId][field_190] * field_198 + field_178; currX = field_140 * field_170 - field_178 * field_138 + field_150; currY = field_140 * field_178 + field_138 * field_170 + field_158; return 1; } Common::Point Walk::sub_4234B0() { // CHECKED Common::Point p; field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(currY); p.x = currX - _leg1[resId][field_190] * field_198; p.y = currY - _leg2[resId][field_190] * field_198; return p; } bool Walk::areEdgesAdjacent(int first_index, int second_index) { // CHECKED if (first_index != second_index) { if (_edges[first_index].x == _edges[second_index].x || _edges[first_index].x == _edges[second_index].y || _edges[first_index].y == _edges[second_index].x || _edges[first_index].y == _edges[second_index].y) { return true; } } return false; } int Walk::sub_423600(Point p) { int j = 0; for (int i = 0; i < _bkg3Count; ++i, ++j) { int *v4 = new int[_bkg3_1[j]]; v4[0] = _bkg3_2[j][0]; for (int k = 0; k < _bkg3_1[j]; ++k) { if (areEdgesAdjacent(v4[0], _bkg3_2[j][k])) { v4[1] = _bkg3_2[j][k]; break; } } for (int k = 2; k < _bkg3_1[j]; ++k) { for (int l = 0; l < _bkg3_1[j]; ++l) { if (areEdgesAdjacent(v4[k - 1], _bkg3_2[j][l]) && v4[k - 2] != _bkg3_2[j][l]) { v4[k] = _bkg3_2[j][l]; break; } } } int v11 = commonPoint(v4[_bkg3_1[j] - 1], v4[0]); int v31 = commonPoint(v4[0], v4[1]); double v12 = angle(p, _vertices[v11], _vertices[v31]); if (p == _vertices[v11] || p == _vertices[v31]) { delete[] v4; return i; } int k; for (k = 1; k < _bkg3_1[j] - 1; ++k) { int v16 = commonPoint(v4[k - 1], v4[k]); int v32 = commonPoint(v4[k], v4[k + 1]); v12 += angle(p, _vertices[v16], _vertices[v32]); if (p == _vertices[v16] || p == _vertices[v32]) { delete[] v4; return i; } } int v19 = commonPoint(v4[k - 1], v4[k]); int v20 = commonPoint(v4[k], v4[0]); delete[] v4; double v23 = angle(p, _vertices[v19], _vertices[v20]); v12 += v23; if (p == _vertices[v19] || p == _vertices[v20]) return i; if (v12 < 0.0) v12 = -v12; if (v12 > kPI) return i; } debug("Walk bug: Point doesn't belong to any convex"); return 0; } int Walk::sub_423970(int a1, int a2) { // CHECKED int index = 0; if (a2 >= 0) { int v5 = sub_423A30(a1, a2); for (int i = 0; i < _bkg3_1[a1]; ++i) { if (_bkg3_2[a1][i] == v5) { index = i + 1; break; } } } for (int i = index; i < _bkg3_1[a1]; ++i) { for (int j = 0; j < _bkg3Count; ++j) { if (j == a1) continue; for (int k = 0; k < _bkg3_1[j]; ++k) { if (_bkg3_2[j][k] == _bkg3_2[a1][i]) { return j; } } } } return -1; } int Walk::sub_423A30(int idx1, int idx2) { // CHECKED for (int i = 0; i < _bkg3_1[idx1]; ++i) { for (int j = 0; j < _bkg3_1[idx2]; ++j) { if (_bkg3_2[idx1][i] == _bkg3_2[idx2][j]) return _bkg3_2[idx1][i]; } } return 0; } double Walk::angle(Point p1, Point p2, Point p3) { // CHECKED return angle(DBLPoint(p1), DBLPoint(p2), DBLPoint(p3)); } double Walk::angle(DBLPoint p1, DBLPoint p2, DBLPoint p3) { // CHECKED if (p1 == p2 || p1 == p3) return 0.0; double xv1 = p2.x - p1.x; double xv2 = p3.x - p1.x; double yv1 = p2.y - p1.y; double yv2 = p3.y - p1.y; double mv1 = Common::hypotenuse(xv1, yv1); double mv2 = Common::hypotenuse(xv2, yv2); double v13 = (xv1 * xv2 + yv1 * yv2) / (mv1 * mv2); if ((xv2 / mv2 * (yv1 / mv1) - yv2 / mv2 * (xv1 / mv1)) < 0.0) // Not sure return -acos(v13); return acos(v13); } int Walk::sub_423E00(Point p1, Point p2, Point p3, Point p4, Point &p5) { if (p1.x > p2.x) { SWAP(p2, p1); } if (p3.x > p4.x) { SWAP(p3, p4); } double v11 = (p2.y - p1.y) * (p4.x - p3.x); double v12 = (p2.x - p1.x) * (p4.y - p3.y); if (v11 == v12) return 0; double v30; if (p2.x - p1.x) { if (p4.x - p3.x) { v30 = ((double)(p3.y - p1.y) * (p4.x - p3.x) * (p2.x - p1.x) + (v11 * p1.x) - (v12 * p3.x)) / (v11 - v12); if (v30 < p1.x || p3.x > v30 || p2.x < v30 || p4.x < v30) return 0; } else { v30 = p3.x; if (p3.x < p1.x || p2.x < p3.x) return 0; } } else { v30 = p1.x; if (p1.x < p3.x) return 0; if (p4.x < p1.x) return 0; } if (p1.y > p2.y) { SWAP(p1, p2); } if (p3.y > p4.y) { SWAP(p3, p4); } if (p2.y - p1.y) { if (p4.y - p3.y) { double v21; if (p2.x - p1.x) { v21 = (v30 - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y; } else { v21 = (v30 - p3.x) * (p4.y - p3.y) / (p4.x - p3.x) + p3.y; } if (v21 >= p1.y && v21 >= p3.y && v21 <= p2.y && v21 <= p4.y) { p5.x = v30; p5.y = v21; return 1; } } else { if (p3.y >= p1.y) { if (p3.y > p2.y) return 0; p5.x = v30; p5.y = p3.y; return 1; } } } else { if (p1.y >= p3.y) { if (p1.y <= p4.y) { p5.x = v30; p5.y = p1.y; return 1; } } } return 0; } bool Walk::sub_424160(Point *p1, Point *p2) { // CHECKED if (*p1 == *p2) return false; Point p; int v = 1; if (_verticesCount <= 1) return sub_423E00(_vertices[v - 1], _vertices[0], *p1, *p2, p) != 0; while (!sub_423E00(_vertices[v - 1], _vertices[v], *p1, *p2, p)) { if (++v >= _verticesCount) return sub_423E00(_vertices[v - 1], _vertices[0], *p1, *p2, p) != 0; } return true; } bool Walk::sub_424230(Point *p1) { // CHECKED Point p(0, 0); int v = sub_424320(p1, &p); p.y = p1->y; v = (sub_424320(p1, &p) & 1) + (v & 1); p.y = 480; v = (sub_424320(p1, &p) & 1) + v; v = (sub_424320(p1, &p) & 1) + v; p.x = 640; v = (sub_424320(p1, &p) & 1) + v; p.y = p1->y; v = (sub_424320(p1, &p) & 1) + v; p.y = 0; v = (sub_424320(p1, &p) & 1) + v; p.x = p1->x; return (sub_424320(p1, &p) & 1) + v < 4; } int Walk::sub_424320(Point *p1, Point *p2) { // CHECKED if (*p1 == *p2) return 0; int ret = 0; Point p; int i; for (i = 1; i < _verticesCount; ++i) { if (sub_423E00(_vertices[i - 1], _vertices[i], *p1, *p2, p) && *p1 != p && *p2 != p) { ret++; } } if (sub_423E00(_vertices[i - 1], _vertices[0], *p1, *p2, p) && *p1 != p && *p2 != p) { ret++; } return ret; } int Walk::moveInside(Point *p) { // CHECKED DBLPoint dp = sub_424610(_vertices[_edges->x], _vertices[_edges->y].x, _vertices[_edges->y].y, *p); int index = 0; double min = (dp.y - p->y) * (dp.y - p->y) + (dp.x - p->x) * (dp.x - p->x); for (int i = 1; i < _verticesCount; ++i) { DBLPoint dp1 = sub_424610(_vertices[_edges[i].x], _vertices[_edges[i].y].x, _vertices[_edges[i].y].y, *p); double curr = (dp1.y - p->y) * (dp1.y - p->y) + (dp1.x - p->x) * (dp1.x - p->x); if (curr < min) { dp = dp1; min = curr; index = i; } } p->x = dp.x; p->y = dp.y; for (int i = 0; i < _verticesCount; ++i) { for (int j = 0; j < _bkg3_1[i]; ++j) { if (_bkg3_2[i][j] == index) return i; } } return 0; } DBLPoint Walk::sub_424610(Point p1, int x, int y, Point p2) { // CHECKED DBLPoint p; double v13; double v14; if (p1.x == x) { v13 = p1.x; v14 = p2.y; } else { double v6 = (double)(y - p1.y) / (x - p1.x); double v7 = p1.y - v6 * p1.x; v13 = ((p2.y - v7) * v6 + p2.x) / (v6 * v6 - -1.0); v14 = v13 * v6 + v7; } p.x = CLIP(v13, MIN(x, p1.x), MAX(x, p1.x)); p.y = CLIP(v14, MIN(y, p1.y), MAX(y, p1.y)); return p; } } // End of namespace Petka