TWP: Fix pathfinding

This commit is contained in:
scemino 2024-01-23 21:27:40 +01:00 committed by Eugene Sandulenko
parent 03dd90452d
commit 97c4353738
17 changed files with 323 additions and 369 deletions

View File

@ -204,7 +204,7 @@ static SQInteger actorDistanceTo(HSQUIRRELVM v) {
return sq_throwerror(v, "failed to get object");
else
obj = g_engine->_actor;
sqpush(v, distance(actor->_node->getPos(), obj->getUsePos()));
sqpush(v, distance((Vector2i)actor->_node->getPos(), (Vector2i)obj->getUsePos()));
return 1;
}
@ -219,7 +219,7 @@ static SQInteger actorDistanceWithin(HSQUIRRELVM v) {
if (!obj)
return sq_throwerror(v, "failed to get spot");
// not sure about this, needs to be check one day ;)
sqpush(v, distance(actor1->_node->getAbsPos(), obj->getUsePos()) < distance(actor2->_node->getAbsPos(), obj->getUsePos()));
sqpush(v, distance((Vector2i)actor1->_node->getAbsPos(), (Vector2i)obj->getUsePos()) < distance((Vector2i)actor2->_node->getAbsPos(), (Vector2i)obj->getUsePos()));
return 1;
} else if (nArgs == 4) {
Object *actor = sqactor(v, 2);
@ -231,7 +231,7 @@ static SQInteger actorDistanceWithin(HSQUIRRELVM v) {
int dist;
if (SQ_FAILED(sqget(v, 4, dist)))
return sq_throwerror(v, "failed to get distance");
sqpush(v, distance(actor->_node->getAbsPos(), obj->getUsePos()) < dist);
sqpush(v, distance((Vector2i)actor->_node->getAbsPos(), (Vector2i)obj->getUsePos()) < dist);
return 1;
} else {
return sq_throwerror(v, "actorDistanceWithin not implemented");
@ -336,7 +336,7 @@ static SQInteger actorInWalkbox(HSQUIRRELVM v) {
for (size_t i = 0; i < g_engine->_room->_walkboxes.size(); i++) {
const Walkbox &walkbox = g_engine->_room->_walkboxes[i];
if (walkbox._name == name) {
if (walkbox.contains(actor->_node->getAbsPos())) {
if (walkbox.contains((Vector2i)actor->_node->getAbsPos())) {
sqpush(v, true);
return 1;
}
@ -689,7 +689,7 @@ static SQInteger actorWalkForward(HSQUIRRELVM v) {
dir = Math::Vector2d(dist, 0);
break;
}
actor->walk(actor->_node->getAbsPos() + dir);
actor->walk((Vector2i)(actor->_node->getAbsPos() + dir));
return 0;
}
@ -760,7 +760,7 @@ static SQInteger actorWalkTo(HSQUIRRELVM v) {
return sq_throwerror(v, "failed to get dir");
facing = (Facing *)&dir;
}
actor->walk(Math::Vector2d(x, y), facing ? *facing : 0);
actor->walk(Vector2i(x, y), facing ? *facing : 0);
} else {
return sq_throwerror(v, "invalid number of arguments in actorWalkTo");
}

View File

@ -38,7 +38,7 @@
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32
#define use_int32
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz
@ -47,7 +47,7 @@
#define use_lines
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
//#define use_deprecated
#include <vector>
#include <list>
@ -302,7 +302,7 @@ private:
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
ZFillCallback m_ZFill; //custom callback
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge &edge);
bool IsEvenOddFillType(const TEdge &edge) const;

View File

@ -19,49 +19,14 @@
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "twp/graph.h"
#include "twp/util.h"
#define EPSILON 1e-9
#include "twp/clipper/clipper.hpp"
namespace Twp {
struct Segment {
Segment(Math::Vector2d s, Math::Vector2d t);
void normalize();
float distance(Math::Vector2d p);
Math::Vector2d start, to;
float left, right, top, bottom;
float a, b, c;
};
Segment::Segment(Math::Vector2d s, Math::Vector2d t) {
start = s;
to = t;
left = MIN(s.getX(), t.getX());
right = MAX(s.getX(), t.getX());
top = MIN(s.getY(), t.getY());
bottom = MAX(s.getY(), t.getY());
a = s.getY() - t.getY();
b = t.getX() - s.getX();
c = -a * s.getX() - b * s.getY();
normalize();
}
void Segment::normalize() {
float z = sqrt(a * a + b * b);
if (abs(z) > EPSILON) {
a /= z;
b /= z;
c /= z;
}
}
float Segment::distance(Math::Vector2d p) {
return a * p.getX() + b * p.getY() + c;
}
IndexedPriorityQueue::IndexedPriorityQueue(Common::Array<float> &keys)
: _keys(keys) {
}
@ -111,25 +76,11 @@ bool IndexedPriorityQueue::isEmpty() {
Graph::Graph() {}
Graph::Graph(const Graph &graph) {
_nodes = graph._nodes;
_concaveVertices = graph._concaveVertices;
for (size_t i = 0; i < graph._edges.size(); i++) {
const Common::Array<GraphEdge> &e = graph._edges[i];
Common::Array<GraphEdge> sEdges;
for (size_t j = 0; j < e.size(); j++) {
const GraphEdge &se = e[j];
sEdges.push_back(GraphEdge(se.start, se.to, se.cost));
}
_edges.push_back(sEdges);
}
}
GraphEdge::GraphEdge(int s, int t, float c)
: start(s), to(t), cost(c) {
}
void Graph::addNode(Math::Vector2d node) {
void Graph::addNode(Vector2i node) {
_nodes.push_back(node);
_edges.push_back(Common::Array<GraphEdge>());
}
@ -139,16 +90,11 @@ AStar::AStar(Graph *graph)
_graph = graph;
}
// TODO this really should have some simd optimization
// matrix multiplication is based on this
static float dot(Math::Vector2d u, Math::Vector2d v) {
float result = 0.f;
result += u.getX() * v.getX();
result += u.getY() * v.getY();
return result;
static float dot(Vector2i u, Vector2i v) {
return (u.x * v.x) + (u.y * v.y);
}
static float length(Math::Vector2d v) { return sqrt(dot(v, v)); }
static float length(Vector2i v) { return sqrt(dot(v, v)); }
void AStar::search(int source, int target) {
IndexedPriorityQueue pq(_fCost);
@ -157,7 +103,6 @@ void AStar::search(int source, int target) {
int NCN = pq.pop();
_spt[NCN] = _sf[NCN];
if (NCN != target) {
// for (edge in _graph->edges[NCN]) {
for (size_t i = 0; i < _graph->_edges[NCN].size(); i++) {
GraphEdge &edge = _graph->_edges[NCN][i];
float Hcost = length(_graph->_nodes[edge.to] - _graph->_nodes[target]);
@ -227,54 +172,18 @@ void PathFinder::setWalkboxes(const Common::Array<Walkbox> &walkboxes) {
_graph = nullptr;
}
// Indicates whether or not the specified position is inside this walkbox.
static bool inside(const Walkbox &self, Math::Vector2d position, bool toleranceOnOutside = true) {
bool result = false;
Math::Vector2d point = position;
const float epsilon = 1.0f;
// Must have 3 or more edges
const Common::Array<Math::Vector2d> &polygon = self.getPoints();
if (polygon.size() < 3)
return false;
Math::Vector2d oldPoint(polygon[polygon.size() - 1]);
float oldSqDist = distanceSquared(oldPoint, point);
for (size_t i = 0; i < polygon.size(); i++) {
Math::Vector2d newPoint = polygon[i];
float newSqDist = distanceSquared(newPoint, point);
if (oldSqDist + newSqDist + 2.0f * sqrt(oldSqDist * newSqDist) - distanceSquared(newPoint, oldPoint) < epsilon)
return toleranceOnOutside;
Math::Vector2d left;
Math::Vector2d right;
if (newPoint.getX() > oldPoint.getX()) {
left = oldPoint;
right = newPoint;
} else {
left = newPoint;
right = oldPoint;
}
if ((left.getX() < point.getX()) && (point.getX() <= right.getX()) && ((point.getY() - left.getY()) * (right.getX() - left.getX()) < (right.getY() - left.getY()) * (point.getX() - left.getX())))
result = !result;
oldPoint = newPoint;
oldSqDist = newSqDist;
}
return result;
static Vector2i toVector2i(float x, float y) {
return Vector2i(round(x), round(y));
}
Math::Vector2d Walkbox::getClosestPointOnEdge(Math::Vector2d p3) const {
Vector2i Walkbox::getClosestPointOnEdge(Vector2i p) const {
int vi1 = -1;
int vi2 = -1;
float minDist = 100000.0f;
const Common::Array<Math::Vector2d> &polygon = getPoints();
const Common::Array<Vector2i> &polygon = getPoints();
for (size_t i = 0; i < polygon.size(); i++) {
float dist = distanceToSegment(p3, polygon[i], polygon[(i + 1) % polygon.size()]);
float dist = distanceToSegment(p, polygon[i], polygon[(i + 1) % polygon.size()]);
if (dist < minDist) {
minDist = dist;
vi1 = i;
@ -282,15 +191,15 @@ Math::Vector2d Walkbox::getClosestPointOnEdge(Math::Vector2d p3) const {
}
}
Math::Vector2d p1 = polygon[vi1];
Math::Vector2d p2 = polygon[vi2];
Vector2i p1 = polygon[vi1];
Vector2i p2 = polygon[vi2];
float x1 = p1.getX();
float y1 = p1.getY();
float x2 = p2.getX();
float y2 = p2.getY();
float x3 = p3.getX();
float y3 = p3.getY();
float x1 = p1.x;
float y1 = p1.y;
float x2 = p2.x;
float y2 = p2.y;
float x3 = p.x;
float y3 = p.y;
float u = (((x3 - x1) * (x2 - x1)) + ((y3 - y1) * (y2 - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
@ -298,67 +207,31 @@ Math::Vector2d Walkbox::getClosestPointOnEdge(Math::Vector2d p3) const {
float yu = y1 + u * (y2 - y1);
if (u < 0)
return Math::Vector2d(x1, y1);
return toVector2i(x1, y1);
if (u > 1)
return Math::Vector2d(x2, y2);
return Math::Vector2d(xu, yu);
return toVector2i(x2, y2);
return toVector2i(xu, yu);
}
static bool less(Math::Vector2d p1, Math::Vector2d p2) {
return (((p1.getX() < p2.getX() - EPSILON) ||
(abs(p1.getX() - p2.getX()) < EPSILON)) &&
(p1.getY() < p2.getY() - EPSILON));
}
static float det(float a, float b, float c, float d) {
return a * d - b * c;
}
static bool betw(float l, float r, float x) {
return (MIN(l, r) <= x + EPSILON) && (x <= MAX(l, r) + EPSILON);
}
static bool intersect_1d(float a, float b, float c, float d) {
float a2 = a;
float b2 = b;
float c2 = c;
float d2 = d;
if (a2 > b2)
SWAP(a2, b2);
if (c2 > d2)
SWAP(c2, d2);
return MAX(a2, c2) <= MIN(b2, d2) + EPSILON;
}
static bool lineSegmentsCross(Math::Vector2d a1, Math::Vector2d b1, Math::Vector2d c1, Math::Vector2d d1) {
Math::Vector2d a = a1;
Math::Vector2d b = b1;
Math::Vector2d c = c1;
Math::Vector2d d = d1;
if ((!intersect_1d(a.getX(), b.getX(), c.getX(), d.getX())) || (!intersect_1d(a.getY(), b.getY(), c.getY(), d.getY())))
static bool lineSegmentsCross(Vector2i a, Vector2i b, Vector2i c, Vector2i d) {
const float EPSILON = 4.f;
const float denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x));
if (abs(denominator) < EPSILON) {
return false;
Segment m(a, b);
Segment n(c, d);
float zn = det(m.a, m.b, n.a, n.b);
if (abs(zn) < EPSILON) {
if ((abs(m.distance(c)) > EPSILON) || (abs(n.distance(a)) > EPSILON))
return false;
if (less(b, a))
SWAP(a, b);
if (less(d, c))
SWAP(c, d);
return true;
}
float lx = -det(m.c, m.b, n.c, n.b) / zn;
float ly = -det(m.a, m.c, n.a, n.c) / zn;
return betw(a.getX(), b.getX(), lx) && betw(a.getY(), b.getY(), ly) && betw(c.getX(), d.getX(), lx) && betw(c.getY(), d.getY(), ly);
const float numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y));
const float numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
if ((abs(numerator1) < EPSILON) || (abs(numerator2) < EPSILON)) {
return false;
}
const float r = numerator1 / denominator;
const float s = numerator2 / denominator;
return ((r > 0.f) && (r < 1.f)) && ((s > 0.f) && (s < 1.f));
}
bool PathFinder::inLineOfSight(Math::Vector2d start, Math::Vector2d to) {
bool PathFinder::inLineOfSight(Vector2i start, Vector2i to) {
const float epsilon = 0.5f;
// Not in LOS if any of the ends is outside the polygon
@ -370,13 +243,13 @@ bool PathFinder::inLineOfSight(Math::Vector2d start, Math::Vector2d to) {
return true;
// Not in LOS if any edge is intersected by the start-end line segment
for (size_t i = 0; i < _walkboxes.size(); i++) {
Walkbox &walkbox = _walkboxes[i];
const Common::Array<Math::Vector2d> &polygon = walkbox.getPoints();
int size = polygon.size();
for (int j = 0; j < size; j++) {
Math::Vector2d v1 = polygon[j];
Math::Vector2d v2 = polygon[(j + 1) % size];
for (uint i = 0; i < _walkboxes.size(); i++) {
const Walkbox &walkbox = _walkboxes[i];
const Common::Array<Vector2i> &polygon = walkbox.getPoints();
const uint size = polygon.size();
for (uint j = 0; j < size; j++) {
Vector2i v1 = polygon[j];
Vector2i v2 = polygon[(j + 1) % size];
if (!lineSegmentsCross(start, to, v1, v2))
continue;
@ -387,19 +260,20 @@ bool PathFinder::inLineOfSight(Math::Vector2d start, Math::Vector2d to) {
}
// Finally the middle point in the segment determines if in LOS or not
Math::Vector2d v2 = (start + to) / 2.0f;
bool result = _walkboxes[0].contains(v2);
for (size_t i = 1; i < _walkboxes.size(); i++) {
const Vector2i v2 = (start + to) / 2.0f;
if (!_walkboxes[0].contains(v2))
return false;
for (uint i = 1; i < _walkboxes.size(); i++) {
if (_walkboxes[i].contains(v2, false))
result = false;
return false;
}
return result;
return true;
}
static int minIndex(const Common::Array<float> values) {
static uint minIndex(const Common::Array<float> &values) {
float min = values[0];
int index = 0;
for (size_t i = 1; i < values.size(); i++) {
uint index = 0;
for (uint i = 1; i < values.size(); i++) {
if (values[i] < min) {
index = i;
min = values[i];
@ -410,13 +284,15 @@ static int minIndex(const Common::Array<float> values) {
Graph *PathFinder::createGraph() {
Graph *result = new Graph();
for (size_t i = 0; i < _walkboxes.size(); i++) {
Walkbox &walkbox = _walkboxes[i];
for (uint i = 0; i < _walkboxes.size(); i++) {
const Walkbox &walkbox = _walkboxes[i];
if (walkbox.getPoints().size() > 2) {
bool visible = walkbox.isVisible();
for (size_t j = 0; j < walkbox.getPoints().size(); j++) {
if (walkbox.concave(j) == visible) {
Math::Vector2d vertex = walkbox.getPoints()[j];
bool firstWalkbox = (i == 0);
if (!walkbox.isVisible())
firstWalkbox = true;
for (uint j = 0; j < walkbox.getPoints().size(); j++) {
if (walkbox.concave(j) == firstWalkbox) {
const Vector2i &vertex = walkbox.getPoints()[j];
result->_concaveVertices.push_back(vertex);
result->addNode(vertex);
}
@ -424,12 +300,12 @@ Graph *PathFinder::createGraph() {
}
}
for (size_t i = 0; i < result->_concaveVertices.size(); i++) {
for (size_t j = 0; j < result->_concaveVertices.size(); j++) {
Math::Vector2d c1(result->_concaveVertices[i]);
Math::Vector2d c2(result->_concaveVertices[j]);
for (uint i = 0; i < result->_concaveVertices.size(); i++) {
for (uint j = 0; j < result->_concaveVertices.size(); j++) {
const Vector2i c1(result->_concaveVertices[i]);
const Vector2i c2(result->_concaveVertices[j]);
if (inLineOfSight(c1, c2)) {
float d = distance(c1, c2);
const float d = distance(c1, c2);
result->addEdge(GraphEdge(i, j, d));
}
}
@ -437,27 +313,27 @@ Graph *PathFinder::createGraph() {
return result;
}
Common::Array<Math::Vector2d> PathFinder::calculatePath(Math::Vector2d start, Math::Vector2d to) {
Common::Array<Math::Vector2d> result;
Common::Array<Vector2i> PathFinder::calculatePath(Vector2i start, Vector2i to) {
Common::Array<Vector2i> result;
if (_walkboxes.size() > 0) {
// find the walkbox where the actor is and put it first
for (size_t i = 0; i < _walkboxes.size(); i++) {
for (uint i = 0; i < _walkboxes.size(); i++) {
const Walkbox &wb = _walkboxes[i];
if (inside(wb, start) && (i != 0)) {
if (wb.contains(start) && (i != 0)) {
SWAP(_walkboxes[0], _walkboxes[i]);
break;
}
}
// if no walkbox has been found => find the nearest walkbox
if (!inside(_walkboxes[0], start)) {
if (!_walkboxes[0].contains(start)) {
Common::Array<float> dists(_walkboxes.size());
for (size_t i = 0; i < _walkboxes.size(); i++) {
Walkbox wb = _walkboxes[i];
for (uint i = 0; i < _walkboxes.size(); i++) {
const Walkbox &wb = _walkboxes[i];
dists[i] = distance(wb.getClosestPointOnEdge(start), start);
}
int index = minIndex(dists);
const uint index = minIndex(dists);
if (index != 0)
SWAP(_walkboxes[0], _walkboxes[index]);
}
@ -466,39 +342,51 @@ Common::Array<Math::Vector2d> PathFinder::calculatePath(Math::Vector2d start, Ma
_graph = createGraph();
// create new node on start position
Graph *walkgraph = new Graph(*_graph);
int startNodeIndex = walkgraph->_nodes.size();
_walkgraph = *_graph;
const uint startNodeIndex = _walkgraph._nodes.size();
// if destination is not inside current walkable area, then get the closest point
const Walkbox &wb = _walkboxes[0];
if (wb.isVisible() && !wb.contains(to))
if (wb.isVisible() && !wb.contains(start)) {
start = wb.getClosestPointOnEdge(start);
}
if (wb.isVisible() && !wb.contains(to)) {
to = wb.getClosestPointOnEdge(to);
}
// we don't want the actor to walk in a different walkbox
// then check if endpoint is inside one of the other walkboxes and find closest point on edge
for (uint i = 1; i < _walkboxes.size(); i++) {
if (_walkboxes[i].contains(to)) {
to = _walkboxes[i].getClosestPointOnEdge(to);
break;
}
}
walkgraph->addNode(start);
_walkgraph.addNode(start);
for (size_t i = 0; i < walkgraph->_concaveVertices.size(); i++) {
Math::Vector2d c = walkgraph->_concaveVertices[i];
for (uint i = 0; i < _walkgraph._concaveVertices.size(); i++) {
const Vector2i c = _walkgraph._concaveVertices[i];
if (inLineOfSight(start, c))
walkgraph->addEdge(GraphEdge(startNodeIndex, i, distance(start, c)));
_walkgraph.addEdge(GraphEdge(startNodeIndex, i, distance(start, c)));
}
// create new node on end position
int endNodeIndex = walkgraph->_nodes.size();
walkgraph->addNode(to);
const uint endNodeIndex = _walkgraph._nodes.size();
_walkgraph.addNode(to);
for (size_t i = 0; i < walkgraph->_concaveVertices.size(); i++) {
Math::Vector2d c = walkgraph->_concaveVertices[i];
for (uint i = 0; i < _walkgraph._concaveVertices.size(); i++) {
const Vector2i c = _walkgraph._concaveVertices[i];
if (inLineOfSight(to, c))
walkgraph->addEdge(GraphEdge(i, endNodeIndex, distance(to, c)));
_walkgraph.addEdge(GraphEdge(i, endNodeIndex, distance(to, c)));
}
if (inLineOfSight(start, to))
walkgraph->addEdge(GraphEdge(startNodeIndex, endNodeIndex, distance(start, to)));
_walkgraph.addEdge(GraphEdge(startNodeIndex, endNodeIndex, distance(start, to)));
Common::Array<int> indices = walkgraph->getPath(startNodeIndex, endNodeIndex);
for (size_t i = 0; i < indices.size(); i++) {
int index = indices[i];
result.push_back(walkgraph->_nodes[index]);
const Common::Array<int> indices = _walkgraph.getPath(startNodeIndex, endNodeIndex);
for (uint i = 0; i < indices.size(); i++) {
const int index = indices[i];
result.push_back(_walkgraph._nodes[index]);
}
}
return result;

View File

@ -24,6 +24,7 @@
#include "common/array.h"
#include "math/vector2d.h"
#include "twp/util.h"
namespace Twp {
@ -62,16 +63,15 @@ struct GraphEdge {
class Graph {
public:
Graph();
Graph(const Graph &graph);
void addNode(Math::Vector2d node);
void addNode(Vector2i node);
void addEdge(GraphEdge edge);
// Gets the edge from 'from' index to 'to' index.
GraphEdge *edge(int start, int to);
Common::Array<int> getPath(int source, int target);
Common::Array<Math::Vector2d> _nodes;
Common::Array<Vector2i> _nodes;
Common::Array<Common::Array<GraphEdge> > _edges;
Common::Array<Math::Vector2d> _concaveVertices;
Common::Array<Vector2i> _concaveVertices;
};
class AStar {
@ -83,27 +83,27 @@ public:
Common::Array<GraphEdge *> _spt; // The Shortest Path Tree
Common::Array<float> _gCost; // This array will store the G cost of each node
Common::Array<float> _fCost; // This array will store the F cost of each node
Common::Array<GraphEdge*> _sf; // The Search Frontier
Common::Array<GraphEdge *> _sf; // The Search Frontier
};
// Represents an area where an actor can or cannot walk
class Walkbox {
public:
Walkbox(const Common::Array<Math::Vector2d> &polygon, bool visible = true);
Walkbox(const Common::Array<Vector2i> &polygon, bool visible = true);
// Indicates whether or not the specified position is inside this walkbox.
bool contains(Math::Vector2d position, bool toleranceOnOutside = true) const;
// Indicates whether or not the specified position is inside this walkbox.
bool contains(Vector2i position, bool toleranceOnOutside = true) const;
bool concave(int vertex) const;
void setVisible(bool visible) { _visible = visible; }
bool isVisible() const { return _visible; }
const Common::Array<Math::Vector2d>& getPoints() const { return _polygon; }
Math::Vector2d getClosestPointOnEdge(Math::Vector2d p3) const;
const Common::Array<Vector2i> &getPoints() const { return _polygon; }
Vector2i getClosestPointOnEdge(Vector2i p) const;
public:
Common::String _name;
private:
Common::Array<Math::Vector2d> _polygon;
Common::Array<Vector2i> _polygon;
bool _visible;
};
@ -111,18 +111,20 @@ private:
class PathFinder {
public:
void setWalkboxes(const Common::Array<Walkbox> &walkboxes);
Common::Array<Math::Vector2d> calculatePath(Math::Vector2d start, Math::Vector2d to);
Common::Array<Walkbox> getWalkboxes() const { return _walkboxes; }
Common::Array<Vector2i> calculatePath(Vector2i start, Vector2i to);
void setDirty(bool dirty) { _isDirty = dirty; }
bool isDirty() const { return _isDirty; }
const Graph* getGraph() const { return _graph; }
const Graph &getGraph() const { return _walkgraph; }
private:
Graph *createGraph();
bool inLineOfSight(Math::Vector2d start, Math::Vector2d to);
bool inLineOfSight(Vector2i start, Vector2i to);
private:
Common::Array<Walkbox> _walkboxes;
Graph *_graph = nullptr;
Graph _walkgraph;
bool _isDirty = true;
};

View File

@ -164,13 +164,14 @@ void ReachAnim::update(float elapsed) {
}
}
WalkTo::WalkTo(Object *obj, Math::Vector2d dest, int facing)
WalkTo::WalkTo(Object *obj, Vector2i dest, int facing)
: _obj(obj), _facing(facing) {
if (obj->_useWalkboxes) {
_path = obj->_room->calculatePath(obj->_node->getAbsPos(), dest);
_path = obj->_room->calculatePath((Vector2i)obj->_node->getAbsPos(), dest);
} else {
_path = {obj->_node->getAbsPos(), dest};
_path = {(Vector2i)obj->_node->getAbsPos(), dest};
}
_wsd = sqrt(obj->_walkSpeed.getX() * obj->_walkSpeed.getX() + obj->_walkSpeed.getY() * obj->_walkSpeed.getY());
if (sqrawexists(obj->_table, "preWalking"))
sqcall(obj->_table, "preWalking");
@ -234,18 +235,18 @@ void WalkTo::actorArrived() {
void WalkTo::update(float elapsed) {
if (_path.size() != 0) {
Math::Vector2d dest = _path[0];
float d = distance(dest, _obj->_node->getAbsPos());
Vector2i dest = _path[0];
float d = distance(dest, (Vector2i)_obj->_node->getAbsPos());
// arrived at destination ?
if (d < 1.0) {
_obj->_node->setPos(_path[0]);
_obj->_node->setPos((Math::Vector2d)_path[0]);
_path.remove_at(0);
if (_path.size() == 0) {
actorArrived();
}
} else {
Math::Vector2d delta = dest - _obj->_node->getAbsPos();
Math::Vector2d delta = (Math::Vector2d)dest - _obj->_node->getAbsPos();
float duration = d / _wsd;
float factor = Twp::clamp(elapsed / duration, 0.f, 1.f);
@ -337,7 +338,8 @@ int Talking::loadActorSpeech(const Common::String &name) {
}
void Talking::say(const Common::String &text) {
if(text.empty()) return;
if (text.empty())
return;
Common::String txt(text);
if (text[0] == '@') {

View File

@ -205,10 +205,10 @@ private:
class WalkTo : public Motor {
public:
WalkTo(Object *obj, Math::Vector2d dest, int facing = 0);
WalkTo(Object *obj, Vector2i dest, int facing = 0);
virtual void disable() override;
const Common::Array<Math::Vector2d> &getPath() const { return _path; }
const Common::Array<Vector2i> &getPath() const { return _path; }
private:
void actorArrived();
@ -216,7 +216,7 @@ private:
private:
Object *_obj = nullptr;
Common::Array<Math::Vector2d> _path;
Common::Array<Vector2i> _path;
int _facing = 0;
float _wsd;
};

View File

@ -665,10 +665,9 @@ static bool verbNotClose(VerbId id) {
}
static void cantReach(Object *self, Object *noun2) {
// TODO: check if we need to use sqrawexists or sqexists
if (sqrawexists(self->_table, "verbCantReach")) {
int nParams = sqparamCount(g_engine->getVm(), self->_table, "verbCantReach");
debug("verbCantReach found in obj '{self.key}' with {nParams} params");
debug("verbCantReach found in obj '%s' with %d params", self->_key.c_str(), nParams);
if (nParams == 1) {
sqcall(self->_table, "verbCantReach");
} else {
@ -676,12 +675,13 @@ static void cantReach(Object *self, Object *noun2) {
sq_resetobject(&table);
if (noun2)
table = noun2->_table;
sqcall(self->_table, "verbCantReach", self->_table, table);
sqcall(self->_table, "verbCantReach", table);
}
} else if (!noun2) {
} else if (noun2) {
cantReach(noun2, nullptr);
} else {
HSQOBJECT nilTbl;
sq_resetobject(&nilTbl);
sqcall(g_engine->_defaultObj, "verbCantReach", self->_table, !noun2 ? nilTbl : noun2->_table);
}
}
@ -701,11 +701,11 @@ void Object::execVerb() {
return;
}
// Did we get close enough?
float dist = distance(getUsePos(), noun1->getUsePos());
float dist = distance((Vector2i)getUsePos(), (Vector2i)noun1->getUsePos());
float min_dist = verb.id == VERB_TALKTO ? MIN_TALK_DIST : MIN_USE_DIST;
debug("actorArrived: noun1 min_dist: %f > %f (actor: {self.getUsePos}, obj: {noun1.getUsePos}) ?", dist, min_dist);
if (!verbNotClose(verb) && (dist > min_dist)) {
cantReach(this, noun1);
cantReach(noun1, noun2);
return;
}
if (noun1->_useDir != dNone) {
@ -719,11 +719,11 @@ void Object::execVerb() {
_exec.enabled = false;
return;
}
float dist = distance(getUsePos(), noun2->getUsePos());
float dist = distance((Vector2i)getUsePos(), (Vector2i)noun2->getUsePos());
float min_dist = verb.id == VERB_TALKTO ? MIN_TALK_DIST : MIN_USE_DIST;
debug("actorArrived: noun2 min_dist: {dist} > {min_dist} ?");
if (dist > min_dist) {
cantReach(this, noun2);
cantReach(noun1, noun2);
return;
}
}
@ -735,8 +735,8 @@ void Object::execVerb() {
}
// Walks an actor to the `pos` or actor `obj` and then faces `dir`.
void Object::walk(Math::Vector2d pos, int facing) {
debug("walk to obj %s: %f,%f, %d", _key.c_str(), pos.getX(), pos.getY(), facing);
void Object::walk(Vector2i pos, int facing) {
debug("walk to obj %s: %d,%d, %d", _key.c_str(), pos.x, pos.y, facing);
if (!_walkTo || (!_walkTo->isEnabled())) {
play(getAnimName(WALK_ANIMNAME), true);
}
@ -747,7 +747,7 @@ void Object::walk(Math::Vector2d pos, int facing) {
void Object::walk(Object *obj) {
debug("walk to obj %s: (%f,%f)", obj->_key.c_str(), obj->getUsePos().getX(), obj->getUsePos().getY());
Facing facing = (Facing)obj->_useDir;
walk(obj->getUsePos(), facing);
walk((Vector2i)obj->getUsePos(), facing);
}
void Object::turn(Facing facing) {

View File

@ -187,7 +187,7 @@ public:
void setReach(Motor *reach);
Motor *getWalkTo() const { return _walkTo; }
Motor *getReach() const { return _reach; }
void walk(Math::Vector2d pos, int facing = 0);
void walk(Vector2i pos, int facing = 0);
void walk(Object* obj);
void setTalking(Motor *talking);

View File

@ -71,16 +71,16 @@ static Math::Vector2d parseParallax(const Common::JSONValue &v) {
}
static Walkbox parseWalkbox(const Common::String &text) {
Common::Array<Math::Vector2d> points;
Common::Array<Vector2i> points;
size_t i = 1;
size_t endPos;
do {
uint32 commaPos = text.find(',', i);
long x = strtol(text.substr(i, commaPos - i).c_str(), nullptr, 10);
int x = (int)strtol(text.substr(i, commaPos - i).c_str(), nullptr, 10);
endPos = text.find('}', commaPos + 1);
long y = strtol(text.substr(commaPos + 1, endPos - commaPos - 1).c_str(), nullptr, 10);
int y = (int)strtol(text.substr(commaPos + 1, endPos - commaPos - 1).c_str(), nullptr, 10);
i = endPos + 3;
points.push_back({(float)x, (float)y});
points.push_back({x, y});
} while ((text.size() - 1) != endPos);
return Walkbox(points);
}
@ -99,18 +99,18 @@ static Scaling parseScaling(const Common::JSONArray &jScalings) {
static ClipperLib::Path toPolygon(const Walkbox &walkbox) {
ClipperLib::Path path;
const Common::Array<Math::Vector2d> &points = walkbox.getPoints();
const Common::Array<Vector2i> &points = walkbox.getPoints();
for (size_t i = 0; i < points.size(); i++) {
path.push_back(ClipperLib::IntPoint(points[i].getX(), points[i].getY()));
path.push_back(ClipperLib::IntPoint(points[i].x, points[i].y));
}
return path;
}
static Walkbox toWalkbox(const ClipperLib::Path &path) {
Common::Array<Math::Vector2d> pts;
Common::Array<Vector2i> pts;
for (size_t i = 0; i < path.size(); i++) {
const ClipperLib::IntPoint &pt = path[i];
pts.push_back(Math::Vector2d(pt.X, pt.Y));
pts.push_back(Vector2i{pt.X, pt.Y});
}
return Walkbox(pts, ClipperLib::Orientation(path));
}
@ -131,11 +131,16 @@ static Common::Array<Walkbox> merge(const Common::Array<Walkbox> &walkboxes) {
ClipperLib::Paths solutions;
ClipperLib::Clipper c;
c.AddPaths(subjects, ClipperLib::ptSubject, true);
c.AddPaths(clips, ClipperLib::ptClip, true);
c.Execute(ClipperLib::ClipType::ctDifference, solutions, ClipperLib::pftEvenOdd);
c.Execute(ClipperLib::ClipType::ctUnion, solutions, ClipperLib::pftEvenOdd);
for (size_t i = 0; i < solutions.size(); i++) {
result.push_back(toWalkbox(solutions[i]));
ClipperLib::Paths solutions2;
ClipperLib::Clipper c2;
c2.AddPaths(solutions, ClipperLib::ptSubject, true);
c2.AddPaths(clips, ClipperLib::ptClip, true);
c2.Execute(ClipperLib::ClipType::ctDifference, solutions2, ClipperLib::pftEvenOdd);
for (size_t i = 0; i < solutions2.size(); i++) {
result.push_back(toWalkbox(solutions2[i]));
}
}
return result;
@ -472,16 +477,16 @@ void Room::walkboxHidden(const Common::String &name, bool hidden) {
if (wb._name == name) {
wb.setVisible(!hidden);
// 1 walkbox has change so update merged polygon
_mergedPolygon = merge(_walkboxes);
_pathFinder.setDirty(true);
return;
}
}
}
Common::Array<Math::Vector2d> Room::calculatePath(Math::Vector2d frm, Math::Vector2d to) {
Common::Array<Vector2i> Room::calculatePath(Vector2i frm, Vector2i to) {
if (_mergedPolygon.size() > 0) {
if (_pathFinder.isDirty()) {
_mergedPolygon = merge(_walkboxes);
_pathFinder.setWalkboxes(_mergedPolygon);
_pathFinder.setDirty(false);
}
@ -502,44 +507,44 @@ Layer::Layer(const Common::StringArray &name, Math::Vector2d parallax, int zsort
_zsort = zsort;
}
Walkbox::Walkbox(const Common::Array<Math::Vector2d> &polygon, bool visible)
Walkbox::Walkbox(const Common::Array<Vector2i> &polygon, bool visible)
: _polygon(polygon), _visible(visible) {
}
bool Walkbox::concave(int vertex) const {
Math::Vector2d current = _polygon[vertex];
Math::Vector2d next = _polygon[(vertex + 1) % _polygon.size()];
Math::Vector2d previous = _polygon[vertex == 0 ? _polygon.size() - 1 : vertex - 1];
Vector2i current = _polygon[vertex];
Vector2i next = _polygon[(vertex + 1) % _polygon.size()];
Vector2i previous = _polygon[vertex == 0 ? _polygon.size() - 1 : vertex - 1];
Math::Vector2d left(current.getX() - previous.getX(), current.getY() - previous.getY());
Math::Vector2d right(next.getX() - current.getX(), next.getY() - current.getY());
Vector2i left{current.x - previous.x, current.y - previous.y};
Vector2i right{next.x - current.x, next.y - current.y};
float cross = (left.getX() * right.getY()) - (left.getY() * right.getX());
return _visible ? cross < 0 : cross >= 0;
float cross = (left.x * right.y) - (left.y * right.x);
return cross < 0;
}
bool Walkbox::contains(Math::Vector2d position, bool toleranceOnOutside) const {
Math::Vector2d point = position;
const float epsilon = 1.0f;
bool Walkbox::contains(Vector2i position, bool toleranceOnOutside) const {
Vector2i point = position;
const float epsilon = 2.0f;
bool result = false;
// Must have 3 or more edges
if (_polygon.size() < 3)
return false;
Math::Vector2d oldPoint(_polygon[_polygon.size() - 1]);
Vector2i oldPoint(_polygon[_polygon.size() - 1]);
float oldSqDist = distanceSquared(oldPoint, point);
for (size_t i = 0; i < _polygon.size(); i++) {
Math::Vector2d newPoint = _polygon[i];
Vector2i newPoint = _polygon[i];
float newSqDist = distanceSquared(newPoint, point);
if (oldSqDist + newSqDist + 2.0f * sqrt(oldSqDist * newSqDist) - distanceSquared(newPoint, oldPoint) < epsilon)
return toleranceOnOutside;
Math::Vector2d left;
Math::Vector2d right;
if (newPoint.getX() > oldPoint.getX()) {
Vector2i left;
Vector2i right;
if (newPoint.x > oldPoint.x) {
left = oldPoint;
right = newPoint;
} else {
@ -547,7 +552,7 @@ bool Walkbox::contains(Math::Vector2d position, bool toleranceOnOutside) const {
right = oldPoint;
}
if ((left.getX() < point.getX()) && (point.getX() <= right.getX()) && ((point.getY() - left.getY()) * (right.getX() - left.getX())) < ((right.getY() - left.getY()) * (point.getX() - left.getX())))
if ((left.x < point.x) && (point.x <= right.x) && ((point.y - left.y) * (right.x - left.x)) < ((right.y - left.y) * (point.x - left.x)))
result = !result;
oldPoint = newPoint;

View File

@ -126,7 +126,7 @@ public:
Color getOverlay() const;
void walkboxHidden(const Common::String &name, bool hidden);
Common::Array<Math::Vector2d> calculatePath(Math::Vector2d frm, Math::Vector2d to);
Common::Array<Vector2i> calculatePath(Vector2i frm, Vector2i to);
public:
Common::String _name; // Name of the room

View File

@ -49,7 +49,7 @@ static SQInteger addTrigger(HSQUIRRELVM v) {
static SQInteger clampInWalkbox(HSQUIRRELVM v) {
SQInteger numArgs = sq_gettop(v);
Math::Vector2d pos1, pos2;
Vector2i pos1, pos2;
if (numArgs == 3) {
int x = 0;
if (SQ_FAILED(sqget(v, 2, x)))
@ -57,7 +57,7 @@ static SQInteger clampInWalkbox(HSQUIRRELVM v) {
int y = 0;
if (SQ_FAILED(sqget(v, 3, y)))
return sq_throwerror(v, "failed to get y");
pos1 = Math::Vector2d(x, y);
pos1 = Vector2i(x, y);
pos2 = pos1;
} else if (numArgs == 5) {
int x1 = 0;
@ -66,14 +66,14 @@ static SQInteger clampInWalkbox(HSQUIRRELVM v) {
int y1 = 0;
if (SQ_FAILED(sqget(v, 3, y1)))
return sq_throwerror(v, "failed to get y1");
pos1 = Math::Vector2d(x1, y1);
pos1 = Vector2i(x1, y1);
int x2 = 0;
if (SQ_FAILED(sqget(v, 4, x2)))
return sq_throwerror(v, "failed to get x2");
int y2 = 0;
if (SQ_FAILED(sqget(v, 5, y1)))
return sq_throwerror(v, "failed to get y2");
pos2 = Math::Vector2d(x2, y2);
pos2 = Vector2i(x2, y2);
} else {
return sq_throwerror(v, "Invalid argument number in clampInWalkbox");
}
@ -85,7 +85,7 @@ static SQInteger clampInWalkbox(HSQUIRRELVM v) {
return 1;
}
}
Math::Vector2d pos = walkboxes[0].getClosestPointOnEdge(pos2);
Vector2i pos = walkboxes[0].getClosestPointOnEdge(pos2);
sqpush(v, pos);
return 1;
}

View File

@ -87,17 +87,22 @@ SQInteger sqpush(HSQUIRRELVM v, HSQOBJECT value) {
}
template<>
SQInteger sqpush(HSQUIRRELVM v, Math::Vector2d value) {
SQInteger sqpush(HSQUIRRELVM v, Vector2i value) {
sq_newtable(v);
sq_pushstring(v, "x", -1);
sq_pushinteger(v, value.getX());
sq_pushinteger(v, value.x);
sq_newslot(v, -3, SQFalse);
sq_pushstring(v, "y", -1);
sq_pushinteger(v, value.getY());
sq_pushinteger(v, value.y);
sq_newslot(v, -3, SQFalse);
return 1;
}
template<>
SQInteger sqpush(HSQUIRRELVM v, Math::Vector2d value) {
return sqpush(v, (Vector2i)value);
}
template<>
SQInteger sqpush(HSQUIRRELVM v, Rectf value) {
sq_newtable(v);

View File

@ -230,7 +230,7 @@ void TwpEngine::clickedAt(Math::Vector2d scrPos) {
// Just clicking on the ground
cancelSentence(_actor);
if (_actor->_room == _room)
_actor->walk(roomPos);
_actor->walk((Vector2i)roomPos);
_hud._verb = _hud.actorSlot(_actor)->verbs[0];
_holdToMove = true;
}
@ -454,8 +454,8 @@ void TwpEngine::update(float elapsed) {
if (_holdToMove && (_time > _nextHoldToMoveTime)) {
walkFast();
cancelSentence(_actor);
if (_actor->_room == _room && (distance(_actor->_node->getAbsPos(), roomPos) > 5)) {
_actor->walk(roomPos);
if (_actor->_room == _room && (distance((Vector2i)_actor->_node->getAbsPos(), (Vector2i)roomPos) > 5)) {
_actor->walk((Vector2i)roomPos);
}
_nextHoldToMoveTime = _time + 0.250f;
}

View File

@ -130,29 +130,29 @@ void parseObjectAnimations(const Common::JSONArray &jAnims, Common::Array<Object
}
}
float distanceSquared(Math::Vector2d p1, Math::Vector2d p2) {
float dx = p1.getX() - p2.getX();
float dy = p1.getY() - p2.getY();
float distanceSquared(Vector2i p1, Vector2i p2) {
const float dx = p1.x - p2.x;
const float dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
float distanceToSegmentSquared(Math::Vector2d p, Math::Vector2d v, Math::Vector2d w) {
float l2 = distanceSquared(v, w);
float distanceToSegmentSquared(Vector2i p, Vector2i v, Vector2i w) {
const float l2 = distanceSquared(v, w);
if (l2 == 0)
return distanceSquared(p, v);
float t = ((p.getX() - v.getX()) * (w.getX() - v.getX()) + (p.getY() - v.getY()) * (w.getY() - v.getY())) / l2;
const float t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0)
return distanceSquared(p, v);
if (t > 1)
return distanceSquared(p, w);
return distanceSquared(p, Math::Vector2d(v.getX() + t * (w.getX() - v.getX()), v.getY() + t * (w.getY() - v.getY())));
return distanceSquared(p, Vector2i(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
}
float distanceToSegment(Math::Vector2d p, Math::Vector2d v, Math::Vector2d w) {
float distanceToSegment(Vector2i p, Vector2i v, Vector2i w) {
return sqrt(distanceToSegmentSquared(p, v, w));
}
float distance(Math::Vector2d p1, Math::Vector2d p2) {
float distance(Vector2i p1, Vector2i p2) {
return sqrt(distanceSquared(p1, p2));
}
@ -160,7 +160,7 @@ Common::String join(const Common::Array<Common::String> &array, const Common::St
Common::String result;
if (array.size() > 0) {
result += array[0];
for (size_t i = 1; i < array.size(); i++) {
for (uint i = 1; i < array.size(); i++) {
result += (sep + array[i]);
}
}
@ -170,7 +170,7 @@ Common::String join(const Common::Array<Common::String> &array, const Common::St
Common::String replace(const Common::String &s, const Common::String &what, const Common::String &by) {
Common::String result;
uint i = 0;
size_t whatSize = what.size();
uint whatSize = what.size();
while (true) {
uint j = s.find(what, i);
if (j == Common::String::npos)

View File

@ -33,6 +33,35 @@ namespace Twp {
class Object;
struct Vector2i {
int x = 0;
int y = 0;
Vector2i() {}
Vector2i(int x_, int y_) : x(x_), y(y_) {}
Vector2i(float x_, float y_) : x(round(x_)), y(round(y_)) {}
explicit Vector2i(const Math::Vector2d &p) : x(round(p.getX())), y(round(p.getY())) {}
explicit operator Math::Vector2d() const {
return Math::Vector2d(x, y);
}
Vector2i operator-(const Vector2i &v) const {
return Vector2i(x - v.x, y - v.y);
}
Vector2i operator+(const Vector2i &v) const {
return Vector2i(x + v.x, y + v.y);
}
Vector2i operator*(float f) const {
return Vector2i(x * f, y * f);
}
Vector2i operator/(float f) const {
return Vector2i(x / f, y / f);
}
};
// general util
template<typename T, class DL = Common::DefaultDeleter<T> >
using unique_ptr = Common::ScopedPtr<T, DL>;
@ -59,7 +88,7 @@ void parseObjectAnimations(const Common::JSONArray &jAnims, Common::Array<Object
// array util
template<typename T>
size_t find(const Common::Array<T>& array, const T& o) {
size_t find(const Common::Array<T> &array, const T &o) {
for (size_t i = 0; i < array.size(); i++) {
if (array[i] == o) {
return i;
@ -69,16 +98,16 @@ size_t find(const Common::Array<T>& array, const T& o) {
}
// string util
Common::String join(const Common::Array<Common::String>& array, const Common::String& sep);
Common::String replace(const Common::String& s, const Common::String& what, const Common::String& by);
Common::String join(const Common::Array<Common::String> &array, const Common::String &sep);
Common::String replace(const Common::String &s, const Common::String &what, const Common::String &by);
Common::String remove(const Common::String &txt, char startC, char endC);
// math util
void scale(Math::Matrix4 &m, const Math::Vector2d &v);
Math::Vector2d operator*(Math::Vector2d v, float f);
float distance(Math::Vector2d p1, Math::Vector2d p2);
float distanceSquared(Math::Vector2d p1, Math::Vector2d p2);
float distanceToSegment(Math::Vector2d p, Math::Vector2d v, Math::Vector2d w);
float distance(Vector2i p1, Vector2i p2);
float distanceSquared(Vector2i p1, Vector2i p2);
float distanceToSegment(Vector2i p, Vector2i v, Vector2i w);
} // namespace Twp

View File

@ -35,25 +35,27 @@ void WalkboxNode::drawCore(Math::Matrix4 trsf) {
Color red(1.f, 0.f, 0.f);
Color green(0.f, 1.f, 0.f);
Color yellow(1.f, 1.f, 0.f);
Common::Array<Walkbox> walkboxes = g_engine->_room ? g_engine->_room->_pathFinder.getWalkboxes() : Common::Array<Walkbox>();
switch (_mode) {
case WalkboxMode::All: {
Math::Matrix4 transf;
// cancel camera pos
Math::Vector2d pos = g_engine->getGfx().cameraPos();
transf.translate(Math::Vector3d(-pos.getX(), pos.getY(), 0.f));
for (uint i = 0; i < g_engine->_room->_walkboxes.size(); i++) {
Walkbox &wb = g_engine->_room->_walkboxes[i];
Color color = wb.isVisible() ? green : red;
transf.translate(Math::Vector3d(-pos.getX(), -pos.getY(), 0.f));
for (uint i = 0; i < walkboxes.size(); i++) {
const Walkbox &wb = walkboxes[i];
const Color color = wb.isVisible() ? green : red;
Common::Array<Vertex> vertices;
for (uint j = 0; j < wb.getPoints().size(); j++) {
Math::Vector2d p = wb.getPoints()[j];
vertices.push_back(Vertex(p, color));
const Common::Array<Vector2i>& points = wb.getPoints();
for (uint j = 0; j < points.size(); j++) {
Vector2i pInt = points[j];
Math::Vector2d p = (Math::Vector2d)pInt;
vertices.push_back(Vertex((Math::Vector2d)p, color));
Color vertexColor = wb.concave(j) ? white : yellow;
Math::Matrix4 t(transf);
p -= Math::Vector2d(2.f, 2.f);
p -= Math::Vector2d(1.f, 1.f);
t.translate(Math::Vector3d(p.getX(), p.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), vertexColor, t);
}
g_engine->getGfx().drawLinesLoop(vertices.data(), vertices.size(), transf);
}
@ -62,20 +64,22 @@ void WalkboxNode::drawCore(Math::Matrix4 trsf) {
Math::Matrix4 transf;
Math::Vector2d pos = g_engine->getGfx().cameraPos();
// cancel camera pos
transf.translate(Math::Vector3d(-pos.getX(), pos.getY(), 0.f));
for (uint i = 0; i < g_engine->_room->_mergedPolygon.size(); i++) {
Walkbox &wb = g_engine->_room->_mergedPolygon[i];
Color color = wb.isVisible() ? green : red;
transf.translate(Math::Vector3d(-pos.getX(), -pos.getY(), 0.f));
for (uint i = 0; i < walkboxes.size(); i++) {
const Walkbox &wb = walkboxes[i];
const Color color = i == 0 ? green : red;
Common::Array<Vertex> vertices;
for (uint j = 0; j < wb.getPoints().size(); j++) {
Math::Vector2d p = wb.getPoints()[j];
const Common::Array<Vector2i>& points = wb.getPoints();
for (uint j = 0; j < points.size(); j++) {
Vector2i pInt = points[j];
Math::Vector2d p = (Math::Vector2d)pInt;
vertices.push_back(Vertex(p, color));
Color vertexColor = wb.concave(j) ? white : yellow;
Math::Matrix4 t(transf);
p -= Math::Vector2d(2.f, 2.f);
p -= Math::Vector2d(1.f, 1.f);
t.translate(Math::Vector3d(p.getX(), p.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), vertexColor, t);
// if (wb.concave(j) == (i == 0))
// g_engine->getGfx().drawQuad(Math::Vector2d(3.f, 3.f), yellow, t);
}
g_engine->getGfx().drawLinesLoop(vertices.data(), vertices.size(), transf);
}
@ -90,11 +94,11 @@ PathNode::PathNode() : Node("Path") {
_zOrder = -1000;
}
Math::Vector2d PathNode::fixPos(Math::Vector2d pos) {
Vector2i PathNode::fixPos(Vector2i pos) {
for (size_t i = 0; i < g_engine->_room->_mergedPolygon.size(); i++) {
Walkbox &wb = g_engine->_room->_mergedPolygon[i];
if (!wb.isVisible() && wb.contains(pos)) {
return wb.getClosestPointOnEdge(pos);
return wb.getClosestPointOnEdge((Vector2i)pos);
}
}
// for wb in gEngine.room.mergedPolygon:
@ -104,21 +108,24 @@ Math::Vector2d PathNode::fixPos(Math::Vector2d pos) {
}
void PathNode::drawCore(Math::Matrix4 trsf) {
if(!g_engine->_room) return;
if (!g_engine->_room)
return;
const Color green(0.f, 1.f, 0.f);
const Color red(1.f, 0.f, 0.f);
const Color yellow(1.f, 1.f, 0.f);
const Color blue(0.f, 0.f, 1.f);
const Object *actor = g_engine->_actor;
Color red(1.f, 0.f, 0.f);
Color yellow(1.f, 1.f, 0.f);
Color blue(0.f, 0.f, 1.f);
Object *actor = g_engine->_actor;
// draw actor path
if (((_mode == PathMode::GraphMode) || (_mode == PathMode::All)) && actor && actor->getWalkTo()) {
WalkTo *walkTo = (WalkTo *)actor->getWalkTo();
const Common::Array<Math::Vector2d> &path = walkTo->getPath();
const WalkTo *walkTo = (WalkTo *)actor->getWalkTo();
const Common::Array<Vector2i> &path = walkTo->getPath();
if (path.size() > 0) {
Common::Array<Vertex> vertices;
vertices.push_back(Vertex(g_engine->roomToScreen(actor->_node->getPos()), yellow));
for (uint i = 0; i < path.size(); i++) {
Math::Vector2d p = g_engine->roomToScreen(path[i]);
Math::Vector2d p = g_engine->roomToScreen((Math::Vector2d)path[i]);
vertices.push_back(Vertex(p, yellow));
Math::Matrix4 t;
@ -131,23 +138,22 @@ void PathNode::drawCore(Math::Matrix4 trsf) {
}
// draw graph nodes
const Twp::Graph *graph = g_engine->_room->_pathFinder.getGraph();
if (((_mode == PathMode::GraphMode) || (_mode == PathMode::All)) && graph) {
for (uint i = 0; i < graph->_concaveVertices.size(); i++) {
Math::Vector2d v = graph->_concaveVertices[i];
const Twp::Graph& graph = g_engine->_room->_pathFinder.getGraph();
if (((_mode == PathMode::GraphMode) || (_mode == PathMode::All))) {
for (uint i = 0; i < graph._concaveVertices.size(); i++) {
const Math::Vector2d p = g_engine->roomToScreen((Math::Vector2d)graph._concaveVertices[i]) - Math::Vector2d(2.f, 2.f);
Math::Matrix4 t;
Math::Vector2d p = g_engine->roomToScreen(v) - Math::Vector2d(2.f, 2.f);
t.translate(Math::Vector3d(p.getX(), p.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), yellow);
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), yellow, t);
}
if (_mode == PathMode::All) {
for (uint i = 0; i < graph->_edges.size(); i++) {
const Common::Array<GraphEdge> &edges = graph->_edges[i];
for (uint i = 0; i < graph._edges.size(); i++) {
const Common::Array<GraphEdge> &edges = graph._edges[i];
for (uint j = 0; j < edges.size(); j++) {
const GraphEdge &edge = edges[j];
Math::Vector2d p1 = g_engine->roomToScreen(graph->_nodes[edge.start]);
Math::Vector2d p2 = g_engine->roomToScreen(graph->_nodes[edge.to]);
const Math::Vector2d p1 = g_engine->roomToScreen((Math::Vector2d)graph._nodes[edge.start]);
const Math::Vector2d p2 = g_engine->roomToScreen((Math::Vector2d)graph._nodes[edge.to]);
Vertex vertices[] = {Vertex(p1), Vertex(p2)};
g_engine->getGfx().drawLines(&vertices[0], 2);
}
@ -162,30 +168,47 @@ void PathNode::drawCore(Math::Matrix4 trsf) {
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), yellow, t);
Math::Vector2d scrPos = g_engine->winToScreen(g_engine->_cursor.pos);
Math::Vector2d roomPos = g_engine->screenToRoom(scrPos);
Math::Vector2d p = fixPos(roomPos);
const Math::Vector2d scrPos = g_engine->winToScreen(g_engine->_cursor.pos);
const Math::Vector2d roomPos = g_engine->screenToRoom(scrPos);
Vector2i p = fixPos((Vector2i)roomPos);
t = Math::Matrix4();
pos = g_engine->roomToScreen(p) - Math::Vector2d(4.f, 4.f);
pos = g_engine->roomToScreen((Math::Vector2d)p) - Math::Vector2d(4.f, 4.f);
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(8.f, 8.f), yellow, t);
Object* obj = g_engine->objAt(roomPos);
if(obj) {
Object *obj = g_engine->objAt(roomPos);
if (obj) {
t = Math::Matrix4();
pos = g_engine->roomToScreen(obj->getUsePos()) - Math::Vector2d(4.f, 4.f);
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(8.f, 8.f), red, t);
}
Common::Array<Math::Vector2d> path = g_engine->_room->calculatePath(fixPos(actor->_node->getPos()), p);
const Common::Array<Vector2i> path = g_engine->_room->calculatePath(fixPos((Vector2i)actor->_node->getPos()), p);
Common::Array<Vertex> vertices;
for (uint i = 0; i < path.size(); i++) {
vertices.push_back(Vertex(g_engine->roomToScreen(path[i]), yellow));
vertices.push_back(Vertex(g_engine->roomToScreen((Math::Vector2d)path[i]), yellow));
}
if (vertices.size() > 0) {
g_engine->getGfx().drawLines(vertices.data(), vertices.size());
}
// draw a green square if inside walkbox, red if not
Common::Array<Walkbox> walkboxes = g_engine->_room ? g_engine->_room->_pathFinder.getWalkboxes() : Common::Array<Walkbox>();
if(walkboxes.empty())
return;
const bool inside = (walkboxes.size() > 0) && walkboxes[0].contains((Vector2i)roomPos);
pos = scrPos - Math::Vector2d(4.f, 4.f);
t = Math::Matrix4();
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(8.f, 8.f), inside ? green : red, t);
// draw a blue square on the closest point
pos = g_engine->roomToScreen((Math::Vector2d)walkboxes[0].getClosestPointOnEdge((Vector2i)roomPos));
t = Math::Matrix4();
t.translate(Math::Vector3d(pos.getX()-2.f, pos.getY()-2.f, 0.f));
g_engine->getGfx().drawQuad(Math::Vector2d(4.f, 4.f), blue, t);
}
}

View File

@ -60,7 +60,7 @@ public:
PathMode getMode() const { return _mode; }
private:
Math::Vector2d fixPos(Math::Vector2d pos);
Vector2i fixPos(Vector2i pos);
virtual void drawCore(Math::Matrix4 trsf) override;
private: