/* 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 . * */ #ifndef ALCACHOFA_SHAPE_H #define ALCACHOFA_SHAPE_H #include "common/stream.h" #include "common/array.h" #include "common/stack.h" #include "common/rect.h" #include "common/span.h" #include "common/util.h" #include "math/vector2d.h" #include "alcachofa/common.h" namespace Alcachofa { struct EdgeDistances { EdgeDistances(Common::Point edgeA, Common::Point edgeB, Common::Point query); float _edgeLength; float _onEdge; float _toEdge; }; struct Polygon { uint _index; Common::Span _points; bool contains(Common::Point query) const; bool intersectsEdge(uint startPointI, Common::Point a, Common::Point b) const; EdgeDistances edgeDistances(uint startPointI, Common::Point query) const; Common::Point closestPointTo(Common::Point query, float &distanceSqr) const; inline Common::Point closestPointTo(Common::Point query) const { float dummy; return closestPointTo(query, dummy); } Common::Point midPoint() const; }; struct PathFindingPolygon : Polygon { Common::Span _pointDepths; int8 _order; using SharedPoint = Common::Pair; float depthAt(Common::Point query) const; uint findSharedPoints(const PathFindingPolygon &other, Common::Span sharedPoints) const; }; struct FloorColorPolygon : Polygon { Common::Span _pointColors; Color colorAt(Common::Point query) const; }; template struct PolygonIterator { using difference_type = uint; using value_type = TPolygon; using my_type = PolygonIterator; inline value_type operator*() const { return _shape.at(_index); } inline my_type &operator++() { assert(_index < _shape.polygonCount()); _index++; return *this; } inline my_type operator++(int) { assert(_index < _shape.polygonCount()); auto tmp = *this; ++*this; return tmp; } inline bool operator==(const my_type &it) const { return &this->_shape == &it._shape && this->_index == it._index; } inline bool operator!=(const my_type &it) const { return &this->_shape != &it._shape || this->_index != it._index; } private: friend typename Common::remove_const_t; PolygonIterator(const TShape &shape, uint index = 0) : _shape(shape) , _index(index) { } const TShape &_shape; uint _index; }; class Shape { public: using iterator = PolygonIterator; Shape(); Shape(Common::ReadStream &stream); inline Common::Point firstPoint() const { return _points.empty() ? Common::Point() : _points[0]; } inline uint polygonCount() const { return _polygons.size(); } inline bool empty() const { return polygonCount() == 0; } inline iterator begin() const { return { *this, 0 }; } inline iterator end() const { return { *this, polygonCount() }; } Polygon at(uint index) const; int32 polygonContaining(Common::Point query) const; bool contains(Common::Point query) const; Common::Point closestPointTo(Common::Point query, int32 &polygonI) const; inline Common::Point closestPointTo(Common::Point query) const { int32 dummy; return closestPointTo(query, dummy); } void setAsRectangle(const Common::Rect &rect); protected: uint addPolygon(uint maxCount); using PolygonRange = Common::Pair; Common::Array _polygons; Common::Array _points; }; /** * @brief Path finding is based on the Shape class with the invariant that * every polygon is a convex polygon with at most four points. * Equal points of different quads link them together, for shared edges we * add an additional link point in the center of the edge. * * The resulting graph is processed using Floyd-Warshall to precalculate for * the actual path finding. Additionally we check whether a character can * walk straight through an edge instead of following the link points. */ class PathFindingShape final : public Shape { public: using iterator = PolygonIterator; static constexpr const uint kPointsPerPolygon = 4; PathFindingShape(); PathFindingShape(Common::ReadStream &stream); inline iterator begin() const { return { *this, 0 }; } inline iterator end() const { return { *this, polygonCount() }; } PathFindingPolygon at(uint index) const; int8 orderAt(Common::Point query) const; float depthAt(Common::Point query) const; bool findPath( Common::Point from, Common::Point to, Common::Stack &path) const; int32 edgeTarget(uint polygonI, uint pointI) const; bool findEvadeTarget( Common::Point centerTarget, float depthScale, float minDistSqr, Common::Point &evadeTarget) const; private: using LinkIndex = Common::Pair; void setupLinks(); void setupLinkPoint( const PathFindingPolygon &outer, const PathFindingPolygon &inner, PathFindingPolygon::SharedPoint pointI); void setupLinkEdge( const PathFindingPolygon &outer, const PathFindingPolygon &inner, LinkIndex outerP, LinkIndex innerP); void initializeFloydWarshall(); void calculateFloydWarshall(); bool canGoStraightThrough( Common::Point from, Common::Point to, int32 fromContaining, int32 toContaining) const; void floydWarshallPath( Common::Point from, Common::Point to, int32 fromContaining, int32 toContaining, Common::Stack &path) const; Common::Array _pointDepths; Common::Array _polygonOrders; /** * These are the edges in the graph, they are either points * that are shared by two polygons or artificial points in * the center of a shared edge */ Common::Array _linkPoints; /** * For each point of each polygon the index (or -1) to * the corresponding link point. The second point is the * index to the artifical center point */ struct LinkPolygonIndices { LinkPolygonIndices(); LinkIndex _points[kPointsPerPolygon]; }; Common::Array _linkIndices; /** * For the going-straight-through-edges check we need * to know for each shared edge (defined by the starting point) * into which quad we will walk. */ Common::Array _targetQuads; Common::Array _distanceMatrix; ///< for Floyd-Warshall Common::Array _previousTarget; ///< for Floyd-Warshall }; using OptionalColor = Common::Pair; class FloorColorShape final : public Shape { public: using iterator = PolygonIterator; static constexpr const uint kPointsPerPolygon = 4; FloorColorShape(); FloorColorShape(Common::ReadStream &stream); inline iterator begin() const { return { *this, 0 }; } inline iterator end() const { return { *this, polygonCount() }; } FloorColorPolygon at(uint index) const; OptionalColor colorAt(Common::Point query) const; private: Common::Array _pointColors; }; } #endif // ALCACHOFA_SHAPE_H