diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 53261019..6e3eca54 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -2389,6 +2389,7 @@ void Player::setMirrored(bool _mirrored) void Player::processSceneSizeChange(int newPlayerWidth) { + // Extend table (and hand, if horizontal) to accomodate the new player width. qreal tableWidth = newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stack->boundingRect().width(); if (!settingsCache->getHorizontalHand()) tableWidth -= hand->boundingRect().width(); diff --git a/cockatrice/src/tablezone.cpp b/cockatrice/src/tablezone.cpp index adb171ff..cfaa6e5f 100644 --- a/cockatrice/src/tablezone.cpp +++ b/cockatrice/src/tablezone.cpp @@ -31,9 +31,9 @@ TableZone::TableZone(Player *_p, QGraphicsItem *parent) updateBgPixmap(); - height = 2 * BOX_LINE_WIDTH + TABLEROWS * (CARD_HEIGHT + 20) + 2 * PADDING_Y; - width = MIN_WIDTH + 2 * MARGIN_X + 2 * BOX_LINE_WIDTH; - currentMinimumWidth = MIN_WIDTH; + height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CARD_HEIGHT + (TABLEROWS-1) * PADDING_Y; + width = MIN_WIDTH; + currentMinimumWidth = width; setCacheMode(DeviceCoordinateCache); #if QT_VERSION < 0x050000 @@ -115,10 +115,12 @@ void TableZone::paintZoneOutline(QPainter *painter) { @painter QPainter object */ void TableZone::paintLandDivider(QPainter *painter){ - painter->setPen(QColor(255, 255, 255, 40)); - qreal separatorY = 2 * (CARD_HEIGHT + 20 + PADDING_Y) + BOX_LINE_WIDTH - PADDING_Y / 2; + // Place the line 2 grid heights down then back it off just enough to allow + // some space between a 3-card stack and the land area. + qreal separatorY = MARGIN_TOP + 2 * (CARD_HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; if (isInverted()) separatorY = height - separatorY; + painter->setPen(QColor(255, 255, 255, 40)); painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY)); } @@ -165,30 +167,9 @@ void TableZone::reorganizeCards() { QList arrowsToUpdate; - // Calculate table grid distortion so that the mapping functions work properly - QMap gridPointStackCount; - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); - if (gridPoint.x() == -1) - continue; - - const int key = gridPoint.x() / 3 + gridPoint.y() * 1000; - gridPointStackCount.insert(key, gridPointStackCount.value(key, 0) + 1); - } - gridPointWidth.clear(); - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); - if (gridPoint.x() == -1) - continue; - - const int key = gridPoint.x() / 3 + gridPoint.y() * 1000; - const int stackCount = gridPointStackCount.value(key, 0); - if (stackCount == 1) - gridPointWidth.insert(key, CARD_WIDTH * (1 + cards[i]->getAttachedCards().size() / 3.0)); - else - gridPointWidth.insert(key, CARD_WIDTH * (1 + (stackCount - 1) / 3.0)); - } - + // Calculate card stack widths so mapping functions work properly + computeCardStackWidths(); + for (int i = 0; i < cards.size(); ++i) { QPoint gridPoint = cards[i]->getGridPos(); if (gridPoint.x() == -1) @@ -199,7 +180,7 @@ void TableZone::reorganizeCards() qreal y = mapPoint.y(); int numberAttachedCards = cards[i]->getAttachedCards().size(); - qreal actualX = x + numberAttachedCards * CARD_WIDTH / 3.0; + qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X; qreal actualY = y; if (numberAttachedCards) actualY += 15; @@ -212,7 +193,7 @@ void TableZone::reorganizeCards() while (attachedCardIterator.hasNext()) { ++j; CardItem *attachedCard = attachedCardIterator.next(); - qreal childX = actualX - j * CARD_WIDTH / 3.0; + qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childY = y + 5; attachedCard->setPos(childX, childY); attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); @@ -271,13 +252,19 @@ CardItem *TableZone::takeCard(int position, int cardId, bool canResize) void TableZone::resizeToContents() { int xMax = 0; + + // Find rightmost card position, which includes the left margin amount. for (int i = 0; i < cards.size(); ++i) if (cards[i]->pos().x() > xMax) xMax = (int) cards[i]->pos().x(); - xMax += 2 * CARD_WIDTH; - if (xMax < MIN_WIDTH) - xMax = MIN_WIDTH; - currentMinimumWidth = xMax + 2 * MARGIN_X + 2 * BOX_LINE_WIDTH; + + // Minimum width is the rightmost card position plus enough room for + // another card with padding, then margin. + currentMinimumWidth = xMax + (2 * CARD_WIDTH) + PADDING_X + MARGIN_RIGHT; + + if (currentMinimumWidth < MIN_WIDTH) + currentMinimumWidth = MIN_WIDTH; + if (currentMinimumWidth != width) { prepareGeometryChange(); width = currentMinimumWidth; @@ -294,6 +281,7 @@ CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const return 0; } + CardItem *TableZone::getCardFromCoords(const QPointF &point) const { QPoint gridPoint = mapToGrid(point); @@ -301,57 +289,110 @@ CardItem *TableZone::getCardFromCoords(const QPointF &point) const } +void TableZone::computeCardStackWidths() +{ + // Each card stack is three grid points worth of card locations. + // First pass: compute the number of cards at each card stack. + QMap cardStackCount; + for (int i = 0; i < cards.size(); ++i) { + const QPoint &gridPoint = cards[i]->getGridPos(); + if (gridPoint.x() == -1) + continue; + + const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); + cardStackCount.insert(key, cardStackCount.value(key, 0) + 1); + } + + // Second pass: compute the width at each card stack. + cardStackWidth.clear(); + for (int i = 0; i < cards.size(); ++i) { + const QPoint &gridPoint = cards[i]->getGridPos(); + if (gridPoint.x() == -1) + continue; + + const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); + const int stackCount = cardStackCount.value(key, 0); + if (stackCount == 1) + cardStackWidth.insert(key, CARD_WIDTH + cards[i]->getAttachedCards().size() * STACKED_CARD_OFFSET_X); + else + cardStackWidth.insert(key, CARD_WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); + } +} + + QPointF TableZone::mapFromGrid(QPoint gridPoint) const { qreal x, y; - x = MARGIN_X + (gridPoint.x() % 3) * CARD_WIDTH / 3.0; + + // Start with margin plus stacked card offset + x = MARGIN_LEFT + (gridPoint.x() % 3) * STACKED_CARD_OFFSET_X; + + // Add in width of card stack plus padding for each column for (int i = 0; i < gridPoint.x() / 3; ++i) - x += gridPointWidth.value(gridPoint.y() * 1000 + i, CARD_WIDTH) + PADDING_X; + { + const int key = getCardStackMapKey(i, gridPoint.y()); + x += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + } if (isInverted()) - gridPoint.setY(2 - gridPoint.y()); + gridPoint.setY(TABLEROWS - 1 - gridPoint.y()); - y = BOX_LINE_WIDTH + gridPoint.y() * (CARD_HEIGHT + PADDING_Y + 20) + (gridPoint.x() % TABLEROWS) * 10; -/* - if (isInverted()) - y = height - CARD_HEIGHT - y; -*/ + // Start with margin plus stacked card offset + y = MARGIN_TOP + (gridPoint.x() % 3) * STACKED_CARD_OFFSET_Y; + + // Add in card size and padding for each row + for (int i = 0; i < gridPoint.y(); ++i) + y += CARD_HEIGHT + PADDING_Y; + return QPointF(x, y); } QPoint TableZone::mapToGrid(const QPointF &mapPoint) const { - qreal x = mapPoint.x() - MARGIN_X; - qreal y = mapPoint.y(); -/* if (isInverted()) - y = height - y; -*/ y -= BOX_LINE_WIDTH; - - if (x < 0) - x = 0; - else if (x > width - CARD_WIDTH - MARGIN_X) - x = width - CARD_WIDTH - MARGIN_X; - if (y < 0) - y = 0; - else if (y > height - CARD_HEIGHT) - y = height - CARD_HEIGHT; - - int resultY = round(y / (CARD_HEIGHT + PADDING_Y + 20)); - if (isInverted()) - resultY = TABLEROWS - 1 - resultY; + // Begin by calculating the y-coordinate of the grid space, which will be + // used for the x-coordinate. - int baseX = -1; - qreal oldTempX = 0, tempX = 0; - do { - ++baseX; - oldTempX = tempX; - tempX += gridPointWidth.value(resultY * 1000 + baseX, CARD_WIDTH) + PADDING_X; - } while (tempX < x + 1); - - qreal xdiff = x - oldTempX; - int resultX = baseX * 3 + qMin((int) floor(xdiff * 3 / CARD_WIDTH), 2); - return QPoint(resultX, resultY); + // Offset point by the margin amount to reference point within grid area. + int y = mapPoint.y() - MARGIN_TOP; + + // Below calculation effectively rounds to the nearest grid point. + const int gridPointHeight = CARD_HEIGHT + PADDING_Y; + int gridPointY = (y + gridPointHeight / 2) / gridPointHeight; + + gridPointY = clampValidTableRow(gridPointY); + + if (isInverted()) + gridPointY = TABLEROWS - 1 - gridPointY; + + // Calculating the x-coordinate of the grid space requires adding up the + // widths of each card stack along the row. + + // Offset point by the margin amount to reference point within grid area. + int x = mapPoint.x() - MARGIN_LEFT; + + // Maximum value is a card width from the right margin, referenced to the + // grid area. + const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CARD_WIDTH; + + int xStack = 0; + int xNextStack = 0; + int nextStackCol = 0; + while ((xNextStack <= x) && (xNextStack <= xMax)) { + xStack = xNextStack; + const int key = getCardStackMapKey(nextStackCol, gridPointY); + xNextStack += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + nextStackCol++; + } + int stackCol = qMax(nextStackCol - 1, 0); + + // Have the stack column, need to refine to the grid column. Take the + // difference between the point and the stack point and divide by stacked + // card offsets. + int xDiff = x - xStack; + int gridPointX = stackCol * 3 + qMin(xDiff / STACKED_CARD_OFFSET_X, 2); + + return QPoint(gridPointX, gridPointY); } diff --git a/cockatrice/src/tablezone.h b/cockatrice/src/tablezone.h index e69bc281..2094a48f 100644 --- a/cockatrice/src/tablezone.h +++ b/cockatrice/src/tablezone.h @@ -20,13 +20,34 @@ signals: void sizeChanged(); private: - static const int BOX_LINE_WIDTH = 10; - static const int PADDING_X = 35; - static const int PADDING_Y = 10; - static const int MARGIN_X = 20; - static const int MIN_WIDTH = 10 * CARD_WIDTH / 2; static const int TABLEROWS = 3; - + + /* + Margins between table edges and cards, paddings between cards + */ + static const int MARGIN_LEFT = 20; + static const int MARGIN_RIGHT = 15; + static const int MARGIN_TOP = 10; + static const int MARGIN_BOTTOM = 30; + static const int PADDING_X = 35; + static const int PADDING_Y = 30; + + /* + Minimum width of the table zone including margins. + */ + static const int MIN_WIDTH = MARGIN_LEFT + (5 * CARD_WIDTH) + MARGIN_RIGHT; + + /* + Offset sizes when cards are stacked on each other in the grid + */ + static const int STACKED_CARD_OFFSET_X = CARD_WIDTH / 3; + static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3; + + /* + Width of the box line drawn in the margin around the active player's area + */ + static const int BOX_LINE_WIDTH = 10; + /* Default background color, inactive mask and boarder gradient */ @@ -38,11 +59,15 @@ private: /* Size and shape variables */ - QMap gridPointWidth; int width; int height; int currentMinimumWidth; + /* + Internal cache for widths of stacks of cards by row and column. + */ + QMap cardStackWidth; + /* Holds any custom background image for the TableZone */ @@ -114,11 +139,9 @@ public: */ CardItem *getCardFromCoords(const QPointF &point) const; - QPointF mapFromGrid(QPoint gridPoint) const; - QPoint mapToGrid(const QPointF &mapPoint) const; QPointF closestGridPoint(const QPointF &point); - int clampValidTableRow(const int row); + static int clampValidTableRow(const int row); /** Removes a card from view. @@ -137,7 +160,7 @@ public: void resizeToContents(); int getMinimumWidth() const { return currentMinimumWidth; } - void setWidth(qreal _width){ prepareGeometryChange(); width = _width;}; + void setWidth(qreal _width) { prepareGeometryChange(); width = _width; } qreal getWidth() const { return width; } void setActive(bool _active) { active = _active; update(); } @@ -147,6 +170,22 @@ protected: private: void paintZoneOutline(QPainter *painter); void paintLandDivider(QPainter *painter); + + /* + Calculates card stack widths so mapping functions work properly + */ + void computeCardStackWidths(); + + /* + Mapping functions for points to/from gridpoints. + */ + QPointF mapFromGrid(QPoint gridPoint) const; + QPoint mapToGrid(const QPointF &mapPoint) const; + + /* + Helper function to create a single key from a card stack location. + */ + int getCardStackMapKey (int x, int y) const { return x + (y * 1000); } }; #endif