diff --git a/source/World.cpp b/source/World.cpp index a87ea89..8b147fc 100755 --- a/source/World.cpp +++ b/source/World.cpp @@ -241,3 +241,46 @@ World::draw(sf::RenderTarget& target, sf::RenderStates states) const { } } } + +/* + * Performs a raycast between two points to check if the path between them is + * clear of walls. Does not consider characters, bullets etc. + * + * @param lineStart First point of the line to test. + * @param lineEnd Second point of the line to test. + * @return True if the ray was not blocked. + */ +bool +World::raycast(const sf::Vector2f& lineStart, + const sf::Vector2f& lineEnd) const { + assert(lineStart != lineEnd); + sf::Vector2f lineCenter = lineStart + 0.5f * (lineEnd - lineStart); + for (const auto& it : mDrawables.at(Sprite::Category::CATEGORY_WORLD)) { + if (it->collisionEnabled(Sprite::Category::CATEGORY_WORLD)) + continue; + sf::Vector2f axis = it->getPosition() - lineCenter; + if (axis == sf::Vector2f()) + return false; + + axis = thor::unitVector(axis); + sf::Vector2f halfsize = it->getSize() / 2.0f; + float rectPosProjected = thor::dotProduct(axis, it->getPosition()); + float lineStartProjected = thor::dotProduct(axis, lineStart); + float lineEndProjected = thor::dotProduct(axis, lineEnd); + // For corner projections, those on the same line with the rect + // center are equal by value, so we only need one on each axis + // and take the maximum. + float rectHalfWidthProjected = std::max( + abs(thor::dotProduct(axis, halfsize)), + abs(thor::dotProduct(axis, + sf::Vector2f(halfsize.x, -halfsize.y)))); + Interval line = Interval::IntervalFromPoints(lineStartProjected, + lineEndProjected); + Interval rect = Interval::IntervalFromRadius(rectPosProjected, + rectHalfWidthProjected); + // Allow movement if sprites are moving apart. + if (line.getOverlap(rect).getLength() > 0.0f) + return false; + } + return true; +} diff --git a/source/World.h b/source/World.h index 3d338f2..c4c96a0 100755 --- a/source/World.h +++ b/source/World.h @@ -28,6 +28,8 @@ public: void think(int elapsed); std::vector > getCharacters(const sf::Vector2f& position, float maxDistance) const; + bool raycast(const sf::Vector2f& lineStart, + const sf::Vector2f& lineEnd) const; private: diff --git a/source/abstract/Character.cpp b/source/abstract/Character.cpp index 62b17c1..0aa7710 100644 --- a/source/abstract/Character.cpp +++ b/source/abstract/Character.cpp @@ -156,7 +156,7 @@ Character::isMoving() const { */ bool Character::isVisible(const sf::Vector2f& target) const { - return mTileManager.raycast(getPosition(), target); + return mWorld.raycast(getPosition(), target); } /** diff --git a/source/sprites/TileManager.cpp b/source/sprites/TileManager.cpp index 058fcc1..1084433 100644 --- a/source/sprites/TileManager.cpp +++ b/source/sprites/TileManager.cpp @@ -96,44 +96,3 @@ TileManager::insertTile(const TilePosition& position, Type type) { mTiles.push_back(tile); mWorld.insert(tile); } - -/* - * Performs a raycast between two points to check if the path between them is - * clear of walls. - * - * @param lineStart First point of the line to test. - * @param lineEnd Second point of the line to test. - * @return True if the ray was not blocked. - */ -bool -TileManager::raycast(const sf::Vector2f& lineStart, - const sf::Vector2f& lineEnd) const { - assert(lineStart != lineEnd); - sf::Vector2f lineCenter = lineStart + 0.5f * (lineEnd - lineStart); - for (auto it : mTiles) { - if (it->getType() == Type::FLOOR) - continue; - sf::Vector2f axis = it->getPosition() - lineCenter; - if (axis == sf::Vector2f()) - return false; - - axis = thor::unitVector(axis); - sf::Vector2f halfsize = it->getSize() / 2.0f; - float rectPosProjected = thor::dotProduct(axis, it->getPosition()); - float lineStartProjected = thor::dotProduct(axis, lineStart); - float lineEndProjected = thor::dotProduct(axis, lineEnd); - // For corner projections, those on the same line with the rect - // center are equal by value, so we only need one on each axis - // and take the maximum. - float rectHalfWidthProjected = std::max( - abs(thor::dotProduct(axis, halfsize)), - abs(thor::dotProduct(axis, - sf::Vector2f(halfsize.x, -halfsize.y)))); - Interval line = Interval::IntervalFromPoints(lineStartProjected, lineEndProjected); - Interval rect = Interval::IntervalFromRadius(rectPosProjected, rectHalfWidthProjected); - // Allow movement if sprites are moving apart. - if (line.getOverlap(rect).getLength() > 0.0f) - return false; - } - return true; -} diff --git a/source/sprites/TileManager.h b/source/sprites/TileManager.h index 5e38697..5a7da93 100644 --- a/source/sprites/TileManager.h +++ b/source/sprites/TileManager.h @@ -31,8 +31,6 @@ public: explicit TileManager(World& world); void insertTile(const TilePosition& position, Type type); - bool raycast(const sf::Vector2f& lineStart, - const sf::Vector2f& lineEnd) const; private: class Tile;