stad
This commit is contained in:
+321
-18
@@ -503,27 +503,33 @@ void QSceneRenderer::drawTickBar(QPainter &painter)
|
||||
void QSceneRenderer::drawUnitsOverlay(QPainter &painter, const QMatrix4x4 &vMatrix)
|
||||
{
|
||||
m_activeActionBubbles.clear();
|
||||
m_cityBannerHits.clear();
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
Unit *selectedUnit = GameManager::instance().getSelectedUnit();
|
||||
|
||||
// 1. Draw City Banners
|
||||
for (const auto &c : GameManager::instance().getCities()) {
|
||||
bool inFront = false;
|
||||
QPointF screenP = UnitRenderer::projectToScreen(c.worldPos + QVector3D(0, 0, 0.65f), Projection, vMatrix, width(), height(), inFront);
|
||||
if (!inFront) continue;
|
||||
// 1. Draw City Banners (if not in city view)
|
||||
if (!GameManager::instance().isInCityView()) {
|
||||
for (const auto &c : GameManager::instance().getCities()) {
|
||||
bool inFront = false;
|
||||
QPointF screenP = UnitRenderer::projectToScreen(c.worldPos + QVector3D(0, 0, 0.65f), Projection, vMatrix, width(), height(), inFront);
|
||||
if (!inFront) continue;
|
||||
|
||||
const Player *pl = GameManager::instance().players().getPlayer(c.playerId);
|
||||
QColor col = pl ? pl->primaryColor : Qt::blue;
|
||||
const Player *pl = GameManager::instance().players().getPlayer(c.playerId);
|
||||
QColor col = pl ? pl->primaryColor : Qt::blue;
|
||||
|
||||
QRect bannerRect((int)screenP.x() - 40, (int)screenP.y() - 14, 80, 20);
|
||||
painter.setBrush(QColor(20, 24, 34, 230));
|
||||
painter.setPen(QPen(col, 1.8));
|
||||
painter.drawRoundedRect(bannerRect, 4, 4);
|
||||
QRect bannerRect((int)screenP.x() - 55, (int)screenP.y() - 14, 110, 22);
|
||||
painter.setBrush(QColor(20, 24, 34, 235));
|
||||
painter.setPen(QPen(col, 2.0));
|
||||
painter.drawRoundedRect(bannerRect, 4, 4);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(bannerRect, Qt::AlignCenter, c.name);
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(Qt::white);
|
||||
QString bannerText = QString("%1 (%2 Pop)").arg(c.name).arg(c.getTotalPop());
|
||||
painter.drawText(bannerRect, Qt::AlignCenter, bannerText);
|
||||
|
||||
m_cityBannerHits.push_back({bannerRect, c.id});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Draw Units: Teardrop (Druppel in secondary color) + Icon (in primary color)
|
||||
@@ -874,8 +880,293 @@ void QSceneRenderer::drawUnitCard(QPainter &painter, Unit *unit)
|
||||
}
|
||||
}
|
||||
|
||||
void QSceneRenderer::drawCityBorders(QPainter &painter, const QMatrix4x4 &vMatrix)
|
||||
{
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
for (const auto &city : GameManager::instance().getCities()) {
|
||||
const Player *player = GameManager::instance().players().getPlayer(city.playerId);
|
||||
QColor ribbonColor = player ? player->primaryColor : QColor(255, 215, 60);
|
||||
|
||||
for (const auto &t : city.territory) {
|
||||
int tx = t.first;
|
||||
int ty = t.second;
|
||||
QVector2D center = Pathfinder::hexToWorld(tx, ty);
|
||||
|
||||
for (int e = 0; e < 6; ++e) {
|
||||
int nx, ny;
|
||||
getHexNeighbor(tx, ty, e, nx, ny);
|
||||
|
||||
if (!city.ownsTile(nx, ny)) {
|
||||
QVector2D p1, p2;
|
||||
getHexEdgeEndpoints(e, p1, p2);
|
||||
|
||||
float wx1 = center.x() + p1.x();
|
||||
float wy1 = center.y() + p1.y();
|
||||
float wz1 = Unit::getTerrainHeight(wx1, wy1, m_point, m_numPoints05) + 0.035f;
|
||||
|
||||
float wx2 = center.x() + p2.x();
|
||||
float wy2 = center.y() + p2.y();
|
||||
float wz2 = Unit::getTerrainHeight(wx2, wy2, m_point, m_numPoints05) + 0.035f;
|
||||
|
||||
bool inFront1 = false, inFront2 = false;
|
||||
QPointF s1 = UnitRenderer::projectToScreen(QVector3D(wx1, wy1, wz1), Projection, vMatrix, width(), height(), inFront1);
|
||||
QPointF s2 = UnitRenderer::projectToScreen(QVector3D(wx2, wy2, wz2), Projection, vMatrix, width(), height(), inFront2);
|
||||
|
||||
if (inFront1 && inFront2) {
|
||||
QColor glow = ribbonColor;
|
||||
glow.setAlpha(90);
|
||||
painter.setPen(QPen(glow, 5.5f, Qt::SolidLine, Qt::RoundCap));
|
||||
painter.drawLine(s1, s2);
|
||||
|
||||
painter.setPen(QPen(ribbonColor, 2.6f, Qt::SolidLine, Qt::RoundCap));
|
||||
painter.drawLine(s1, s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSceneRenderer::drawCityViewOverlay(QPainter &painter, const QMatrix4x4 &vMatrix)
|
||||
{
|
||||
m_citySlotHits.clear();
|
||||
City *city = GameManager::instance().getActiveCityView();
|
||||
if (!city) return;
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
const Player *player = GameManager::instance().players().getPlayer(city->playerId);
|
||||
QColor primaryCol = player ? player->primaryColor : Qt::blue;
|
||||
QColor secondaryCol = player ? player->secondaryColor : Qt::white;
|
||||
|
||||
int maxRadius = TerrainYieldManager::instance().getCityConfig().maxWorkableRadius; // 3 deep hexagons
|
||||
|
||||
// 1. Render all hexes up to 3 distance around the city
|
||||
for (int dx = -maxRadius - 1; dx <= maxRadius + 1; ++dx) {
|
||||
for (int dy = -maxRadius - 1; dy <= maxRadius + 1; ++dy) {
|
||||
int hx = city->tileX + dx;
|
||||
int hy = city->tileY + dy;
|
||||
if (hx < 0 || hx >= MAP_WIDTH || hy < 0 || hy >= MAP_HEIGHT) continue;
|
||||
|
||||
int dist = Pathfinder::hexDistance(city->tileX, city->tileY, hx, hy);
|
||||
if (dist > maxRadius) continue;
|
||||
|
||||
QVector2D center = Pathfinder::hexToWorld(hx, hy);
|
||||
float gz = Unit::getTerrainHeight(center.x(), center.y(), m_point, m_numPoints05);
|
||||
bool inFront = false;
|
||||
QPointF sCenter = UnitRenderer::projectToScreen(QVector3D(center.x(), center.y(), gz + 0.05f), Projection, vMatrix, width(), height(), inFront);
|
||||
if (!inFront) continue;
|
||||
|
||||
bool isOwned = city->ownsTile(hx, hy);
|
||||
|
||||
// Draw hexagon boundary outline
|
||||
QPolygonF hexPoly;
|
||||
bool polyValid = true;
|
||||
for (int e = 0; e < 6; ++e) {
|
||||
QVector2D p1, p2;
|
||||
getHexEdgeEndpoints(e, p1, p2);
|
||||
float px = center.x() + p1.x();
|
||||
float py = center.y() + p1.y();
|
||||
float pz = Unit::getTerrainHeight(px, py, m_point, m_numPoints05) + 0.02f;
|
||||
bool pInFront = false;
|
||||
QPointF sp = UnitRenderer::projectToScreen(QVector3D(px, py, pz), Projection, vMatrix, width(), height(), pInFront);
|
||||
if (!pInFront) { polyValid = false; break; }
|
||||
hexPoly << sp;
|
||||
}
|
||||
|
||||
if (polyValid) {
|
||||
if (isOwned) {
|
||||
painter.setPen(QPen(QColor(80, 200, 255, 140), 1.4, Qt::SolidLine));
|
||||
painter.setBrush(QColor(40, 120, 200, 30));
|
||||
} else {
|
||||
painter.setPen(QPen(QColor(120, 130, 145, 90), 1.0, Qt::DashLine));
|
||||
painter.setBrush(QColor(20, 25, 35, 40));
|
||||
}
|
||||
painter.drawPolygon(hexPoly);
|
||||
}
|
||||
|
||||
// Check workability
|
||||
QString tType = TerrainYieldManager::instance().getTerrainType(hx, hy, m_point, m_numPoints05);
|
||||
const TerrainYieldConfig *yCfg = TerrainYieldManager::instance().getYieldConfig(tType);
|
||||
|
||||
if (isOwned) {
|
||||
if (yCfg && yCfg->workable) {
|
||||
// Tile has 1 slot!
|
||||
bool isWorked = city->isTileWorked(hx, hy);
|
||||
QString res = isWorked ? city->getWorkedResource(hx, hy) : yCfg->primaryResource;
|
||||
int amount = yCfg->getYield(res);
|
||||
|
||||
float slotR = 15.0f;
|
||||
QRect slotRect((int)(sCenter.x() - slotR), (int)(sCenter.y() - slotR), (int)(slotR * 2), (int)(slotR * 2));
|
||||
m_citySlotHits.push_back({slotRect, hx, hy, res});
|
||||
|
||||
// Slot circle background
|
||||
painter.setBrush(isWorked ? QColor(25, 55, 38, 245) : QColor(18, 25, 36, 225));
|
||||
painter.setPen(QPen(isWorked ? QColor(80, 240, 120) : QColor(140, 175, 215), isWorked ? 2.2 : 1.2));
|
||||
painter.drawEllipse(sCenter, slotR, slotR);
|
||||
|
||||
// Resource icon
|
||||
QImage rIcon = getTintedIcon("images/resources/" + res + ".png", isWorked ? QColor(80, 255, 120) : QColor(180, 210, 245));
|
||||
painter.drawImage(QRectF(sCenter.x() - 8.5, sCenter.y() - 8.5, 17, 17), rIcon);
|
||||
|
||||
// Yield pill below slot
|
||||
QRect pillRect((int)sCenter.x() - 26, (int)sCenter.y() + 16, 52, 14);
|
||||
painter.setBrush(QColor(15, 20, 28, 220));
|
||||
painter.setPen(QPen(isWorked ? QColor(80, 240, 120, 160) : QColor(80, 110, 140, 120), 1.0));
|
||||
painter.drawRoundedRect(pillRect, 3, 3);
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::Bold));
|
||||
painter.setPen(isWorked ? QColor(100, 255, 140) : QColor(180, 205, 235));
|
||||
painter.drawText(pillRect, Qt::AlignCenter, QString("+%1").arg(amount));
|
||||
|
||||
// If unworked and pop is available: show small green +
|
||||
if (!isWorked && city->getAvailablePop() > 0) {
|
||||
float pulse = (std::sin(m_elapsedTimer.elapsed() * 0.008f) + 1.0f) * 0.5f;
|
||||
painter.setBrush(QColor(60, 220, 90, (int)(180 + pulse * 75)));
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawEllipse(QPointF(sCenter.x() + 10, sCenter.y() - 10), 4.5, 4.5);
|
||||
}
|
||||
}
|
||||
// If not workable (e.g. Sneeuw / Snow): no slot is shown, as requested!
|
||||
} else {
|
||||
// Outside borders: draw subtle locked badge
|
||||
QRect lockRect((int)sCenter.x() - 12, (int)sCenter.y() - 7, 24, 14);
|
||||
painter.setBrush(QColor(20, 24, 32, 180));
|
||||
painter.setPen(QPen(QColor(100, 120, 140, 100), 1.0));
|
||||
painter.drawRoundedRect(lockRect, 3, 3);
|
||||
painter.setFont(QFont("sans-serif", 6, QFont::Bold));
|
||||
painter.setPen(QColor(140, 155, 175));
|
||||
painter.drawText(lockRect, Qt::AlignCenter, "SLOT");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. City Management Top Card
|
||||
int cardW = 500, cardH = 145;
|
||||
QRect cardRect((width() - cardW) / 2, 16, cardW, cardH);
|
||||
|
||||
painter.setBrush(QColor(16, 22, 32, 242));
|
||||
painter.setPen(QPen(primaryCol, 1.8));
|
||||
painter.drawRoundedRect(cardRect, 10, 10);
|
||||
|
||||
// City Title & Close Button
|
||||
painter.setFont(QFont("sans-serif", 13, QFont::Bold));
|
||||
painter.setPen(QColor(245, 250, 255));
|
||||
painter.drawText(QRect(cardRect.left() + 16, cardRect.top() + 10, 260, 24), Qt::AlignLeft | Qt::AlignVCenter, city->name.toUpper() + " - STADSBEHEER");
|
||||
|
||||
// Close button [X]
|
||||
m_closeCityViewBtnRect = QRect(cardRect.right() - 88, cardRect.top() + 10, 74, 24);
|
||||
painter.setBrush(QColor(40, 50, 68, 220));
|
||||
painter.setPen(QPen(secondaryCol, 1.2));
|
||||
painter.drawRoundedRect(m_closeCityViewBtnRect, 4, 4);
|
||||
painter.setFont(QFont("sans-serif", 8, QFont::Bold));
|
||||
painter.setPen(QColor(220, 235, 255));
|
||||
painter.drawText(m_closeCityViewBtnRect, Qt::AlignCenter, "[X] Sluiten");
|
||||
|
||||
// Population Stats Row
|
||||
int totalPop = city->getTotalPop();
|
||||
int assigned = city->getAssignedPop();
|
||||
int avail = city->getAvailablePop();
|
||||
|
||||
painter.setFont(QFont("sans-serif", 9));
|
||||
painter.setPen(QColor(180, 205, 235));
|
||||
QString popInfo = QString("Inwoners: %L1 | Totale Pop: %2 | Aan het werk: %3 | Beschikbaar: %4")
|
||||
.arg(city->inhabitants).arg(totalPop).arg(assigned).arg(avail);
|
||||
painter.drawText(QRect(cardRect.left() + 16, cardRect.top() + 38, cardRect.width() - 32, 18), Qt::AlignLeft | Qt::AlignVCenter, popInfo);
|
||||
|
||||
// Separator line
|
||||
painter.setPen(QPen(QColor(60, 85, 120, 150), 1.0));
|
||||
painter.drawLine(cardRect.left() + 16, cardRect.top() + 60, cardRect.right() - 16, cardRect.top() + 60);
|
||||
|
||||
// Total Yields Summary Row
|
||||
CityYieldSummary summary = GameManager::instance().calculateCityYields(*city, m_point, m_numPoints05);
|
||||
|
||||
struct YieldItem { QString name; int val; QString icon; QColor col; };
|
||||
std::vector<YieldItem> yields = {
|
||||
{"Voedsel", summary.food, "images/resources/voedsel.png", QColor(100, 230, 110)},
|
||||
{"Arbeid", summary.labor, "images/resources/arbeid.png", QColor(240, 140, 50)},
|
||||
{"Kennis", summary.science, "images/resources/kennis.png", QColor(60, 190, 255)},
|
||||
{"Cultuur", summary.culture, "images/resources/cultuur.png", QColor(200, 110, 240)},
|
||||
{"Geloof", summary.faith, "images/resources/geloof.png", QColor(255, 230, 80)},
|
||||
{"Goud", summary.gold, "images/resources/goud.png", QColor(255, 210, 50)}
|
||||
};
|
||||
|
||||
int colW = (cardRect.width() - 32) / 6;
|
||||
for (size_t i = 0; i < yields.size(); ++i) {
|
||||
int cx = cardRect.left() + 16 + (int)i * colW;
|
||||
QRect yRect(cx, cardRect.top() + 68, colW - 6, 44);
|
||||
|
||||
painter.setBrush(QColor(12, 16, 24, 180));
|
||||
painter.setPen(QPen(QColor(40, 60, 85, 120), 1.0));
|
||||
painter.drawRoundedRect(yRect, 4, 4);
|
||||
|
||||
QImage icon = getTintedIcon(yields[i].icon, yields[i].col);
|
||||
painter.drawImage(QRect(yRect.left() + 6, yRect.top() + 6, 16, 16), icon);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 7));
|
||||
painter.setPen(QColor(160, 180, 205));
|
||||
painter.drawText(QRect(yRect.left() + 26, yRect.top() + 6, yRect.width() - 28, 16), Qt::AlignLeft | Qt::AlignVCenter, yields[i].name);
|
||||
|
||||
painter.setFont(QFont("sans-serif", 9, QFont::Bold));
|
||||
painter.setPen(yields[i].col);
|
||||
painter.drawText(QRect(yRect.left() + 6, yRect.top() + 24, yRect.width() - 12, 16), Qt::AlignLeft | Qt::AlignVCenter, QString("+%1").arg(yields[i].val));
|
||||
}
|
||||
|
||||
// Help instruction at bottom
|
||||
painter.setFont(QFont("sans-serif", 7, QFont::StyleItalic));
|
||||
painter.setPen(QColor(140, 170, 200));
|
||||
painter.drawText(QRect(cardRect.left() + 16, cardRect.bottom() - 20, cardRect.width() - 32, 16), Qt::AlignCenter,
|
||||
"Klik op een tegel binnen de grenzen om 1 Pop toe te wijzen of vrij te maken.");
|
||||
}
|
||||
|
||||
void QSceneRenderer::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
// 0. If in City View: handle close button and slot clicks
|
||||
if (GameManager::instance().isInCityView()) {
|
||||
if (m_closeCityViewBtnRect.contains(event->pos())) {
|
||||
GameManager::instance().exitCityView();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &slot : m_citySlotHits) {
|
||||
if (slot.rect.contains(event->pos())) {
|
||||
City *city = GameManager::instance().getActiveCityView();
|
||||
if (city) {
|
||||
QVector2D w = Pathfinder::hexToWorld(slot.tx, slot.ty);
|
||||
float gz = Unit::getTerrainHeight(w.x(), w.y(), m_point, m_numPoints05);
|
||||
if (city->isTileWorked(slot.tx, slot.ty)) {
|
||||
city->unassignWorker(slot.tx, slot.ty);
|
||||
GameManager::instance().addFloatingText("Pop vrijgemaakt", QVector3D(w.x(), w.y(), gz + 0.8f), QColor(255, 220, 80));
|
||||
} else {
|
||||
if (city->getAvailablePop() > 0) {
|
||||
city->assignWorker(slot.tx, slot.ty, slot.defaultRes);
|
||||
GameManager::instance().addFloatingText("+1 Pop aan het werk", QVector3D(w.x(), w.y(), gz + 0.8f), QColor(80, 255, 120));
|
||||
} else {
|
||||
GameManager::instance().addFloatingText("Geen vrije Pop!", QVector3D(w.x(), w.y(), gz + 0.8f), QColor(255, 80, 80));
|
||||
}
|
||||
}
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if clicked on a City Banner to open City View
|
||||
for (const auto &cb : m_cityBannerHits) {
|
||||
if (cb.rect.contains(event->pos())) {
|
||||
GameManager::instance().enterCityView(cb.cityId);
|
||||
City *c = GameManager::instance().getCityById(cb.cityId);
|
||||
if (c) {
|
||||
cameraPosition.setX(c->worldPos.x());
|
||||
cameraPosition.setY(c->worldPos.y() - 2.8f);
|
||||
}
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Zoom Out / Reset / In buttons
|
||||
if (getZoomOutButtonRect().contains(event->pos())) {
|
||||
m_minimapZoom = std::max(1.0f, m_minimapZoom / 1.5f);
|
||||
@@ -1010,8 +1301,15 @@ void QSceneRenderer::mousePressEvent(QMouseEvent *event)
|
||||
Unit *unitOnTile = GameManager::instance().getUnitAt(hitX, hitY);
|
||||
if (unitOnTile) {
|
||||
GameManager::instance().selectUnit(unitOnTile->id);
|
||||
} else if (!m_unitCardRect.contains(event->pos())) {
|
||||
GameManager::instance().deselectUnit();
|
||||
} else {
|
||||
City *cityOnTile = GameManager::instance().getCityAt(hitX, hitY);
|
||||
if (cityOnTile) {
|
||||
GameManager::instance().enterCityView(cityOnTile->id);
|
||||
cameraPosition.setX(cityOnTile->worldPos.x());
|
||||
cameraPosition.setY(cityOnTile->worldPos.y() - 2.8f);
|
||||
} else if (!m_unitCardRect.contains(event->pos())) {
|
||||
GameManager::instance().deselectUnit();
|
||||
}
|
||||
}
|
||||
update();
|
||||
return;
|
||||
@@ -1103,7 +1401,9 @@ void QSceneRenderer::keyPressEvent(QKeyEvent *e)
|
||||
GameManager::instance().commandBuildCitySelected(m_point, m_numPoints05);
|
||||
}
|
||||
else if (e->key() == Qt::Key_Escape) {
|
||||
if (GameManager::instance().currentActionMode() != ActionMode::None) {
|
||||
if (GameManager::instance().isInCityView()) {
|
||||
GameManager::instance().exitCityView();
|
||||
} else if (GameManager::instance().currentActionMode() != ActionMode::None) {
|
||||
GameManager::instance().setActionMode(ActionMode::None);
|
||||
} else {
|
||||
GameManager::instance().deselectUnit();
|
||||
@@ -1243,13 +1543,16 @@ void QSceneRenderer::paintGL()
|
||||
|
||||
// 3. Render 2D UI & HUD Overlays via QPainter
|
||||
QPainter painter(this);
|
||||
drawCityBorders(painter, vMatrix);
|
||||
drawPathAndRange(painter, selected, vMatrix);
|
||||
drawMinimap(painter);
|
||||
drawTickBar(painter);
|
||||
drawUnitsOverlay(painter, vMatrix);
|
||||
drawFloatingTexts(painter, vMatrix);
|
||||
|
||||
if (selected) {
|
||||
if (GameManager::instance().isInCityView()) {
|
||||
drawCityViewOverlay(painter, vMatrix);
|
||||
} else if (selected) {
|
||||
drawUnitCard(painter, selected);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
Reference in New Issue
Block a user