1 #include "carditem.h"
2 
3 #include "arrowitem.h"
4 #include "carddatabase.h"
5 #include "carddragitem.h"
6 #include "cardzone.h"
7 #include "gamescene.h"
8 #include "main.h"
9 #include "pb/serverinfo_card.pb.h"
10 #include "player.h"
11 #include "settingscache.h"
12 #include "tab_game.h"
13 #include "tablezone.h"
14 #include "zoneviewzone.h"
15 
16 #include <QApplication>
17 #include <QGraphicsSceneMouseEvent>
18 #include <QMenu>
19 #include <QPainter>
20 
CardItem(Player * _owner,const QString & _name,int _cardid,bool _revealedCard,QGraphicsItem * parent)21 CardItem::CardItem(Player *_owner, const QString &_name, int _cardid, bool _revealedCard, QGraphicsItem *parent)
22     : AbstractCardItem(_name, _owner, _cardid, parent), zone(0), revealedCard(_revealedCard), attacking(false),
23       destroyOnZoneChange(false), doesntUntap(false), dragItem(0), attachedTo(0)
24 {
25     owner->addCard(this);
26 
27     cardMenu = new QMenu;
28     ptMenu = new QMenu;
29     moveMenu = new QMenu;
30 
31     retranslateUi();
32     emit updateCardMenu(this);
33 }
34 
~CardItem()35 CardItem::~CardItem()
36 {
37     prepareDelete();
38 
39     if (scene())
40         static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
41 
42     delete cardMenu;
43     delete ptMenu;
44     delete moveMenu;
45 
46     deleteDragItem();
47 }
48 
prepareDelete()49 void CardItem::prepareDelete()
50 {
51     if (owner != nullptr) {
52         if (owner->getCardMenu() == cardMenu) {
53             owner->setCardMenu(nullptr);
54             owner->getGame()->setActiveCard(nullptr);
55         }
56         owner = nullptr;
57     }
58 
59     while (!attachedCards.isEmpty()) {
60         attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
61         attachedCards.first()->setAttachedTo(nullptr);
62     }
63 
64     if (attachedTo != nullptr) {
65         attachedTo->removeAttachedCard(this);
66         attachedTo = nullptr;
67     }
68 }
69 
deleteLater()70 void CardItem::deleteLater()
71 {
72     prepareDelete();
73     AbstractCardItem::deleteLater();
74 }
75 
setZone(CardZone * _zone)76 void CardItem::setZone(CardZone *_zone)
77 {
78     zone = _zone;
79     emit updateCardMenu(this);
80 }
81 
retranslateUi()82 void CardItem::retranslateUi()
83 {
84     moveMenu->setTitle(tr("&Move to"));
85     ptMenu->setTitle(tr("&Power / toughness"));
86 }
87 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)88 void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
89 {
90     painter->save();
91     AbstractCardItem::paint(painter, option, widget);
92 
93     int i = 0;
94     QMapIterator<int, int> counterIterator(counters);
95     while (counterIterator.hasNext()) {
96         counterIterator.next();
97         QColor color;
98         color.setHsv(counterIterator.key() * 60, 150, 255);
99 
100         paintNumberEllipse(counterIterator.value(), 14, color, i, counters.size(), painter);
101         ++i;
102     }
103 
104     QSizeF translatedSize = getTranslatedSize(painter);
105     qreal scaleFactor = translatedSize.width() / boundingRect().width();
106 
107     if (!pt.isEmpty()) {
108         painter->save();
109         transformPainter(painter, translatedSize, tapAngle);
110 
111         if (!getFaceDown() && info && pt == info->getPowTough()) {
112             painter->setPen(Qt::white);
113         } else {
114             painter->setPen(QColor(255, 150, 0)); // dark orange
115         }
116 
117         painter->setBackground(Qt::black);
118         painter->setBackgroundMode(Qt::OpaqueMode);
119 
120         painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 10 * scaleFactor,
121                                  translatedSize.height() - 8 * scaleFactor),
122                           Qt::AlignRight | Qt::AlignBottom, pt);
123         painter->restore();
124     }
125 
126     if (!annotation.isEmpty()) {
127         painter->save();
128 
129         transformPainter(painter, translatedSize, tapAngle);
130         painter->setBackground(Qt::black);
131         painter->setBackgroundMode(Qt::OpaqueMode);
132         painter->setPen(Qt::white);
133 
134         painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 8 * scaleFactor,
135                                  translatedSize.height() - 8 * scaleFactor),
136                           Qt::AlignCenter | Qt::TextWrapAnywhere, annotation);
137         painter->restore();
138     }
139 
140     if (getBeingPointedAt()) {
141         painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100)));
142     }
143 
144     if (doesntUntap) {
145         painter->save();
146 
147         painter->setRenderHint(QPainter::Antialiasing, false);
148         transformPainter(painter, translatedSize, tapAngle);
149 
150         QPen pen;
151         pen.setColor(Qt::magenta);
152         const int penWidth = 1;
153         pen.setWidth(penWidth);
154         painter->setPen(pen);
155         painter->drawRect(QRectF(0, 0, translatedSize.width() + penWidth, translatedSize.height() - penWidth));
156 
157         painter->restore();
158     }
159 
160     painter->restore();
161 }
162 
setAttacking(bool _attacking)163 void CardItem::setAttacking(bool _attacking)
164 {
165     attacking = _attacking;
166     update();
167 }
168 
setCounter(int _id,int _value)169 void CardItem::setCounter(int _id, int _value)
170 {
171     if (_value)
172         counters.insert(_id, _value);
173     else
174         counters.remove(_id);
175     update();
176 }
177 
setAnnotation(const QString & _annotation)178 void CardItem::setAnnotation(const QString &_annotation)
179 {
180     annotation = _annotation;
181     update();
182 }
183 
setDoesntUntap(bool _doesntUntap)184 void CardItem::setDoesntUntap(bool _doesntUntap)
185 {
186     doesntUntap = _doesntUntap;
187     update();
188 }
189 
setPT(const QString & _pt)190 void CardItem::setPT(const QString &_pt)
191 {
192     pt = _pt;
193     update();
194 }
195 
setAttachedTo(CardItem * _attachedTo)196 void CardItem::setAttachedTo(CardItem *_attachedTo)
197 {
198     if (attachedTo)
199         attachedTo->removeAttachedCard(this);
200 
201     gridPoint.setX(-1);
202     attachedTo = _attachedTo;
203     if (attachedTo) {
204         setParentItem(attachedTo->getZone());
205         attachedTo->addAttachedCard(this);
206         if (zone != attachedTo->getZone())
207             attachedTo->getZone()->reorganizeCards();
208     } else
209         setParentItem(zone);
210 
211     if (zone)
212         zone->reorganizeCards();
213 
214     emit updateCardMenu(this);
215 }
216 
resetState()217 void CardItem::resetState()
218 {
219     attacking = false;
220     facedown = false;
221     counters.clear();
222     pt.clear();
223     annotation.clear();
224     attachedTo = 0;
225     attachedCards.clear();
226     setTapped(false, false);
227     setDoesntUntap(false);
228     if (scene())
229         static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
230     update();
231 }
232 
processCardInfo(const ServerInfo_Card & info)233 void CardItem::processCardInfo(const ServerInfo_Card &info)
234 {
235     counters.clear();
236     const int counterListSize = info.counter_list_size();
237     for (int i = 0; i < counterListSize; ++i) {
238         const ServerInfo_CardCounter &counterInfo = info.counter_list(i);
239         counters.insert(counterInfo.id(), counterInfo.value());
240     }
241 
242     setId(info.id());
243     setName(QString::fromStdString(info.name()));
244     setAttacking(info.attacking());
245     setFaceDown(info.face_down());
246     setPT(QString::fromStdString(info.pt()));
247     setAnnotation(QString::fromStdString(info.annotation()));
248     setColor(QString::fromStdString(info.color()));
249     setTapped(info.tapped());
250     setDestroyOnZoneChange(info.destroy_on_zone_change());
251     setDoesntUntap(info.doesnt_untap());
252 }
253 
createDragItem(int _id,const QPointF & _pos,const QPointF & _scenePos,bool faceDown)254 CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown)
255 {
256     deleteDragItem();
257     dragItem = new CardDragItem(this, _id, _pos, faceDown);
258     dragItem->setVisible(false);
259     scene()->addItem(dragItem);
260     dragItem->updatePosition(_scenePos);
261     dragItem->setVisible(true);
262 
263     return dragItem;
264 }
265 
deleteDragItem()266 void CardItem::deleteDragItem()
267 {
268     if (dragItem) {
269         dragItem->deleteLater();
270     }
271     dragItem = nullptr;
272 }
273 
drawArrow(const QColor & arrowColor)274 void CardItem::drawArrow(const QColor &arrowColor)
275 {
276     if (static_cast<TabGame *>(owner->parent())->getSpectator())
277         return;
278 
279     Player *arrowOwner = static_cast<TabGame *>(owner->parent())->getActiveLocalPlayer();
280     ArrowDragItem *arrow = new ArrowDragItem(arrowOwner, this, arrowColor);
281     scene()->addItem(arrow);
282     arrow->grabMouse();
283 
284     QListIterator<QGraphicsItem *> itemIterator(scene()->selectedItems());
285     while (itemIterator.hasNext()) {
286         CardItem *c = qgraphicsitem_cast<CardItem *>(itemIterator.next());
287         if (!c || (c == this))
288             continue;
289         if (c->getZone() != zone)
290             continue;
291 
292         ArrowDragItem *childArrow = new ArrowDragItem(arrowOwner, c, arrowColor);
293         scene()->addItem(childArrow);
294         arrow->addChildArrow(childArrow);
295     }
296 }
297 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)298 void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
299 {
300     if (event->buttons().testFlag(Qt::RightButton)) {
301         if ((event->screenPos() - event->buttonDownScreenPos(Qt::RightButton)).manhattanLength() <
302             2 * QApplication::startDragDistance())
303             return;
304 
305         QColor arrowColor = Qt::red;
306         if (event->modifiers().testFlag(Qt::ControlModifier))
307             arrowColor = Qt::yellow;
308         else if (event->modifiers().testFlag(Qt::AltModifier))
309             arrowColor = Qt::blue;
310         else if (event->modifiers().testFlag(Qt::ShiftModifier))
311             arrowColor = Qt::green;
312 
313         drawArrow(arrowColor);
314     } else if (event->buttons().testFlag(Qt::LeftButton)) {
315         if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
316             2 * QApplication::startDragDistance())
317             return;
318         if (zone->getIsView()) {
319             const ZoneViewZone *view = static_cast<const ZoneViewZone *>(zone);
320             if (view->getRevealZone() && !view->getWriteableRevealZone())
321                 return;
322         } else if (!owner->getLocalOrJudge())
323             return;
324 
325         bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
326 
327         createDragItem(id, event->pos(), event->scenePos(), facedown || forceFaceDown);
328         dragItem->grabMouse();
329 
330         QList<QGraphicsItem *> sel = scene()->selectedItems();
331         int j = 0;
332         for (int i = 0; i < sel.size(); i++) {
333             CardItem *c = static_cast<CardItem *>(sel.at(i));
334             if ((c == this) || (c->getZone() != zone))
335                 continue;
336             ++j;
337             QPointF childPos;
338             if (zone->getHasCardAttr())
339                 childPos = c->pos() - pos();
340             else
341                 childPos = QPointF(j * CARD_WIDTH / 2, 0);
342             CardDragItem *drag = new CardDragItem(c, c->getId(), childPos, c->getFaceDown() || forceFaceDown, dragItem);
343             drag->setPos(dragItem->pos() + childPos);
344             scene()->addItem(drag);
345         }
346     }
347     setCursor(Qt::OpenHandCursor);
348 }
349 
playCard(bool faceDown)350 void CardItem::playCard(bool faceDown)
351 {
352     // Do nothing if the card belongs to another player
353     if (!owner->getLocalOrJudge())
354         return;
355 
356     TableZone *tz = qobject_cast<TableZone *>(zone);
357     if (tz)
358         tz->toggleTapped();
359     else
360         zone->getPlayer()->playCard(this, faceDown, info ? info->getCipt() : false);
361 }
362 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)363 void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
364 {
365     if (event->button() == Qt::RightButton) {
366         if (cardMenu && !cardMenu->isEmpty() && owner != nullptr) {
367             owner->updateCardMenu(this);
368             cardMenu->exec(event->screenPos());
369         }
370     } else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) &&
371                (!SettingsCache::instance().getDoubleClickToPlay())) {
372         bool hideCard = false;
373         if (zone && zone->getIsView()) {
374             auto *view = static_cast<ZoneViewZone *>(zone);
375             if (view->getRevealZone() && !view->getWriteableRevealZone())
376                 hideCard = true;
377         }
378         if (zone && hideCard) {
379             zone->removeCard(this);
380         } else {
381             playCard(event->modifiers().testFlag(Qt::ShiftModifier));
382         }
383     }
384 
385     if (owner != nullptr){ // cards without owner will be deleted
386         setCursor(Qt::OpenHandCursor);
387     }
388     AbstractCardItem::mouseReleaseEvent(event);
389 }
390 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)391 void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
392 {
393     if ((event->modifiers() != Qt::AltModifier) && (SettingsCache::instance().getDoubleClickToPlay()) &&
394         (event->buttons() == Qt::LeftButton)) {
395         if (revealedCard)
396             zone->removeCard(this);
397         else
398             playCard(event->modifiers().testFlag(Qt::ShiftModifier));
399     }
400     event->accept();
401 }
402 
animationEvent()403 bool CardItem::animationEvent()
404 {
405     int rotation = ROTATION_DEGREES_PER_FRAME;
406     bool animationIncomplete = true;
407     if (!tapped)
408         rotation *= -1;
409 
410     tapAngle += rotation;
411     if (tapped && (tapAngle > 90)) {
412         tapAngle = 90;
413         animationIncomplete = false;
414     }
415     if (!tapped && (tapAngle < 0)) {
416         tapAngle = 0;
417         animationIncomplete = false;
418     }
419 
420     setTransform(QTransform()
421                      .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
422                      .rotate(tapAngle)
423                      .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
424     setHovered(false);
425     update();
426 
427     return animationIncomplete;
428 }
429 
itemChange(GraphicsItemChange change,const QVariant & value)430 QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
431 {
432     if ((change == ItemSelectedHasChanged) && owner) {
433         if (value == true) {
434             owner->setCardMenu(cardMenu);
435             owner->getGame()->setActiveCard(this);
436         } else if (owner->getCardMenu() == cardMenu) {
437             owner->setCardMenu(0);
438             owner->getGame()->setActiveCard(0);
439         }
440     }
441     return QGraphicsItem::itemChange(change, value);
442 }
443