Unify tunnel build mode into a single Tunnel button
This commit is contained in:
115
src/lib/core/TunnelCompletion.cpp
Normal file
115
src/lib/core/TunnelCompletion.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "TunnelCompletion.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
QPoint stepTile(QPoint tile, Rotation dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case Rotation::North: return {tile.x(), tile.y() - 1};
|
||||
case Rotation::East: return {tile.x() + 1, tile.y() };
|
||||
case Rotation::South: return {tile.x(), tile.y() + 1};
|
||||
case Rotation::West: return {tile.x() - 1, tile.y() };
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
Rotation oppositeRotation(Rotation dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case Rotation::North: return Rotation::South;
|
||||
case Rotation::East: return Rotation::West;
|
||||
case Rotation::South: return Rotation::North;
|
||||
case Rotation::West: return Rotation::East;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
float tileCenterDistanceSq(QPoint tile, QVector2D cursorWorldPos)
|
||||
{
|
||||
const QVector2D center(static_cast<float>(tile.x()) + 0.5f,
|
||||
static_cast<float>(tile.y()) + 0.5f);
|
||||
return (center - cursorWorldPos).lengthSquared();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<QPoint> firstTunnelFacing(const TunnelLookup& lookup, QPoint start,
|
||||
Rotation stepDir, Rotation targetFacing,
|
||||
int maxDistance)
|
||||
{
|
||||
QPoint probe = start;
|
||||
for (int distance = 1; distance <= maxDistance; ++distance)
|
||||
{
|
||||
probe = stepTile(probe, stepDir);
|
||||
const std::optional<TunnelTileInfo> info = lookup(probe);
|
||||
if (info.has_value() && info->rotation == targetFacing)
|
||||
{
|
||||
return probe;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TunnelCompletion resolveTunnelCompletion(const TunnelLookup& lookup, QPoint hoverTile,
|
||||
Rotation rotation, int maxDistance,
|
||||
QVector2D cursorWorldPos)
|
||||
{
|
||||
// Entry-completion: a hypothetical entry at the hovered tile searches ahead (its
|
||||
// facing direction) for a same-direction exit to pair with. The first
|
||||
// same-direction tunnel ahead completes the entry only if it is an exit.
|
||||
std::optional<QPoint> exitPartner;
|
||||
if (const std::optional<QPoint> ahead =
|
||||
firstTunnelFacing(lookup, hoverTile, rotation, rotation, maxDistance);
|
||||
ahead.has_value())
|
||||
{
|
||||
const std::optional<TunnelTileInfo> info = lookup(*ahead);
|
||||
if (info.has_value() && info->type == BuildingType::TunnelExit)
|
||||
{
|
||||
exitPartner = ahead;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit-completion: a hypothetical exit at the hovered tile pairs with an existing
|
||||
// entry behind it — one whose forward search (its facing direction) reaches the
|
||||
// hovered tile as its first same-direction tunnel. Scanning backwards, the first
|
||||
// same-direction tunnel completes the exit only if it is an entry.
|
||||
std::optional<QPoint> entryPartner;
|
||||
if (const std::optional<QPoint> behind =
|
||||
firstTunnelFacing(lookup, hoverTile, oppositeRotation(rotation), rotation,
|
||||
maxDistance);
|
||||
behind.has_value())
|
||||
{
|
||||
const std::optional<TunnelTileInfo> info = lookup(*behind);
|
||||
if (info.has_value() && info->type == BuildingType::TunnelEntry)
|
||||
{
|
||||
entryPartner = behind;
|
||||
}
|
||||
}
|
||||
|
||||
// Default: a TunnelEntry with no partner.
|
||||
if (!entryPartner.has_value() && !exitPartner.has_value())
|
||||
{
|
||||
return TunnelCompletion{BuildingType::TunnelEntry, std::nullopt};
|
||||
}
|
||||
if (entryPartner.has_value() && !exitPartner.has_value())
|
||||
{
|
||||
return TunnelCompletion{BuildingType::TunnelExit, entryPartner};
|
||||
}
|
||||
if (exitPartner.has_value() && !entryPartner.has_value())
|
||||
{
|
||||
return TunnelCompletion{BuildingType::TunnelEntry, exitPartner};
|
||||
}
|
||||
|
||||
// Both apply: complete whichever existing partner is closer to the sub-tile
|
||||
// cursor position (REQ-BLD-TUNNEL-MODE).
|
||||
const float entryDistanceSq = tileCenterDistanceSq(*entryPartner, cursorWorldPos);
|
||||
const float exitDistanceSq = tileCenterDistanceSq(*exitPartner, cursorWorldPos);
|
||||
if (entryDistanceSq <= exitDistanceSq)
|
||||
{
|
||||
return TunnelCompletion{BuildingType::TunnelExit, entryPartner};
|
||||
}
|
||||
return TunnelCompletion{BuildingType::TunnelEntry, exitPartner};
|
||||
}
|
||||
Reference in New Issue
Block a user