holesome/src/game/physics/holes/ground/collectable_sim_ground.cpp

193 lines
5 KiB
C++

#include "collectable_sim_ground.hpp"
#include <utility>
#include "../../../../config.h"
void CollectableSimGround::closeAllHoles()
{
// Create one segment for the ground
createSegment(-groundWidth / 2.f, groundWidth / 2.f);
}
void CollectableSimGround::createLayout(DepthHoleLayout &layout)
{
if (!hasLayoutChanged(layout))
{
return;
}
currentLayoutHoles = layout.holes;
segments.clear();
if (currentLayoutHoles.empty())
{
closeAllHoles();
return;
}
// Sort holes from left to right
std::sort(currentLayoutHoles.begin(), currentLayoutHoles.end(),
[](DepthHoleDescription a, DepthHoleDescription b)
{
return a.x < b.x;
});
// Create segments for holes
float leftCorner = -groundWidth / 2.f;
float leftHoleId = -1;
for (auto &hole: currentLayoutHoles)
{
auto rightCorner = hole.x - hole.width / 2.f;
auto segment = createSegment(leftCorner, rightCorner);
if (segment != nullptr)
{
segment->rightHoleId = hole.playerId;
segment->leftHoleId = leftHoleId;
}
leftHoleId = hole.playerId;
leftCorner = hole.x + hole.width / 2.f;
}
// Create segment for the right side
auto segment = createSegment(leftCorner, groundWidth / 2.f);
if (segment != nullptr)
{
segment->leftHoleId = leftHoleId;
}
}
CollectableSimGround::CollectableSimGround(std::shared_ptr<b2World> world, float groundWidth)
: world(std::move(world)), groundWidth(groundWidth)
{
closeAllHoles();
}
void CollectableSimGround::setGroundWidth(float width)
{
if (width <= 0)
{
throw std::runtime_error("Ground width must be greater than 0");
}
if (width == groundWidth)
{
return;
}
groundWidth = width;
updateOuterSegmentsToWidth();
}
void CollectableSimGround::updateOuterSegmentsToWidth()
{
auto outerSegments = getOuterSegments();
auto borderPoints = groundWidth / 2.f;
// Left segment
if (outerSegments.left != nullptr)
{
auto leftSegment = outerSegments.left->body;
auto leftCenter = leftSegment->getCenter();
auto leftSize = leftSegment->getSize();
auto rightPoint = leftCenter.x + leftSize.x / 2.f;
createSegment(-borderPoints, rightPoint, outerSegments.left);
}
// Right segment
if (outerSegments.right != nullptr)
{
auto rightSegment = outerSegments.right->body;
auto rightCenter = rightSegment->getCenter();
auto rightSize = rightSegment->getSize();
auto leftPoint = rightCenter.x - rightSize.x / 2.f;
createSegment(leftPoint, borderPoints, outerSegments.right);
}
}
std::shared_ptr<CollectableSimGroundSegment> CollectableSimGround::createSegment(float leftCorner, float rightCorner,
std::shared_ptr<CollectableSimGroundSegment> segment)
{
if (leftCorner > rightCorner)
{
// Segment would have negative width, don't create it and leave empty instead
return segment;
}
if (segment == nullptr)
{
segment = std::make_shared<CollectableSimGroundSegment>(world);
segments.push_back(segment);
}
auto center = sf::Vector2f((leftCorner + rightCorner) / 2.f, -COLLECTABLES_SIM_GROUND_THICKNESS / 2.f);
auto size = sf::Vector2f(rightCorner - leftCorner, COLLECTABLES_SIM_GROUND_THICKNESS);
segment->body->createSquare(b2_kinematicBody, center, size);
return segment;
}
CollectableSimGround::SideSegments CollectableSimGround::getOuterSegments()
{
SideSegments sideSegments;
for (const auto &segment: segments)
{
if (segment->rightHoleId < 0)
{
sideSegments.right = segment;
}
if (segment->leftHoleId < 0)
{
sideSegments.left = segment;
}
if (sideSegments.left != nullptr && sideSegments.right != nullptr)
{
break;
}
}
return sideSegments;
}
bool CollectableSimGround::hasLayoutChanged(DepthHoleLayout &layout)
{
if (layout.holes.size() != currentLayoutHoles.size())
{
// Number of holes changed
return true;
}
// Below here: Number of holes is the same
if (currentLayoutHoles.empty())
{
// Both are empty, so no change
return false;
}
// Sort holes from left to right
std::sort(layout.holes.begin(), layout.holes.end(),
[](DepthHoleDescription a, DepthHoleDescription b)
{
return a.x < b.x;
});
// Compare holes
for (int i = 0; i < layout.holes.size(); i++)
{
auto currentHole = currentLayoutHoles[i];
auto newHole = layout.holes[i];
if (newHole.x != currentHole.x ||
newHole.width != currentHole.width)
{
// Hole changed
return true;
}
}
return false;
}