2023-05-09 20:50:50 +02:00
|
|
|
#include "world_view.h"
|
2023-05-14 22:23:25 +02:00
|
|
|
#include "../../utilities/vector_utils.hpp"
|
2023-05-09 20:50:50 +02:00
|
|
|
|
|
|
|
WorldView::~WorldView()
|
|
|
|
{
|
2023-05-10 11:03:14 +02:00
|
|
|
delete view;
|
2023-05-14 22:23:25 +02:00
|
|
|
delete marker;
|
2023-05-09 20:50:50 +02:00
|
|
|
}
|
|
|
|
|
2023-05-14 22:23:25 +02:00
|
|
|
void WorldView::lateUpdate(Game *game)
|
2023-05-09 20:50:50 +02:00
|
|
|
{
|
2023-05-10 11:30:11 +02:00
|
|
|
// Initialize if necessary
|
2023-05-10 11:03:14 +02:00
|
|
|
if (view == nullptr)
|
|
|
|
{
|
|
|
|
initializeView(game);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update size
|
2023-05-10 11:30:11 +02:00
|
|
|
setSize(game->window->getSize());
|
2023-05-09 20:50:50 +02:00
|
|
|
|
2023-05-14 22:23:25 +02:00
|
|
|
if (target != nullptr)
|
|
|
|
{
|
|
|
|
followTarget();
|
|
|
|
}
|
2023-05-10 11:30:11 +02:00
|
|
|
|
|
|
|
// Update window if necessary
|
|
|
|
if (hasViewChanged)
|
2023-05-09 20:50:50 +02:00
|
|
|
{
|
2023-05-10 11:30:11 +02:00
|
|
|
game->window->setView(*this->view);
|
|
|
|
hasViewChanged = false;
|
2023-05-09 20:50:50 +02:00
|
|
|
}
|
2023-05-10 11:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WorldView::initializeView(Game *game)
|
|
|
|
{
|
|
|
|
auto size = game->window->getView().getSize();
|
2023-05-14 22:23:25 +02:00
|
|
|
view = new sf::View({0, 0}, size);
|
2023-05-09 20:50:50 +02:00
|
|
|
}
|
2023-05-10 11:30:11 +02:00
|
|
|
|
|
|
|
void WorldView::setSize(sf::Vector2u windowSize)
|
|
|
|
{
|
2023-05-14 22:23:25 +02:00
|
|
|
// TODO: Maybe listen to resize events instead of checking every frame?
|
|
|
|
// Is different?
|
|
|
|
if (view->getSize().x == windowSize.x && view->getSize().y == windowSize.y)
|
|
|
|
{
|
|
|
|
// Nothing to do
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-10 11:30:11 +02:00
|
|
|
sf::Vector2f viewSize = sf::Vector2f(windowSize.x, windowSize.y);
|
|
|
|
view->setSize(viewSize);
|
|
|
|
hasViewChanged = true;
|
|
|
|
}
|
|
|
|
|
2023-05-14 22:23:25 +02:00
|
|
|
WorldView::WorldView(ITrackable *trackable, sf::Vector2f freeMoveArea, sf::Vector2f dynamicFollowArea)
|
|
|
|
: target(trackable), freeMoveArea(freeMoveArea), dynamicFollowArea(dynamicFollowArea), view(nullptr),
|
|
|
|
hasViewChanged(false)
|
|
|
|
{
|
|
|
|
marker = new CircleObject(2, sf::Color::Yellow);
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Vector2f WorldView::getSize() const
|
2023-05-10 11:30:11 +02:00
|
|
|
{
|
2023-05-14 22:23:25 +02:00
|
|
|
return view->getSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Vector2f WorldView::getCenter() const
|
|
|
|
{
|
|
|
|
return view->getCenter();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldView::followTarget()
|
|
|
|
{
|
|
|
|
|
|
|
|
auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea);
|
|
|
|
marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic));
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (isTargetInArea(freeMoveArea))
|
2023-05-10 11:30:11 +02:00
|
|
|
{
|
2023-05-14 22:23:25 +02:00
|
|
|
// Nothing to do
|
2023-05-10 11:30:11 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-14 22:23:25 +02:00
|
|
|
// performHardFollow();
|
|
|
|
// if (isTargetInArea(dynamicFollowArea))
|
|
|
|
// {
|
|
|
|
// // Slowly push target back into free area
|
|
|
|
// performDynamicFollow();
|
|
|
|
// } else
|
|
|
|
// {
|
|
|
|
// // Hard follow
|
|
|
|
// performHardFollow();
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldView::isTargetInArea(sf::Vector2f areaSize)
|
|
|
|
{
|
|
|
|
// Assuming target is not null
|
|
|
|
|
|
|
|
// Target specifications
|
|
|
|
auto targetSize = target->getTrackableSize();
|
|
|
|
auto targetTopLeft = target->getTrackablePosition() - targetSize / 2.f;
|
|
|
|
auto targetBottomRight = targetTopLeft + targetSize;
|
|
|
|
|
|
|
|
// Free Movement Area
|
|
|
|
auto areaTopLeft = getCenter() - areaSize / 2.f;
|
|
|
|
auto areaBottomRight = areaTopLeft + areaSize;
|
|
|
|
|
|
|
|
// Check collision of all edges
|
|
|
|
if (targetTopLeft.x < areaTopLeft.x ||
|
|
|
|
targetTopLeft.y < areaTopLeft.y ||
|
|
|
|
targetBottomRight.x > areaBottomRight.x ||
|
|
|
|
targetBottomRight.y > areaBottomRight.y)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Vector2f WorldView::getClosestPositionInArea(sf::Vector2f areaSize) const
|
|
|
|
{
|
|
|
|
// Reduce area to only consider target position, not size
|
|
|
|
auto positionOnlyAreaSize = areaSize - target->getTrackableSize();
|
|
|
|
|
|
|
|
// Calculate max-length of rubber to land inside of area
|
|
|
|
sf::Vector2f rubber = getRubber();
|
|
|
|
|
|
|
|
// Problem is symmetrical for all corners, so can simplify to use only absolute values and part of the area
|
|
|
|
auto absRubber = abs(rubber);
|
|
|
|
auto cornerArea = positionOnlyAreaSize / 2.f;
|
|
|
|
|
|
|
|
// Calculate factor for each dimension, that would scale it to the border
|
|
|
|
float xScale = INFINITY;
|
|
|
|
if (absRubber.x > 0)
|
|
|
|
{
|
|
|
|
xScale = cornerArea.x / absRubber.x;
|
|
|
|
}
|
|
|
|
float yScale = INFINITY;
|
|
|
|
if (absRubber.y > 0)
|
|
|
|
{
|
|
|
|
yScale = cornerArea.y / absRubber.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
float length = std::min(xScale, yScale);
|
|
|
|
|
|
|
|
return rubber * length;
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Vector2f WorldView::getRubber() const
|
|
|
|
{
|
|
|
|
return normalize(target->getTrackablePosition() - getCenter());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldView::performHardFollow()
|
|
|
|
{
|
|
|
|
auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea);
|
|
|
|
auto delta = getCenter() - closestPositionInDynamic;
|
|
|
|
moveCenter(-delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldView::moveCenter(sf::Vector2<float> delta)
|
|
|
|
{
|
|
|
|
view->move(delta);
|
2023-05-10 11:30:11 +02:00
|
|
|
hasViewChanged = true;
|
|
|
|
}
|
2023-05-14 22:23:25 +02:00
|
|
|
|
|
|
|
void WorldView::performDynamicFollow()
|
|
|
|
{
|
|
|
|
auto closestPositionInDynamic = getClosestPositionInArea(freeMoveArea);
|
|
|
|
auto delta = getCenter() - closestPositionInDynamic;
|
|
|
|
moveCenter(delta * VIEW_RUBBER_FOLLOW_SPEED * FRAME_TIME.asSeconds());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldView::draw(sf::RenderWindow *window) const
|
|
|
|
{
|
|
|
|
marker->draw(window);
|
|
|
|
}
|