Major Directory Refactoring (#5118)

* refactored cardzone.cpp, added doc and changed if to switch case

* started moving every files into different folders

* remove undercase to match with other files naming convention

* refactored dialog files

* ran format.sh

* refactored client/tabs folder

* refactored client/tabs folder

* refactored client/tabs folder

* refactored client folder

* refactored carddbparser

* refactored dialogs

* Create sonar-project.properties

temporary file for lint

* Create build.yml

temporary file for lint

* removed all files from root directory

* removed all files from root directory

* added current branch to workflow

* fixed most broken import

* fixed issues while renaming files

* fixed oracle importer

* fixed dbconverter

* updated translations

* made sub-folders for client

* removed linter

* removed linter folder

* fixed oracle import

* revert card_zone documentation

* renamed db parser files name and deck_view imports

* fixed dlg file issue

* ran format file and fixed test file

* fixed carddb test files

* moved player folder in game

* updated translations and format files

* fixed peglib import

* format cmake files

* removing vcpkg to try to add it back later

* tried fixing vcpkg file

* renamed filter to filters and moved database parser to cards folder

* reverted translation files

* reverted oracle translated

* Update cockatrice/src/dialogs/dlg_register.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* Update cockatrice/src/client/ui/window_main.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* removed empty line at file start

* removed useless include from tab_supervisor.cpp

* refactored cardzone.cpp, added doc and changed if to switch case

* started moving every files into different folders

* remove undercase to match with other files naming convention

* refactored dialog files

* ran format.sh

* refactored client/tabs folder

* refactored client folder

* refactored carddbparser

* refactored dialogs

* removed all files from root directory

* Create sonar-project.properties

temporary file for lint

* Create build.yml

temporary file for lint

* added current branch to workflow

* fixed most broken import

* fixed issues while renaming files

* fixed oracle importer

* fixed dbconverter

* updated translations

* made sub-folders for client

* removed linter

* removed linter folder

* fixed oracle import

* revert card_zone documentation

* renamed db parser files name and deck_view imports

* fixed dlg file issue

* ran format file and fixed test file

* fixed carddb test files

* moved player folder in game

* updated translations and format files

* fixed peglib import

* reverted translation files

* format cmake files

* removing vcpkg to try to add it back later

* tried fixing vcpkg file

* pre-updating of cockatrice changes

* removed empty line at file start

* reverted oracle translated

* Update cockatrice/src/dialogs/dlg_register.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* Update cockatrice/src/client/ui/window_main.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* removed useless include from tab_supervisor.cpp

---------

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
This commit is contained in:
LunaticCat 2024-10-20 16:11:35 +02:00 committed by GitHub
parent d1e0f9dfc5
commit fa999880ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
261 changed files with 735 additions and 729 deletions

View file

@ -0,0 +1,217 @@
#include "card_zone.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_move_card.pb.h"
#include "pb/serverinfo_user.pb.h"
#include "view_zone.h"
#include <QAction>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
CardZone::CardZone(Player *_p,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent,
bool _isView)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), views{}, menu(nullptr),
doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable), isView(_isView)
{
if (!isView)
player->addZone(this);
}
CardZone::~CardZone()
{
qDebug() << "CardZone destructor: " << name;
for (auto *view : views) {
if (view != nullptr) {
view->deleteLater();
}
}
clearContents();
}
void CardZone::retranslateUi()
{
for (int i = 0; i < cards.size(); ++i)
cards[i]->retranslateUi();
}
void CardZone::clearContents()
{
for (int i = 0; i < cards.size(); i++) {
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a
// player, we have to return them to avoid a crash.
const QList<CardItem *> &attachedCards = cards[i]->getAttachedCards();
for (auto attachedCard : attachedCards)
attachedCard->setParentItem(attachedCard->getZone());
player->deleteCard(cards.at(i));
}
cards.clear();
emit cardCountChanged();
}
QString CardZone::getTranslatedName(bool theirOwn, GrammaticalCase gc) const
{
QString ownerName = player->getName();
if (name == "hand")
return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName));
else if (name == "deck")
switch (gc) {
case CaseLookAtZone:
return (theirOwn ? tr("their library", "look at zone")
: tr("%1's library", "look at zone").arg(ownerName));
case CaseTopCardsOfZone:
return (theirOwn ? tr("of their library", "top cards of zone,")
: tr("of %1's library", "top cards of zone").arg(ownerName));
case CaseRevealZone:
return (theirOwn ? tr("their library", "reveal zone")
: tr("%1's library", "reveal zone").arg(ownerName));
case CaseShuffleZone:
return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName));
default:
return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName));
}
else if (name == "grave")
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
else if (name == "rfg")
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
else if (name == "sb")
switch (gc) {
case CaseLookAtZone:
return (theirOwn ? tr("their sideboard", "look at zone")
: tr("%1's sideboard", "look at zone").arg(ownerName));
case CaseNominative:
return (theirOwn ? tr("their sideboard", "nominative")
: tr("%1's sideboard", "nominative").arg(ownerName));
default:
break;
}
return QString();
}
void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * /*event*/)
{
if (doubleClickAction)
doubleClickAction->trigger();
}
bool CardZone::showContextMenu(const QPoint &screenPos)
{
if (menu) {
menu->exec(screenPos);
return true;
}
return false;
}
void CardZone::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
if (showContextMenu(event->screenPos()))
event->accept();
else
event->ignore();
} else
event->ignore();
}
void CardZone::addCard(CardItem *card, bool reorganize, int x, int y)
{
for (auto *view : views) {
if ((x <= view->getCards().size()) || (view->getNumberCards() == -1)) {
view->addCard(new CardItem(player, card->getName(), card->getId()), reorganize, x, y);
}
}
card->setZone(this);
addCardImpl(card, x, y);
if (reorganize)
reorganizeCards();
emit cardCountChanged();
}
CardItem *CardZone::getCard(int cardId, const QString &cardName)
{
CardItem *c = cards.findCard(cardId, false);
if (!c) {
qDebug() << "CardZone::getCard: card id=" << cardId << "not found";
return 0;
}
// If the card's id is -1, this zone is invisible,
// so we need to give the card an id and a name as it comes out.
// It can be assumed that in an invisible zone, all cards are equal.
if ((c->getId() == -1) || (c->getName().isEmpty())) {
c->setId(cardId);
c->setName(cardName);
}
return c;
}
CardItem *CardZone::takeCard(int position, int cardId, bool /*canResize*/)
{
if (position == -1) {
// position == -1 means either that the zone is indexed by card id
// or that it doesn't matter which card you take.
for (int i = 0; i < cards.size(); ++i)
if (cards[i]->getId() == cardId) {
position = i;
break;
}
if (position == -1)
position = 0;
}
if (position >= cards.size())
return 0;
CardItem *c = cards.takeAt(position);
for (auto *view : views) {
view->removeCard(position);
}
c->setId(cardId);
reorganizeCards();
emit cardCountChanged();
return c;
}
void CardZone::removeCard(CardItem *card)
{
cards.removeOne(card);
reorganizeCards();
emit cardCountChanged();
player->deleteCard(card);
}
void CardZone::moveAllToZone()
{
QList<QVariant> data = static_cast<QAction *>(sender())->data().toList();
QString targetZone = data[0].toString();
int targetX = data[1].toInt();
Command_MoveCard cmd;
cmd.set_start_zone(getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(targetX);
for (int i = 0; i < cards.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(cards[i]->getId());
player->sendGameCommand(cmd);
}
QPointF CardZone::closestGridPoint(const QPointF &point)
{
return point;
}

View file

@ -0,0 +1,121 @@
#ifndef CARDZONE_H
#define CARDZONE_H
#include "../../client/translation.h"
#include "../board/abstract_graphics_item.h"
#include "../cards/card_list.h"
#include <QString>
class Player;
class ZoneViewZone;
class QMenu;
class QAction;
class QPainter;
class CardDragItem;
class CardZone : public AbstractGraphicsItem
{
Q_OBJECT
protected:
Player *player;
QString name;
CardList cards;
QList<ZoneViewZone *> views;
QMenu *menu;
QAction *doubleClickAction;
bool hasCardAttr;
bool isShufflable;
bool isView;
bool alwaysRevealTopCard;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void addCardImpl(CardItem *card, int x, int y) = 0;
signals:
void cardCountChanged();
public slots:
void moveAllToZone();
bool showContextMenu(const QPoint &screenPos);
public:
enum
{
Type = typeZone
};
int type() const
{
return Type;
}
virtual void
handleDropEvent(const QList<CardDragItem *> &dragItem, CardZone *startZone, const QPoint &dropPoint) = 0;
CardZone(Player *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent = nullptr,
bool _isView = false);
~CardZone();
void retranslateUi();
void clearContents();
bool getHasCardAttr() const
{
return hasCardAttr;
}
bool getIsShufflable() const
{
return isShufflable;
}
QMenu *getMenu() const
{
return menu;
}
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0)
{
menu = _menu;
doubleClickAction = _doubleClickAction;
}
QString getName() const
{
return name;
}
QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const;
Player *getPlayer() const
{
return player;
}
bool contentsKnown() const
{
return cards.getContentsKnown();
}
const CardList &getCards() const
{
return cards;
}
void addCard(CardItem *card, bool reorganize, int x, int y = -1);
// getCard() finds a card by id.
CardItem *getCard(int cardId, const QString &cardName);
// takeCard() finds a card by position and removes it from the zone and from all of its views.
virtual CardItem *takeCard(int position, int cardId, bool canResize = true);
void removeCard(CardItem *card);
QList<ZoneViewZone *> &getViews()
{
return views;
}
virtual void reorganizeCards() = 0;
virtual QPointF closestGridPoint(const QPointF &point);
bool getIsView() const
{
return isView;
}
bool getAlwaysRevealTopCard() const
{
return alwaysRevealTopCard;
}
void setAlwaysRevealTopCard(bool _alwaysRevealTopCard)
{
alwaysRevealTopCard = _alwaysRevealTopCard;
}
};
#endif

View file

@ -0,0 +1,148 @@
#include "hand_zone.h"
#include "../../client/ui/theme_manager.h"
#include "../../settings/cache_settings.h"
#include "../cards/card_drag_item.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_move_card.pb.h"
#include <QPainter>
HandZone::HandZone(Player *_p, bool _contentsKnown, int _zoneHeight, QGraphicsItem *parent)
: SelectZone(_p, "hand", false, false, _contentsKnown, parent), zoneHeight(_zoneHeight)
{
connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg()));
updateBg();
setCacheMode(DeviceCoordinateCache);
}
void HandZone::updateBg()
{
update();
}
void HandZone::addCardImpl(CardItem *card, int x, int /*y*/)
{
// if x is negative set it to add at end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
if (!cards.getContentsKnown()) {
card->setId(-1);
card->setName();
}
card->setParentItem(this);
card->resetState();
card->setVisible(true);
card->update();
}
void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint)
{
QPoint point = dropPoint + scenePos().toPoint();
int x = -1;
if (SettingsCache::instance().getHorizontalHand()) {
for (x = 0; x < cards.size(); x++)
if (point.x() < static_cast<CardItem *>(cards.at(x))->scenePos().x())
break;
} else {
for (x = 0; x < cards.size(); x++)
if (point.y() < static_cast<CardItem *>(cards.at(x))->scenePos().y())
break;
}
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(getName().toStdString());
cmd.set_x(x);
cmd.set_y(-1);
for (int i = 0; i < dragItems.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
player->sendGameCommand(cmd);
}
QRectF HandZone::boundingRect() const
{
if (SettingsCache::instance().getHorizontalHand())
return QRectF(0, 0, width, CARD_HEIGHT + 10);
else
return QRectF(0, 0, 100, zoneHeight);
}
void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
QBrush brush = themeManager->getHandBgBrush();
if (player->getZoneId() > 0) {
// If the extra image is not found, load the default one
brush = themeManager->getExtraHandBgBrush(QString::number(player->getZoneId()), brush);
}
painter->fillRect(boundingRect(), brush);
}
void HandZone::reorganizeCards()
{
if (!cards.isEmpty()) {
const int cardCount = cards.size();
if (SettingsCache::instance().getHorizontalHand()) {
bool leftJustified = SettingsCache::instance().getLeftJustified();
qreal cardWidth = cards.at(0)->boundingRect().width();
const int xPadding = leftJustified ? cardWidth * 1.4 : 5;
qreal totalWidth =
leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding;
for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i);
// If the total width of the cards is smaller than the available width,
// the cards do not need to overlap and are displayed in the center of the area.
if (cardWidth * cardCount > totalWidth)
c->setPos(xPadding + ((qreal)i) * (totalWidth - cardWidth) / (cardCount - 1), 5);
else {
qreal xPosition =
leftJustified ? xPadding + ((qreal)i) * cardWidth
: xPadding + ((qreal)i) * cardWidth + (totalWidth - cardCount * cardWidth) / 2;
c->setPos(xPosition, 5);
}
c->setRealZValue(i);
}
} else {
qreal totalWidth = boundingRect().width();
qreal cardWidth = cards.at(0)->boundingRect().width();
qreal xspace = 5;
qreal x1 = xspace;
qreal x2 = totalWidth - xspace - cardWidth;
for (int i = 0; i < cardCount; i++) {
CardItem *card = cards.at(i);
qreal x = (i % 2) ? x2 : x1;
qreal y =
divideCardSpaceInZone(i, cardCount, boundingRect().height(), cards.at(0)->boundingRect().height());
card->setPos(x, y);
card->setRealZValue(i);
}
}
}
update();
}
void HandZone::setWidth(qreal _width)
{
if (SettingsCache::instance().getHorizontalHand()) {
prepareGeometryChange();
width = _width;
reorganizeCards();
}
}
void HandZone::updateOrientation()
{
prepareGeometryChange();
reorganizeCards();
}

View file

@ -0,0 +1,28 @@
#ifndef HANDZONE_H
#define HANDZONE_H
#include "select_zone.h"
class HandZone : public SelectZone
{
Q_OBJECT
private:
qreal width, zoneHeight;
private slots:
void updateBg();
public slots:
void updateOrientation();
public:
HandZone(Player *_p, bool _contentsKnown, int _zoneHeight, QGraphicsItem *parent = nullptr);
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void reorganizeCards();
void setWidth(qreal _width);
protected:
void addCardImpl(CardItem *card, int x, int y);
};
#endif

View file

@ -0,0 +1,136 @@
#include "pile_zone.h"
#include "../cards/card_drag_item.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_move_card.pb.h"
#include "view_zone.h"
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
PileZone::PileZone(Player *_p, const QString &_name, bool _isShufflable, bool _contentsKnown, QGraphicsItem *parent)
: CardZone(_p, _name, false, _isShufflable, _contentsKnown, parent)
{
setCacheMode(DeviceCoordinateCache); // Do not move this line to the parent constructor!
setAcceptHoverEvents(true);
setCursor(Qt::OpenHandCursor);
setTransform(QTransform()
.translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2)
.rotate(90)
.translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2));
}
QRectF PileZone::boundingRect() const
{
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT);
}
QPainterPath PileZone::shape() const
{
QPainterPath shape;
shape.addRoundedRect(boundingRect(), 0.05 * CARD_WIDTH, 0.05 * CARD_WIDTH);
return shape;
}
void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
painter->drawPath(shape());
if (!cards.isEmpty())
cards.at(0)->paintPicture(painter, cards.at(0)->getTranslatedSize(painter), 90);
painter->translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2);
painter->rotate(-90);
painter->translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2);
paintNumberEllipse(cards.size(), 28, Qt::white, -1, -1, painter);
}
void PileZone::addCardImpl(CardItem *card, int x, int /*y*/)
{
connect(card, SIGNAL(sigPixmapUpdated()), this, SLOT(callUpdate()));
// if x is negative set it to add at end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
card->setPos(0, 0);
if (!contentsKnown()) {
card->setName(QString());
card->setId(-1);
// If we obscure a previously revealed card, its name has to be forgotten
if (cards.size() > x + 1)
cards.at(x + 1)->setName(QString());
}
card->setVisible(false);
card->resetState();
card->setParentItem(this);
}
void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
CardZone *startZone,
const QPoint & /*dropPoint*/)
{
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(getName().toStdString());
cmd.set_x(0);
cmd.set_y(0);
for (int i = 0; i < dragItems.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
player->sendGameCommand(cmd);
}
void PileZone::reorganizeCards()
{
update();
}
void PileZone::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
CardZone::mousePressEvent(event);
if (event->isAccepted())
return;
if (event->button() == Qt::LeftButton) {
setCursor(Qt::ClosedHandCursor);
event->accept();
} else
event->ignore();
}
void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
QApplication::startDragDistance())
return;
if (cards.isEmpty())
return;
bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier);
bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier);
CardItem *card = bottomCard ? cards.last() : cards.first();
const int cardid = contentsKnown() ? card->getId() : (bottomCard ? cards.size() - 1 : 0);
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), faceDown);
drag->grabMouse();
setCursor(Qt::OpenHandCursor);
}
void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
{
setCursor(Qt::OpenHandCursor);
}
void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
if (!cards.isEmpty())
cards[0]->processHoverEvent();
QGraphicsItem::hoverEnterEvent(event);
}

View file

@ -0,0 +1,35 @@
#ifndef PILEZONE_H
#define PILEZONE_H
#include "card_zone.h"
class PileZone : public CardZone
{
Q_OBJECT
private slots:
void callUpdate()
{
update();
}
public:
PileZone(Player *_p,
const QString &_name,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent = nullptr);
QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void reorganizeCards() override;
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint) override;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void addCardImpl(CardItem *card, int x, int y) override;
};
#endif

View file

@ -0,0 +1,90 @@
#include "select_zone.h"
#include "../../settings/cache_settings.h"
#include "../cards/card_item.h"
#include "../game_scene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal cardHeight, bool reverse)
{
qreal cardMinOverlap = cardHeight * SettingsCache::instance().getStackCardOverlapPercent() / 100;
qreal desiredHeight = cardHeight * cardCount - cardMinOverlap * (cardCount - 1);
qreal y;
if (desiredHeight > totalHeight) {
if (reverse) {
y = index / ((totalHeight - cardHeight) / (cardCount - 1));
} else {
y = index * (totalHeight - cardHeight) / (cardCount - 1);
}
} else {
qreal start = (totalHeight - desiredHeight) / 2;
if (reverse) {
if (index <= start) {
return 0;
}
y = (index - start) / (cardHeight - cardMinOverlap);
} else {
y = index * (cardHeight - cardMinOverlap) + start;
}
}
return y;
}
SelectZone::SelectZone(Player *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent,
bool isView)
: CardZone(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent, isView)
{
}
void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (event->buttons().testFlag(Qt::LeftButton)) {
QPointF pos = event->pos();
if (pos.x() < 0)
pos.setX(0);
QRectF br = boundingRect();
if (pos.x() > br.width())
pos.setX(br.width());
if (pos.y() < 0)
pos.setY(0);
if (pos.y() > br.height())
pos.setY(br.height());
QRectF selectionRect = QRectF(selectionOrigin, pos).normalized();
for (int i = 0; i < cards.size(); ++i) {
if (cards[i]->getAttachedTo())
if (cards[i]->getAttachedTo()->getZone() != this)
continue;
cards[i]->setSelected(selectionRect.intersects(cards[i]->mapRectToParent(cards[i]->boundingRect())));
}
static_cast<GameScene *>(scene())->resizeRubberBand(
deviceTransform(static_cast<GameScene *>(scene())->getViewportTransform()).map(pos));
event->accept();
}
}
void SelectZone::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
scene()->clearSelection();
selectionOrigin = event->pos();
static_cast<GameScene *>(scene())->startRubberBand(event->scenePos());
event->accept();
} else
CardZone::mousePressEvent(event);
}
void SelectZone::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
selectionOrigin = QPoint();
static_cast<GameScene *>(scene())->stopRubberBand();
event->accept();
}

View file

@ -0,0 +1,29 @@
#ifndef SELECTZONE_H
#define SELECTZONE_H
#include "card_zone.h"
class SelectZone : public CardZone
{
Q_OBJECT
private:
QPointF selectionOrigin;
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
public:
SelectZone(Player *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent = nullptr,
bool isView = false);
};
qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal cardHeight, bool reverse = false);
#endif

View file

@ -0,0 +1,123 @@
#include "stack_zone.h"
#include "../../client/ui/theme_manager.h"
#include "../../settings/cache_settings.h"
#include "../board/arrow_item.h"
#include "../cards/card_drag_item.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_move_card.pb.h"
#include <QPainter>
#include <QSet>
StackZone::StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent)
: SelectZone(_p, "stack", false, false, true, parent), zoneHeight(_zoneHeight)
{
connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg()));
updateBg();
setCacheMode(DeviceCoordinateCache);
}
void StackZone::updateBg()
{
update();
}
void StackZone::addCardImpl(CardItem *card, int x, int /*y*/)
{
// if x is negative set it to add at end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
if (!cards.getContentsKnown()) {
card->setId(-1);
card->setName();
}
card->setParentItem(this);
card->resetState();
card->setVisible(true);
card->update();
}
QRectF StackZone::boundingRect() const
{
return QRectF(0, 0, 100, zoneHeight);
}
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
QBrush brush = themeManager->getStackBgBrush();
if (player->getZoneId() > 0) {
// If the extra image is not found, load the default one
brush = themeManager->getExtraStackBgBrush(QString::number(player->getZoneId()), brush);
}
painter->fillRect(boundingRect(), brush);
}
void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint)
{
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(getName().toStdString());
int index;
if (cards.isEmpty()) {
index = 0;
} else {
const int cardCount = cards.size();
index = qRound(divideCardSpaceInZone(dropPoint.y(), cardCount, boundingRect().height(),
cards.at(0)->boundingRect().height(), true));
}
if (startZone == this) {
if (cards.at(index)->getId() == dragItems.at(0)->getId()) {
return;
}
}
cmd.set_x(index);
cmd.set_y(0);
for (CardDragItem *item : dragItems) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(item->getId());
}
player->sendGameCommand(cmd);
}
void StackZone::reorganizeCards()
{
if (!cards.isEmpty()) {
QSet<ArrowItem *> arrowsToUpdate;
const int cardCount = cards.size();
qreal totalWidth = boundingRect().width();
qreal cardWidth = cards.at(0)->boundingRect().width();
qreal xspace = 5;
qreal x1 = xspace;
qreal x2 = totalWidth - xspace - cardWidth;
for (int i = 0; i < cardCount; i++) {
CardItem *card = cards.at(i);
qreal x = (i % 2) ? x2 : x1;
qreal y =
divideCardSpaceInZone(i, cardCount, boundingRect().height(), cards.at(0)->boundingRect().height());
card->setPos(x, y);
card->setRealZValue(i);
for (ArrowItem *item : card->getArrowsFrom()) {
arrowsToUpdate.insert(item);
}
for (ArrowItem *item : card->getArrowsTo()) {
arrowsToUpdate.insert(item);
}
}
for (ArrowItem *item : arrowsToUpdate) {
item->updatePath();
}
}
update();
}

View file

@ -0,0 +1,25 @@
#ifndef STACKZONE_H
#define STACKZONE_H
#include "select_zone.h"
class StackZone : public SelectZone
{
Q_OBJECT
private:
qreal zoneHeight;
private slots:
void updateBg();
public:
StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent = nullptr);
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void reorganizeCards();
protected:
void addCardImpl(CardItem *card, int x, int y);
};
#endif

View file

@ -0,0 +1,404 @@
#include "table_zone.h"
#include "../../client/ui/theme_manager.h"
#include "../../settings/cache_settings.h"
#include "../board/arrow_item.h"
#include "../cards/card_database.h"
#include "../cards/card_drag_item.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_move_card.pb.h"
#include "pb/command_set_card_attr.pb.h"
#include <QGraphicsScene>
#include <QPainter>
#include <QSet>
const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100);
const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80);
const QColor TableZone::GRADIENT_COLOR = QColor(255, 255, 255, 150);
const QColor TableZone::GRADIENT_COLORLESS = QColor(255, 255, 255, 0);
TableZone::TableZone(Player *_p, QGraphicsItem *parent)
: SelectZone(_p, "table", true, false, true, parent), active(false)
{
connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg()));
connect(&SettingsCache::instance(), SIGNAL(invertVerticalCoordinateChanged()), this, SLOT(reorganizeCards()));
updateBg();
height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CARD_HEIGHT + (TABLEROWS - 1) * PADDING_Y;
width = MIN_WIDTH;
currentMinimumWidth = width;
setCacheMode(DeviceCoordinateCache);
setAcceptHoverEvents(true);
}
void TableZone::updateBg()
{
update();
}
QRectF TableZone::boundingRect() const
{
return QRectF(0, 0, width, height);
}
bool TableZone::isInverted() const
{
return ((player->getMirrored() && !SettingsCache::instance().getInvertVerticalCoordinate()) ||
(!player->getMirrored() && SettingsCache::instance().getInvertVerticalCoordinate()));
}
void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
QBrush brush = themeManager->getTableBgBrush();
if (player->getZoneId() > 0) {
// If the extra image is not found, load the default one
brush = themeManager->getExtraTableBgBrush(QString::number(player->getZoneId()), brush);
}
painter->fillRect(boundingRect(), brush);
if (active) {
paintZoneOutline(painter);
} else {
// inactive player gets a darker table zone with a semi transparent black mask
// this means if the user provides a custom background it will fade
painter->fillRect(boundingRect(), FADE_MASK);
}
paintLandDivider(painter);
}
/**
Render a soft outline around the edge of the TableZone.
@param painter QPainter object
*/
void TableZone::paintZoneOutline(QPainter *painter)
{
QLinearGradient grad1(0, 0, 0, 1);
grad1.setCoordinateMode(QGradient::ObjectBoundingMode);
grad1.setColorAt(0, GRADIENT_COLOR);
grad1.setColorAt(1, GRADIENT_COLORLESS);
painter->fillRect(QRectF(0, 0, width, BOX_LINE_WIDTH), QBrush(grad1));
grad1.setFinalStop(1, 0);
painter->fillRect(QRectF(0, 0, BOX_LINE_WIDTH, height), QBrush(grad1));
grad1.setStart(0, 1);
grad1.setFinalStop(0, 0);
painter->fillRect(QRectF(0, height - BOX_LINE_WIDTH, width, BOX_LINE_WIDTH), QBrush(grad1));
grad1.setStart(1, 0);
painter->fillRect(QRectF(width - BOX_LINE_WIDTH, 0, BOX_LINE_WIDTH, height), QBrush(grad1));
}
/**
Render a division line for land placement
@painter QPainter object
*/
void TableZone::paintLandDivider(QPainter *painter)
{
// 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));
}
void TableZone::addCardImpl(CardItem *card, int _x, int _y)
{
cards.append(card);
card->setGridPoint(QPoint(_x, _y));
card->setParentItem(this);
card->setVisible(true);
card->update();
}
void TableZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint)
{
handleDropEventByGrid(dragItems, startZone, mapToGrid(dropPoint));
}
void TableZone::handleDropEventByGrid(const QList<CardDragItem *> &dragItems,
CardZone *startZone,
const QPoint &gridPoint)
{
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(getName().toStdString());
cmd.set_x(gridPoint.x());
cmd.set_y(gridPoint.y());
for (const auto &item : dragItems) {
CardToMove *ctm = cmd.mutable_cards_to_move()->add_card();
ctm->set_card_id(item->getId());
ctm->set_face_down(item->getFaceDown());
if (startZone->getName() != name && !item->getFaceDown()) {
const auto &info = item->getItem()->getInfo();
if (info) {
ctm->set_pt(info->getPowTough().toStdString());
}
}
}
startZone->getPlayer()->sendGameCommand(cmd);
}
void TableZone::reorganizeCards()
{
QSet<ArrowItem *> arrowsToUpdate;
// 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)
continue;
QPointF mapPoint = mapFromGrid(gridPoint);
qreal x = mapPoint.x();
qreal y = mapPoint.y();
int numberAttachedCards = cards[i]->getAttachedCards().size();
qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X;
qreal actualY = y;
if (numberAttachedCards)
actualY += 15;
cards[i]->setPos(actualX, actualY);
cards[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100);
QListIterator<CardItem *> attachedCardIterator(cards[i]->getAttachedCards());
int j = 0;
while (attachedCardIterator.hasNext()) {
++j;
CardItem *attachedCard = attachedCardIterator.next();
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);
for (ArrowItem *item : attachedCard->getArrowsFrom()) {
arrowsToUpdate.insert(item);
}
for (ArrowItem *item : attachedCard->getArrowsTo()) {
arrowsToUpdate.insert(item);
}
}
for (ArrowItem *item : cards[i]->getArrowsFrom()) {
arrowsToUpdate.insert(item);
}
for (ArrowItem *item : cards[i]->getArrowsTo()) {
arrowsToUpdate.insert(item);
}
}
for (ArrowItem *item : arrowsToUpdate) {
item->updatePath();
}
resizeToContents();
update();
}
void TableZone::toggleTapped()
{
QList<QGraphicsItem *> selectedItems = scene()->selectedItems();
bool tapAll = false;
for (int i = 0; i < selectedItems.size(); i++)
if (!qgraphicsitem_cast<CardItem *>(selectedItems[i])->getTapped()) {
tapAll = true;
break;
}
QList<const ::google::protobuf::Message *> cmdList;
for (int i = 0; i < selectedItems.size(); i++) {
CardItem *temp = qgraphicsitem_cast<CardItem *>(selectedItems[i]);
if (temp->getTapped() != tapAll) {
Command_SetCardAttr *cmd = new Command_SetCardAttr;
cmd->set_zone(name.toStdString());
cmd->set_card_id(temp->getId());
cmd->set_attribute(AttrTapped);
cmd->set_attr_value(tapAll ? "1" : "0");
cmdList.append(cmd);
}
}
player->sendGameCommand(player->prepareGameCommand(cmdList));
}
CardItem *TableZone::takeCard(int position, int cardId, bool canResize)
{
CardItem *result = CardZone::takeCard(position, cardId);
if (canResize)
resizeToContents();
return result;
}
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();
// 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;
emit sizeChanged();
}
}
CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const
{
for (int i = 0; i < cards.size(); i++)
if (cards.at(i)->getGridPoint() == gridPoint)
return cards.at(i);
return 0;
}
CardItem *TableZone::getCardFromCoords(const QPointF &point) const
{
QPoint gridPoint = mapToGrid(point);
return getCardFromGrid(gridPoint);
}
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<int, int> 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;
// 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) {
const int key = getCardStackMapKey(i, gridPoint.y());
x += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X;
}
if (isInverted())
gridPoint.setY(TABLEROWS - 1 - gridPoint.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
{
// Begin by calculating the y-coordinate of the grid space, which will be
// used for the x-coordinate.
// 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 + PADDING_Y / 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 + PADDING_X / 2;
// 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);
}
QPointF TableZone::closestGridPoint(const QPointF &point)
{
QPoint gridPoint = mapToGrid(point);
gridPoint.setX((gridPoint.x() / 3) * 3);
if (getCardFromGrid(gridPoint))
gridPoint.setX(gridPoint.x() + 1);
if (getCardFromGrid(gridPoint))
gridPoint.setX(gridPoint.x() + 1);
return mapFromGrid(gridPoint);
}
int TableZone::clampValidTableRow(const int row)
{
if (row < 0)
return 0;
if (row >= TABLEROWS)
return TABLEROWS - 1;
return row;
}

View file

@ -0,0 +1,207 @@
#ifndef TABLEZONE_H
#define TABLEZONE_H
#include "../cards/abstract_card_item.h"
#include "select_zone.h"
/*
* TableZone is the grid based rect where CardItems may be placed.
* It is the main play zone and can be customized with background images.
*
* TODO: Refactor methods to make more readable, extract some logic to
* private methods (Im looking at you TableZone::reorganizeCards())
*/
class TableZone : public SelectZone
{
Q_OBJECT
signals:
void sizeChanged();
private:
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 inactive mask and border gradient
*/
static const QColor BACKGROUND_COLOR;
static const QColor FADE_MASK;
static const QColor GRADIENT_COLOR;
static const QColor GRADIENT_COLORLESS;
/*
Size and shape variables
*/
int width;
int height;
int currentMinimumWidth;
/*
Internal cache for widths of stacks of cards by row and column.
*/
QMap<int, int> cardStackWidth;
/*
Holds any custom background image for the TableZone
*/
QPixmap backgroundPixelMap;
/*
If this TableZone is currently active
*/
bool active;
bool isInverted() const;
private slots:
/**
Loads in any found custom background and updates
*/
void updateBg();
public slots:
/**
Reorganizes CardItems in the TableZone
*/
void reorganizeCards();
public:
/**
Constructs TableZone.
@param _p the Player
@param parent defaults to null
*/
TableZone(Player *_p, QGraphicsItem *parent = nullptr);
/**
@return a QRectF of the TableZone bounding box.
*/
QRectF boundingRect() const;
/**
Render the TableZone
@param painter
@param option
*/
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
/**
Toggles the selected items as tapped.
*/
void toggleTapped();
/**
See HandleDropEventByGrid
*/
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint);
/**
Handles the placement of cards
*/
void handleDropEventByGrid(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &gridPoint);
/**
@return CardItem from grid location
*/
CardItem *getCardFromGrid(const QPoint &gridPoint) const;
/**
@return CardItem from coordinate location
*/
CardItem *getCardFromCoords(const QPointF &point) const;
QPointF closestGridPoint(const QPointF &point);
static int clampValidTableRow(const int row);
/**
Removes a card from view.
@param position card position
@param cardId id of card to take
@param canResize defaults to true
@return CardItem that has been removed
*/
CardItem *takeCard(int position, int cardId, bool canResize = true);
/**
Resizes the TableZone in case CardItems are within or
outside of the TableZone constraints.
*/
void resizeToContents();
int getMinimumWidth() const
{
return currentMinimumWidth;
}
void setWidth(qreal _width)
{
prepareGeometryChange();
width = _width;
}
qreal getWidth() const
{
return width;
}
void setActive(bool _active)
{
active = _active;
update();
}
protected:
void addCardImpl(CardItem *card, int x, int y);
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

View file

@ -0,0 +1,259 @@
#include "view_zone.h"
#include "../../server/pending_command.h"
#include "../cards/card_database.h"
#include "../cards/card_drag_item.h"
#include "../cards/card_item.h"
#include "../player/player.h"
#include "pb/command_dump_zone.pb.h"
#include "pb/command_move_card.pb.h"
#include "pb/response_dump_zone.pb.h"
#include "pb/serverinfo_card.pb.h"
#include <QBrush>
#include <QDebug>
#include <QGraphicsSceneWheelEvent>
#include <QPainter>
#include <QtMath>
ZoneViewZone::ZoneViewZone(Player *_p,
CardZone *_origZone,
int _numberCards,
bool _revealZone,
bool _writeableRevealZone,
QGraphicsItem *parent)
: SelectZone(_p, _origZone->getName(), false, false, true, parent, true), bRect(QRectF()), minRows(0),
numberCards(_numberCards), origZone(_origZone), revealZone(_revealZone),
writeableRevealZone(_writeableRevealZone), sortByName(false), sortByType(false)
{
if (!(revealZone && !writeableRevealZone)) {
origZone->getViews().append(this);
}
}
ZoneViewZone::~ZoneViewZone()
{
emit beingDeleted();
qDebug("ZoneViewZone destructor");
if (!(revealZone && !writeableRevealZone)) {
origZone->getViews().removeOne(this);
}
}
QRectF ZoneViewZone::boundingRect() const
{
return bRect;
}
void ZoneViewZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
QBrush windowBrush(QColor(240, 240, 240));
windowBrush.setColor(windowBrush.color().darker(150));
painter->fillRect(boundingRect(), windowBrush);
}
void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardList)
{
if (!cardList.isEmpty()) {
for (int i = 0; i < cardList.size(); ++i)
addCard(
new CardItem(player, QString::fromStdString(cardList[i]->name()), cardList[i]->id(), revealZone, this),
false, i);
reorganizeCards();
} else if (!origZone->contentsKnown()) {
Command_DumpZone cmd;
cmd.set_player_id(player->getId());
cmd.set_zone_name(name.toStdString());
cmd.set_number_cards(numberCards);
PendingCommand *pend = player->prepareGameCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(zoneDumpReceived(const Response &)));
player->sendGameCommand(pend);
} else {
const CardList &c = origZone->getCards();
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
for (int i = 0; i < number; i++) {
CardItem *card = c.at(i);
addCard(new CardItem(player, card->getName(), card->getId(), revealZone, this), false, i);
}
reorganizeCards();
}
}
void ZoneViewZone::zoneDumpReceived(const Response &r)
{
const Response_DumpZone &resp = r.GetExtension(Response_DumpZone::ext);
const int respCardListSize = resp.zone_info().card_list_size();
for (int i = 0; i < respCardListSize; ++i) {
const ServerInfo_Card &cardInfo = resp.zone_info().card_list(i);
auto cardName = QString::fromStdString(cardInfo.name());
auto *card = new CardItem(player, cardName, cardInfo.id(), revealZone, this, this);
cards.insert(i, card);
}
reorganizeCards();
emit cardCountChanged();
}
// Because of boundingRect(), this function must not be called before the zone was added to a scene.
void ZoneViewZone::reorganizeCards()
{
int cardCount = cards.size();
if (!origZone->contentsKnown())
for (int i = 0; i < cardCount; ++i)
cards[i]->setId(i);
int cols = qFloor(qSqrt((double)cardCount / 2));
if (cols > 7)
cols = 7;
int rows = qCeil((double)cardCount / cols);
if (rows < 1)
rows = 1;
if (minRows == 0)
minRows = rows;
else if (rows < minRows) {
rows = minRows;
cols = qCeil((double)cardCount / minRows);
}
if (cols < 2)
cols = 2;
qDebug() << "reorganizeCards: rows=" << rows << "cols=" << cols;
CardList cardsToDisplay(cards);
if (sortByName || sortByType)
cardsToDisplay.sort((sortByName ? CardList::SortByName : 0) | (sortByType ? CardList::SortByType : 0));
int typeColumn = 0;
int longestRow = 0;
if (pileView && sortByType) { // we need sort by type enabled for the feature to work
int typeRow = 0;
QString lastCardType;
for (int i = 0; i < cardCount; i++) {
CardItem *c = cardsToDisplay.at(i);
QString cardType = c->getInfo() ? c->getInfo()->getMainCardType() : "";
if (i) { // if not the first card
if (cardType == lastCardType)
typeRow++; // add below current card
else { // if no match then move card to next column
typeColumn++;
typeRow = 0;
}
}
lastCardType = cardType;
qreal x = 7 + (typeColumn * CARD_WIDTH);
qreal y = typeRow * CARD_HEIGHT / 3;
c->setPos(x + 5, y + 5);
c->setRealZValue(i);
longestRow = qMax(typeRow, longestRow);
}
} else {
for (int i = 0; i < cardCount; i++) {
CardItem *c = cardsToDisplay.at(i);
qreal x = 7 + ((i / rows) * CARD_WIDTH);
qreal y = (i % rows) * CARD_HEIGHT / 3;
c->setPos(x + 5, y + 5);
c->setRealZValue(i);
}
}
qreal aleft = 0;
qreal atop = 0;
qreal awidth = (pileView && sortByType) ? qMax(typeColumn + 1, 3) * CARD_WIDTH + (CARD_WIDTH / 2)
: qMax(cols, 1) * CARD_WIDTH + (CARD_WIDTH / 2);
qreal aheight = (pileView && sortByType) ? (longestRow * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3
: (rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3;
optimumRect = QRectF(aleft, atop, awidth, aheight);
updateGeometry();
emit optimumRectChanged();
}
void ZoneViewZone::setSortByName(int _sortByName)
{
sortByName = _sortByName;
reorganizeCards();
}
void ZoneViewZone::setSortByType(int _sortByType)
{
sortByType = _sortByType;
if (!sortByType)
pileView = false;
reorganizeCards();
}
void ZoneViewZone::setPileView(int _pileView)
{
pileView = _pileView;
reorganizeCards();
}
void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/)
{
// if x is negative set it to add at end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
card->setParentItem(this);
card->update();
reorganizeCards();
}
void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
CardZone *startZone,
const QPoint & /*dropPoint*/)
{
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(player->getId());
cmd.set_target_zone(getName().toStdString());
cmd.set_x(0);
cmd.set_y(0);
for (int i = 0; i < dragItems.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
player->sendGameCommand(cmd);
}
void ZoneViewZone::removeCard(int position)
{
if (position >= cards.size())
return;
CardItem *card = cards.takeAt(position);
card->deleteLater();
reorganizeCards();
}
void ZoneViewZone::setGeometry(const QRectF &rect)
{
prepareGeometryChange();
setPos(rect.x(), rect.y());
bRect = QRectF(0, 0, rect.width(), rect.height());
}
QSizeF ZoneViewZone::sizeHint(Qt::SizeHint /*which*/, const QSizeF & /*constraint*/) const
{
return optimumRect.size();
}
void ZoneViewZone::setWriteableRevealZone(bool _writeableRevealZone)
{
if (writeableRevealZone && !_writeableRevealZone) {
origZone->getViews().append(this);
} else if (!writeableRevealZone && _writeableRevealZone) {
origZone->getViews().removeOne(this);
}
writeableRevealZone = _writeableRevealZone;
}
void ZoneViewZone::wheelEvent(QGraphicsSceneWheelEvent *event)
{
emit wheelEventReceived(event);
}

View file

@ -0,0 +1,74 @@
#ifndef ZONEVIEWERZONE_H
#define ZONEVIEWERZONE_H
#include "select_zone.h"
#include <QGraphicsLayoutItem>
class ZoneViewWidget;
class Response;
class ServerInfo_Card;
class QGraphicsSceneWheelEvent;
class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsLayoutItem)
private:
QRectF bRect, optimumRect;
int minRows, numberCards;
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint);
CardZone *origZone;
bool revealZone, writeableRevealZone;
bool sortByName, sortByType;
bool pileView;
public:
ZoneViewZone(Player *_p,
CardZone *_origZone,
int _numberCards = -1,
bool _revealZone = false,
bool _writeableRevealZone = false,
QGraphicsItem *parent = nullptr);
~ZoneViewZone();
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void reorganizeCards();
void initializeCards(const QList<const ServerInfo_Card *> &cardList = QList<const ServerInfo_Card *>());
void removeCard(int position);
int getNumberCards() const
{
return numberCards;
}
void setGeometry(const QRectF &rect);
QRectF getOptimumRect() const
{
return optimumRect;
}
bool getRevealZone() const
{
return revealZone;
}
bool getWriteableRevealZone() const
{
return writeableRevealZone;
}
void setWriteableRevealZone(bool _writeableRevealZone);
public slots:
void setSortByName(int _sortByName);
void setSortByType(int _sortByType);
void setPileView(int _pileView);
private slots:
void zoneDumpReceived(const Response &r);
signals:
void beingDeleted();
void optimumRectChanged();
void wheelEventReceived(QGraphicsSceneWheelEvent *event);
protected:
void addCardImpl(CardItem *card, int x, int y);
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
void wheelEvent(QGraphicsSceneWheelEvent *event);
};
#endif

View file

@ -0,0 +1,220 @@
#include "view_zone_widget.h"
#include "../../settings/cache_settings.h"
#include "../cards/card_item.h"
#include "../game_scene.h"
#include "../player/player.h"
#include "pb/command_shuffle.pb.h"
#include "view_zone.h"
#include <QCheckBox>
#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsSceneMouseEvent>
#include <QLabel>
#include <QPainter>
#include <QScrollBar>
#include <QStyleOption>
#include <QStyleOptionTitleBar>
ZoneViewWidget::ZoneViewWidget(Player *_player,
CardZone *_origZone,
int numberCards,
bool _revealZone,
bool _writeableRevealZone,
const QList<const ServerInfo_Card *> &cardList)
: QGraphicsWidget(0, Qt::Window), canBeShuffled(_origZone->getIsShufflable()), player(_player)
{
setAcceptHoverEvents(true);
setAttribute(Qt::WA_DeleteOnClose);
setZValue(2000000006);
setFlag(ItemIgnoresTransformations);
QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical);
QGraphicsLinearLayout *hPilebox = 0;
if (numberCards < 0) {
hPilebox = new QGraphicsLinearLayout(Qt::Horizontal);
QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal);
QGraphicsProxyWidget *sortByNameProxy = new QGraphicsProxyWidget;
sortByNameProxy->setWidget(&sortByNameCheckBox);
hFilterbox->addItem(sortByNameProxy);
QGraphicsProxyWidget *sortByTypeProxy = new QGraphicsProxyWidget;
sortByTypeProxy->setWidget(&sortByTypeCheckBox);
hFilterbox->addItem(sortByTypeProxy);
vbox->addItem(hFilterbox);
QGraphicsProxyWidget *lineProxy = new QGraphicsProxyWidget;
QFrame *line = new QFrame;
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
lineProxy->setWidget(line);
vbox->addItem(lineProxy);
QGraphicsProxyWidget *pileViewProxy = new QGraphicsProxyWidget;
pileViewProxy->setWidget(&pileViewCheckBox);
hPilebox->addItem(pileViewProxy);
}
if (_origZone->getIsShufflable() && (numberCards == -1)) {
shuffleCheckBox.setChecked(true);
QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget;
shuffleProxy->setWidget(&shuffleCheckBox);
hPilebox->addItem(shuffleProxy);
}
vbox->addItem(hPilebox);
extraHeight = vbox->sizeHint(Qt::PreferredSize).height();
resize(150, 150);
QGraphicsLinearLayout *zoneHBox = new QGraphicsLinearLayout(Qt::Horizontal);
zoneContainer = new QGraphicsWidget(this);
zoneContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
zoneContainer->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
zoneHBox->addItem(zoneContainer);
scrollBar = new QScrollBar(Qt::Vertical);
scrollBar->setMinimum(0);
scrollBar->setSingleStep(20);
scrollBar->setPageStep(200);
connect(scrollBar, SIGNAL(valueChanged(int)), this, SLOT(handleScrollBarChange(int)));
scrollBarProxy = new ScrollableGraphicsProxyWidget;
scrollBarProxy->setWidget(scrollBar);
zoneHBox->addItem(scrollBarProxy);
vbox->addItem(zoneHBox);
zone = new ZoneViewZone(player, _origZone, numberCards, _revealZone, _writeableRevealZone, zoneContainer);
connect(zone, SIGNAL(wheelEventReceived(QGraphicsSceneWheelEvent *)), scrollBarProxy,
SLOT(recieveWheelEvent(QGraphicsSceneWheelEvent *)));
// numberCard is the num of cards we want to reveal from an area. Ex: scry the top 3 cards.
// If the number is < 0 then it means that we can make the area sorted and we dont care about the order.
if (numberCards < 0) {
connect(&sortByNameCheckBox, SIGNAL(stateChanged(int)), this, SLOT(processSortByName(int)));
connect(&sortByTypeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(processSortByType(int)));
connect(&pileViewCheckBox, SIGNAL(stateChanged(int)), this, SLOT(processSetPileView(int)));
sortByNameCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByName());
sortByTypeCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByType());
pileViewCheckBox.setChecked(SettingsCache::instance().getZoneViewPileView());
if (!SettingsCache::instance().getZoneViewSortByType())
pileViewCheckBox.setEnabled(false);
}
retranslateUi();
setLayout(vbox);
connect(zone, SIGNAL(optimumRectChanged()), this, SLOT(resizeToZoneContents()));
connect(zone, SIGNAL(beingDeleted()), this, SLOT(zoneDeleted()));
zone->initializeCards(cardList);
}
void ZoneViewWidget::processSortByType(int value)
{
pileViewCheckBox.setEnabled(value);
SettingsCache::instance().setZoneViewSortByType(value);
zone->setPileView(pileViewCheckBox.isChecked());
zone->setSortByType(value);
}
void ZoneViewWidget::processSortByName(int value)
{
SettingsCache::instance().setZoneViewSortByName(value);
zone->setSortByName(value);
}
void ZoneViewWidget::processSetPileView(int value)
{
SettingsCache::instance().setZoneViewPileView(value);
zone->setPileView(value);
}
void ZoneViewWidget::retranslateUi()
{
setWindowTitle(zone->getTranslatedName(false, CaseNominative));
sortByNameCheckBox.setText(tr("sort by name"));
sortByTypeCheckBox.setText(tr("sort by type"));
shuffleCheckBox.setText(tr("shuffle when closing"));
pileViewCheckBox.setText(tr("pile view"));
}
void ZoneViewWidget::moveEvent(QGraphicsSceneMoveEvent * /* event */)
{
if (!scene())
return;
int titleBarHeight = 24;
QPointF scenePos = pos();
if (scenePos.x() < 0) {
scenePos.setX(0);
} else {
qreal maxw = scene()->sceneRect().width() - 100;
if (scenePos.x() > maxw)
scenePos.setX(maxw);
}
if (scenePos.y() < titleBarHeight) {
scenePos.setY(titleBarHeight);
} else {
qreal maxh = scene()->sceneRect().height() - titleBarHeight;
if (scenePos.y() > maxh)
scenePos.setY(maxh);
}
if (scenePos != pos())
setPos(scenePos);
}
void ZoneViewWidget::resizeToZoneContents()
{
QRectF zoneRect = zone->getOptimumRect();
qreal totalZoneHeight = zoneRect.height();
if (zoneRect.height() > 500)
zoneRect.setHeight(500);
QSizeF newSize(qMax(QGraphicsWidget::layout()->effectiveSizeHint(Qt::MinimumSize, QSizeF()).width(),
zoneRect.width() + scrollBar->width() + 10),
zoneRect.height() + extraHeight + 10);
setMaximumSize(newSize);
resize(newSize);
zone->setGeometry(QRectF(0, -scrollBar->value(), zoneContainer->size().width(), totalZoneHeight));
scrollBar->setMaximum(totalZoneHeight - zoneRect.height());
if (layout())
layout()->invalidate();
}
void ZoneViewWidget::handleScrollBarChange(int value)
{
zone->setY(-value);
}
void ZoneViewWidget::closeEvent(QCloseEvent *event)
{
disconnect(zone, SIGNAL(beingDeleted()), this, 0);
if (shuffleCheckBox.isChecked())
player->sendGameCommand(Command_Shuffle());
emit closePressed(this);
deleteLater();
event->accept();
}
void ZoneViewWidget::zoneDeleted()
{
emit closePressed(this);
deleteLater();
}
void ZoneViewWidget::initStyleOption(QStyleOption *option) const
{
QStyleOptionTitleBar *titleBar = qstyleoption_cast<QStyleOptionTitleBar *>(option);
if (titleBar)
titleBar->icon = QPixmap("theme:cockatrice");
}

View file

@ -0,0 +1,83 @@
#ifndef ZONEVIEWWIDGET_H
#define ZONEVIEWWIDGET_H
#include <QCheckBox>
#include <QGraphicsProxyWidget>
#include <QGraphicsWidget>
class QLabel;
class QPushButton;
class CardZone;
class ZoneViewZone;
class Player;
class CardDatabase;
class QScrollBar;
class QCheckBox;
class GameScene;
class ServerInfo_Card;
class QGraphicsSceneMouseEvent;
class QGraphicsSceneWheelEvent;
class QStyleOption;
class ScrollableGraphicsProxyWidget : public QGraphicsProxyWidget
{
Q_OBJECT
public slots:
void recieveWheelEvent(QGraphicsSceneWheelEvent *event)
{
wheelEvent(event);
}
};
class ZoneViewWidget : public QGraphicsWidget
{
Q_OBJECT
private:
ZoneViewZone *zone;
QGraphicsWidget *zoneContainer;
QPushButton *closeButton;
QScrollBar *scrollBar;
ScrollableGraphicsProxyWidget *scrollBarProxy;
QCheckBox sortByNameCheckBox;
QCheckBox sortByTypeCheckBox;
QCheckBox shuffleCheckBox;
QCheckBox pileViewCheckBox;
bool canBeShuffled;
int extraHeight;
Player *player;
signals:
void closePressed(ZoneViewWidget *zv);
private slots:
void processSortByType(int value);
void processSortByName(int value);
void processSetPileView(int value);
void resizeToZoneContents();
void handleScrollBarChange(int value);
void zoneDeleted();
void moveEvent(QGraphicsSceneMoveEvent * /* event */);
public:
ZoneViewWidget(Player *_player,
CardZone *_origZone,
int numberCards = 0,
bool _revealZone = false,
bool _writeableRevealZone = false,
const QList<const ServerInfo_Card *> &cardList = QList<const ServerInfo_Card *>());
ZoneViewZone *getZone() const
{
return zone;
}
Player *getPlayer() const
{
return player;
}
void retranslateUi();
protected:
void closeEvent(QCloseEvent *event);
void initStyleOption(QStyleOption *option) const;
};
#endif