The ship layout preview and Configure Layout button are now always shown for a selected shipyard and disabled while no schematic is selected, rather than being hidden. The preview shows an empty placeholder box in that state. Update REQ-MOD-UI-PREVIEW to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZR44tA8sn4dPqDzAVXyps
229 lines
5.9 KiB
C++
229 lines
5.9 KiB
C++
#include "ShipLayoutPreview.h"
|
|
|
|
#include <QPainter>
|
|
#include <QPaintEvent>
|
|
|
|
namespace
|
|
{
|
|
|
|
const int kCellSize = 8;
|
|
|
|
// Size of the empty placeholder box shown when no schematic is selected.
|
|
const int kPlaceholderWidth = 64;
|
|
const int kPlaceholderHeight = 40;
|
|
|
|
const ModuleDef* findModuleDef(const std::vector<ModuleDef>& modules,
|
|
const std::string& id)
|
|
{
|
|
for (const ModuleDef& def : modules)
|
|
{
|
|
if (def.id == id)
|
|
{
|
|
return &def;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::string> rotateMaskCW(const std::vector<std::string>& grid)
|
|
{
|
|
if (grid.empty())
|
|
{
|
|
return {};
|
|
}
|
|
const int srcH = static_cast<int>(grid.size());
|
|
int srcW = 0;
|
|
for (const std::string& row : grid)
|
|
{
|
|
const int w = static_cast<int>(row.size());
|
|
if (w > srcW)
|
|
{
|
|
srcW = w;
|
|
}
|
|
}
|
|
const int dstW = srcH;
|
|
const int dstH = srcW;
|
|
std::vector<std::string> dst(dstH, std::string(dstW, 'X'));
|
|
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]
|
|
: 'X';
|
|
dst[col][srcH - 1 - row] = ch;
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
std::vector<std::string> rotateMask(const std::vector<std::string>& mask,
|
|
Rotation rotation)
|
|
{
|
|
int steps = 0;
|
|
switch (rotation)
|
|
{
|
|
case Rotation::East: steps = 0; break;
|
|
case Rotation::South: steps = 1; break;
|
|
case Rotation::West: steps = 2; break;
|
|
case Rotation::North: steps = 3; break;
|
|
}
|
|
std::vector<std::string> result = mask;
|
|
for (int i = 0; i < steps; ++i)
|
|
{
|
|
result = rotateMaskCW(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
ShipLayoutPreview::ShipLayoutPreview(QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_modules(nullptr)
|
|
, m_rows(0)
|
|
, m_cols(0)
|
|
{
|
|
}
|
|
|
|
void ShipLayoutPreview::clear()
|
|
{
|
|
m_grid.clear();
|
|
m_placedModules.clear();
|
|
m_modules = nullptr;
|
|
m_rows = 0;
|
|
m_cols = 0;
|
|
m_placeholder = false;
|
|
setFixedSize(0, 0);
|
|
update();
|
|
}
|
|
|
|
void ShipLayoutPreview::showPlaceholder()
|
|
{
|
|
m_grid.clear();
|
|
m_placedModules.clear();
|
|
m_modules = nullptr;
|
|
m_rows = 0;
|
|
m_cols = 0;
|
|
m_placeholder = true;
|
|
setFixedSize(kPlaceholderWidth, kPlaceholderHeight);
|
|
update();
|
|
}
|
|
|
|
void ShipLayoutPreview::setShipAndLayout(const std::vector<std::string>& shipLayout,
|
|
const ShipLayoutConfig& layout,
|
|
const std::vector<ModuleDef>* modules)
|
|
{
|
|
m_placeholder = false;
|
|
m_modules = modules;
|
|
m_placedModules = layout.placedModules;
|
|
m_rows = static_cast<int>(shipLayout.size());
|
|
m_cols = 0;
|
|
for (const std::string& row : shipLayout)
|
|
{
|
|
const int w = static_cast<int>(row.size());
|
|
if (w > m_cols)
|
|
{
|
|
m_cols = w;
|
|
}
|
|
}
|
|
|
|
m_grid.assign(m_rows, std::vector<CellInfo>(m_cols, {false, -1}));
|
|
for (int r = 0; r < m_rows; ++r)
|
|
{
|
|
for (int c = 0; c < static_cast<int>(shipLayout[r].size()); ++c)
|
|
{
|
|
if (shipLayout[r][c] == 'O')
|
|
{
|
|
m_grid[r][c].buildable = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < static_cast<int>(m_placedModules.size()); ++i)
|
|
{
|
|
const PlacedModule& pm = m_placedModules[i];
|
|
const ModuleDef* def = findModuleDef(*m_modules, pm.moduleId);
|
|
if (!def)
|
|
{
|
|
continue;
|
|
}
|
|
const std::vector<std::string> rotated = rotateMask(def->surfaceMask, pm.rotation);
|
|
for (int mr = 0; mr < static_cast<int>(rotated.size()); ++mr)
|
|
{
|
|
for (int mc = 0; mc < static_cast<int>(rotated[mr].size()); ++mc)
|
|
{
|
|
if (rotated[mr][mc] != 'O')
|
|
{
|
|
continue;
|
|
}
|
|
const int gr = pm.position.y() + mr;
|
|
const int gc = pm.position.x() + mc;
|
|
if (gr >= 0 && gr < m_rows && gc >= 0 && gc < m_cols)
|
|
{
|
|
m_grid[gr][gc].moduleIndex = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setFixedSize(m_cols * kCellSize, m_rows * kCellSize);
|
|
update();
|
|
}
|
|
|
|
void ShipLayoutPreview::paintEvent(QPaintEvent* /*event*/)
|
|
{
|
|
if (m_placeholder)
|
|
{
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
const QRect box = rect().adjusted(0, 0, -1, -1);
|
|
painter.fillRect(box, QColor(40, 40, 40));
|
|
painter.setPen(QColor(128, 128, 128));
|
|
painter.drawRect(box);
|
|
painter.drawText(box, Qt::AlignCenter, tr("No schematic"));
|
|
return;
|
|
}
|
|
|
|
if (m_rows == 0 || m_cols == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
for (int r = 0; r < m_rows; ++r)
|
|
{
|
|
for (int c = 0; c < m_cols; ++c)
|
|
{
|
|
const QRect cellRect(c * kCellSize, r * kCellSize, kCellSize, kCellSize);
|
|
const CellInfo& cell = m_grid[r][c];
|
|
|
|
if (!cell.buildable)
|
|
{
|
|
painter.fillRect(cellRect, Qt::black);
|
|
}
|
|
else if (cell.moduleIndex >= 0)
|
|
{
|
|
const PlacedModule& pm = m_placedModules[cell.moduleIndex];
|
|
const ModuleDef* def = findModuleDef(*m_modules, pm.moduleId);
|
|
QColor color(Qt::gray);
|
|
if (def)
|
|
{
|
|
color = QColor(QString::fromStdString(def->fillColor));
|
|
}
|
|
painter.fillRect(cellRect, color);
|
|
}
|
|
else
|
|
{
|
|
painter.fillRect(cellRect, Qt::white);
|
|
}
|
|
|
|
painter.setPen(QColor(128, 128, 128));
|
|
painter.drawRect(cellRect);
|
|
}
|
|
}
|
|
}
|