mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
* [Cleanup] Unused #includes Took 44 minutes * [Cleanup] More unused #includes Took 55 minutes * [Cleanup] Include QSet Took 4 minutes * [Cleanup] Include QDebug in deck_list.cpp Took 3 minutes * [Cleanup] Include protocol stuff in servatrice_database_interface.h Took 3 minutes * [Cleanup] Include QDialogButtonBox Took 8 minutes * [Cleanup] Include QUrl Took 8 minutes * [Cleanup] Include QTextOption in header. Took 3 minutes * [Cleanup] Include QMap in user_list_manager.h Took 8 minutes * [Cleanup] Adjust qjson Took 8 minutes * [Cleanup] include button box. Took 3 minutes * [Cleanup] Redo fwd declarations. * [Cleanup] Redo last removed fwd declarations. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
366 lines
12 KiB
C++
366 lines
12 KiB
C++
#define _USE_MATH_DEFINES
|
|
#include "arrow_item.h"
|
|
|
|
#include "../../client/settings/cache_settings.h"
|
|
#include "../player/player.h"
|
|
#include "../player/player_actions.h"
|
|
#include "../player/player_target.h"
|
|
#include "../zones/card_zone.h"
|
|
#include "card_item.h"
|
|
|
|
#include <QDebug>
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QPainter>
|
|
#include <QtMath>
|
|
#include <libcockatrice/card/card_info.h>
|
|
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
|
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
|
|
#include <libcockatrice/utility/color.h>
|
|
|
|
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
|
|
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false),
|
|
color(_color), fullColor(true)
|
|
{
|
|
setZValue(2000000005);
|
|
|
|
if (startItem)
|
|
startItem->addArrowFrom(this);
|
|
if (targetItem)
|
|
targetItem->addArrowTo(this);
|
|
|
|
if (startItem && targetItem)
|
|
updatePath();
|
|
}
|
|
|
|
ArrowItem::~ArrowItem()
|
|
{
|
|
}
|
|
|
|
void ArrowItem::delArrow()
|
|
{
|
|
if (startItem) {
|
|
startItem->removeArrowFrom(this);
|
|
startItem = 0;
|
|
}
|
|
|
|
if (targetItem) {
|
|
targetItem->setBeingPointedAt(false);
|
|
targetItem->removeArrowTo(this);
|
|
targetItem = 0;
|
|
}
|
|
|
|
player->removeArrow(this);
|
|
deleteLater();
|
|
}
|
|
|
|
void ArrowItem::updatePath()
|
|
{
|
|
if (!targetItem)
|
|
return;
|
|
|
|
QPointF endPoint = targetItem->mapToScene(
|
|
QPointF(targetItem->boundingRect().width() / 2, targetItem->boundingRect().height() / 2));
|
|
updatePath(endPoint);
|
|
}
|
|
|
|
void ArrowItem::updatePath(const QPointF &endPoint)
|
|
{
|
|
const double arrowWidth = 15.0;
|
|
const double headWidth = 40.0;
|
|
const double headLength =
|
|
headWidth / qPow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++
|
|
const double phi = 15;
|
|
|
|
if (!startItem)
|
|
return;
|
|
|
|
QPointF startPoint =
|
|
startItem->mapToScene(QPointF(startItem->boundingRect().width() / 2, startItem->boundingRect().height() / 2));
|
|
QLineF line(startPoint, endPoint);
|
|
qreal lineLength = line.length();
|
|
|
|
prepareGeometryChange();
|
|
if (lineLength < 30)
|
|
path = QPainterPath();
|
|
else {
|
|
QPointF c(lineLength / 2, qTan(phi * M_PI / 180) * lineLength);
|
|
|
|
QPainterPath centerLine;
|
|
centerLine.moveTo(0, 0);
|
|
centerLine.quadTo(c, QPointF(lineLength, 0));
|
|
|
|
double percentage = 1 - headLength / lineLength;
|
|
QPointF arrowBodyEndPoint = centerLine.pointAtPercent(percentage);
|
|
QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001));
|
|
qreal alpha = testLine.angle() - 90;
|
|
QPointF endPoint1 =
|
|
arrowBodyEndPoint + arrowWidth / 2 * QPointF(qCos(alpha * M_PI / 180), -qSin(alpha * M_PI / 180));
|
|
QPointF endPoint2 =
|
|
arrowBodyEndPoint + arrowWidth / 2 * QPointF(-qCos(alpha * M_PI / 180), qSin(alpha * M_PI / 180));
|
|
QPointF point1 =
|
|
endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(qCos(alpha * M_PI / 180), -qSin(alpha * M_PI / 180));
|
|
QPointF point2 =
|
|
endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-qCos(alpha * M_PI / 180), qSin(alpha * M_PI / 180));
|
|
|
|
path = QPainterPath(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)));
|
|
path.quadTo(c, endPoint1);
|
|
path.lineTo(point1);
|
|
path.lineTo(QPointF(lineLength, 0));
|
|
path.lineTo(point2);
|
|
path.lineTo(endPoint2);
|
|
path.quadTo(c, arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)));
|
|
path.lineTo(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)));
|
|
}
|
|
|
|
setPos(startPoint);
|
|
setTransform(QTransform().rotate(-line.angle()));
|
|
}
|
|
|
|
void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
|
{
|
|
QColor paintColor(color);
|
|
if (fullColor)
|
|
paintColor.setAlpha(200);
|
|
else
|
|
paintColor.setAlpha(150);
|
|
painter->setBrush(paintColor);
|
|
painter->drawPath(path);
|
|
}
|
|
|
|
void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (!player->getPlayerInfo()->getLocal()) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
QList<QGraphicsItem *> colliding = scene()->items(event->scenePos());
|
|
for (QGraphicsItem *item : colliding) {
|
|
if (qgraphicsitem_cast<CardItem *>(item)) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
}
|
|
|
|
event->accept();
|
|
if (event->button() == Qt::RightButton) {
|
|
Command_DeleteArrow cmd;
|
|
cmd.set_arrow_id(id);
|
|
player->getPlayerActions()->sendGameCommand(cmd);
|
|
}
|
|
}
|
|
|
|
ArrowDragItem::ArrowDragItem(Player *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase)
|
|
: ArrowItem(_owner, -1, _startItem, 0, _color), deleteInPhase(_deleteInPhase)
|
|
{
|
|
}
|
|
|
|
void ArrowDragItem::addChildArrow(ArrowDragItem *childArrow)
|
|
{
|
|
childArrows.append(childArrow);
|
|
}
|
|
|
|
void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
// This ensures that if a mouse move event happens after a call to delArrow(),
|
|
// the event will be discarded as it would create some stray pointers.
|
|
if (targetLocked || !startItem)
|
|
return;
|
|
|
|
QPointF endPos = event->scenePos();
|
|
|
|
QList<QGraphicsItem *> colliding = scene()->items(endPos);
|
|
ArrowTarget *cursorItem = 0;
|
|
qreal cursorItemZ = -1;
|
|
for (int i = colliding.size() - 1; i >= 0; i--) {
|
|
if (qgraphicsitem_cast<PlayerTarget *>(colliding.at(i)) || qgraphicsitem_cast<CardItem *>(colliding.at(i))) {
|
|
if (colliding.at(i)->zValue() > cursorItemZ) {
|
|
cursorItem = static_cast<ArrowTarget *>(colliding.at(i));
|
|
cursorItemZ = cursorItem->zValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((cursorItem != targetItem) && targetItem) {
|
|
targetItem->setBeingPointedAt(false);
|
|
targetItem->removeArrowTo(this);
|
|
}
|
|
if (!cursorItem) {
|
|
fullColor = false;
|
|
targetItem = 0;
|
|
updatePath(endPos);
|
|
} else {
|
|
if (cursorItem != targetItem) {
|
|
fullColor = true;
|
|
if (cursorItem != startItem) {
|
|
cursorItem->setBeingPointedAt(true);
|
|
cursorItem->addArrowTo(this);
|
|
}
|
|
targetItem = cursorItem;
|
|
}
|
|
updatePath();
|
|
}
|
|
update();
|
|
|
|
for (ArrowDragItem *child : childArrows) {
|
|
child->mouseMoveEvent(event);
|
|
}
|
|
}
|
|
|
|
void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (!startItem)
|
|
return;
|
|
|
|
if (targetItem && (targetItem != startItem)) {
|
|
CardZoneLogic *startZone = static_cast<CardItem *>(startItem)->getZone();
|
|
// For now, we can safely assume that the start item is always a card.
|
|
// The target item can be a player as well.
|
|
CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem);
|
|
CardItem *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
|
|
|
|
Command_CreateArrow cmd;
|
|
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(color));
|
|
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
|
cmd.set_start_zone(startZone->getName().toStdString());
|
|
cmd.set_start_card_id(startCard->getId());
|
|
|
|
if (targetCard) {
|
|
CardZoneLogic *targetZone = targetCard->getZone();
|
|
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
|
|
cmd.set_target_zone(targetZone->getName().toStdString());
|
|
cmd.set_target_card_id(targetCard->getId());
|
|
} else { // failed to cast target to card, this means it's a player
|
|
PlayerTarget *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem);
|
|
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
|
|
}
|
|
|
|
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow
|
|
if (startZone->getName() == "hand") {
|
|
startCard->playCard(false);
|
|
CardInfoPtr ci = startCard->getCard().getCardPtr();
|
|
bool playToStack = SettingsCache::instance().getPlayToStack();
|
|
if (ci &&
|
|
((!playToStack && ci->getUiAttributes().tableRow == 3) ||
|
|
(playToStack && ci->getUiAttributes().tableRow != 0 && startCard->getZone()->getName() != "stack")))
|
|
cmd.set_start_zone("stack");
|
|
else
|
|
cmd.set_start_zone(playToStack ? "stack" : "table");
|
|
}
|
|
|
|
if (deleteInPhase != 0) {
|
|
cmd.set_delete_in_phase(deleteInPhase);
|
|
}
|
|
|
|
player->getPlayerActions()->sendGameCommand(cmd);
|
|
}
|
|
delArrow();
|
|
|
|
for (ArrowDragItem *child : childArrows) {
|
|
child->mouseReleaseEvent(event);
|
|
}
|
|
}
|
|
|
|
ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
|
|
: ArrowItem(_startItem->getOwner(), -1, _startItem, 0, Qt::green)
|
|
{
|
|
}
|
|
|
|
void ArrowAttachItem::addChildArrow(ArrowAttachItem *childArrow)
|
|
{
|
|
childArrows.append(childArrow);
|
|
}
|
|
|
|
void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (targetLocked || !startItem)
|
|
return;
|
|
|
|
QPointF endPos = event->scenePos();
|
|
|
|
QList<QGraphicsItem *> colliding = scene()->items(endPos);
|
|
ArrowTarget *cursorItem = 0;
|
|
qreal cursorItemZ = -1;
|
|
for (int i = colliding.size() - 1; i >= 0; i--) {
|
|
if (qgraphicsitem_cast<CardItem *>(colliding.at(i))) {
|
|
if (colliding.at(i)->zValue() > cursorItemZ) {
|
|
cursorItem = static_cast<ArrowTarget *>(colliding.at(i));
|
|
cursorItemZ = cursorItem->zValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((cursorItem != targetItem) && targetItem) {
|
|
targetItem->setBeingPointedAt(false);
|
|
}
|
|
if (!cursorItem) {
|
|
fullColor = false;
|
|
targetItem = 0;
|
|
updatePath(endPos);
|
|
} else {
|
|
fullColor = true;
|
|
if (cursorItem != startItem) {
|
|
cursorItem->setBeingPointedAt(true);
|
|
}
|
|
targetItem = cursorItem;
|
|
updatePath();
|
|
}
|
|
update();
|
|
|
|
for (ArrowAttachItem *child : childArrows) {
|
|
child->mouseMoveEvent(event);
|
|
}
|
|
}
|
|
|
|
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
|
|
{
|
|
// do nothing if target is already attached to another card or is not in play
|
|
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != "table") {
|
|
return;
|
|
}
|
|
|
|
CardZoneLogic *startZone = startCard->getZone();
|
|
CardZoneLogic *targetZone = targetCard->getZone();
|
|
|
|
// move card onto table first if attaching from some other zone
|
|
if (startZone->getName() != "table") {
|
|
player->getPlayerActions()->playCardToTable(startCard, false);
|
|
}
|
|
|
|
Command_AttachCard cmd;
|
|
cmd.set_start_zone("table");
|
|
cmd.set_card_id(startCard->getId());
|
|
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
|
|
cmd.set_target_zone(targetZone->getName().toStdString());
|
|
cmd.set_target_card_id(targetCard->getId());
|
|
|
|
player->getPlayerActions()->sendGameCommand(cmd);
|
|
}
|
|
|
|
void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (!startItem)
|
|
return;
|
|
|
|
// Attaching could move startItem under the current cursor position, causing all children to retarget to it right
|
|
// before they are processed. Prevent that.
|
|
for (ArrowAttachItem *child : childArrows) {
|
|
child->setTargetLocked(true);
|
|
}
|
|
|
|
if (targetItem && (targetItem != startItem)) {
|
|
auto startCard = qgraphicsitem_cast<CardItem *>(startItem);
|
|
auto targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
|
|
if (startCard && targetCard) {
|
|
attachCards(startCard, targetCard);
|
|
}
|
|
}
|
|
|
|
delArrow();
|
|
|
|
for (ArrowAttachItem *child : childArrows) {
|
|
child->mouseReleaseEvent(event);
|
|
}
|
|
}
|