mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-23 19:16:21 +00:00
32fb690aae
The audio is in raw format, like the rest of the sounds in the Amiga port. Overall: - A new class has been created, NightlongVideoDecoder, which encapsulates the PC and Amiga video playing functionality - The AnimManager class has been moved into animmanager.* - The AnimType class has been moved from anim.* to animtype.*
1699 lines
48 KiB
C++
1699 lines
48 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "trecision/actor.h"
|
|
#include "trecision/animtype.h"
|
|
#include "trecision/pathfinding3d.h"
|
|
#include "trecision/sound.h"
|
|
#include "trecision/trecision.h"
|
|
#include "trecision/video.h"
|
|
|
|
namespace Trecision {
|
|
|
|
PathFinding3D::PathFinding3D(TrecisionEngine *vm) : _vm(vm) {
|
|
_lookX = 0.0f;
|
|
_lookZ = 0.0f;
|
|
_curStep = 0;
|
|
_lastStep = 0;
|
|
|
|
_characterInMovement = false;
|
|
_characterGoToPosition = -1;
|
|
|
|
_actorPos = 0;
|
|
_forcedActorPos = 0;
|
|
_panelNum = 0;
|
|
_curPanel = -1;
|
|
_oldPanel = -1;
|
|
|
|
for (int i = 0; i < MAXPATHNODES; ++i)
|
|
_pathNode[i].clear();
|
|
|
|
_numPathNodes = 0;
|
|
_numSortPanel = 0;
|
|
_x3d = 0.0f;
|
|
_y3d = 0.0f;
|
|
_z3d = 0.0f;
|
|
|
|
_curX = 0.0f;
|
|
_curZ = 0.0f;
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
_invP[i][0] = 0.0f;
|
|
_invP[i][1] = 0.0f;
|
|
_invP[i][2] = 0.0f;
|
|
}
|
|
|
|
for (int i = 0; i < MAXPANELSINROOM; ++i)
|
|
_panel[i].clear();
|
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
_sortPan[i]._num = 0;
|
|
_sortPan[i]._min = 0.0f;
|
|
}
|
|
|
|
for (int i = 0; i < MAXSTEP; ++i)
|
|
_step[i].clear();
|
|
}
|
|
|
|
PathFinding3D::~PathFinding3D() {
|
|
}
|
|
|
|
void PathFinding3D::findPath() {
|
|
Actor *actor = _vm->_actor;
|
|
actor->_px += actor->_dx;
|
|
actor->_pz += actor->_dz;
|
|
|
|
int inters = 0;
|
|
_numPathNodes = 0;
|
|
|
|
// if you have clicked behind the starting panel or the corner it's not possible to walk
|
|
if (_curPanel < 0 && _oldPanel >= 0) {
|
|
int16 b;
|
|
// behind the starting panel
|
|
if (pointInside(b = _oldPanel, _curX, _curZ) ||
|
|
// behind the panel corner1
|
|
((_vm->dist2D(_panel[_oldPanel]._x1, _panel[_oldPanel]._z1, actor->_px, actor->_pz) < EPSILON) &&
|
|
(pointInside(b = _panel[_oldPanel]._nearPanel1, _curX, _curZ) || pointInside(b = _panel[_oldPanel]._nearPanel2, _curX, _curZ))) ||
|
|
// behind the panel corner2
|
|
((_vm->dist2D(_panel[_oldPanel]._x2, _panel[_oldPanel]._z2, actor->_px, actor->_pz) < EPSILON) &&
|
|
(pointInside(b = _panel[_oldPanel]._nearPanel2, _curX, _curZ) || pointInside(b = _panel[_oldPanel]._nearPanel1, _curX, _curZ)))) {
|
|
_curX = actor->_px;
|
|
_curZ = actor->_pz;
|
|
actor->_px -= actor->_dx;
|
|
actor->_pz -= actor->_dz;
|
|
_curPanel = b;
|
|
_numPathNodes = 0;
|
|
lookAt(_lookX, _lookZ);
|
|
return;
|
|
}
|
|
}
|
|
|
|
float dist = _vm->dist2D(actor->_px, actor->_pz, _curX, _curZ);
|
|
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
if (_panel[i]._flags & 0x80000000) { // it must be a wide panel
|
|
if (intersectLineLine(_panel[i]._x1, _panel[i]._z1,
|
|
_panel[i]._x2, _panel[i]._z2,
|
|
actor->_px, actor->_pz, _curX, _curZ)) {
|
|
++inters;
|
|
|
|
_pathNode[_numPathNodes]._x = _x3d;
|
|
_pathNode[_numPathNodes]._z = _z3d;
|
|
_pathNode[_numPathNodes]._dist = _vm->dist2D(actor->_px, actor->_pz, _x3d, _z3d);
|
|
_pathNode[_numPathNodes]._oldPanel = i;
|
|
_pathNode[_numPathNodes]._curPanel = i;
|
|
++_numPathNodes;
|
|
|
|
// CORNERS - lever intersections in corners
|
|
if (_oldPanel >= 0 && (i == _panel[_oldPanel]._nearPanel1 || i == _panel[_oldPanel]._nearPanel2)) {
|
|
// otherwise if it's near the starting panel
|
|
if ((_pathNode[_numPathNodes - 1]._dist < EPSILON) &&
|
|
(i != _oldPanel) && (i != _curPanel)) {
|
|
// and the distance is very small to the intersection
|
|
--inters;
|
|
--_numPathNodes;
|
|
|
|
// If the click is inside the nearby panel
|
|
if (_curPanel < 0 && pointInside(i, _curX, _curZ)) {
|
|
_curX = actor->_px;
|
|
_curZ = actor->_pz;
|
|
actor->_px -= actor->_dx;
|
|
actor->_pz -= actor->_dz;
|
|
|
|
_curPanel = i;
|
|
lookAt(_lookX, _lookZ);
|
|
return;
|
|
}
|
|
}
|
|
} else if (_curPanel >= 0 && (i == _panel[_curPanel]._nearPanel1 || i == _panel[_curPanel]._nearPanel2)) {
|
|
// otherwise if it is near the finish panel
|
|
if (ABS(_pathNode[_numPathNodes - 1]._dist - dist) < EPSILON && i != _oldPanel && i != _curPanel) {
|
|
// and the distance is very small to the intersection
|
|
--inters;
|
|
--_numPathNodes;
|
|
}
|
|
}
|
|
|
|
} else if (i == _oldPanel) {
|
|
// always adds start and finish node only in on a panel
|
|
++inters;
|
|
|
|
_pathNode[_numPathNodes]._x = actor->_px;
|
|
_pathNode[_numPathNodes]._z = actor->_pz;
|
|
_pathNode[_numPathNodes]._dist = 0.0f;
|
|
_pathNode[_numPathNodes]._oldPanel = _oldPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _oldPanel;
|
|
++_numPathNodes;
|
|
} else if (i == _curPanel) {
|
|
++inters;
|
|
|
|
_pathNode[_numPathNodes]._x = _curX;
|
|
_pathNode[_numPathNodes]._z = _curZ;
|
|
_pathNode[_numPathNodes]._dist = dist;
|
|
_pathNode[_numPathNodes]._oldPanel = _curPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _curPanel;
|
|
++_numPathNodes;
|
|
}
|
|
}
|
|
}
|
|
|
|
// the path is defined by:
|
|
// start _actor._px, _actor._pz
|
|
// _numPathNodes _pathNode
|
|
// end _curX, _curZ
|
|
|
|
// if it collides with any panel
|
|
if (inters) {
|
|
sortPath();
|
|
|
|
// if odd and I go to the floor but I did not start from the panel
|
|
// if it arrives on the floor and the last two nodes are not on the same block
|
|
// if outside the last panel it moves the last node
|
|
|
|
if (((inters & 1) && (_curPanel < 0) && (_oldPanel < 0)) ||
|
|
(((inters - 1) & 1) && (_curPanel < 0) &&
|
|
(!findAttachedPanel(_pathNode[_numPathNodes - 2]._curPanel, _pathNode[_numPathNodes - 1]._curPanel) ||
|
|
pointInside(_pathNode[_numPathNodes - 1]._curPanel, _curX, _curZ)))) {
|
|
|
|
_curPanel = _pathNode[_numPathNodes - 1]._curPanel;
|
|
|
|
pointOut(); // remove the point found
|
|
|
|
_pathNode[_numPathNodes]._x = _curX;
|
|
_pathNode[_numPathNodes]._z = _curZ;
|
|
_pathNode[_numPathNodes]._oldPanel = _curPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _curPanel;
|
|
|
|
++_numPathNodes;
|
|
}
|
|
|
|
// if it arrives on the floor
|
|
inters = 0;
|
|
|
|
// Count the intersections with narrow panels
|
|
// and with the union of large panels and small panels
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
if (!(_panel[i]._flags & 0x80000000)) {
|
|
if (intersectLineLine(_panel[i]._x1, _panel[i]._z1,
|
|
_panel[i]._x2, _panel[i]._z2,
|
|
_pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z,
|
|
_curX, _curZ))
|
|
++inters;
|
|
} else {
|
|
if (_panel[i]._col1 & 0x80) {
|
|
if (intersectLineLine(_panel[i]._x1, _panel[i]._z1,
|
|
_panel[_panel[i]._col1 & 0x7F]._x2, _panel[_panel[i]._col1 & 0x7F]._z2,
|
|
_pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z,
|
|
_curX, _curZ)) {
|
|
if (_vm->dist2D(_x3d, _z3d, _pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z) > EPSILON &&
|
|
_vm->dist2D(_x3d, _z3d, _curX, _curZ) > EPSILON)
|
|
++inters;
|
|
}
|
|
} else if (intersectLineLine(_panel[i]._x1, _panel[i]._z1,
|
|
_panel[_panel[i]._col1 & 0x7F]._x1, _panel[_panel[i]._col1 & 0x7F]._z1,
|
|
_pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z,
|
|
_curX, _curZ)) {
|
|
if (_vm->dist2D(_x3d, _z3d, _pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z) > EPSILON &&
|
|
_vm->dist2D(_x3d, _z3d, _curX, _curZ) > EPSILON)
|
|
++inters;
|
|
}
|
|
|
|
if (_panel[i]._col2 & 0x80) {
|
|
if (intersectLineLine(_panel[i]._x2, _panel[i]._z2,
|
|
_panel[_panel[i]._col2 & 0x7F]._x2, _panel[_panel[i]._col2 & 0x7F]._z2,
|
|
_pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z,
|
|
_curX, _curZ)) {
|
|
if (_vm->dist2D(_x3d, _z3d, _pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z) > EPSILON &&
|
|
_vm->dist2D(_x3d, _z3d, _curX, _curZ) > EPSILON)
|
|
++inters;
|
|
}
|
|
} else if (intersectLineLine(_panel[i]._x2, _panel[i]._z2,
|
|
_panel[_panel[i]._col2 & 0x7F]._x1, _panel[_panel[i]._col2 & 0x7F]._z1,
|
|
_pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z,
|
|
_curX, _curZ)) {
|
|
if (_vm->dist2D(_x3d, _z3d, _pathNode[_numPathNodes - 1]._x, _pathNode[_numPathNodes - 1]._z) > EPSILON &&
|
|
_vm->dist2D(_x3d, _z3d, _curX, _curZ) > EPSILON)
|
|
++inters;
|
|
}
|
|
}
|
|
|
|
if (inters)
|
|
break;
|
|
}
|
|
|
|
// If in the last line there's an obstacle, remove the first node
|
|
if (inters) {
|
|
_curPanel = _pathNode[_numPathNodes - 1]._curPanel;
|
|
|
|
pointOut(); // take out the point found
|
|
|
|
_pathNode[_numPathNodes]._x = _curX;
|
|
_pathNode[_numPathNodes]._z = _curZ;
|
|
_pathNode[_numPathNodes]._oldPanel = _curPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _curPanel;
|
|
|
|
++_numPathNodes;
|
|
}
|
|
|
|
_pathNode[_numPathNodes]._x = _curX;
|
|
_pathNode[_numPathNodes]._z = _curZ;
|
|
_pathNode[_numPathNodes]._dist = _vm->dist2D(actor->_px, actor->_pz, _curX, _curZ);
|
|
_pathNode[_numPathNodes]._oldPanel = _curPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _curPanel;
|
|
++_numPathNodes;
|
|
|
|
findShortPath();
|
|
displayPath();
|
|
} else { // otherwise if it's direct
|
|
_pathNode[_numPathNodes]._x = actor->_px;
|
|
_pathNode[_numPathNodes]._z = actor->_pz;
|
|
_pathNode[_numPathNodes]._dist = 0.0f;
|
|
_pathNode[_numPathNodes]._oldPanel = _oldPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _oldPanel;
|
|
++_numPathNodes;
|
|
|
|
_pathNode[_numPathNodes]._x = _curX;
|
|
_pathNode[_numPathNodes]._z = _curZ;
|
|
_pathNode[_numPathNodes]._dist = _vm->dist2D(actor->_px, actor->_pz, _curX, _curZ);
|
|
_pathNode[_numPathNodes]._oldPanel = _curPanel;
|
|
_pathNode[_numPathNodes]._curPanel = _curPanel;
|
|
++_numPathNodes;
|
|
|
|
displayPath();
|
|
}
|
|
|
|
actor->_px -= actor->_dx;
|
|
actor->_pz -= actor->_dz;
|
|
}
|
|
/**
|
|
* Look for the shorter route avoiding obstacle
|
|
*/
|
|
void PathFinding3D::findShortPath() {
|
|
SPathNode tempPath[MAXPATHNODES];
|
|
float len1, len2;
|
|
int curPanel, nearPanel, oldPanel;
|
|
float destX, destZ;
|
|
|
|
bool fail = false;
|
|
|
|
int count = 0;
|
|
// Add departure
|
|
tempPath[count]._x = _vm->_actor->_px;
|
|
tempPath[count]._z = _vm->_actor->_pz;
|
|
tempPath[count]._dist = 0.0f;
|
|
tempPath[count]._oldPanel = _oldPanel;
|
|
tempPath[count]._curPanel = _oldPanel;
|
|
++count;
|
|
|
|
// for every obstacle, try to go around it by the right and the left
|
|
// then take the sorter path
|
|
for (int i = 0; i < _numPathNodes - 1; ++i) {
|
|
memcpy(&tempPath[count], &_pathNode[i], sizeof(SPathNode));
|
|
++count;
|
|
if (count >= MAXPATHNODES - 2)
|
|
count = MAXPATHNODES - 2;
|
|
|
|
curPanel = _pathNode[i]._curPanel;
|
|
|
|
// if source and destination panel are on the same block
|
|
if (!findAttachedPanel(curPanel, _pathNode[i + 1]._curPanel))
|
|
continue;
|
|
|
|
// go around obstacle starting with _nearPanel1
|
|
len1 = evalPath(i, _panel[curPanel]._x1, _panel[curPanel]._z1, _panel[curPanel]._nearPanel1) + _vm->dist2D(_pathNode[i]._x, _pathNode[i]._z, _panel[curPanel]._x1, _panel[curPanel]._z1);
|
|
|
|
// go around obstacle starting with _nearPanel2
|
|
len2 = evalPath(i, _panel[curPanel]._x2, _panel[curPanel]._z2, _panel[curPanel]._nearPanel2) + _vm->dist2D(_pathNode[i]._x, _pathNode[i]._z, _panel[curPanel]._x2, _panel[curPanel]._z2);
|
|
|
|
// Check which route was shorter
|
|
if ((len1 < 32000.0f) && (len2 < 32000.0f)) {
|
|
if (len1 < len2) {
|
|
destX = _panel[curPanel]._x1;
|
|
destZ = _panel[curPanel]._z1;
|
|
nearPanel = _panel[curPanel]._nearPanel1;
|
|
} else {
|
|
destX = _panel[curPanel]._x2;
|
|
destZ = _panel[curPanel]._z2;
|
|
nearPanel = _panel[curPanel]._nearPanel2;
|
|
}
|
|
|
|
float curX = _pathNode[i]._x;
|
|
float curZ = _pathNode[i]._z;
|
|
oldPanel = curPanel;
|
|
|
|
int index = 0;
|
|
|
|
// Save the shorter path
|
|
for (;;) {
|
|
tempPath[count]._x = curX;
|
|
tempPath[count]._z = curZ;
|
|
tempPath[count]._oldPanel = oldPanel;
|
|
tempPath[count]._curPanel = curPanel;
|
|
++count;
|
|
if (count >= MAXPATHNODES - 2)
|
|
count = MAXPATHNODES - 2;
|
|
|
|
// if it reaches the point, exit the loop
|
|
if (curPanel == _pathNode[i + 1]._curPanel) {
|
|
memcpy(&tempPath[count], &_pathNode[i + 1], sizeof(SPathNode));
|
|
++count;
|
|
if (count >= MAXPATHNODES - 2)
|
|
count = MAXPATHNODES - 2;
|
|
break;
|
|
}
|
|
|
|
// If it's back to the starting panel, it didn't find a route
|
|
if (((curPanel == _pathNode[i]._curPanel) && index) || (index > _panelNum)) {
|
|
fail = true; // stop at the edge first
|
|
break; // and stop walking
|
|
}
|
|
|
|
// otherwise go to the next panel
|
|
|
|
if (_panel[nearPanel]._nearPanel1 == curPanel) {
|
|
// go to summit 2 next time
|
|
curX = destX;
|
|
curZ = destZ;
|
|
|
|
destX = _panel[nearPanel]._x2;
|
|
destZ = _panel[nearPanel]._z2;
|
|
|
|
oldPanel = curPanel;
|
|
curPanel = nearPanel;
|
|
nearPanel = _panel[curPanel]._nearPanel2;
|
|
} else {
|
|
// go to summit 1 next time
|
|
curX = destX;
|
|
curZ = destZ;
|
|
|
|
destX = _panel[nearPanel]._x1;
|
|
destZ = _panel[nearPanel]._z1;
|
|
|
|
oldPanel = curPanel;
|
|
curPanel = nearPanel;
|
|
nearPanel = _panel[curPanel]._nearPanel1;
|
|
}
|
|
|
|
++index;
|
|
}
|
|
} else {
|
|
fail = true;
|
|
}
|
|
|
|
if (fail) // if it failed to go around the obstacle, stop
|
|
break;
|
|
}
|
|
|
|
// adds arrival
|
|
tempPath[count]._x = _curX;
|
|
tempPath[count]._z = _curZ;
|
|
tempPath[count]._dist = 0.0f;
|
|
tempPath[count]._oldPanel = _curPanel;
|
|
tempPath[count]._curPanel = _curPanel;
|
|
++count;
|
|
|
|
// after walking around all obstacles, optimize
|
|
_numPathNodes = 0;
|
|
for (int i = 0; i < count; ++i) {
|
|
if (_numPathNodes > MAXPATHNODES - 2)
|
|
_numPathNodes = MAXPATHNODES - 2;
|
|
|
|
int j;
|
|
// remove all the attached nodes
|
|
for (j = count - 1; j >= i; --j) {
|
|
if (_vm->dist2D(tempPath[j]._x, tempPath[j]._z, tempPath[i]._x, tempPath[i]._z) < EPSILON)
|
|
break;
|
|
}
|
|
|
|
i = j;
|
|
|
|
memcpy(&_pathNode[_numPathNodes], &tempPath[i], sizeof(SPathNode));
|
|
++_numPathNodes;
|
|
|
|
for (j = count - 1; j > i + 1; --j) {
|
|
int inters = 0;
|
|
for (int k = 0; k < _panelNum; ++k) {
|
|
// it must never intersect the small panel
|
|
if (!(_panel[k]._flags & 0x80000000)) {
|
|
if (intersectLineLine(_panel[k]._x1, _panel[k]._z1,
|
|
_panel[k]._x2, _panel[k]._z2,
|
|
tempPath[i]._x, tempPath[i]._z,
|
|
tempPath[j]._x, tempPath[j]._z))
|
|
++inters;
|
|
|
|
if (_panel[k]._col1 & 0x80) {
|
|
if (intersectLineLine(_panel[k]._x1, _panel[k]._z1,
|
|
_panel[_panel[k]._col1 & 0x7F]._x2, _panel[_panel[k]._col1 & 0x7F]._z2,
|
|
tempPath[i]._x, tempPath[i]._z,
|
|
tempPath[j]._x, tempPath[j]._z)) {
|
|
len2 = _vm->dist2D(_x3d, _z3d, tempPath[i]._x, tempPath[i]._z);
|
|
len1 = _vm->dist2D(_x3d, _z3d, tempPath[j]._x, tempPath[j]._z);
|
|
|
|
// intersect at a point distant from the start and the finish
|
|
if ((len1 > EPSILON) && (len2 > EPSILON))
|
|
++inters;
|
|
}
|
|
} else if (intersectLineLine(_panel[k]._x1, _panel[k]._z1,
|
|
_panel[_panel[k]._col1 & 0x7F]._x1, _panel[_panel[k]._col1 & 0x7F]._z1,
|
|
tempPath[i]._x, tempPath[i]._z,
|
|
tempPath[j]._x, tempPath[j]._z)) {
|
|
len2 = _vm->dist2D(_x3d, _z3d, tempPath[i]._x, tempPath[i]._z);
|
|
len1 = _vm->dist2D(_x3d, _z3d, tempPath[j]._x, tempPath[j]._z);
|
|
|
|
// intersect at a point distant from the start and the finish
|
|
if ((len1 > EPSILON) && (len2 > EPSILON))
|
|
++inters;
|
|
}
|
|
|
|
if (_panel[k]._col2 & 0x80) {
|
|
if (intersectLineLine(_panel[k]._x2, _panel[k]._z2,
|
|
_panel[_panel[k]._col2 & 0x7F]._x2, _panel[_panel[k]._col2 & 0x7F]._z2,
|
|
tempPath[i]._x, tempPath[i]._z,
|
|
tempPath[j]._x, tempPath[j]._z)) {
|
|
len2 = _vm->dist2D(_x3d, _z3d, tempPath[i]._x, tempPath[i]._z);
|
|
len1 = _vm->dist2D(_x3d, _z3d, tempPath[j]._x, tempPath[j]._z);
|
|
|
|
// intersect at a point distant from the start and the finish
|
|
if ((len1 > EPSILON) && (len2 > EPSILON))
|
|
++inters;
|
|
}
|
|
} else if (intersectLineLine(_panel[k]._x2, _panel[k]._z2,
|
|
_panel[_panel[k]._col2 & 0x7F]._x1, _panel[_panel[k]._col2 & 0x7F]._z1,
|
|
tempPath[i]._x, tempPath[i]._z,
|
|
tempPath[j]._x, tempPath[j]._z)) {
|
|
len2 = _vm->dist2D(_x3d, _z3d, tempPath[i]._x, tempPath[i]._z);
|
|
len1 = _vm->dist2D(_x3d, _z3d, tempPath[j]._x, tempPath[j]._z);
|
|
|
|
// intersect at a point distant from the start and the finish
|
|
if ((len1 > EPSILON) && (len2 > EPSILON))
|
|
++inters;
|
|
}
|
|
|
|
if (inters)
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if from A it's possible to reach B directly
|
|
if (!inters) {
|
|
curPanel = _pathNode[_numPathNodes - 1]._curPanel;
|
|
oldPanel = tempPath[j]._oldPanel;
|
|
|
|
int c;
|
|
for (c = i; c <= j; ++c) {
|
|
if ((tempPath[c]._oldPanel == curPanel) && (tempPath[c]._curPanel == oldPanel))
|
|
break;
|
|
}
|
|
|
|
// if they weren't connected it means it went through the floor
|
|
if (c > j) {
|
|
_pathNode[_numPathNodes - 1]._curPanel = -1; // start
|
|
tempPath[j]._oldPanel = -1; // destination
|
|
}
|
|
i = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluate path length
|
|
*/
|
|
float PathFinding3D::evalPath(int a, float destX, float destZ, int nearP) {
|
|
int index = 0;
|
|
float len = 0.0f;
|
|
|
|
int curPanel = _pathNode[a]._curPanel;
|
|
float curX = _pathNode[a]._x;
|
|
float curZ = _pathNode[a]._z;
|
|
|
|
for (;;) {
|
|
// if the point is reached, stop
|
|
if (curPanel == _pathNode[a + 1]._curPanel) {
|
|
len += _vm->dist2D(curX, curZ, _pathNode[a + 1]._x, _pathNode[a + 1]._z);
|
|
break;
|
|
}
|
|
|
|
// if it's back to the starting plane, there's no route
|
|
if (((curPanel == _pathNode[a]._curPanel) && index) || (index > _panelNum)) {
|
|
len += 32000.0f; // Absurd length
|
|
break;
|
|
}
|
|
|
|
// Otherwise it goes to the next plane
|
|
|
|
// if nearP is attached to curp via vertex1
|
|
if (_panel[nearP]._nearPanel1 == curPanel) {
|
|
// go to vertex 2 next time
|
|
len += _vm->dist2D(curX, curZ, destX, destZ);
|
|
|
|
curX = destX;
|
|
curZ = destZ;
|
|
|
|
destX = _panel[nearP]._x2;
|
|
destZ = _panel[nearP]._z2;
|
|
|
|
curPanel = nearP;
|
|
nearP = _panel[curPanel]._nearPanel2;
|
|
} else {
|
|
// go to vertex 1 newt time
|
|
len += _vm->dist2D(curX, curZ, destX, destZ);
|
|
|
|
curX = destX;
|
|
curZ = destZ;
|
|
|
|
destX = _panel[nearP]._x1;
|
|
destZ = _panel[nearP]._z1;
|
|
|
|
curPanel = nearP;
|
|
nearP = _panel[curPanel]._nearPanel1;
|
|
}
|
|
|
|
++index;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Check if a point is inside a panel
|
|
*/
|
|
bool PathFinding3D::pointInside(int pan, float x, float z) const {
|
|
if (pan < 0)
|
|
return false;
|
|
|
|
if (!(_panel[pan]._flags & 0x80000000))
|
|
return true;
|
|
|
|
double pgon[4][2];
|
|
pgon[0][0] = (double)_panel[pan]._x1;
|
|
pgon[0][1] = (double)_panel[pan]._z1;
|
|
pgon[3][0] = (double)_panel[pan]._x2;
|
|
pgon[3][1] = (double)_panel[pan]._z2;
|
|
|
|
uint8 idx = _panel[pan]._col1 & 0x7F;
|
|
if (_panel[pan]._col1 & 0x80) {
|
|
pgon[1][0] = (double)_panel[idx]._x2;
|
|
pgon[1][1] = (double)_panel[idx]._z2;
|
|
} else {
|
|
pgon[1][0] = (double)_panel[idx]._x1;
|
|
pgon[1][1] = (double)_panel[idx]._z1;
|
|
}
|
|
|
|
idx = _panel[pan]._col2 & 0x7F;
|
|
if (_panel[pan]._col2 & 0x80) {
|
|
pgon[2][0] = (double)_panel[idx]._x2;
|
|
pgon[2][1] = (double)_panel[idx]._z2;
|
|
} else {
|
|
pgon[2][0] = (double)_panel[idx]._x1;
|
|
pgon[2][1] = (double)_panel[idx]._z1;
|
|
}
|
|
|
|
double ox = pgon[3][0] - pgon[0][0];
|
|
double oz = pgon[3][1] - pgon[0][1];
|
|
double s = sqrt(ox * ox + oz * oz);
|
|
ox /= s;
|
|
oz /= s;
|
|
pgon[0][0] -= EPSILON * ox;
|
|
pgon[0][1] -= EPSILON * oz;
|
|
pgon[3][0] += EPSILON * ox;
|
|
pgon[3][1] += EPSILON * oz;
|
|
|
|
ox = pgon[2][0] - pgon[1][0];
|
|
oz = pgon[2][1] - pgon[1][1];
|
|
s = sqrt(ox * ox + oz * oz);
|
|
ox /= s;
|
|
oz /= s;
|
|
pgon[1][0] -= EPSILON * ox;
|
|
pgon[1][1] -= EPSILON * oz;
|
|
pgon[2][0] += EPSILON * ox;
|
|
pgon[2][1] += EPSILON * oz;
|
|
|
|
// Crossing-Multiply algorithm
|
|
double *vtx0 = pgon[3];
|
|
// get test bit for above/below X axis
|
|
bool yFlag0 = (vtx0[1] >= z);
|
|
double *vtx1 = pgon[0];
|
|
|
|
int counter = 0;
|
|
for (int j = 5; --j;) {
|
|
const bool yFlag1 = (vtx1[1] >= z);
|
|
if (yFlag0 != yFlag1) {
|
|
const bool xFlag0 = (vtx0[0] >= x);
|
|
if ((xFlag0 == (vtx1[0] >= x)) && (xFlag0))
|
|
counter += (yFlag0 ? -1 : 1);
|
|
else if ((vtx1[0] - (vtx1[1] - z) * (vtx0[0] - vtx1[0]) / (vtx0[1] - vtx1[1])) >= x)
|
|
counter += (yFlag0 ? -1 : 1);
|
|
}
|
|
|
|
// Move to the next pair of vertices, retaining info as possible.
|
|
yFlag0 = yFlag1;
|
|
vtx0 = vtx1;
|
|
vtx1 += 2;
|
|
}
|
|
|
|
return (counter != 0);
|
|
}
|
|
|
|
void PathFinding3D::setPosition(int num) {
|
|
SLight *curLight = _vm->_actor->_light;
|
|
|
|
for (uint32 i = 0; i < _vm->_actor->_lightNum; ++i, ++curLight) {
|
|
if (curLight->_inten != 0 || curLight->_position != num)
|
|
continue;
|
|
|
|
// If it's off and If it's the required position
|
|
|
|
_vm->_actor->_px = curLight->_x;
|
|
_vm->_actor->_pz = curLight->_z;
|
|
_vm->_actor->_dx = 0.0f;
|
|
_vm->_actor->_dz = 0.0f;
|
|
|
|
float ox = curLight->_dx;
|
|
float oz = curLight->_dz;
|
|
|
|
// If it's a null light
|
|
if (_vm->floatComp(ox, 0.0f) == 0 && _vm->floatComp(oz, 0.0f) == 0) // ox == 0.0f && oz == 0.0f
|
|
warning("setPosition: Unknown error : null light");
|
|
|
|
float t = sqrt(ox * ox + oz * oz);
|
|
ox /= t;
|
|
oz /= t;
|
|
|
|
float theta = _vm->sinCosAngle(ox, oz) * 180.0f / PI;
|
|
if (_vm->floatComp(theta, 360.0f) >= 0) // theta >= 360.0f
|
|
theta -= 360.0f;
|
|
if (_vm->floatComp(theta, 0.0f) == -1) // theta < 0.0f
|
|
theta += 360.0f;
|
|
|
|
_vm->_actor->_theta = theta;
|
|
|
|
_curStep = 0;
|
|
_lastStep = 0;
|
|
_curPanel = -1;
|
|
_oldPanel = -1;
|
|
|
|
reset(0, _vm->_actor->_px + _vm->_actor->_dx, _vm->_actor->_pz + _vm->_actor->_dz, _vm->_actor->_theta);
|
|
|
|
_characterGoToPosition = num;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PathFinding3D::goToPosition(int num) {
|
|
SLight *curLight = _vm->_actor->_light;
|
|
|
|
for (uint32 i = 0; i < _vm->_actor->_lightNum; ++i, ++curLight) {
|
|
if (curLight->_inten != 0 || curLight->_position != num)
|
|
continue;
|
|
|
|
// If it's off and if it's the right position
|
|
_curX = curLight->_x;
|
|
_curZ = curLight->_z;
|
|
_lookX = _curX - curLight->_dx;
|
|
_lookZ = _curZ - curLight->_dz;
|
|
|
|
_curStep = 0;
|
|
_lastStep = 0;
|
|
|
|
reset(0, _vm->_actor->_px + _vm->_actor->_dx, _vm->_actor->_pz + _vm->_actor->_dz, _vm->_actor->_theta);
|
|
|
|
_oldPanel = _curPanel;
|
|
_curPanel = -1;
|
|
|
|
findPath();
|
|
|
|
_characterGoToPosition = num;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PathFinding3D::lookAt(float x, float z) {
|
|
float ox = _step[_lastStep]._px - x;
|
|
float oz = _step[_lastStep]._pz - z;
|
|
|
|
// If the light is null
|
|
if (_vm->floatComp(ox, 0.0f) == 0 && _vm->floatComp(oz, 0.0f) == 0) {
|
|
memcpy(&_step[_lastStep + 1], &_step[_lastStep], sizeof(SStep));
|
|
memcpy(&_step[_lastStep + 2], &_step[_lastStep + 1], sizeof(SStep));
|
|
_lastStep += 2;
|
|
|
|
return;
|
|
}
|
|
|
|
float t = sqrt(ox * ox + oz * oz);
|
|
ox /= t;
|
|
oz /= t;
|
|
|
|
float theta = _vm->sinCosAngle(ox, oz) * 180.0f / PI;
|
|
if (_vm->floatComp(theta, 360.0f) >= 0) //theta >= 360.0f
|
|
theta -= 360.0f;
|
|
if (_vm->floatComp(theta, 0.0f) == -1) // theta < 0.0f
|
|
theta += 360.0f;
|
|
|
|
float approx = theta - _step[_lastStep]._theta;
|
|
|
|
if (_vm->floatComp(approx, 30.0f) == -1 && _vm->floatComp(approx, -30.0f) == 1) // approx < 30.0f && approx > -30.0f
|
|
approx = 0.0f;
|
|
else if (_vm->floatComp(approx, 180.0f) == 1) // approx > 180.0f
|
|
approx = -360.0f + approx;
|
|
else if (_vm->floatComp(approx, -180.0f) == -1) // approx < -180.0f
|
|
approx = 360.0f + approx;
|
|
|
|
approx /= 3.0f;
|
|
|
|
// Antepenultimate 1/3
|
|
_step[_lastStep]._theta += approx;
|
|
_step[_lastStep]._theta = _vm->floatComp(_step[_lastStep]._theta, 360.0f) == 1 ? _step[_lastStep]._theta - 360.0f : _vm->floatComp(_step[_lastStep]._theta, 0.0f) == -1 ? _step[_lastStep]._theta + 360.0f : _step[_lastStep]._theta;
|
|
|
|
// Penultimate 2/3
|
|
memcpy(&_step[_lastStep + 1], &_step[_lastStep], sizeof(SStep));
|
|
++_lastStep;
|
|
_step[_lastStep]._theta += approx;
|
|
_step[_lastStep]._theta = _vm->floatComp(_step[_lastStep]._theta, 360.0f) == 1 ? _step[_lastStep]._theta - 360.0f : _vm->floatComp(_step[_lastStep]._theta, 0.0f) == -1 ? _step[_lastStep]._theta + 360.0f : _step[_lastStep]._theta;
|
|
|
|
// Last right step
|
|
memcpy(&_step[_lastStep + 1], &_step[_lastStep], sizeof(SStep));
|
|
++_lastStep;
|
|
_step[_lastStep]._theta = theta;
|
|
|
|
// ????
|
|
memcpy(&_step[_lastStep + 1], &_step[_lastStep], sizeof(SStep));
|
|
++_lastStep;
|
|
_step[_lastStep]._theta = theta;
|
|
}
|
|
|
|
/**
|
|
* Build list containing all the frames
|
|
*/
|
|
void PathFinding3D::buildFramelist() {
|
|
// check that it never crosses or touches a narrow panel
|
|
for (int i = 1; i < _numPathNodes; ++i) {
|
|
for (int c = 0; c < _panelNum; ++c) {
|
|
// it must never intersect narrow panel
|
|
if (!(_panel[c]._flags & 0x80000000) && intersectLineLine(_panel[c]._x1, _panel[c]._z1,
|
|
_panel[c]._x2, _panel[c]._z2,
|
|
_pathNode[i - 1]._x, _pathNode[i - 1]._z,
|
|
_pathNode[i]._x, _pathNode[i]._z)) {
|
|
_numPathNodes = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
float len = 0.0f;
|
|
float curLen = 0.0f;
|
|
|
|
float ox = _pathNode[0]._x;
|
|
float oz = _pathNode[0]._z;
|
|
|
|
for (int i = 1; i < _numPathNodes; ++i) {
|
|
len += _vm->dist3D(_pathNode[i]._x, 0.0f, _pathNode[i]._z, ox, 0.0f, oz);
|
|
|
|
ox = _pathNode[i]._x;
|
|
oz = _pathNode[i]._z;
|
|
}
|
|
// total route length calculated - if too small, returns
|
|
if (_vm->floatComp(len, 2.0f) == -1) {
|
|
lookAt(_lookX, _lookZ);
|
|
return;
|
|
}
|
|
|
|
int i = 0;
|
|
// compute offset
|
|
SVertex *v = _vm->_actor->_characterArea;
|
|
float firstFrame = _vm->_actor->frameCenter(v);
|
|
|
|
// if he was already walking
|
|
int curAction, curFrame, cfp;
|
|
if (_vm->_actor->_curAction == hWALK) {
|
|
// compute current frame
|
|
cfp = defActionLen[hSTART] + 1 + _vm->_actor->_curFrame;
|
|
v += cfp * _vm->_actor->_vertexNum;
|
|
|
|
curAction = hWALK;
|
|
curFrame = _vm->_actor->_curFrame;
|
|
|
|
// if it wasn't the last frame, take the next step
|
|
if (_vm->_actor->_curFrame < defActionLen[hWALK] - 1) {
|
|
++cfp;
|
|
++curFrame;
|
|
v += _vm->_actor->_vertexNum;
|
|
}
|
|
} else if ((_vm->_actor->_curAction >= hSTOP0) && (_vm->_actor->_curAction <= hSTOP9)) {
|
|
// if he was stopped, starts moving again
|
|
|
|
// compute current frame
|
|
curAction = hWALK;
|
|
curFrame = _vm->_actor->_curAction - hSTOP0;
|
|
|
|
cfp = defActionLen[hSTART] + 1 + curFrame;
|
|
v += cfp * _vm->_actor->_vertexNum;
|
|
} else {
|
|
// if he was standing, start working or turn
|
|
oz = 0.0f;
|
|
cfp = 1;
|
|
|
|
curAction = hSTART;
|
|
curFrame = 0;
|
|
|
|
// start from the first frame
|
|
v += _vm->_actor->_vertexNum;
|
|
}
|
|
oz = -_vm->_actor->frameCenter(v) + firstFrame;
|
|
|
|
// at this point, CurA / _curAction is either hSTART or hWALK
|
|
|
|
// until it arrives at the destination
|
|
curLen = oz + _vm->_actor->frameCenter(v) - firstFrame;
|
|
while (_vm->floatComp(curLen, len) == -1 || !i) {
|
|
_step[i]._pz = oz - firstFrame; // where to render
|
|
_step[i]._dz = curLen; // where it is
|
|
_step[i]._curAction = curAction;
|
|
_step[i]._curFrame = curFrame;
|
|
|
|
++i;
|
|
v += _vm->_actor->_vertexNum;
|
|
|
|
++curFrame;
|
|
++cfp;
|
|
|
|
if (curFrame >= defActionLen[curAction]) {
|
|
if (curAction == hSTART) {
|
|
curAction = hWALK;
|
|
curFrame = 0;
|
|
cfp = defActionLen[hSTART] + 1;
|
|
|
|
ox = 0.0f;
|
|
} else if (curAction == hWALK) {
|
|
curAction = hWALK;
|
|
curFrame = 0;
|
|
cfp = defActionLen[hSTART] + 1;
|
|
|
|
// end walk frame
|
|
ox = _vm->_actor->frameCenter(v) - firstFrame;
|
|
|
|
v = &_vm->_actor->_characterArea[cfp * _vm->_actor->_vertexNum];
|
|
ox -= _vm->_actor->frameCenter(v);
|
|
}
|
|
|
|
v = &_vm->_actor->_characterArea[cfp * _vm->_actor->_vertexNum];
|
|
|
|
// only if it doesn't end
|
|
if (_vm->floatComp(oz + ox + _vm->_actor->frameCenter(v) - firstFrame, len) == -1) // oz + ox + _vm->_actor->frameCenter(v) - firstFrame < len
|
|
oz += ox;
|
|
else
|
|
break;
|
|
}
|
|
curLen = oz + _vm->_actor->frameCenter(v) - firstFrame;
|
|
}
|
|
|
|
// After the destination, add the stop frame
|
|
|
|
// if he was walking
|
|
if (_step[i - 1]._curAction == hWALK)
|
|
curAction = _step[i - 1]._curFrame + hSTOP0; // stop previous step.
|
|
else
|
|
curAction = hSTOP0; // stop step 01
|
|
|
|
assert(curAction <= hLAST); // _defActionLen below has a size of hLAST + 1
|
|
|
|
curFrame = 0;
|
|
|
|
int index = 0;
|
|
cfp = 0;
|
|
while (index != curAction)
|
|
cfp += defActionLen[index++];
|
|
|
|
v = &_vm->_actor->_characterArea[cfp * _vm->_actor->_vertexNum];
|
|
|
|
for (index = 0; index < defActionLen[curAction]; ++index) {
|
|
curLen = oz + _vm->_actor->frameCenter(v) - firstFrame;
|
|
_step[i]._pz = oz - firstFrame; // where to render
|
|
_step[i]._dz = curLen; // where it is
|
|
_step[i]._curAction = curAction;
|
|
_step[i]._curFrame = curFrame;
|
|
|
|
++i;
|
|
++curFrame;
|
|
v += _vm->_actor->_vertexNum;
|
|
}
|
|
|
|
// how far is it from the destination?
|
|
int divider = i - 2;
|
|
if (divider == 0)
|
|
// Safeguard, should never be useful... but if it is, it'll avoid a divide by 0 error.
|
|
divider = 1;
|
|
|
|
float approx = (len - curLen - EPSILON) / (float)divider;
|
|
float theta = 0.0f;
|
|
// Adjust all the steps so it arrives exactly where clicked
|
|
for (index = 1; index < i; ++index) {
|
|
// verify there's no reverse step
|
|
if (_vm->floatComp(_step[index - 1]._dz, _step[index]._dz + approx * index) == 1 || _vm->floatComp(_step[index]._dz + approx * index + EPSILON, len) >= 0) {
|
|
theta = _step[index]._dz - _step[index]._pz;
|
|
_step[index]._dz = _step[index - 1]._dz;
|
|
_step[index]._pz = _step[index]._dz - theta;
|
|
} else {
|
|
_step[index]._pz += (approx * index);
|
|
_step[index]._dz += (approx * index);
|
|
}
|
|
}
|
|
float cx = _step[index - 1]._dz;
|
|
|
|
_lastStep = index; // last step
|
|
_curStep = 0; // current step
|
|
|
|
// now insert exact directions and start and destination points
|
|
index = 0;
|
|
|
|
len = 0.0f;
|
|
float startPos = 0.0f;
|
|
for (i = 0; i < _numPathNodes - 1; ++i) {
|
|
curLen = 0.0f;
|
|
len += _vm->dist3D(_pathNode[i]._x, 0.0f, _pathNode[i]._z,
|
|
_pathNode[i + 1]._x, 0.0f, _pathNode[i + 1]._z);
|
|
|
|
// determine the direction
|
|
ox = _pathNode[i + 1]._x - _pathNode[i]._x;
|
|
oz = _pathNode[i + 1]._z - _pathNode[i]._z;
|
|
// if it's a useless node, remove it
|
|
if (_vm->floatComp(ox, 0.0f) == 0 && _vm->floatComp(oz, 0.0f) == 0)
|
|
continue;
|
|
|
|
approx = sqrt(ox * ox + oz * oz);
|
|
ox /= approx;
|
|
oz /= approx;
|
|
|
|
theta = _vm->sinCosAngle(ox, oz) * 180.0f / PI + 180.0f;
|
|
if (_vm->floatComp(theta, 360.0f) >= 0)
|
|
theta -= 360.0f;
|
|
if (_vm->floatComp(theta, 0.0f) == -1)
|
|
theta += 360.0f;
|
|
|
|
while (index < _lastStep && _vm->floatComp(_step[index]._dz, len) <= 0) {
|
|
curLen = _step[index]._dz - _step[index]._pz;
|
|
|
|
_step[index]._px = _pathNode[i]._x + (_step[index]._pz - startPos) * ox;
|
|
_step[index]._pz = _pathNode[i]._z + (_step[index]._pz - startPos) * oz;
|
|
_step[index]._dx = curLen * ox;
|
|
_step[index]._dz = curLen * oz;
|
|
_step[index]._theta = theta;
|
|
|
|
_step[index]._curPanel = _pathNode[i]._curPanel;
|
|
|
|
++index;
|
|
}
|
|
startPos = len;
|
|
}
|
|
|
|
reset(index, _curX, _curZ, theta);
|
|
|
|
_lastStep = index; // last step
|
|
_curStep = 0; // current step
|
|
|
|
// starting angle
|
|
float oldTheta = _vm->_actor->_theta;
|
|
// first angle walk
|
|
theta = _step[0]._theta;
|
|
|
|
// if he starts from standstill position
|
|
if ((_step[0]._curAction == hSTART) && (_step[0]._curFrame == 0) && (_lastStep > 4) && _vm->floatComp(_step[0]._theta, _step[1]._theta) == 0) {
|
|
approx = theta - oldTheta;
|
|
|
|
if (_vm->floatComp(approx, 180.0f) == 1)
|
|
approx = -360.0f + approx;
|
|
else if (_vm->floatComp(approx, -180.0f) == -1)
|
|
approx = 360.0f + approx;
|
|
|
|
approx /= 3.0f;
|
|
|
|
for (index = 0; index < 2; ++index) {
|
|
_step[index]._theta = oldTheta + (float)(index + 1) * approx;
|
|
_step[index]._theta = _vm->floatComp(_step[index]._theta, 360.0f) == 1 ? _step[index]._theta - 360.0f : _vm->floatComp(_step[index]._theta, 0.0f) == -1 ? _step[index]._theta + 360.0f : _step[index]._theta;
|
|
|
|
theta = _step[index]._theta;
|
|
|
|
curLen = sqrt(_step[index]._dx * _step[index]._dx + _step[index]._dz * _step[index]._dz);
|
|
|
|
theta = ((270.0f - theta) * PI) / 180.0f;
|
|
ox = cos(theta) * curLen;
|
|
oz = sin(theta) * curLen;
|
|
|
|
cx = _step[index]._px + _step[index]._dx;
|
|
float cz = _step[index]._pz + _step[index]._dz;
|
|
|
|
_step[index]._px += _step[index]._dx - ox;
|
|
_step[index]._pz += _step[index]._dz - oz;
|
|
|
|
_step[index]._dx = cx - _step[index]._px;
|
|
_step[index]._dz = cz - _step[index]._pz;
|
|
}
|
|
}
|
|
|
|
// makes the curve
|
|
oldTheta = _step[2]._theta;
|
|
for (index = 3; index <= _lastStep; ++index) {
|
|
theta = _step[index]._theta;
|
|
|
|
// if it made a curve
|
|
if (_vm->floatComp(oldTheta, theta) != 0) {
|
|
approx = theta - oldTheta;
|
|
|
|
if (_vm->floatComp(approx, 180.0f) == 1)
|
|
approx = -360.0f + approx;
|
|
else if (_vm->floatComp(approx, -180.0f) == -1)
|
|
approx = 360.0f + approx;
|
|
|
|
approx /= 3.0f;
|
|
|
|
// for the previous one
|
|
_step[index - 1]._theta += approx;
|
|
_step[index - 1]._theta = _vm->floatComp(_step[index - 1]._theta, 360.0f) == 1 ? _step[index - 1]._theta - 360.0f : _vm->floatComp(_step[index - 1]._theta, 0.0f) == -1 ? _step[index - 1]._theta + 360.0f : _step[index - 1]._theta;
|
|
|
|
oldTheta = _step[index - 1]._theta;
|
|
|
|
curLen = sqrt(_step[index - 1]._dx * _step[index - 1]._dx + _step[index - 1]._dz * _step[index - 1]._dz);
|
|
|
|
oldTheta = ((270.0f - oldTheta) * PI) / 180.0f;
|
|
ox = cos(oldTheta) * curLen;
|
|
oz = sin(oldTheta) * curLen;
|
|
|
|
cx = _step[index - 1]._px + _step[index - 1]._dx;
|
|
float cz = _step[index - 1]._pz + _step[index - 1]._dz;
|
|
|
|
_step[index - 1]._px += _step[index - 1]._dx - ox;
|
|
_step[index - 1]._pz += _step[index - 1]._dz - oz;
|
|
|
|
_step[index - 1]._dx = cx - _step[index - 1]._px;
|
|
_step[index - 1]._dz = cz - _step[index - 1]._pz;
|
|
|
|
// for the next one
|
|
_step[index]._theta -= approx;
|
|
_step[index]._theta = _vm->floatComp(_step[index]._theta, 360.0f) == 1 ? _step[index]._theta - 360.0f : _vm->floatComp(_step[index]._theta, 0.0f) == -1 ? _step[index]._theta + 360.0f : _step[index]._theta;
|
|
|
|
oldTheta = theta;
|
|
theta = _step[index]._theta;
|
|
|
|
curLen = sqrt(_step[index]._dx * _step[index]._dx + _step[index]._dz * _step[index]._dz);
|
|
|
|
theta = ((270.0f - theta) * PI) / 180.0f;
|
|
ox = cos(theta) * curLen;
|
|
oz = sin(theta) * curLen;
|
|
|
|
cx = _step[index]._px + _step[index]._dx;
|
|
cz = _step[index]._pz + _step[index]._dz;
|
|
|
|
_step[index]._px += _step[index]._dx - ox;
|
|
_step[index]._pz += _step[index]._dz - oz;
|
|
|
|
_step[index]._dx = cx - _step[index]._px;
|
|
_step[index]._dz = cz - _step[index]._pz;
|
|
|
|
} else
|
|
oldTheta = theta;
|
|
}
|
|
|
|
lookAt(_lookX, _lookZ);
|
|
}
|
|
|
|
/**
|
|
* Take the next frame walk
|
|
*/
|
|
int PathFinding3D::nextStep() {
|
|
Actor *actor = _vm->_actor;
|
|
actor->_px = _step[_curStep]._px;
|
|
actor->_pz = _step[_curStep]._pz;
|
|
actor->_dx = _step[_curStep]._dx;
|
|
actor->_dz = _step[_curStep]._dz;
|
|
actor->_theta = _step[_curStep]._theta;
|
|
actor->_curAction = _step[_curStep]._curAction;
|
|
actor->_curFrame = _step[_curStep]._curFrame;
|
|
_curPanel = _step[_curStep]._curPanel;
|
|
|
|
// increase the current step if it's not the last frame
|
|
if (_curStep < _lastStep) {
|
|
++_curStep;
|
|
return false;
|
|
}
|
|
|
|
if (_characterGoToPosition != -1)
|
|
setPosition(_characterGoToPosition);
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* View route
|
|
*/
|
|
void PathFinding3D::displayPath() {
|
|
buildFramelist();
|
|
}
|
|
|
|
/**
|
|
* Check if two panels are in the same block
|
|
*/
|
|
bool PathFinding3D::findAttachedPanel(int16 srcPanel, int16 destPanel) {
|
|
// if at least one is on the floor, return false
|
|
if (srcPanel < 0 || destPanel < 0)
|
|
return false;
|
|
|
|
// if they are equal, return true
|
|
if (srcPanel == destPanel)
|
|
return true;
|
|
|
|
int16 curPanel = srcPanel;
|
|
int16 nearPanel = _panel[srcPanel]._nearPanel1;
|
|
|
|
for (int i = 0;; ++i) {
|
|
// if they are attached, return true
|
|
if (curPanel == destPanel)
|
|
return true;
|
|
|
|
// if it has returned to the starting panel, return false
|
|
if (srcPanel == curPanel && i)
|
|
return false;
|
|
|
|
if (i > _panelNum)
|
|
return false;
|
|
|
|
// if they are attached to vertex 1, take 2
|
|
if (_panel[nearPanel]._nearPanel1 == curPanel) {
|
|
curPanel = nearPanel;
|
|
nearPanel = _panel[curPanel]._nearPanel2;
|
|
} else {
|
|
curPanel = nearPanel;
|
|
nearPanel = _panel[curPanel]._nearPanel1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compare route distance (qsort)
|
|
*/
|
|
int pathCompare(const void *arg1, const void *arg2) {
|
|
const SPathNode *p1 = (const SPathNode *)arg1;
|
|
const SPathNode *p2 = (const SPathNode *)arg2;
|
|
|
|
if (p1->_dist < p2->_dist)
|
|
return -1;
|
|
|
|
if (p1->_dist > p2->_dist)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sort the nodes of the path found
|
|
*/
|
|
void PathFinding3D::sortPath() {
|
|
qsort(&_pathNode[0], _numPathNodes, sizeof(SPathNode), pathCompare);
|
|
}
|
|
|
|
/**
|
|
* Initializes sort panel
|
|
*/
|
|
void PathFinding3D::initSortPan() {
|
|
_numSortPanel = 31;
|
|
|
|
for (int i = 1; i < _numSortPanel - 1; ++i) {
|
|
_sortPan[i]._min = 32000.0f;
|
|
_sortPan[i]._num = i;
|
|
}
|
|
|
|
// First panel is behind everything and is not sorted
|
|
_sortPan[0]._min = 30000.0f;
|
|
_sortPan[0]._num = BOX_BACKGROUND;
|
|
|
|
// Last panel is in front of everything and is not sorted
|
|
_sortPan[30]._min = 0.0f;
|
|
_sortPan[30]._num = BOX_FOREGROUND;
|
|
|
|
Actor *actor = _vm->_actor;
|
|
// Sort panel blocks by increasing distance from the camera
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
if (!(_panel[i]._flags & 0x80000000)) {
|
|
float dist1 = _vm->dist3D(actor->_camera->_ex, 0.0, actor->_camera->_ez, _panel[i]._x1, 0.0, _panel[i]._z1);
|
|
float dist2 = _vm->dist3D(actor->_camera->_ex, 0.0, actor->_camera->_ez, _panel[i]._x2, 0.0, _panel[i]._z2);
|
|
|
|
float min = MIN(dist1, dist2);
|
|
|
|
for (int j = 0; j < _numSortPanel; ++j) {
|
|
if (_panel[i]._flags & (1 << j)) {
|
|
if (_sortPan[j + 1]._min > min)
|
|
_sortPan[j + 1]._min = min;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sortPanel();
|
|
|
|
for (int i = 0; i < _numSortPanel; ++i) {
|
|
if (_sortPan[i]._num == BOX_BACKGROUND) {
|
|
// now the panels go from 0 (foreground) to _numSortPanel (background)
|
|
_numSortPanel = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PathFinding3D::read3D(Common::SeekableReadStreamEndian *ff) {
|
|
// read panels
|
|
_panelNum = ff->readSint32();
|
|
if (_panelNum > MAXPANELSINROOM)
|
|
error("read3D(): Too many panels");
|
|
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
_panel[i]._x1 = ff->readFloat();
|
|
_panel[i]._z1 = ff->readFloat();
|
|
_panel[i]._x2 = ff->readFloat();
|
|
_panel[i]._z2 = ff->readFloat();
|
|
_panel[i]._h = ff->readFloat();
|
|
_panel[i]._flags = ff->readUint32();
|
|
|
|
// Note : Despite the panels are stored in an int16 with a MAXPANELSINROOM set to 400,
|
|
// _panelNum is stored in a int32 and nearPanel1 and 2 were stored in an int8
|
|
// in the data files. It's weird, but that's how the original game works so please
|
|
// don't change that.
|
|
_panel[i]._nearPanel1 = ff->readSByte();
|
|
_panel[i]._nearPanel2 = ff->readSByte();
|
|
_panel[i]._col1 = ff->readSByte();
|
|
_panel[i]._col2 = ff->readSByte();
|
|
}
|
|
|
|
// projection matrix
|
|
float _proj[3][3];
|
|
SCamera *cam = _vm->_actor->_camera;
|
|
_proj[0][0] = cam->_e1[0];
|
|
_proj[0][1] = cam->_e1[1];
|
|
_proj[0][2] = cam->_e1[2];
|
|
_proj[1][0] = cam->_e2[0];
|
|
_proj[1][1] = cam->_e2[1];
|
|
_proj[1][2] = cam->_e2[2];
|
|
_proj[2][0] = cam->_e3[0];
|
|
_proj[2][1] = cam->_e3[1];
|
|
_proj[2][2] = cam->_e3[2];
|
|
|
|
// Compute 3x3 inverse matrix for 2D points on 3D
|
|
float det = _proj[0][0] * _proj[1][1] * _proj[2][2] +
|
|
_proj[0][1] * _proj[1][2] * _proj[2][0] +
|
|
_proj[0][2] * _proj[1][0] * _proj[2][1] -
|
|
_proj[2][0] * _proj[1][1] * _proj[0][2] -
|
|
_proj[2][1] * _proj[1][2] * _proj[2][0] -
|
|
_proj[2][2] * _proj[1][0] * _proj[2][1];
|
|
|
|
if (_vm->floatComp(det, 0.0f) == 0)
|
|
error("read3D : Unexpected data error while computing inverse matrix");
|
|
|
|
_invP[0][0] = (_proj[1][1] * _proj[2][2] - _proj[1][2] * _proj[2][1]) / det;
|
|
_invP[0][1] = (_proj[0][1] * _proj[2][2] - _proj[0][2] * _proj[2][1]) / (-det);
|
|
_invP[0][2] = (_proj[0][1] * _proj[1][2] - _proj[0][2] * _proj[1][1]) / det;
|
|
_invP[1][0] = (_proj[1][0] * _proj[2][2] - _proj[1][2] * _proj[2][0]) / (-det);
|
|
_invP[1][1] = (_proj[0][0] * _proj[2][2] - _proj[0][2] * _proj[2][0]) / det;
|
|
_invP[1][2] = (_proj[0][0] * _proj[1][2] - _proj[0][2] * _proj[1][0]) / (-det);
|
|
_invP[2][0] = (_proj[1][0] * _proj[2][1] - _proj[1][1] * _proj[2][0]) / det;
|
|
_invP[2][1] = (_proj[0][0] * _proj[2][1] - _proj[0][1] * _proj[2][0]) / (-det);
|
|
_invP[2][2] = (_proj[0][0] * _proj[1][1] - _proj[0][1] * _proj[1][0]) / det;
|
|
}
|
|
|
|
void PathFinding3D::reset(uint16 idx,float px, float pz, float theta) {
|
|
_step[idx]._px = px;
|
|
_step[idx]._pz = pz;
|
|
_step[idx]._dx = 0.0f;
|
|
_step[idx]._dz = 0.0f;
|
|
|
|
_step[idx]._theta = theta;
|
|
_step[idx]._curAction = hSTAND;
|
|
_step[idx]._curFrame = 0;
|
|
_step[idx]._curPanel = _curPanel;
|
|
}
|
|
|
|
/**
|
|
* Compare panel distance (qsort)
|
|
*/
|
|
int panelCompare(const void *arg1, const void *arg2) {
|
|
const SSortPan *p1 = (const SSortPan *)arg1;
|
|
const SSortPan *p2 = (const SSortPan *)arg2;
|
|
|
|
if (p1->_min > p2->_min)
|
|
return 1;
|
|
|
|
if (p1->_min < p2->_min)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sort the panels
|
|
*/
|
|
void PathFinding3D::sortPanel() {
|
|
qsort(&_sortPan[0], _numSortPanel, sizeof(SSortPan), panelCompare);
|
|
}
|
|
|
|
/**
|
|
* Find the 3D point corresponding to the 2D point
|
|
*/
|
|
void PathFinding3D::whereIs(int px, int py) {
|
|
float inters = 32000.0f;
|
|
|
|
_vm->_actor->_px += _vm->_actor->_dx;
|
|
_vm->_actor->_pz += _vm->_actor->_dz;
|
|
|
|
_oldPanel = _curPanel;
|
|
_curPanel = -2;
|
|
|
|
invPointProject(px, py);
|
|
float x = _x3d;
|
|
float y = _y3d;
|
|
float z = _z3d;
|
|
|
|
// Try to intersect with the floor
|
|
if (intersectLineFloor(x, y, z)) {
|
|
_curPanel = -1;
|
|
_curX = _x3d;
|
|
_curZ = _z3d;
|
|
}
|
|
|
|
// try all the panels and choose the closest one
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
if (intersectLinePanel(&_panel[i], x, y, z)) {
|
|
float temp = _vm->dist3D(_vm->_actor->_camera->_ex, _vm->_actor->_camera->_ey, _vm->_actor->_camera->_ez, _x3d, _y3d, _z3d);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
_curX = _x3d;
|
|
_curZ = _z3d;
|
|
}
|
|
}
|
|
}
|
|
|
|
_lookX = _curX;
|
|
_lookZ = _curZ;
|
|
|
|
pointOut();
|
|
|
|
_vm->_actor->_px -= _vm->_actor->_dx;
|
|
_vm->_actor->_pz -= _vm->_actor->_dz;
|
|
}
|
|
|
|
/**
|
|
* Brings out point from inner panel
|
|
*/
|
|
void PathFinding3D::pointOut() {
|
|
const float largeValue = 60.0f; // 30 cm = 15 enlarge * 2
|
|
|
|
float x = 0.0f, z = 0.0f;
|
|
float inters = 32000.0f;
|
|
|
|
// If I hit the floor, I have to count how many times
|
|
// the straight line intersects with the wide panels
|
|
if (_curPanel < 0)
|
|
return;
|
|
|
|
SPan *panel = &_panel[_curPanel];
|
|
float nx = panel->_z1 - panel->_z2;
|
|
float nz = panel->_x2 - panel->_x1;
|
|
float temp = sqrt(nx * nx + nz * nz);
|
|
nx /= temp;
|
|
nz /= temp;
|
|
|
|
// move the point on the wide panel
|
|
for (int i = 0; i < _panelNum; ++i) {
|
|
panel = &_panel[i];
|
|
// Only check the external panels with the same flag
|
|
if ((panel->_flags & 0x80000000) && (panel->_flags & (_panel[_curPanel]._flags & 0x7FFFFFFF))) {
|
|
// check point 1
|
|
temp = _vm->dist2D(_curX, _curZ, panel->_x1, panel->_z1);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = panel->_x1;
|
|
z = panel->_z1;
|
|
}
|
|
|
|
// check point 2
|
|
temp = _vm->dist2D(_curX, _curZ, panel->_x2, panel->_z2);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = panel->_x2;
|
|
z = panel->_z2;
|
|
}
|
|
|
|
// check point a 1/3
|
|
temp = _vm->dist2D(_curX, _curZ, (panel->_x1 * 2.0f + panel->_x2) / 3.0f, (panel->_z1 * 2.0f + panel->_z2) / 3.0f);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = (panel->_x1 * 2.0f + panel->_x2) / 3.0f;
|
|
z = (panel->_z1 * 2.0f + panel->_z2) / 3.0f;
|
|
}
|
|
|
|
// check point a 2/3
|
|
temp = _vm->dist2D(_curX, _curZ, (panel->_x1 + panel->_x2 * 2.0f) / 3.0f, (panel->_z1 + panel->_z2 * 2.0f) / 3.0f);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = (panel->_x1 + panel->_x2 * 2.0f) / 3.0f;
|
|
z = (panel->_z1 + panel->_z2 * 2.0f) / 3.0f;
|
|
}
|
|
|
|
// check intersection with camera
|
|
if (intersectLineLine(panel->_x1, panel->_z1, panel->_x2, panel->_z2, _vm->_actor->_camera->_ex, _vm->_actor->_camera->_ez, _curX, _curZ)) {
|
|
temp = _vm->dist2D(_curX, _curZ, _x3d, _z3d);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = _x3d;
|
|
z = _z3d;
|
|
}
|
|
}
|
|
|
|
// check intersection with character
|
|
if (intersectLineLine(panel->_x1, panel->_z1, panel->_x2, panel->_z2, _vm->_actor->_px, _vm->_actor->_pz, _curX, _curZ)) {
|
|
temp = _vm->dist2D(_curX, _curZ, _x3d, _z3d);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = _x3d;
|
|
z = _z3d;
|
|
}
|
|
}
|
|
|
|
// check intersection with normal panel
|
|
if (intersectLineLine(panel->_x1, panel->_z1, panel->_x2, panel->_z2,
|
|
_curX + nx * largeValue, _curZ + nz * largeValue, _curX - nx * largeValue, _curZ - nz * largeValue)) {
|
|
temp = _vm->dist2D(_curX, _curZ, _x3d, _z3d);
|
|
|
|
if (_vm->floatComp(temp, inters) == -1) {
|
|
inters = temp;
|
|
_curPanel = i;
|
|
x = _x3d;
|
|
z = _z3d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_curX = x;
|
|
_curZ = z;
|
|
}
|
|
|
|
/**
|
|
* Projects 2D point in a 3D world
|
|
*/
|
|
void PathFinding3D::invPointProject(int x, int y) {
|
|
float px = (float)(x - _vm->_cx) / _vm->_actor->_camera->_fovX;
|
|
float py = (float)(y - _vm->_cy) / _vm->_actor->_camera->_fovY;
|
|
|
|
_x3d = px * _invP[0][0] + py * _invP[0][1] + _invP[0][2];
|
|
_y3d = px * _invP[1][0] + py * _invP[1][1] + _invP[1][2];
|
|
_z3d = px * _invP[2][0] + py * _invP[2][1] + _invP[2][2];
|
|
|
|
_x3d += _vm->_actor->_camera->_ex;
|
|
_y3d += _vm->_actor->_camera->_ey;
|
|
_z3d += _vm->_actor->_camera->_ez;
|
|
}
|
|
|
|
/**
|
|
* Intersects a 3D line with the panel
|
|
*/
|
|
bool PathFinding3D::intersectLinePanel(SPan *p, float x, float y, float z) {
|
|
// If it's an enlarged panel
|
|
if (p->_flags & 0x80000000)
|
|
return false;
|
|
|
|
float x1 = _vm->_actor->_camera->_ex;
|
|
float y1 = _vm->_actor->_camera->_ey;
|
|
float z1 = _vm->_actor->_camera->_ez;
|
|
|
|
float dx = (x - x1);
|
|
float dy = (y - y1);
|
|
float dz = (z - z1);
|
|
float t = sqrt(dx * dx + dy * dy + dz * dz);
|
|
dx /= t;
|
|
dy /= t;
|
|
dz /= t;
|
|
|
|
float nx = p->_z1 - p->_z2;
|
|
float nz = p->_x2 - p->_x1;
|
|
t = sqrt(nx * nx + nz * nz);
|
|
nx /= t;
|
|
nz /= t;
|
|
// ny is always equal to zero for panels
|
|
|
|
float n = nx * p->_x1 + nz * p->_z1 - nx * x - nz * z;
|
|
float d = dx * nx + dz * nz;
|
|
|
|
if (_vm->floatComp(d, 0.0f) != 0) {
|
|
t = n / d;
|
|
|
|
if (_vm->floatComp(t, 0.0f) <= 0)
|
|
return false;
|
|
|
|
_x3d = x1 + dx * t;
|
|
_y3d = y1 + dy * t;
|
|
_z3d = z1 + dz * t;
|
|
|
|
float minX = MIN(p->_x1, p->_x2) - 1.5f;
|
|
float maxX = MAX(p->_x1, p->_x2) + 1.5f;
|
|
float minZ = MIN(p->_z1, p->_z2) - 1.5f;
|
|
float maxZ = MAX(p->_z1, p->_z2) + 1.5f;
|
|
|
|
// check if it fits inside the panel
|
|
if (_vm->floatComp(_x3d, minX) >= 0 && _vm->floatComp(_x3d, maxX) <= 0 && _vm->floatComp(_y3d, 0.0) >= 0 && _vm->floatComp(_y3d, p->_h) <= 0 && _vm->floatComp(_z3d, minZ) >= 0 && _vm->floatComp(_z3d, maxZ) <= 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Intersects 3D line with the floor
|
|
*/
|
|
bool PathFinding3D::intersectLineFloor(float x, float y, float z) {
|
|
float x1 = _vm->_actor->_camera->_ex;
|
|
float y1 = _vm->_actor->_camera->_ey;
|
|
float z1 = _vm->_actor->_camera->_ez;
|
|
|
|
float dx = (x - x1);
|
|
float dy = (y - y1);
|
|
float dz = (z - z1);
|
|
float t = sqrt(dx * dx + dy * dy + dz * dz);
|
|
dx /= t;
|
|
dy /= t;
|
|
dz /= t;
|
|
|
|
// ny is always equal to 1 for the floor
|
|
|
|
if (_vm->floatComp(dy, 0.0f) != 0) {
|
|
t = -y / dy;
|
|
|
|
if (_vm->floatComp(t, 0.0f) <= 0)
|
|
return false;
|
|
|
|
_x3d = x1 + dx * t;
|
|
_y3d = y1 + dy * t;
|
|
_z3d = z1 + dz * t;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Intersects a 2D line with a 2D line
|
|
*/
|
|
bool PathFinding3D::intersectLineLine(float xa, float ya, float xb, float yb, float xc, float yc, float xd, float yd) {
|
|
float divisor = (float)((xb - xa) * (yd - yc) - (yb - ya) * (xd - xc));
|
|
if (_vm->floatComp(divisor, 0.0f) == 0)
|
|
return false;
|
|
|
|
float r = (float)((ya - yc) * (xd - xc) - (xa - xc) * (yd - yc)) / divisor;
|
|
float s = (float)((ya - yc) * (xb - xa) - (xa - xc) * (yb - ya)) / divisor;
|
|
if (_vm->floatComp(r, 0.0f) == -1 || _vm->floatComp(r, 1.0f) == 1 || _vm->floatComp(s, 0.0f) == -1 || _vm->floatComp(s, 1.0f) == 1)
|
|
return false;
|
|
|
|
_x3d = xa + r * (xb - xa);
|
|
_y3d = 0.0f;
|
|
_z3d = ya + r * (yb - ya);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Tells after which panel the character stands
|
|
*/
|
|
void PathFinding3D::actorOrder() {
|
|
const float largeValue = 15.0f; // 30 cm (max)
|
|
Actor *actor = _vm->_actor;
|
|
|
|
if (_forcedActorPos != BOX_NORMAL) {
|
|
_actorPos = _forcedActorPos;
|
|
return;
|
|
}
|
|
|
|
float ox = actor->_px + actor->_dx - actor->_camera->_ex;
|
|
float oz = actor->_pz + actor->_dz - actor->_camera->_ez;
|
|
float dist = sqrt(ox * ox + oz * oz);
|
|
float lx = (-oz / dist) * largeValue;
|
|
float lz = (ox / dist) * largeValue;
|
|
|
|
ox = actor->_px + actor->_dx;
|
|
oz = actor->_pz + actor->_dz;
|
|
|
|
// It must be copied in front of the nearest box
|
|
_actorPos = _sortPan[1]._num;
|
|
// from closest to farthest
|
|
for (int i = 1; i < _numSortPanel; ++i) {
|
|
for (int j = 0; j < _panelNum; ++j) {
|
|
// If it's not wide and belongs to this level
|
|
if (!(_panel[j]._flags & 0x80000000) && (_panel[j]._flags & (1 << (_sortPan[i]._num - 1)))) {
|
|
// If it intersects the center of the character camera
|
|
if (intersectLineLine(_panel[j]._x1, _panel[j]._z1, _panel[j]._x2, _panel[j]._z2, actor->_camera->_ex, actor->_camera->_ez, ox, oz) || intersectLineLine(_panel[j]._x1, _panel[j]._z1, _panel[j]._x2, _panel[j]._z2, actor->_camera->_ex, actor->_camera->_ez, ox + lx, oz + lz) || intersectLineLine(_panel[j]._x1, _panel[j]._z1, _panel[j]._x2, _panel[j]._z2, actor->_camera->_ex, actor->_camera->_ez, ox - lx, oz - lz)) {
|
|
// If it intersects it must be copied after the next box
|
|
_actorPos = _sortPan[i + 1]._num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PathFinding3D::syncGameStream(Common::Serializer &ser) {
|
|
ser.syncAsSint32LE(_curPanel);
|
|
ser.syncAsSint32LE(_oldPanel);
|
|
}
|
|
|
|
} // End of namespace Trecision
|