[Game][Arrows] Split Arrows into ArrowData and ArrowItem (#6918)

* [Game][Arrows] Split Arrows into ArrowData and ArrowItem

Took 13 minutes

Took 5 seconds

Took 1 minute

Took 26 seconds

* Address comments.

Took 17 minutes

Took 9 seconds


Took 1 minute

* Change check.

Took 3 minutes

* Pass by const reference.

Took 10 minutes

* Remove extra method

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-05-21 20:31:14 +02:00 committed by GitHub
parent bddf9bd818
commit 491d1c9187
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 337 additions and 327 deletions

View file

@ -26,16 +26,23 @@ ArrowItem::ArrowItem(PlayerLogic *_player,
ArrowTarget *_startItem,
ArrowTarget *_targetItem,
const QColor &_color)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false),
color(_color), fullColor(true)
: player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color)
{
setZValue(ZValues::ARROWS);
auto doUpdate = [this]() {
if (startItem && targetItem) {
updatePath();
}
};
if (startItem) {
startItem->addArrowFrom(this);
connect(startItem, &ArrowTarget::scenePositionChanged, this, doUpdate);
connect(startItem, &QObject::destroyed, this, &ArrowItem::delArrow);
}
if (targetItem) {
targetItem->addArrowTo(this);
connect(targetItem, &ArrowTarget::scenePositionChanged, this, doUpdate);
connect(targetItem, &QObject::destroyed, this, &ArrowItem::delArrow);
}
if (startItem && targetItem) {
@ -43,24 +50,11 @@ ArrowItem::ArrowItem(PlayerLogic *_player,
}
}
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();
}
@ -148,8 +142,7 @@ void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
return;
}
QList<QGraphicsItem *> colliding = scene()->items(event->scenePos());
for (QGraphicsItem *item : colliding) {
for (auto *item : scene()->items(event->scenePos())) {
if (qgraphicsitem_cast<CardItem *>(item)) {
event->ignore();
return;
@ -164,60 +157,62 @@ void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
}
// ArrowDragItem
ArrowDragItem::ArrowDragItem(PlayerLogic *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase)
: ArrowItem(_owner, -1, _startItem, 0, _color), deleteInPhase(_deleteInPhase)
: ArrowItem(_owner, -1, _startItem, nullptr, _color), deleteInPhase(_deleteInPhase)
{
}
void ArrowDragItem::addChildArrow(ArrowDragItem *childArrow)
void ArrowDragItem::addChildArrow(ArrowDragItem *child)
{
childArrows.append(childArrow);
childArrows.append(child);
}
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();
const QPointF endPos = event->scenePos();
QList<QGraphicsItem *> colliding = scene()->items(endPos);
ArrowTarget *cursorItem = 0;
ArrowTarget *cursorItem = nullptr;
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();
}
for (auto *item : scene()->items(endPos)) {
ArrowTarget *candidate = nullptr;
if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
candidate = card;
} else if (auto *pt = qgraphicsitem_cast<PlayerTarget *>(item)) {
candidate = pt;
}
if (candidate && candidate->zValue() > cursorItemZ) {
cursorItem = candidate;
cursorItemZ = candidate->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;
if (cursorItem != targetItem) {
if (targetItem) {
disconnect(positionConnection);
targetItem->setBeingPointedAt(false);
}
targetItem = cursorItem;
fullColor = (cursorItem != nullptr);
if (cursorItem && cursorItem != startItem) {
cursorItem->setBeingPointedAt(true);
positionConnection =
connect(cursorItem, &ArrowTarget::scenePositionChanged, this, [this]() { updatePath(); });
}
updatePath();
}
targetItem ? updatePath() : updatePath(endPos);
update();
for (ArrowDragItem *child : childArrows) {
for (auto *child : childArrows) {
child->mouseMoveEvent(event);
}
}
@ -228,12 +223,16 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
return;
}
if (targetItem && (targetItem != startItem)) {
CardZoneLogic *startZone = static_cast<CardItem *>(startItem)->getZone();
if (targetItem && targetItem != startItem) {
CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem);
// 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);
if (!startCard) {
delArrow();
return;
}
CardZoneLogic *startZone = startCard->getZone();
Command_CreateArrow cmd;
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(color));
@ -241,14 +240,16 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_start_card_id(startCard->getId());
if (targetCard) {
if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
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);
} else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
} else {
delArrow();
return;
}
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow
@ -271,21 +272,22 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
player->getPlayerActions()->sendGameCommand(cmd);
}
delArrow();
for (ArrowDragItem *child : childArrows) {
delArrow();
for (auto *child : childArrows) {
child->mouseReleaseEvent(event);
}
}
// ArrowAttachItem
ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
: ArrowItem(_startItem->getOwner(), -1, _startItem, 0, Qt::green)
: ArrowItem(_startItem->getOwner(), -1, _startItem, nullptr, Qt::green)
{
}
void ArrowAttachItem::addChildArrow(ArrowAttachItem *childArrow)
void ArrowAttachItem::addChildArrow(ArrowAttachItem *child)
{
childArrows.append(childArrow);
childArrows.append(child);
}
void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@ -294,67 +296,43 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return;
}
QPointF endPos = event->scenePos();
const QPointF endPos = event->scenePos();
QList<QGraphicsItem *> colliding = scene()->items(endPos);
ArrowTarget *cursorItem = 0;
ArrowTarget *cursorItem = nullptr;
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();
for (auto *item : scene()->items(endPos)) {
if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
if (card->zValue() > cursorItemZ) {
cursorItem = card;
cursorItemZ = card->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);
if (cursorItem != targetItem) {
if (targetItem) {
disconnect(positionConnection);
targetItem->setBeingPointedAt(false);
}
targetItem = cursorItem;
updatePath();
fullColor = (cursorItem != nullptr);
if (cursorItem && cursorItem != startItem) {
cursorItem->setBeingPointedAt(true);
positionConnection =
connect(cursorItem, &ArrowTarget::scenePositionChanged, this, [this]() { updatePath(); });
}
}
targetItem ? updatePath() : updatePath(endPos);
update();
for (ArrowAttachItem *child : childArrows) {
for (auto *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() != ZoneNames::TABLE) {
return;
}
CardZoneLogic *startZone = startCard->getZone();
CardZoneLogic *targetZone = targetCard->getZone();
// move card onto table first if attaching from some other zone
if (startZone->getName() != ZoneNames::TABLE) {
player->getPlayerActions()->playCardToTable(startCard, false);
}
Command_AttachCard cmd;
cmd.set_start_zone(ZoneNames::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) {
@ -363,21 +341,40 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
// 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) {
for (auto *child : childArrows) {
child->setTargetLocked(true);
}
if (targetItem && (targetItem != startItem)) {
auto startCard = qgraphicsitem_cast<CardItem *>(startItem);
auto targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
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) {
for (auto *child : childArrows) {
child->mouseReleaseEvent(event);
}
}
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
{
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
return;
}
// move card onto table first if attaching from some other zone
if (startCard->getZone()->getName() != ZoneNames::TABLE) {
player->getPlayerActions()->playCardToTable(startCard, false);
}
Command_AttachCard cmd;
cmd.set_start_zone(ZoneNames::TABLE);
cmd.set_card_id(startCard->getId());
cmd.set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetCard->getZone()->getName().toStdString());
cmd.set_target_card_id(targetCard->getId());
player->getPlayerActions()->sendGameCommand(cmd);
}