1 #define _USE_MATH_DEFINES
2 #include "arrowitem.h"
3
4 #include "carddatabase.h"
5 #include "carditem.h"
6 #include "cardzone.h"
7 #include "color.h"
8 #include "pb/command_attach_card.pb.h"
9 #include "pb/command_create_arrow.pb.h"
10 #include "pb/command_delete_arrow.pb.h"
11 #include "player.h"
12 #include "playertarget.h"
13 #include "settingscache.h"
14
15 #include <QDebug>
16 #include <QGraphicsScene>
17 #include <QGraphicsSceneMouseEvent>
18 #include <QPainter>
19 #include <cmath>
20
ArrowItem(Player * _player,int _id,ArrowTarget * _startItem,ArrowTarget * _targetItem,const QColor & _color)21 ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
22 : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color),
23 fullColor(true)
24 {
25 qDebug() << "ArrowItem constructor: startItem=" << static_cast<QGraphicsItem *>(startItem);
26 setZValue(2000000005);
27
28 if (startItem)
29 startItem->addArrowFrom(this);
30 if (targetItem)
31 targetItem->addArrowTo(this);
32
33 if (startItem && targetItem)
34 updatePath();
35 }
36
~ArrowItem()37 ArrowItem::~ArrowItem()
38 {
39 qDebug() << "ArrowItem destructor";
40 }
41
delArrow()42 void ArrowItem::delArrow()
43 {
44 if (startItem) {
45 startItem->removeArrowFrom(this);
46 startItem = 0;
47 }
48
49 if (targetItem) {
50 targetItem->setBeingPointedAt(false);
51 targetItem->removeArrowTo(this);
52 targetItem = 0;
53 }
54
55 player->removeArrow(this);
56 deleteLater();
57 }
58
updatePath()59 void ArrowItem::updatePath()
60 {
61 if (!targetItem)
62 return;
63
64 QPointF endPoint = targetItem->mapToScene(
65 QPointF(targetItem->boundingRect().width() / 2, targetItem->boundingRect().height() / 2));
66 updatePath(endPoint);
67 }
68
updatePath(const QPointF & endPoint)69 void ArrowItem::updatePath(const QPointF &endPoint)
70 {
71 const double arrowWidth = 15.0;
72 const double headWidth = 40.0;
73 const double headLength =
74 headWidth / pow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++
75 const double phi = 15;
76
77 if (!startItem)
78 return;
79
80 QPointF startPoint =
81 startItem->mapToScene(QPointF(startItem->boundingRect().width() / 2, startItem->boundingRect().height() / 2));
82 QLineF line(startPoint, endPoint);
83 qreal lineLength = line.length();
84
85 prepareGeometryChange();
86 if (lineLength < 30)
87 path = QPainterPath();
88 else {
89 QPointF c(lineLength / 2, tan(phi * M_PI / 180) * lineLength);
90
91 QPainterPath centerLine;
92 centerLine.moveTo(0, 0);
93 centerLine.quadTo(c, QPointF(lineLength, 0));
94
95 double percentage = 1 - headLength / lineLength;
96 QPointF arrowBodyEndPoint = centerLine.pointAtPercent(percentage);
97 QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001));
98 qreal alpha = testLine.angle() - 90;
99 QPointF endPoint1 =
100 arrowBodyEndPoint + arrowWidth / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
101 QPointF endPoint2 =
102 arrowBodyEndPoint + arrowWidth / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
103 QPointF point1 =
104 endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
105 QPointF point2 =
106 endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
107
108 path = QPainterPath(-arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180)));
109 path.quadTo(c, endPoint1);
110 path.lineTo(point1);
111 path.lineTo(QPointF(lineLength, 0));
112 path.lineTo(point2);
113 path.lineTo(endPoint2);
114 path.quadTo(c, arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180)));
115 path.lineTo(-arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180)));
116 }
117
118 setPos(startPoint);
119 setTransform(QTransform().rotate(-line.angle()));
120 }
121
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)122 void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
123 {
124 QColor paintColor(color);
125 if (fullColor)
126 paintColor.setAlpha(200);
127 else
128 paintColor.setAlpha(150);
129 painter->setBrush(paintColor);
130 painter->drawPath(path);
131 }
132
mousePressEvent(QGraphicsSceneMouseEvent * event)133 void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
134 {
135 if (!player->getLocal()) {
136 event->ignore();
137 return;
138 }
139
140 QList<QGraphicsItem *> colliding = scene()->items(event->scenePos());
141 for (int i = 0; i < colliding.size(); ++i) {
142 if (qgraphicsitem_cast<CardItem *>(colliding[i])) {
143 event->ignore();
144 return;
145 }
146 }
147
148 event->accept();
149 if (event->button() == Qt::RightButton) {
150 Command_DeleteArrow cmd;
151 cmd.set_arrow_id(id);
152 player->sendGameCommand(cmd);
153 }
154 }
155
ArrowDragItem(Player * _owner,ArrowTarget * _startItem,const QColor & _color)156 ArrowDragItem::ArrowDragItem(Player *_owner, ArrowTarget *_startItem, const QColor &_color)
157 : ArrowItem(_owner, -1, _startItem, 0, _color)
158 {
159 }
160
addChildArrow(ArrowDragItem * childArrow)161 void ArrowDragItem::addChildArrow(ArrowDragItem *childArrow)
162 {
163 childArrows.append(childArrow);
164 }
165
mouseMoveEvent(QGraphicsSceneMouseEvent * event)166 void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
167 {
168 // This ensures that if a mouse move event happens after a call to delArrow(),
169 // the event will be discarded as it would create some stray pointers.
170 if (!startItem)
171 return;
172
173 QPointF endPos = event->scenePos();
174
175 QList<QGraphicsItem *> colliding = scene()->items(endPos);
176 ArrowTarget *cursorItem = 0;
177 qreal cursorItemZ = -1;
178 for (int i = colliding.size() - 1; i >= 0; i--) {
179 if (qgraphicsitem_cast<PlayerTarget *>(colliding.at(i)) || qgraphicsitem_cast<CardItem *>(colliding.at(i))) {
180 if (colliding.at(i)->zValue() > cursorItemZ) {
181 cursorItem = static_cast<ArrowTarget *>(colliding.at(i));
182 cursorItemZ = cursorItem->zValue();
183 }
184 }
185 }
186
187 if ((cursorItem != targetItem) && targetItem) {
188 targetItem->setBeingPointedAt(false);
189 targetItem->removeArrowTo(this);
190 }
191 if (!cursorItem) {
192 fullColor = false;
193 targetItem = 0;
194 updatePath(endPos);
195 } else {
196 if (cursorItem != targetItem) {
197 fullColor = true;
198 if (cursorItem != startItem) {
199 cursorItem->setBeingPointedAt(true);
200 cursorItem->addArrowTo(this);
201 }
202 targetItem = cursorItem;
203 }
204 updatePath();
205 }
206 update();
207
208 for (int i = 0; i < childArrows.size(); ++i) {
209 childArrows[i]->mouseMoveEvent(event);
210 }
211 }
212
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)213 void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
214 {
215 if (!startItem)
216 return;
217
218 if (targetItem && (targetItem != startItem)) {
219 CardZone *startZone = static_cast<CardItem *>(startItem)->getZone();
220 // For now, we can safely assume that the start item is always a card.
221 // The target item can be a player as well.
222 CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem);
223 CardItem *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
224
225 Command_CreateArrow cmd;
226 cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(color));
227 cmd.set_start_player_id(startZone->getPlayer()->getId());
228 cmd.set_start_zone(startZone->getName().toStdString());
229 cmd.set_start_card_id(startCard->getId());
230
231 if (targetCard) {
232 CardZone *targetZone = targetCard->getZone();
233 cmd.set_target_player_id(targetZone->getPlayer()->getId());
234 cmd.set_target_zone(targetZone->getName().toStdString());
235 cmd.set_target_card_id(targetCard->getId());
236 } else {
237 PlayerTarget *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem);
238 cmd.set_target_player_id(targetPlayer->getOwner()->getId());
239 }
240 if (startZone->getName().compare("hand") == 0) {
241 startCard->playCard(false);
242 CardInfoPtr ci = startCard->getInfo();
243 if (ci && (((!SettingsCache::instance().getPlayToStack() && ci->getTableRow() == 3) ||
244 ((SettingsCache::instance().getPlayToStack() && ci->getTableRow() != 0) &&
245 startCard->getZone()->getName().toStdString() != "stack"))))
246 cmd.set_start_zone("stack");
247 else
248 cmd.set_start_zone(SettingsCache::instance().getPlayToStack() ? "stack" : "table");
249 }
250 player->sendGameCommand(cmd);
251 }
252 delArrow();
253
254 for (int i = 0; i < childArrows.size(); ++i)
255 childArrows[i]->mouseReleaseEvent(event);
256 }
257
ArrowAttachItem(ArrowTarget * _startItem)258 ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
259 : ArrowItem(_startItem->getOwner(), -1, _startItem, 0, Qt::green)
260 {
261 }
262
mouseMoveEvent(QGraphicsSceneMouseEvent * event)263 void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
264 {
265 if (!startItem)
266 return;
267
268 QPointF endPos = event->scenePos();
269
270 QList<QGraphicsItem *> colliding = scene()->items(endPos);
271 ArrowTarget *cursorItem = 0;
272 qreal cursorItemZ = -1;
273 for (int i = colliding.size() - 1; i >= 0; i--) {
274 if (qgraphicsitem_cast<CardItem *>(colliding.at(i))) {
275 if (colliding.at(i)->zValue() > cursorItemZ) {
276 cursorItem = static_cast<ArrowTarget *>(colliding.at(i));
277 cursorItemZ = cursorItem->zValue();
278 }
279 }
280 }
281
282 if ((cursorItem != targetItem) && targetItem) {
283 targetItem->setBeingPointedAt(false);
284 }
285 if (!cursorItem) {
286 fullColor = false;
287 targetItem = 0;
288 updatePath(endPos);
289 } else {
290 fullColor = true;
291 if (cursorItem != startItem) {
292 cursorItem->setBeingPointedAt(true);
293 }
294 targetItem = cursorItem;
295 updatePath();
296 }
297 update();
298 }
299
mouseReleaseEvent(QGraphicsSceneMouseEvent *)300 void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
301 {
302 if (!startItem)
303 return;
304
305 if (targetItem && (targetItem != startItem)) {
306 CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem);
307 CardZone *startZone = startCard->getZone();
308 CardItem *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
309 CardZone *targetZone = targetCard->getZone();
310
311 Command_AttachCard cmd;
312 cmd.set_start_zone(startZone->getName().toStdString());
313 cmd.set_card_id(startCard->getId());
314 cmd.set_target_player_id(targetZone->getPlayer()->getId());
315 cmd.set_target_zone(targetZone->getName().toStdString());
316 cmd.set_target_card_id(targetCard->getId());
317
318 player->sendGameCommand(cmd);
319 }
320
321 delArrow();
322 }
323