make selection box sub-tile aware
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QRectF>
|
||||
|
||||
#include "BeltSystem.h"
|
||||
#include "Building.h"
|
||||
@@ -135,6 +136,26 @@ TEST_CASE("BuildingSystem: place miner occupies expected body tiles", "[building
|
||||
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(1, 1)));
|
||||
}
|
||||
|
||||
TEST_CASE("buildingsInBox covers a body cell the box only reaches into", "[building]")
|
||||
{
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId id = f.bs.place(f.state, BuildingType::Miner, QPoint(0, 0),
|
||||
Rotation::East, 0).value();
|
||||
|
||||
// Body at (0,0),(1,0),(0,1). The box is unsnapped and lies wholly within cell
|
||||
// (1,0) without filling it, which is enough: a building is covered when the box
|
||||
// overlaps any of its body cells (REQ-UI-MULTI-SELECT, Coverage).
|
||||
const std::vector<BuildingId> grazed =
|
||||
buildingsInBox(f.state, QRectF(1.6, 0.4, 0.2, 0.2));
|
||||
REQUIRE(grazed.size() == 1);
|
||||
REQUIRE(grazed.front() == id);
|
||||
|
||||
// (1,1) is the output-port tile, not a body cell, so a box inside it covers
|
||||
// nothing even though it is surrounded by the miner's cells.
|
||||
REQUIRE(buildingsInBox(f.state, QRectF(1.2, 1.2, 0.5, 0.5)).empty());
|
||||
}
|
||||
|
||||
// -- World-bounds rejection (REQ-BLD-PLACE-VALID) ---------------------------
|
||||
|
||||
TEST_CASE("BuildingSystem: place rejects a building above the world (y < 0)", "[building]")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <QRectF>
|
||||
#include <QSize>
|
||||
#include <QVector2D>
|
||||
|
||||
@@ -210,17 +211,16 @@ TEST_CASE("entityAtWorldPos never returns debris", "[debris]")
|
||||
REQUIRE((entityAtWorldPos(admin, QVector2D(3.0f, 4.0f)) == entt::null));
|
||||
}
|
||||
|
||||
TEST_CASE("debrisInBox returns exactly the debris inside the tile rectangle", "[debris]")
|
||||
TEST_CASE("debrisInBox returns exactly the debris the box encloses", "[debris]")
|
||||
{
|
||||
EntityAdmin admin;
|
||||
DebrisSystem ss(admin);
|
||||
|
||||
const entt::entity inA = ss.spawn(QVector2D(1.2f, 2.7f), 1, 100); // tile (1,2)
|
||||
const entt::entity inB = ss.spawn(QVector2D(4.9f, 5.1f), 1, 100); // tile (4,5)
|
||||
const entt::entity inA = ss.spawn(QVector2D(1.2f, 2.7f), 1, 100);
|
||||
const entt::entity inB = ss.spawn(QVector2D(4.9f, 5.1f), 1, 100);
|
||||
const entt::entity outX = ss.spawn(QVector2D(10.0f, 10.0f), 1, 100);
|
||||
|
||||
// Box given in reversed corner order to confirm normalization.
|
||||
const std::vector<entt::entity> hit = debrisInBox(admin, QPoint(5, 5), QPoint(0, 0));
|
||||
const std::vector<entt::entity> hit = debrisInBox(admin, QRectF(0.0, 0.0, 6.0, 6.0));
|
||||
|
||||
REQUIRE(hit.size() == 2);
|
||||
REQUIRE(contains(hit, inA));
|
||||
@@ -228,6 +228,23 @@ TEST_CASE("debrisInBox returns exactly the debris inside the tile rectangle", "[
|
||||
REQUIRE_FALSE(contains(hit, outX));
|
||||
}
|
||||
|
||||
TEST_CASE("debrisInBox cuts within a tile, not along the tile grid", "[debris]")
|
||||
{
|
||||
EntityAdmin admin;
|
||||
DebrisSystem ss(admin);
|
||||
|
||||
const entt::entity inTile = ss.spawn(QVector2D(1.8f, 2.5f), 1, 100);
|
||||
// Same tile (1,2) as the piece above, but on the far side of the box's left edge:
|
||||
// a tile-snapped box would take both (REQ-UI-MULTI-SELECT, Coverage).
|
||||
const entt::entity outTile = ss.spawn(QVector2D(1.2f, 2.5f), 1, 100);
|
||||
|
||||
const std::vector<entt::entity> hit = debrisInBox(admin, QRectF(1.5, 2.0, 4.0, 4.0));
|
||||
|
||||
REQUIRE(hit.size() == 1);
|
||||
REQUIRE(contains(hit, inTile));
|
||||
REQUIRE_FALSE(contains(hit, outTile));
|
||||
}
|
||||
|
||||
TEST_CASE("actorsInBox returns living ships and stations, excluding debris and dead actors",
|
||||
"[actor]")
|
||||
{
|
||||
@@ -236,10 +253,10 @@ TEST_CASE("actorsInBox returns living ships and stations, excluding debris and d
|
||||
// Two living ships inside the box: one player, one enemy.
|
||||
const entt::entity playerShip = admin.spawnShip(
|
||||
QVector2D(1.5f, 2.5f), 100.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 5.0f,
|
||||
"fighter", false); // tile (1,2)
|
||||
"fighter", false);
|
||||
const entt::entity enemyShip = admin.spawnShip(
|
||||
QVector2D(4.2f, 5.8f), 100.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 5.0f,
|
||||
"raider", true); // tile (4,5)
|
||||
"raider", true);
|
||||
|
||||
// A dead ship inside the box is excluded.
|
||||
const entt::entity deadShip = admin.spawnShip(
|
||||
@@ -251,7 +268,7 @@ TEST_CASE("actorsInBox returns living ships and stations, excluding debris and d
|
||||
QVector2D(20.0f, 20.0f), 100.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 5.0f,
|
||||
"fighter", false);
|
||||
|
||||
// A station is included when any body cell lies inside the box.
|
||||
// A station is included when the box overlaps any body cell.
|
||||
const std::vector<QPoint> stationCells{ QPoint(2, 2), QPoint(3, 2) };
|
||||
const entt::entity station = admin.spawnStation(
|
||||
QPoint(2, 2), QSize(2, 1), stationCells, 200.0f, 200.0f, true);
|
||||
@@ -260,7 +277,7 @@ TEST_CASE("actorsInBox returns living ships and stations, excluding debris and d
|
||||
admin.spawnDebris(QVector2D(1.0f, 1.0f), 5, Tick(1000));
|
||||
admin.spawnHqProxy(QVector2D(0.5f, 0.5f), 500.0f, 500.0f);
|
||||
|
||||
const std::vector<entt::entity> hit = actorsInBox(admin, QPoint(5, 5), QPoint(0, 0));
|
||||
const std::vector<entt::entity> hit = actorsInBox(admin, QRectF(0.0, 0.0, 6.0, 6.0));
|
||||
|
||||
REQUIRE(hit.size() == 3);
|
||||
REQUIRE(contains(hit, playerShip));
|
||||
@@ -269,3 +286,32 @@ TEST_CASE("actorsInBox returns living ships and stations, excluding debris and d
|
||||
REQUIRE_FALSE(contains(hit, deadShip));
|
||||
REQUIRE_FALSE(contains(hit, outsideShip));
|
||||
}
|
||||
|
||||
TEST_CASE("actorsInBox takes a ship by its position and a station by its footprint",
|
||||
"[actor]")
|
||||
{
|
||||
EntityAdmin admin;
|
||||
|
||||
// Ship inside the tile the box only reaches into: taken, because the box contains
|
||||
// its position (REQ-UI-MULTI-SELECT, Coverage).
|
||||
const entt::entity shipInside = admin.spawnShip(
|
||||
QVector2D(3.9f, 3.9f), 100.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 5.0f,
|
||||
"fighter", false);
|
||||
// Same tile (3,3), outside the box: a tile-snapped box would take it too.
|
||||
const entt::entity shipOutside = admin.spawnShip(
|
||||
QVector2D(3.1f, 3.1f), 100.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 5.0f,
|
||||
"fighter", false);
|
||||
|
||||
// The box reaches 0.5 tiles into the station's only cell, which is enough: a
|
||||
// station is covered when the box overlaps its footprint.
|
||||
const std::vector<QPoint> stationCells{ QPoint(4, 3) };
|
||||
const entt::entity station = admin.spawnStation(
|
||||
QPoint(4, 3), QSize(1, 1), stationCells, 200.0f, 200.0f, true);
|
||||
|
||||
const std::vector<entt::entity> hit = actorsInBox(admin, QRectF(3.5, 3.5, 1.0, 1.0));
|
||||
|
||||
REQUIRE(hit.size() == 2);
|
||||
REQUIRE(contains(hit, shipInside));
|
||||
REQUIRE(contains(hit, station));
|
||||
REQUIRE_FALSE(contains(hit, shipOutside));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user