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