feat(ui): place city project list vertically on the right with queue management and cancellation
This commit is contained in:
+306
-243
@@ -620,6 +620,9 @@ void QSceneRenderer::drawUnitsOverlay(QPainter &painter, const QMatrix4x4 &vMatr
|
||||
QColor col = pl ? pl->primaryColor : Qt::blue;
|
||||
|
||||
int bWidth = c.hasActiveProject() ? 150 : 110;
|
||||
if (c.hasActiveProject() && !c.projectQueue.empty()) {
|
||||
bWidth += 35;
|
||||
}
|
||||
QRect bannerRect((int)screenP.x() - bWidth / 2, (int)screenP.y() - 14, bWidth, 22);
|
||||
painter.setBrush(QColor(20, 24, 34, 235));
|
||||
painter.setPen(QPen(col, 2.0));
|
||||
@@ -631,6 +634,9 @@ void QSceneRenderer::drawUnitsOverlay(QPainter &painter, const QMatrix4x4 &vMatr
|
||||
if (c.hasActiveProject()) {
|
||||
int pct = (int)(c.currentProject.getProgress() * 100.0f);
|
||||
bannerText += QString(" | %1 %2%").arg(c.currentProject.name).arg(pct);
|
||||
if (!c.projectQueue.empty()) {
|
||||
bannerText += QString(" (+%1)").arg(c.projectQueue.size());
|
||||
}
|
||||
}
|
||||
painter.drawText(bannerRect, Qt::AlignCenter, bannerText);
|
||||
|
||||
@@ -768,16 +774,23 @@ void QSceneRenderer::drawUnitsOverlay(QPainter &painter, const QMatrix4x4 &vMatr
|
||||
tip = Localization::instance().get("ui.unit.tip_build_farm", "Build Farm (F)");
|
||||
}
|
||||
} else if (act == "build_road" || act == "weg_bouwen") {
|
||||
bool isMode = (GameManager::instance().currentActionMode() == ActionMode::BuildRoad);
|
||||
bool isBuildingRoad = unit->isBuilding() && unit->buildingImprovementId.startsWith("road");
|
||||
bool hasRoute = unit->hasRoadRoute();
|
||||
bool isQueued = unit->hasQueuedActionType(UnitActionType::BuildRoad);
|
||||
isActive = (unit->isBuilding() && unit->buildingImprovementId == "road") || isQueued;
|
||||
isActive = isMode || isBuildingRoad || hasRoute || isQueued;
|
||||
iconFile = "images/actions/road.png";
|
||||
if (unit->isBuilding() && unit->buildingImprovementId == "road") {
|
||||
if (isBuildingRoad) {
|
||||
int pct = (int)(std::min(1.0f, unit->buildProgress) * 100.0f);
|
||||
tip = Localization::instance().get("ui.unit.tip_road_building", "Busy building road... %1%").arg(pct);
|
||||
} else if (hasRoute) {
|
||||
int cur = (int)unit->roadRouteIndex + 1;
|
||||
int total = (int)unit->roadRoute.size();
|
||||
tip = Localization::instance().get("ui.unit.status_road_route", "Road Route: %1/%2").arg(cur).arg(total);
|
||||
} else if (isQueued) {
|
||||
tip = Localization::instance().get("ui.unit.tip_road_queued", "Build Road (Queued on arrival - click to cancel)");
|
||||
} else {
|
||||
tip = Localization::instance().get("ui.unit.tip_build_road", "Build Road (R)");
|
||||
tip = Localization::instance().get("ui.unit.tip_build_road", "Build Road (R) - Click target tile to build route");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,11 +994,21 @@ void QSceneRenderer::drawUnitCard(QPainter &painter, Unit *unit)
|
||||
} else if (unit->isBuilding()) {
|
||||
int pct = (int)(std::min(1.0f, unit->buildProgress) * 100.0f);
|
||||
QString buildInfo = Localization::instance().get("ui.unit.building_pct", "Building... %1%").arg(pct);
|
||||
if (!unit->config.infiniteCharges && unit->buildCharges > 0) {
|
||||
if (unit->hasRoadRoute()) {
|
||||
int cur = (int)unit->roadRouteIndex + 1;
|
||||
int total = (int)unit->roadRoute.size();
|
||||
buildInfo += QString(" | ") + Localization::instance().get("ui.unit.status_road_route", "Road Route: %1/%2").arg(cur).arg(total);
|
||||
} else if (!unit->config.infiniteCharges && unit->buildCharges > 0) {
|
||||
buildInfo += QString(" | ") + Localization::instance().get("ui.unit.charges_format", "Charges: %1").arg(unit->buildCharges);
|
||||
}
|
||||
painter.setPen(QColor(255, 210, 80));
|
||||
painter.drawText(QRect(cardRect.left() + 12, cardRect.top() + 94, 280, 14), Qt::AlignLeft | Qt::AlignVCenter, buildInfo);
|
||||
} else if (unit->hasRoadRoute()) {
|
||||
int cur = (int)unit->roadRouteIndex + 1;
|
||||
int total = (int)unit->roadRoute.size();
|
||||
QString routeInfo = Localization::instance().get("ui.unit.status_road_route", "Road Route: %1/%2").arg(cur).arg(total);
|
||||
painter.setPen(QColor(255, 200, 70));
|
||||
painter.drawText(QRect(cardRect.left() + 12, cardRect.top() + 94, 280, 14), Qt::AlignLeft | Qt::AlignVCenter, routeInfo);
|
||||
} else if (unit->hasQueuedAction()) {
|
||||
QString qSummary = unit->getQueuedActionSummary();
|
||||
QString queuedInfo = Localization::instance().get("ui.unit.status_queued", "Queued: %1").arg(qSummary);
|
||||
@@ -1056,19 +1079,27 @@ void QSceneRenderer::drawUnitCard(QPainter &painter, Unit *unit)
|
||||
tip = Localization::instance().get("ui.unit.tip_build_farm", "Build Farm (F)");
|
||||
}
|
||||
} else if (act == "build_road" || act == "weg_bouwen") {
|
||||
bool isMode = (GameManager::instance().currentActionMode() == ActionMode::BuildRoad);
|
||||
bool isBuildingRoad = unit->isBuilding() && unit->buildingImprovementId.startsWith("road");
|
||||
bool hasRoute = unit->hasRoadRoute();
|
||||
bool isQueued = unit->hasQueuedActionType(UnitActionType::BuildRoad);
|
||||
isActive = (unit->isBuilding() && unit->buildingImprovementId == "road") || isQueued;
|
||||
isActive = isMode || isBuildingRoad || hasRoute || isQueued;
|
||||
iconFile = "images/actions/road.png";
|
||||
if (unit->isBuilding() && unit->buildingImprovementId == "road") {
|
||||
if (isBuildingRoad) {
|
||||
int pct = (int)(std::min(1.0f, unit->buildProgress) * 100.0f);
|
||||
label = Localization::instance().get("ui.unit.btn_road_building", "Building %1%").arg(pct);
|
||||
tip = Localization::instance().get("ui.unit.tip_road_building", "Busy building road... %1%").arg(pct);
|
||||
} else if (hasRoute) {
|
||||
int cur = (int)unit->roadRouteIndex + 1;
|
||||
int total = (int)unit->roadRoute.size();
|
||||
label = Localization::instance().get("ui.unit.btn_road_route", "Route (%1/%2)").arg(cur).arg(total);
|
||||
tip = Localization::instance().get("ui.unit.status_road_route", "Road Route: %1/%2").arg(cur).arg(total);
|
||||
} else if (isQueued) {
|
||||
label = Localization::instance().get("ui.unit.btn_road_queued", "Road (Queued)");
|
||||
tip = Localization::instance().get("ui.unit.tip_road_queued", "Build Road (Queued on arrival - click to cancel)");
|
||||
} else {
|
||||
label = Localization::instance().get("ui.unit.btn_build_road", "Road [R]");
|
||||
tip = Localization::instance().get("ui.unit.tip_build_road", "Build Road (R)");
|
||||
tip = Localization::instance().get("ui.unit.tip_build_road", "Build Road (R) - Click target tile to build route");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,118 +1172,6 @@ void QSceneRenderer::drawCityBorders(QPainter &painter, const QMatrix4x4 &vMatri
|
||||
}
|
||||
}
|
||||
|
||||
void QSceneRenderer::drawRoadsOverlay(QPainter &painter, const QMatrix4x4 &vMatrix)
|
||||
{
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
const auto &roads = GameManager::instance().getTileRoads();
|
||||
if (roads.empty()) return;
|
||||
|
||||
RoadConfigManager &roadConfigs = GameManager::instance().roadConfigs();
|
||||
float meanderAmp = roadConfigs.meanderAmplitude();
|
||||
int subdivisions = std::max(2, roadConfigs.meanderSubdivisions());
|
||||
|
||||
struct RoadBatch {
|
||||
std::vector<QPolygonF> polylines;
|
||||
std::vector<QPointF> centerJunctions;
|
||||
std::vector<QPointF> isolatedCenters;
|
||||
};
|
||||
QMap<QString, RoadBatch> batches;
|
||||
|
||||
for (const auto &pair : roads) {
|
||||
const TileRoad &road = pair.second;
|
||||
QVector2D center = Pathfinder::hexToWorld(road.tileX, road.tileY);
|
||||
float centerZ = Unit::getTerrainHeight(center.x(), center.y(), m_point, m_numPoints05) + 0.028f;
|
||||
bool cInFront = false;
|
||||
QPointF cScreen = UnitRenderer::projectToScreen(QVector3D(center.x(), center.y(), centerZ), Projection, vMatrix, width(), height(), cInFront);
|
||||
|
||||
RoadBatch &batch = batches[road.roadTypeId];
|
||||
|
||||
if (road.connectionMask == 0) {
|
||||
if (cInFront) {
|
||||
batch.isolatedCenters.push_back(cScreen);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cInFront) {
|
||||
batch.centerJunctions.push_back(cScreen);
|
||||
}
|
||||
|
||||
for (int e = 0; e < 6; ++e) {
|
||||
if (!road.hasConnection(e)) continue;
|
||||
|
||||
QVector2D endP = center + getHexEdgeMidpoint(e);
|
||||
QVector2D dir = endP - center;
|
||||
float len = dir.length();
|
||||
QVector2D perp = (len > 0.0001f) ? QVector2D(-dir.y() / len, dir.x() / len) : QVector2D(0, 0);
|
||||
|
||||
// Deterministic pseudo-random seed for subtle, natural meandering
|
||||
float seed = std::sin(float(road.tileX * 41 + road.tileY * 113 + e * 29)) * 43758.5453f;
|
||||
float meanderSign = (seed - std::floor(seed) > 0.5f) ? 1.0f : -1.0f;
|
||||
|
||||
QPolygonF poly;
|
||||
bool allVisible = true;
|
||||
|
||||
for (int i = 0; i <= subdivisions; ++i) {
|
||||
float t = float(i) / float(subdivisions);
|
||||
// Zero offset at both endpoints (t=0 and t=1) ensures perfect tile-to-tile alignment
|
||||
float offset = std::sin(2.0f * 3.14159265f * t) * meanderAmp * meanderSign;
|
||||
QVector2D p2d = center + dir * t + perp * offset;
|
||||
float wz = Unit::getTerrainHeight(p2d.x(), p2d.y(), m_point, m_numPoints05) + 0.028f;
|
||||
bool inFront = false;
|
||||
QPointF sPos = UnitRenderer::projectToScreen(QVector3D(p2d.x(), p2d.y(), wz), Projection, vMatrix, width(), height(), inFront);
|
||||
if (!inFront) {
|
||||
allVisible = false;
|
||||
break;
|
||||
}
|
||||
poly.append(sPos);
|
||||
}
|
||||
|
||||
if (allVisible && poly.size() >= 2) {
|
||||
batch.polylines.push_back(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = batches.begin(); it != batches.end(); ++it) {
|
||||
const QString &typeId = it.key();
|
||||
const RoadBatch &batch = it.value();
|
||||
const RoadTypeConfig *rCfg = roadConfigs.getRoadType(typeId);
|
||||
|
||||
QColor casingCol = rCfg ? rCfg->getBorderColor() : QColor(92, 64, 40);
|
||||
float casingWidth = rCfg ? (rCfg->width + 1.5f) : 4.5f;
|
||||
QColor surfaceCol = rCfg ? rCfg->getColor() : QColor(194, 166, 117);
|
||||
float surfaceWidth = rCfg ? rCfg->width : 2.8f;
|
||||
|
||||
// Pass 1: Outer Casing / Under-layer
|
||||
painter.setPen(QPen(casingCol, casingWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
for (const auto &poly : batch.polylines) {
|
||||
painter.drawPolyline(poly);
|
||||
}
|
||||
|
||||
// Pass 2: Road Surface Core
|
||||
painter.setPen(QPen(surfaceCol, surfaceWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
for (const auto &poly : batch.polylines) {
|
||||
painter.drawPolyline(poly);
|
||||
}
|
||||
|
||||
// Pass 3: Center junction smooth cap
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(surfaceCol);
|
||||
for (const auto &cP : batch.centerJunctions) {
|
||||
painter.drawEllipse(cP, surfaceWidth * 0.5f, surfaceWidth * 0.5f);
|
||||
}
|
||||
|
||||
// Isolated road markers (tile has road, no connected neighbor yet)
|
||||
for (const auto &cP : batch.isolatedCenters) {
|
||||
painter.setBrush(surfaceCol);
|
||||
painter.setPen(QPen(casingCol, 1.5f));
|
||||
painter.drawEllipse(cP, surfaceWidth * 0.9f, surfaceWidth * 0.9f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSceneRenderer::drawImprovementsOverlay(QPainter &painter, const QMatrix4x4 &vMatrix)
|
||||
{
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
@@ -1295,6 +1214,7 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
m_citySlotHits.clear();
|
||||
m_cityBuyTileHits.clear();
|
||||
m_cityProjectHits.clear();
|
||||
m_cityQueueItemHits.clear();
|
||||
m_cancelProjectBtnRect = QRect();
|
||||
m_tabBuildingsRect = QRect();
|
||||
m_tabUnitsRect = QRect();
|
||||
@@ -1469,8 +1389,13 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
}
|
||||
|
||||
// 2. City Management Top Card
|
||||
int cardW = 600, cardH = 174;
|
||||
QRect cardRect((width() - cardW) / 2, 14, cardW, cardH);
|
||||
int panelW = 340;
|
||||
int panelX = width() - panelW - 14;
|
||||
int availableCardW = std::max(360, panelX - 28);
|
||||
int cardW = std::min(600, availableCardW);
|
||||
int cardH = 174;
|
||||
int cardX = 14 + (panelX - 14 - cardW) / 2;
|
||||
QRect cardRect(cardX, 14, cardW, cardH);
|
||||
|
||||
painter.setBrush(QColor(16, 22, 32, 245));
|
||||
painter.setPen(QPen(primaryCol, 1.8));
|
||||
@@ -1558,25 +1483,45 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
painter.drawText(QRect(cardRect.left() + 16, cardRect.bottom() - 18, cardRect.width() - 32, 16), Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.help_text", "Click a tile to assign 1 Pop | Click an unowned tile to buy with Culture."));
|
||||
|
||||
// 3. City Production & Districts Bottom Panel
|
||||
int panelW = std::min(980, width() - 40);
|
||||
int panelH = 195;
|
||||
QRect prodPanelRect((width() - panelW) / 2, height() - panelH - 14, panelW, panelH);
|
||||
// 3. City Production & Queue Vertical Panel (Right Side)
|
||||
int panelH = height() - 28;
|
||||
int panelY = 14;
|
||||
QRect prodPanelRect(panelX, panelY, panelW, panelH);
|
||||
|
||||
painter.setBrush(QColor(16, 22, 32, 248));
|
||||
painter.setPen(QPen(primaryCol, 1.8));
|
||||
painter.drawRoundedRect(prodPanelRect, 10, 10);
|
||||
|
||||
// Left Column: Active Production & District (width 340)
|
||||
int leftColW = 340;
|
||||
QRect leftColRect(prodPanelRect.left() + 14, prodPanelRect.top() + 10, leftColW, panelH - 20);
|
||||
int curY = prodPanelRect.top() + 10;
|
||||
int innerX = prodPanelRect.left() + 12;
|
||||
int innerW = prodPanelRect.width() - 24;
|
||||
|
||||
// Header: Current Production
|
||||
const CityConfig &cityCfg = TerrainYieldManager::instance().getCityConfig();
|
||||
int maxQueue = cityCfg.maxProductionQueueSize;
|
||||
|
||||
// Header: City Production Title
|
||||
painter.setFont(QFont("sans-serif", 9, QFont::Bold));
|
||||
painter.setPen(QColor(230, 240, 255));
|
||||
painter.drawText(QRect(leftColRect.left(), leftColRect.top(), leftColRect.width(), 18),
|
||||
painter.drawText(QRect(innerX, curY, innerW - 80, 18),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.production_panel_title", "CITY PRODUCTION"));
|
||||
|
||||
// Queue count indicator
|
||||
int totalQueuedCount = (int)city->projectQueue.size() + (city->hasActiveProject() ? 1 : 0);
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(totalQueuedCount > maxQueue ? QColor(255, 120, 120) : QColor(140, 185, 225));
|
||||
painter.drawText(QRect(innerX + innerW - 80, curY, 80, 18),
|
||||
Qt::AlignRight | Qt::AlignVCenter,
|
||||
QString("%1 / %2").arg(totalQueuedCount).arg(maxQueue + 1));
|
||||
curY += 22;
|
||||
|
||||
// Section 1: Active Production Header
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(220, 200, 140));
|
||||
painter.drawText(QRect(innerX, curY, innerW, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.current_production", "CURRENT PRODUCTION"));
|
||||
curY += 18;
|
||||
|
||||
if (city->hasActiveProject()) {
|
||||
const CityProject &proj = city->currentProject;
|
||||
@@ -1586,7 +1531,12 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
int laborRate = std::max(1, summary.labor);
|
||||
int estTicks = (int)std::ceil((float)remainingLabor / (float)laborRate);
|
||||
|
||||
// Project Icon & Name
|
||||
QRect activeBox(innerX, curY, innerW, 82);
|
||||
painter.setBrush(QColor(24, 34, 48, 220));
|
||||
painter.setPen(QPen(QColor(255, 215, 60, 180), 1.2));
|
||||
painter.drawRoundedRect(activeBox, 6, 6);
|
||||
|
||||
// Project Icon
|
||||
QString projIconPath = "images/improvements/granary.png";
|
||||
if (proj.type == CityProjectType::Unit) {
|
||||
const UnitConfig *uCfg = GameManager::instance().unitConfigs().getConfig(proj.id);
|
||||
@@ -1596,21 +1546,25 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
if (bCfg && !bCfg->iconPath.isEmpty()) projIconPath = bCfg->iconPath;
|
||||
}
|
||||
QImage pIcon = getTintedIcon(projIconPath, QColor(255, 215, 60));
|
||||
painter.drawImage(QRect(leftColRect.left(), leftColRect.top() + 24, 28, 28), pIcon);
|
||||
painter.drawImage(QRect(activeBox.left() + 8, activeBox.top() + 8, 26, 26), pIcon);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 10, QFont::Bold));
|
||||
// Name
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(255, 235, 120));
|
||||
painter.drawText(QRect(leftColRect.left() + 34, leftColRect.top() + 22, 220, 18),
|
||||
painter.drawText(QRect(activeBox.left() + 38, activeBox.top() + 6, activeBox.width() - 105, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, proj.name);
|
||||
|
||||
// District / Type
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(170, 195, 220));
|
||||
QString typeDesc = (proj.type == CityProjectType::Unit) ? "Unit" : QString("Building (%1)").arg(proj.district);
|
||||
painter.drawText(QRect(leftColRect.left() + 34, leftColRect.top() + 38, 220, 14),
|
||||
QString typeDesc = (proj.type == CityProjectType::Unit)
|
||||
? Localization::instance().get("ui.city.tab_units", "Unit")
|
||||
: QString("Building (%1)").arg(proj.district);
|
||||
painter.drawText(QRect(activeBox.left() + 38, activeBox.top() + 21, activeBox.width() - 105, 13),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, typeDesc);
|
||||
|
||||
// Cancel button
|
||||
m_cancelProjectBtnRect = QRect(leftColRect.right() - 65, leftColRect.top() + 24, 65, 22);
|
||||
m_cancelProjectBtnRect = QRect(activeBox.right() - 62, activeBox.top() + 7, 56, 22);
|
||||
painter.setBrush(QColor(50, 25, 30, 220));
|
||||
painter.setPen(QPen(QColor(220, 90, 90), 1.0));
|
||||
painter.drawRoundedRect(m_cancelProjectBtnRect, 4, 4);
|
||||
@@ -1620,12 +1574,12 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
Localization::instance().get("ui.city.cancel_btn", "Cancel"));
|
||||
|
||||
// Progress Bar
|
||||
QRect barBg(leftColRect.left(), leftColRect.top() + 56, leftColRect.width(), 16);
|
||||
painter.setBrush(QColor(25, 35, 48));
|
||||
painter.setPen(QPen(QColor(60, 85, 120), 1.0));
|
||||
QRect barBg(activeBox.left() + 8, activeBox.top() + 40, activeBox.width() - 16, 15);
|
||||
painter.setBrush(QColor(18, 25, 36));
|
||||
painter.setPen(QPen(QColor(55, 80, 115), 1.0));
|
||||
painter.drawRoundedRect(barBg, 3, 3);
|
||||
|
||||
QRect barFg(leftColRect.left(), leftColRect.top() + 56, (int)(leftColRect.width() * progress), 16);
|
||||
QRect barFg(activeBox.left() + 8, activeBox.top() + 40, (int)((activeBox.width() - 16) * progress), 15);
|
||||
painter.setBrush(QColor(240, 145, 40));
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRoundedRect(barFg, 3, 3);
|
||||
@@ -1638,94 +1592,164 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
// Subtitle: Estimated ticks remaining
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(160, 185, 215));
|
||||
painter.drawText(QRect(leftColRect.left(), leftColRect.top() + 74, leftColRect.width(), 14),
|
||||
painter.drawText(QRect(activeBox.left() + 8, activeBox.top() + 59, activeBox.width() - 16, 14),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
QString("~%1 %2 remaining (+%3 Labor/tick)")
|
||||
.arg(estTicks).arg(estTicks == 1 ? "tick" : "ticks").arg(laborRate));
|
||||
|
||||
curY += 88;
|
||||
} else {
|
||||
// No project active
|
||||
painter.setFont(QFont("sans-serif", 9, QFont::StyleItalic));
|
||||
// No active project
|
||||
QRect noActiveBox(innerX, curY, innerW, 44);
|
||||
painter.setBrush(QColor(20, 26, 36, 180));
|
||||
painter.setPen(QPen(QColor(50, 65, 85), 1.0));
|
||||
painter.drawRoundedRect(noActiveBox, 6, 6);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::StyleItalic));
|
||||
painter.setPen(QColor(180, 195, 210));
|
||||
painter.drawText(QRect(leftColRect.left(), leftColRect.top() + 28, leftColRect.width(), 20),
|
||||
painter.drawText(QRect(noActiveBox.left() + 10, noActiveBox.top() + 6, noActiveBox.width() - 20, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.no_project", "No active project"));
|
||||
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(140, 160, 180));
|
||||
painter.drawText(QRect(leftColRect.left(), leftColRect.top() + 50, leftColRect.width(), 30),
|
||||
Qt::AlignLeft | Qt::TextWordWrap,
|
||||
Localization::instance().get("ui.city.select_prompt", "Select a unit or building to construct"));
|
||||
painter.drawText(QRect(noActiveBox.left() + 10, noActiveBox.top() + 22, noActiveBox.width() - 20, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.select_prompt", "Select an item below to construct"));
|
||||
curY += 50;
|
||||
}
|
||||
|
||||
// Separator between project and districts
|
||||
// Section 2: Queue Header
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(180, 205, 235));
|
||||
QString qHeader = Localization::instance().get("ui.city.queue_header", "QUEUE (%1 / %2)")
|
||||
.arg(city->projectQueue.size()).arg(maxQueue);
|
||||
painter.drawText(QRect(innerX, curY, innerW, 16), Qt::AlignLeft | Qt::AlignVCenter, qHeader);
|
||||
curY += 18;
|
||||
|
||||
if (city->projectQueue.empty()) {
|
||||
QRect emptyQBox(innerX, curY, innerW, 26);
|
||||
painter.setBrush(QColor(18, 24, 34, 140));
|
||||
painter.setPen(QPen(QColor(40, 55, 75, 100), 1.0, Qt::DashLine));
|
||||
painter.drawRoundedRect(emptyQBox, 4, 4);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::StyleItalic));
|
||||
painter.setPen(QColor(130, 150, 175));
|
||||
painter.drawText(emptyQBox, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.queue_empty", "Queue is empty"));
|
||||
curY += 32;
|
||||
} else {
|
||||
for (size_t qi = 0; qi < city->projectQueue.size(); ++qi) {
|
||||
const CityProject &qProj = city->projectQueue[qi];
|
||||
QRect qRow(innerX, curY, innerW, 28);
|
||||
|
||||
painter.setBrush(QColor(22, 30, 42, 210));
|
||||
painter.setPen(QPen(QColor(50, 70, 95), 1.0));
|
||||
painter.drawRoundedRect(qRow, 4, 4);
|
||||
|
||||
// Badge #1, #2
|
||||
QRect numBadge(qRow.left() + 4, qRow.top() + 4, 22, 20);
|
||||
painter.setBrush(QColor(40, 60, 90));
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRoundedRect(numBadge, 3, 3);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(180, 220, 255));
|
||||
painter.drawText(numBadge, Qt::AlignCenter, QString("#%1").arg(qi + 1));
|
||||
|
||||
// Icon
|
||||
QString qIconPath = "images/improvements/granary.png";
|
||||
if (qProj.type == CityProjectType::Unit) {
|
||||
const UnitConfig *uCfg = GameManager::instance().unitConfigs().getConfig(qProj.id);
|
||||
if (uCfg && !uCfg->iconPath.isEmpty()) qIconPath = uCfg->iconPath;
|
||||
} else if (qProj.type == CityProjectType::Building) {
|
||||
const BuildingConfig *bCfg = GameManager::instance().buildingConfigs().getBuilding(qProj.id);
|
||||
if (bCfg && !bCfg->iconPath.isEmpty()) qIconPath = bCfg->iconPath;
|
||||
}
|
||||
QImage qIcon = getTintedIcon(qIconPath, QColor(255, 215, 60));
|
||||
painter.drawImage(QRect(qRow.left() + 30, qRow.top() + 5, 18, 18), qIcon);
|
||||
|
||||
// Name
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(230, 240, 255));
|
||||
painter.drawText(QRect(qRow.left() + 52, qRow.top() + 2, innerW - 138, 24),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, qProj.name);
|
||||
|
||||
// Labor Cost
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(240, 160, 50));
|
||||
painter.drawText(QRect(qRow.right() - 84, qRow.top() + 2, 54, 24),
|
||||
Qt::AlignRight | Qt::AlignVCenter, QString("%1 L").arg(qProj.laborCost));
|
||||
|
||||
// Remove Button [X]
|
||||
QRect remRect(qRow.right() - 24, qRow.top() + 4, 20, 20);
|
||||
painter.setBrush(QColor(50, 25, 30, 200));
|
||||
painter.setPen(QPen(QColor(200, 80, 80), 1.0));
|
||||
painter.drawRoundedRect(remRect, 3, 3);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(255, 140, 140));
|
||||
painter.drawText(remRect, Qt::AlignCenter, "X");
|
||||
|
||||
m_cityQueueItemHits.push_back({remRect, (int)qi});
|
||||
curY += 32;
|
||||
}
|
||||
}
|
||||
|
||||
// Section 3: Districts Header
|
||||
painter.setPen(QPen(QColor(60, 85, 120, 150), 1.0));
|
||||
painter.drawLine(leftColRect.left(), leftColRect.top() + 94, leftColRect.right(), leftColRect.top() + 94);
|
||||
painter.drawLine(innerX, curY + 2, innerX + innerW, curY + 2);
|
||||
curY += 8;
|
||||
|
||||
// District Section: Centrum / City Center
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(220, 200, 140));
|
||||
painter.drawText(QRect(leftColRect.left(), leftColRect.top() + 100, leftColRect.width(), 16),
|
||||
painter.drawText(QRect(innerX, curY, innerW, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.districts_header", "DISTRICT: CITY CENTER"));
|
||||
curY += 18;
|
||||
|
||||
// Completed buildings inside this district
|
||||
int bCount = 0;
|
||||
int bOffsetY = leftColRect.top() + 120;
|
||||
if (!city->completedBuildings.empty()) {
|
||||
for (const QString &bId : city->completedBuildings) {
|
||||
const BuildingConfig *bCfg = GameManager::instance().buildingConfigs().getBuilding(bId);
|
||||
if (!bCfg) continue;
|
||||
|
||||
QRect bBadge(leftColRect.left() + bCount * 168, bOffsetY, 162, 46);
|
||||
painter.setBrush(QColor(25, 38, 30, 220));
|
||||
painter.setPen(QPen(QColor(80, 220, 120), 1.2));
|
||||
QRect bBadge(innerX, curY, innerW, 28);
|
||||
painter.setBrush(QColor(22, 38, 28, 210));
|
||||
painter.setPen(QPen(QColor(80, 220, 120), 1.0));
|
||||
painter.drawRoundedRect(bBadge, 4, 4);
|
||||
|
||||
QImage bIcon = getTintedIcon(bCfg->iconPath.isEmpty() ? "images/improvements/granary.png" : bCfg->iconPath, QColor(80, 240, 120));
|
||||
painter.drawImage(QRect(bBadge.left() + 6, bBadge.top() + 6, 20, 20), bIcon);
|
||||
painter.drawImage(QRect(bBadge.left() + 6, bBadge.top() + 5, 18, 18), bIcon);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(210, 255, 220));
|
||||
painter.drawText(QRect(bBadge.left() + 30, bBadge.top() + 4, bBadge.width() - 34, 16),
|
||||
painter.drawText(QRect(bBadge.left() + 28, bBadge.top() + 2, 100, 24),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, bCfg->name);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(160, 230, 180));
|
||||
painter.drawText(QRect(bBadge.left() + 6, bBadge.top() + 24, bBadge.width() - 10, 16),
|
||||
painter.drawText(QRect(bBadge.left() + 132, bBadge.top() + 2, bBadge.width() - 136, 24),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, bCfg->description);
|
||||
bCount++;
|
||||
if (bCount >= 2) break;
|
||||
curY += 32;
|
||||
}
|
||||
|
||||
if (city->completedBuildings.empty()) {
|
||||
} else {
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::StyleItalic));
|
||||
painter.setPen(QColor(140, 160, 180));
|
||||
painter.drawText(QRect(leftColRect.left(), bOffsetY + 8, leftColRect.width(), 16),
|
||||
painter.drawText(QRect(innerX, curY, innerW, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.no_buildings", "No extensions built yet"));
|
||||
curY += 20;
|
||||
}
|
||||
|
||||
// Vertical divider line between left and right columns
|
||||
int divX = leftColRect.right() + 14;
|
||||
// Section 4: Projects Section Divider & Tabs
|
||||
painter.setPen(QPen(QColor(60, 85, 120, 150), 1.0));
|
||||
painter.drawLine(divX, prodPanelRect.top() + 10, divX, prodPanelRect.bottom() - 10);
|
||||
painter.drawLine(innerX, curY + 2, innerX + innerW, curY + 2);
|
||||
curY += 8;
|
||||
|
||||
// Right Column: Project Selection (Tabs + Items)
|
||||
int rightColX = divX + 14;
|
||||
int rightColW = prodPanelRect.right() - rightColX - 14;
|
||||
// Tabs: Buildings & Units
|
||||
int tabW = (innerW - 8) / 2;
|
||||
int tabH = 22;
|
||||
m_tabBuildingsRect = QRect(innerX, curY, tabW, tabH);
|
||||
m_tabUnitsRect = QRect(m_tabBuildingsRect.right() + 8, curY, tabW, tabH);
|
||||
|
||||
// Header & Tabs
|
||||
painter.setFont(QFont("sans-serif", 9, QFont::Bold));
|
||||
painter.setPen(QColor(230, 240, 255));
|
||||
painter.drawText(QRect(rightColX, prodPanelRect.top() + 10, 160, 20),
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
Localization::instance().get("ui.city.choose_project", "CHOOSE CITY PROJECT"));
|
||||
|
||||
// Tabs: [Buildings & Extensions] and [Units]
|
||||
int tabW = 155, tabH = 22;
|
||||
m_tabBuildingsRect = QRect(rightColX + 170, prodPanelRect.top() + 9, tabW, tabH);
|
||||
m_tabUnitsRect = QRect(m_tabBuildingsRect.right() + 8, prodPanelRect.top() + 9, 90, tabH);
|
||||
|
||||
// Draw Tab Buildings
|
||||
bool isBTab = (m_cityProjectTab == 0);
|
||||
painter.setBrush(isBTab ? QColor(45, 75, 120, 240) : QColor(25, 35, 50, 180));
|
||||
painter.setPen(QPen(isBTab ? QColor(100, 190, 255) : QColor(60, 85, 120), isBTab ? 1.5 : 1.0));
|
||||
@@ -1733,9 +1757,8 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
painter.setFont(QFont("sans-serif", 8, isBTab ? QFont::Bold : QFont::Normal));
|
||||
painter.setPen(isBTab ? Qt::white : QColor(160, 185, 215));
|
||||
painter.drawText(m_tabBuildingsRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.tab_buildings", "Buildings & Extensions"));
|
||||
Localization::instance().get("ui.city.tab_buildings", "Buildings"));
|
||||
|
||||
// Draw Tab Units
|
||||
bool isUTab = (m_cityProjectTab == 1);
|
||||
painter.setBrush(isUTab ? QColor(45, 75, 120, 240) : QColor(25, 35, 50, 180));
|
||||
painter.setPen(QPen(isUTab ? QColor(100, 190, 255) : QColor(60, 85, 120), isUTab ? 1.5 : 1.0));
|
||||
@@ -1744,57 +1767,61 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
painter.setPen(isUTab ? Qt::white : QColor(160, 185, 215));
|
||||
painter.drawText(m_tabUnitsRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.tab_units", "Units"));
|
||||
curY += 28;
|
||||
|
||||
// Render Items under active tab
|
||||
int listY = prodPanelRect.top() + 38;
|
||||
int itemH = 65;
|
||||
|
||||
// List of available projects under selected tab
|
||||
int itemH = 50;
|
||||
if (m_cityProjectTab == 0) {
|
||||
// Buildings tab
|
||||
const auto &buildings = GameManager::instance().buildingConfigs().getAllBuildings();
|
||||
int idx = 0;
|
||||
int colItemW = (rightColW - 10) / 2;
|
||||
|
||||
for (auto it = buildings.begin(); it != buildings.end(); ++it) {
|
||||
if (curY + itemH > prodPanelRect.bottom() - 6) break;
|
||||
|
||||
const BuildingConfig &bCfg = it.value();
|
||||
int ix = rightColX + (idx % 2) * (colItemW + 10);
|
||||
int iy = listY + (idx / 2) * (itemH + 6);
|
||||
QRect itemRect(ix, iy, colItemW, itemH);
|
||||
QRect itemRect(innerX, curY, innerW, itemH);
|
||||
|
||||
bool alreadyBuilt = city->hasBuilding(bCfg.id);
|
||||
bool isActiveProj = (city->hasActiveProject() && city->currentProject.type == CityProjectType::Building && city->currentProject.id == bCfg.id);
|
||||
int qPos = city->getQueuedBuildingPosition(bCfg.id);
|
||||
bool isQueued = (qPos > 0);
|
||||
bool isQueueFull = ((int)city->projectQueue.size() >= maxQueue && city->hasActiveProject());
|
||||
|
||||
painter.setBrush(isActiveProj ? QColor(35, 50, 75, 230) : (alreadyBuilt ? QColor(20, 25, 32, 190) : QColor(24, 32, 46, 220)));
|
||||
painter.setPen(QPen(isActiveProj ? QColor(255, 220, 50) : (alreadyBuilt ? QColor(60, 75, 95) : QColor(70, 100, 140)), isActiveProj ? 1.8 : 1.0));
|
||||
painter.setBrush(isActiveProj ? QColor(35, 50, 75, 230)
|
||||
: (isQueued ? QColor(26, 40, 58, 220)
|
||||
: (alreadyBuilt ? QColor(20, 25, 32, 190) : QColor(24, 32, 46, 220))));
|
||||
painter.setPen(QPen(isActiveProj ? QColor(255, 220, 50)
|
||||
: (isQueued ? QColor(100, 190, 255)
|
||||
: (alreadyBuilt ? QColor(60, 75, 95) : QColor(70, 100, 140))),
|
||||
(isActiveProj || isQueued) ? 1.5 : 1.0));
|
||||
painter.drawRoundedRect(itemRect, 6, 6);
|
||||
|
||||
// Icon
|
||||
QImage bIcon = getTintedIcon(bCfg.iconPath.isEmpty() ? "images/improvements/granary.png" : bCfg.iconPath,
|
||||
alreadyBuilt ? QColor(120, 140, 160) : QColor(255, 215, 60));
|
||||
painter.drawImage(QRect(itemRect.left() + 8, itemRect.top() + 8, 28, 28), bIcon);
|
||||
painter.drawImage(QRect(itemRect.left() + 6, itemRect.top() + 7, 24, 24), bIcon);
|
||||
|
||||
// Building Name
|
||||
// Name
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(alreadyBuilt ? QColor(150, 165, 180) : QColor(245, 250, 255));
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 6, itemRect.width() - 110, 16),
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 4, itemRect.width() - 105, 15),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, bCfg.name);
|
||||
|
||||
// District & Cost
|
||||
// Cost & estimated ticks
|
||||
int estTicks = (int)std::ceil((float)bCfg.laborCost / (float)std::max(1, summary.labor));
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(240, 160, 50));
|
||||
QString costStr = QString("%L1 Labor (%2 %3)").arg(bCfg.laborCost).arg(estTicks).arg(estTicks == 1 ? "tick" : "ticks");
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 22, itemRect.width() - 110, 14),
|
||||
QString costStr = QString("%L1 Labor (~%2 %3)").arg(bCfg.laborCost).arg(estTicks).arg(estTicks == 1 ? "tick" : "ticks");
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 19, itemRect.width() - 105, 13),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, costStr);
|
||||
|
||||
// Description / Effect
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(alreadyBuilt ? QColor(120, 180, 140) : QColor(100, 240, 130));
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 38, itemRect.width() - 50, 16),
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 32, itemRect.width() - 40, 14),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, bCfg.description);
|
||||
|
||||
// Action Button / Status badge on right side
|
||||
QRect btnRect(itemRect.right() - 66, itemRect.top() + 10, 58, 24);
|
||||
QRect btnRect(itemRect.right() - 66, itemRect.top() + 12, 60, 26);
|
||||
if (alreadyBuilt) {
|
||||
painter.setBrush(QColor(30, 40, 35, 180));
|
||||
painter.setPen(QPen(QColor(70, 120, 85), 1.0));
|
||||
@@ -1811,54 +1838,76 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
painter.setPen(QColor(255, 230, 80));
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.status_in_progress", "ACTIVE"));
|
||||
} else if (isQueued) {
|
||||
painter.setBrush(QColor(30, 50, 75, 220));
|
||||
painter.setPen(QPen(QColor(80, 180, 255), 1.2));
|
||||
painter.drawRoundedRect(btnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(140, 215, 255));
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.status_queued", "QUEUED #%1").arg(qPos));
|
||||
} else if (isQueueFull) {
|
||||
painter.setBrush(QColor(40, 30, 35, 180));
|
||||
painter.setPen(QPen(QColor(120, 70, 75), 1.0));
|
||||
painter.drawRoundedRect(btnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(180, 130, 135));
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.status_full", "FULL"));
|
||||
} else {
|
||||
painter.setBrush(QColor(30, 60, 100, 240));
|
||||
painter.setPen(QPen(QColor(80, 160, 255), 1.2));
|
||||
bool hasAct = city->hasActiveProject();
|
||||
painter.setBrush(hasAct ? QColor(25, 55, 90, 240) : QColor(30, 60, 100, 240));
|
||||
painter.setPen(QPen(hasAct ? QColor(60, 175, 245) : QColor(80, 160, 255), 1.2));
|
||||
painter.drawRoundedRect(btnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.btn_build", "Build"));
|
||||
QString btnLabel = hasAct
|
||||
? Localization::instance().get("ui.city.btn_queue", "+ Queue")
|
||||
: Localization::instance().get("ui.city.btn_build", "Build");
|
||||
painter.drawText(btnRect, Qt::AlignCenter, btnLabel);
|
||||
|
||||
m_cityProjectHits.push_back({btnRect, CityProjectType::Building, bCfg.id});
|
||||
}
|
||||
|
||||
idx++;
|
||||
curY += itemH + 6;
|
||||
}
|
||||
} else {
|
||||
// Units tab
|
||||
const auto &units = GameManager::instance().unitConfigs().getAllConfigs();
|
||||
int idx = 0;
|
||||
int colItemW = (rightColW - 10) / 2;
|
||||
|
||||
for (auto it = units.begin(); it != units.end(); ++it) {
|
||||
if (curY + itemH > prodPanelRect.bottom() - 6) break;
|
||||
|
||||
const UnitConfig &uCfg = it.value();
|
||||
int ix = rightColX + (idx % 2) * (colItemW + 10);
|
||||
int iy = listY + (idx / 2) * (itemH + 6);
|
||||
QRect itemRect(ix, iy, colItemW, itemH);
|
||||
QRect itemRect(innerX, curY, innerW, itemH);
|
||||
|
||||
bool isActiveProj = (city->hasActiveProject() && city->currentProject.type == CityProjectType::Unit && city->currentProject.id == uCfg.id);
|
||||
int qCount = city->getQueuedUnitCount(uCfg.id);
|
||||
bool isQueueFull = ((int)city->projectQueue.size() >= maxQueue && city->hasActiveProject());
|
||||
|
||||
painter.setBrush(isActiveProj ? QColor(35, 50, 75, 230) : QColor(24, 32, 46, 220));
|
||||
painter.setPen(QPen(isActiveProj ? QColor(255, 220, 50) : QColor(70, 100, 140), isActiveProj ? 1.8 : 1.0));
|
||||
painter.setPen(QPen(isActiveProj ? QColor(255, 220, 50) : QColor(70, 100, 140), isActiveProj ? 1.5 : 1.0));
|
||||
painter.drawRoundedRect(itemRect, 6, 6);
|
||||
|
||||
// Icon
|
||||
QImage uIcon = getTintedIcon(uCfg.iconPath, primaryCol);
|
||||
painter.drawImage(QRect(itemRect.left() + 8, itemRect.top() + 8, 28, 28), uIcon);
|
||||
painter.drawImage(QRect(itemRect.left() + 6, itemRect.top() + 7, 24, 24), uIcon);
|
||||
|
||||
// Unit Name
|
||||
// Name + Queued Count
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(245, 250, 255));
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 6, itemRect.width() - 110, 16),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, uCfg.name);
|
||||
QString uName = uCfg.name;
|
||||
if (qCount > 0) {
|
||||
uName += QString(" (%1 queued)").arg(qCount);
|
||||
}
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 4, itemRect.width() - 105, 15),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, uName);
|
||||
|
||||
// Cost & Estimated Ticks
|
||||
// Cost & estimated ticks
|
||||
int estTicks = (int)std::ceil((float)uCfg.laborCost / (float)std::max(1, summary.labor));
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(240, 160, 50));
|
||||
QString costStr = QString("%L1 Labor (%2 %3)").arg(uCfg.laborCost).arg(estTicks).arg(estTicks == 1 ? "tick" : "ticks");
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 22, itemRect.width() - 110, 14),
|
||||
QString costStr = QString("%L1 Labor (~%2 %3)").arg(uCfg.laborCost).arg(estTicks).arg(estTicks == 1 ? "tick" : "ticks");
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 19, itemRect.width() - 105, 13),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, costStr);
|
||||
|
||||
// Stats info
|
||||
@@ -1868,32 +1917,35 @@ void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vM
|
||||
if (uCfg.isMilitary()) {
|
||||
infoStr += QString(" | HP: %1 | Atk: %2").arg(uCfg.health).arg(uCfg.attackDamage);
|
||||
}
|
||||
painter.drawText(QRect(itemRect.left() + 42, itemRect.top() + 38, itemRect.width() - 50, 16),
|
||||
painter.drawText(QRect(itemRect.left() + 36, itemRect.top() + 32, itemRect.width() - 40, 14),
|
||||
Qt::AlignLeft | Qt::AlignVCenter, infoStr);
|
||||
|
||||
// Action Button / Status badge on right side
|
||||
QRect btnRect(itemRect.right() - 66, itemRect.top() + 10, 58, 24);
|
||||
if (isActiveProj) {
|
||||
painter.setBrush(QColor(60, 50, 20, 220));
|
||||
painter.setPen(QPen(QColor(255, 215, 50), 1.2));
|
||||
// Action Button on right side
|
||||
QRect btnRect(itemRect.right() - 66, itemRect.top() + 12, 60, 26);
|
||||
if (isQueueFull) {
|
||||
painter.setBrush(QColor(40, 30, 35, 180));
|
||||
painter.setPen(QPen(QColor(120, 70, 75), 1.0));
|
||||
painter.drawRoundedRect(btnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(QColor(255, 230, 80));
|
||||
painter.setPen(QColor(180, 130, 135));
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.status_in_progress", "ACTIVE"));
|
||||
Localization::instance().get("ui.city.status_full", "FULL"));
|
||||
} else {
|
||||
painter.setBrush(QColor(30, 60, 100, 240));
|
||||
painter.setPen(QPen(QColor(80, 160, 255), 1.2));
|
||||
bool hasAct = city->hasActiveProject();
|
||||
painter.setBrush(hasAct ? QColor(25, 55, 90, 240) : QColor(30, 60, 100, 240));
|
||||
painter.setPen(QPen(hasAct ? QColor(60, 175, 245) : QColor(80, 160, 255), 1.2));
|
||||
painter.drawRoundedRect(btnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(btnRect, Qt::AlignCenter,
|
||||
Localization::instance().get("ui.city.btn_train", "Train"));
|
||||
QString btnLabel = hasAct
|
||||
? Localization::instance().get("ui.city.btn_queue", "+ Queue")
|
||||
: Localization::instance().get("ui.city.btn_train", "Train");
|
||||
painter.drawText(btnRect, Qt::AlignCenter, btnLabel);
|
||||
|
||||
m_cityProjectHits.push_back({btnRect, CityProjectType::Unit, uCfg.id});
|
||||
}
|
||||
|
||||
idx++;
|
||||
curY += itemH + 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2155,18 +2207,29 @@ void QSceneRenderer::mousePressEvent(QMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel project
|
||||
// Cancel active project
|
||||
if (city && m_cancelProjectBtnRect.contains(event->pos())) {
|
||||
GameManager::instance().cancelCityProject(city->id);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel individual queued project item
|
||||
if (city) {
|
||||
for (const auto &qHit : m_cityQueueItemHits) {
|
||||
if (qHit.rect.contains(event->pos())) {
|
||||
GameManager::instance().removeQueuedCityProject(city->id, qHit.queueIndex);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Project selection clicks
|
||||
if (city) {
|
||||
for (const auto &pHit : m_cityProjectHits) {
|
||||
if (pHit.rect.contains(event->pos())) {
|
||||
GameManager::instance().setCityProject(city->id, pHit.type, pHit.id);
|
||||
GameManager::instance().queueCityProject(city->id, pHit.type, pHit.id);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
@@ -2687,7 +2750,7 @@ void QSceneRenderer::paintGL()
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Render 3D Units (Cylinders)
|
||||
// 2. Render 3D Units & Cities (Cylinders & Bastions)
|
||||
float fSunAngle = 45.0f;
|
||||
QVector3D vSunPos(-sin(fSunAngle * PI / 180.0f), -cos(fSunAngle * PI / 180.0f), 0.6f);
|
||||
vSunPos.normalize();
|
||||
|
||||
Reference in New Issue
Block a user