implement building system
This commit is contained in:
@@ -8,6 +8,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StationsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -15,6 +16,7 @@ SET(SRCS
|
||||
${SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Formula.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path)
|
||||
|
||||
cfg.heightTiles = static_cast<int>(requireInt(tbl["world"]["height_tiles"], file, "world.height_tiles"));
|
||||
cfg.refundPercentage = static_cast<int>(requireInt(tbl["world"]["refund_percentage"], file, "world.refund_percentage"));
|
||||
cfg.startingBuildingBlocks = static_cast<int>(requireInt(tbl["world"]["starting_building_blocks"], file, "world.starting_building_blocks"));
|
||||
cfg.scrapDespawnSeconds = requireDouble(tbl["world"]["scrap_despawn_seconds"], file, "world.scrap_despawn_seconds");
|
||||
cfg.beltSpeedTilesPerSecond = requireDouble(tbl["world"]["belt_speed_tiles_per_second"], file, "world.belt_speed_tiles_per_second");
|
||||
|
||||
|
||||
172
src/lib/config/SurfaceMask.cpp
Normal file
172
src/lib/config/SurfaceMask.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "SurfaceMask.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Rotate direction character 90° clockwise.
|
||||
char rotateDirCharCW(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '>': return 'v';
|
||||
case 'v': return '<';
|
||||
case '<': return '^';
|
||||
case '^': return '>';
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the character grid 90° clockwise.
|
||||
// Input grid[row][col]. Returns a new grid with swapped dimensions.
|
||||
std::vector<std::string> rotateCW(const std::vector<std::string>& grid)
|
||||
{
|
||||
if (grid.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const int srcH = static_cast<int>(grid.size());
|
||||
// Pad all rows to the same width.
|
||||
int srcW = 0;
|
||||
for (const std::string& row : grid)
|
||||
{
|
||||
const int w = static_cast<int>(row.size());
|
||||
if (w > srcW)
|
||||
{
|
||||
srcW = w;
|
||||
}
|
||||
}
|
||||
|
||||
// After 90° CW: new width = srcH, new height = srcW.
|
||||
const int dstW = srcH;
|
||||
const int dstH = srcW;
|
||||
std::vector<std::string> dst(dstH, std::string(dstW, ' '));
|
||||
|
||||
for (int row = 0; row < srcH; ++row)
|
||||
{
|
||||
for (int col = 0; col < srcW; ++col)
|
||||
{
|
||||
const char ch = (col < static_cast<int>(grid[row].size()))
|
||||
? grid[row][col]
|
||||
: ' ';
|
||||
// 90° CW mapping: (col, row) -> (dstCol = srcH-1-row, dstRow = col)
|
||||
const int dstCol = srcH - 1 - row;
|
||||
const int dstRow = col;
|
||||
dst[dstRow][dstCol] = rotateDirCharCW(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
||||
Rotation rotation)
|
||||
{
|
||||
// Number of 90° CW steps to apply.
|
||||
int cwSteps = 0;
|
||||
switch (rotation)
|
||||
{
|
||||
case Rotation::East: cwSteps = 0; break;
|
||||
case Rotation::South: cwSteps = 1; break;
|
||||
case Rotation::West: cwSteps = 2; break;
|
||||
case Rotation::North: cwSteps = 3; break;
|
||||
}
|
||||
|
||||
// Apply rotations.
|
||||
std::vector<std::string> grid = rows;
|
||||
for (int i = 0; i < cwSteps; ++i)
|
||||
{
|
||||
grid = rotateCW(grid);
|
||||
}
|
||||
|
||||
// Scan grid: collect body cells, ship dock cells, and output port indicators.
|
||||
std::vector<QPoint> rawBodyCells;
|
||||
std::vector<QPoint> rawShipDockCells;
|
||||
struct RawPort
|
||||
{
|
||||
QPoint tile;
|
||||
Rotation direction;
|
||||
};
|
||||
std::vector<RawPort> rawPorts;
|
||||
|
||||
for (int row = 0; row < static_cast<int>(grid.size()); ++row)
|
||||
{
|
||||
for (int col = 0; col < static_cast<int>(grid[row].size()); ++col)
|
||||
{
|
||||
const char ch = grid[row][col];
|
||||
if (ch == 'A')
|
||||
{
|
||||
rawBodyCells.push_back(QPoint(col, row));
|
||||
}
|
||||
else if (ch == 'S')
|
||||
{
|
||||
rawBodyCells.push_back(QPoint(col, row));
|
||||
rawShipDockCells.push_back(QPoint(col, row));
|
||||
}
|
||||
else if (ch == '>')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::East});
|
||||
}
|
||||
else if (ch == '<')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::West});
|
||||
}
|
||||
else if (ch == '^')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::North});
|
||||
}
|
||||
else if (ch == 'v')
|
||||
{
|
||||
rawPorts.push_back({QPoint(col, row), Rotation::South});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute bounding box of body cells and normalize to (0,0).
|
||||
int minCol = INT_MAX;
|
||||
int minRow = INT_MAX;
|
||||
int maxCol = INT_MIN;
|
||||
int maxRow = INT_MIN;
|
||||
|
||||
for (const QPoint& pt : rawBodyCells)
|
||||
{
|
||||
if (pt.x() < minCol) { minCol = pt.x(); }
|
||||
if (pt.x() > maxCol) { maxCol = pt.x(); }
|
||||
if (pt.y() < minRow) { minRow = pt.y(); }
|
||||
if (pt.y() > maxRow) { maxRow = pt.y(); }
|
||||
}
|
||||
|
||||
// If there are no body cells, return an empty mask.
|
||||
if (rawBodyCells.empty())
|
||||
{
|
||||
return ParsedSurfaceMask{};
|
||||
}
|
||||
|
||||
const QPoint offset(-minCol, -minRow);
|
||||
|
||||
ParsedSurfaceMask result;
|
||||
result.footprint = QSize(maxCol - minCol + 1, maxRow - minRow + 1);
|
||||
|
||||
for (const QPoint& pt : rawBodyCells)
|
||||
{
|
||||
result.bodyCells.push_back(pt + offset);
|
||||
}
|
||||
for (const QPoint& pt : rawShipDockCells)
|
||||
{
|
||||
result.shipDockCells.push_back(pt + offset);
|
||||
}
|
||||
for (const RawPort& rp : rawPorts)
|
||||
{
|
||||
Port port;
|
||||
port.tile = rp.tile + offset;
|
||||
port.direction = rp.direction;
|
||||
result.outputPorts.push_back(port);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
28
src/lib/config/SurfaceMask.h
Normal file
28
src/lib/config/SurfaceMask.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
#include "Port.h"
|
||||
#include "Rotation.h"
|
||||
|
||||
// Parsed representation of a building's surface_mask after applying rotation.
|
||||
// All coordinates are relative to the building's anchor tile (the point passed
|
||||
// to BuildingSystem::place), which corresponds to (0,0) in this system.
|
||||
struct ParsedSurfaceMask
|
||||
{
|
||||
QSize footprint; // bounding box of body cells (A + S tiles)
|
||||
std::vector<QPoint> bodyCells; // relative positions of A and S tiles
|
||||
std::vector<Port> outputPorts; // port.tile = cell adjacent to body, outside footprint;
|
||||
// port.direction = flow direction away from building
|
||||
std::vector<QPoint> shipDockCells; // relative positions of S tiles (subset of bodyCells)
|
||||
};
|
||||
|
||||
// Parse a surface_mask definition (as loaded from TOML) and apply the given
|
||||
// rotation. The canonical mask orientation corresponds to Rotation::East.
|
||||
// Rotations are applied clockwise (East=0, South=90°, West=180°, North=270°).
|
||||
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
||||
Rotation rotation);
|
||||
@@ -39,6 +39,7 @@ struct WorldConfig
|
||||
{
|
||||
int heightTiles; // REQ-GW-HEIGHT
|
||||
int refundPercentage; // REQ-BLD-DEMOLISH
|
||||
int startingBuildingBlocks; // REQ-HQ-STARTING-BLOCKS
|
||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||
double beltSpeedTilesPerSecond; // REQ-GW-BELT-SPEED
|
||||
|
||||
|
||||
Reference in New Issue
Block a user