mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-09 17:44:01 -07:00
* Add ZoneNames constants for protocol zone identifiers. Introduce a centralized ZoneNames namespace providing constexpr constants for zone identifiers used in the client-server protocol. This establishes a single source of truth for zone names like TABLE, GRAVE, EXILE, HAND, DECK, SIDEBOARD, and STACK. The protocol values remain unchanged (e.g., EXILE maps to rfg for backwards compatibility) while providing meaningful constant names. * refactor(server): use ZoneNames constants in server game logic Replace hardcoded zone name strings with ZoneNames:: constants in: - server_player.cpp: zone setup, draw, shuffle, mulligan operations - server_abstract_player.cpp: card movement and token destruction - server_game.cpp: returning cards when players leave No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in core player/zone logic Update the foundational player and zone classes to use ZoneNames:: constants instead of string literals. Changes include: - player.h/cpp: zone initialization and builtinZones set - card_zone_logic.cpp: zone name translation for UI display - table_zone.cpp: table zone operations No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in player actions and events Replace zone name strings with ZoneNames:: constants in the player action and event handling code. player_actions.cpp contains the most extensive changes (~90+ replacements) covering all card movement commands. No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in zone menu handlers Update all zone-specific menu files to use ZoneNames:: constants for QAction data values and zone targeting. This covers context menus for cards, graveyard, hand, and exile (RFG) zones. No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in game scene components Update remaining game scene components to use ZoneNames:: constants: - arrow_item.cpp: arrow drawing between cards - game_scene.cpp: zone view positioning - message_log_widget.cpp: removes duplicate local static constants that were previously defining zone names redundantly - phases_toolbar.cpp: phase actions (untap all) Notable: message_log_widget.cpp previously had its own local constants (TABLE_ZONE_NAME, GRAVE_ZONE_NAME, etc.) which are now removed in favor of the centralized ZoneNames:: constants. * formatting fix
216 lines
No EOL
7.6 KiB
C++
216 lines
No EOL
7.6 KiB
C++
#include "card_zone_logic.h"
|
|
|
|
#include "../../board/card_item.h"
|
|
#include "../../player/player.h"
|
|
#include "../../player/player_actions.h"
|
|
#include "../view_zone.h"
|
|
#include "view_zone_logic.h"
|
|
|
|
#include <QAction>
|
|
#include <QDebug>
|
|
#include <libcockatrice/card/database/card_database_manager.h>
|
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
|
#include <libcockatrice/utility/zone_names.h>
|
|
|
|
/**
|
|
* @param _player the player that the zone belongs to
|
|
* @param _name internal name of the zone
|
|
* @param _hasCardAttr whether cards in the zone can have attributes set on them
|
|
* @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it
|
|
* @param _contentsKnown whether the cards in the zone are known to the client
|
|
* @param parent the parent QObject.
|
|
*/
|
|
CardZoneLogic::CardZoneLogic(Player *_player,
|
|
const QString &_name,
|
|
bool _hasCardAttr,
|
|
bool _isShufflable,
|
|
bool _contentsKnown,
|
|
QObject *parent)
|
|
: QObject(parent), player(_player), name(_name), cards(_contentsKnown), views{}, hasCardAttr(_hasCardAttr),
|
|
isShufflable(_isShufflable)
|
|
{
|
|
// If we join a game before the card db finishes loading, the cards might have the wrong printings.
|
|
// Force refresh all cards in the zone when db finishes loading to fix that.
|
|
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
|
|
&CardZoneLogic::refreshCardInfos);
|
|
}
|
|
|
|
void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x, const int y)
|
|
{
|
|
if (!card) {
|
|
qCWarning(CardZoneLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen";
|
|
return;
|
|
}
|
|
|
|
for (auto *view : views) {
|
|
if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) {
|
|
auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId());
|
|
copy->setFaceDown(card->getFaceDown());
|
|
|
|
view->getLogic()->addCard(copy, reorganize, x, y);
|
|
}
|
|
}
|
|
|
|
card->setZone(this);
|
|
emit cardAdded(card);
|
|
addCardImpl(card, x, y);
|
|
|
|
if (reorganize)
|
|
emit reorganizeCards();
|
|
|
|
emit cardCountChanged();
|
|
}
|
|
|
|
CardItem *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone)
|
|
{
|
|
if (position == -1) {
|
|
// position == -1 means either that the zone is indexed by card id
|
|
// or that it doesn't matter which card you take.
|
|
for (int i = 0; i < cards.size(); ++i)
|
|
if (cards[i]->getId() == cardId) {
|
|
position = i;
|
|
break;
|
|
}
|
|
if (position == -1)
|
|
position = 0;
|
|
}
|
|
if (position >= cards.size())
|
|
return nullptr;
|
|
|
|
for (auto *view : views) {
|
|
qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->removeCard(position, toNewZone);
|
|
}
|
|
|
|
CardItem *c = cards.takeAt(position);
|
|
|
|
c->setId(cardId);
|
|
|
|
emit reorganizeCards();
|
|
emit cardCountChanged();
|
|
return c;
|
|
}
|
|
|
|
CardItem *CardZoneLogic::getCard(int cardId)
|
|
{
|
|
CardItem *c = cards.findCard(cardId);
|
|
if (!c) {
|
|
qCWarning(CardZoneLog) << "CardZoneLogic::getCard: card id=" << cardId << "not found";
|
|
return nullptr;
|
|
}
|
|
// If the card's id is -1, this zone is invisible,
|
|
// so we need to give the card an id as it comes out.
|
|
// It can be assumed that in an invisible zone, all cards are equal.
|
|
if (c->getId() == -1) {
|
|
c->setId(cardId);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void CardZoneLogic::removeCard(CardItem *card)
|
|
{
|
|
if (!card) {
|
|
qCWarning(CardZoneLog) << "CardZoneLogic::removeCard: card is null, this shouldn't normally happen";
|
|
return;
|
|
}
|
|
|
|
cards.removeOne(card);
|
|
|
|
emit reorganizeCards();
|
|
emit cardCountChanged();
|
|
player->deleteCard(card);
|
|
}
|
|
|
|
void CardZoneLogic::refreshCardInfos()
|
|
{
|
|
for (const auto &cardItem : cards) {
|
|
cardItem->refreshCardInfo();
|
|
}
|
|
}
|
|
|
|
void CardZoneLogic::moveAllToZone()
|
|
{
|
|
QList<QVariant> data = static_cast<QAction *>(sender())->data().toList();
|
|
if (data.length() < 2) {
|
|
return;
|
|
}
|
|
QString targetZone = data[0].toString();
|
|
int targetX = data[1].toInt();
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone(getName().toStdString());
|
|
cmd.set_target_player_id(player->getPlayerInfo()->getId());
|
|
cmd.set_target_zone(targetZone.toStdString());
|
|
cmd.set_x(targetX);
|
|
|
|
for (int i = 0; i < cards.size(); ++i)
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(cards[i]->getId());
|
|
|
|
player->getPlayerActions()->sendGameCommand(cmd);
|
|
}
|
|
|
|
void CardZoneLogic::clearContents()
|
|
{
|
|
// First gather the cards into a safe temporary list.
|
|
const CardList toClear = cards;
|
|
|
|
// Detach and notify attached cards and zones *before* deleting anything.
|
|
for (CardItem *card : toClear) {
|
|
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a
|
|
// player, we have to return them to avoid a crash.
|
|
const QList<CardItem *> &attachedCards = card->getAttachedCards();
|
|
for (CardItem *attachedCard : attachedCards) {
|
|
emit attachedCard->getZone()->cardAdded(attachedCard);
|
|
}
|
|
}
|
|
|
|
// Now request deletions after all manipulations are done.
|
|
for (CardItem *card : toClear) {
|
|
player->deleteCard(card);
|
|
}
|
|
|
|
cards.clear();
|
|
emit cardCountChanged();
|
|
}
|
|
|
|
QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const
|
|
{
|
|
QString ownerName = player->getPlayerInfo()->getName();
|
|
if (name == ZoneNames::HAND)
|
|
return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName));
|
|
else if (name == ZoneNames::DECK)
|
|
switch (gc) {
|
|
case CaseLookAtZone:
|
|
return (theirOwn ? tr("their library", "look at zone")
|
|
: tr("%1's library", "look at zone").arg(ownerName));
|
|
case CaseTopCardsOfZone:
|
|
return (theirOwn ? tr("of their library", "top cards of zone,")
|
|
: tr("of %1's library", "top cards of zone").arg(ownerName));
|
|
case CaseRevealZone:
|
|
return (theirOwn ? tr("their library", "reveal zone")
|
|
: tr("%1's library", "reveal zone").arg(ownerName));
|
|
case CaseShuffleZone:
|
|
return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName));
|
|
default:
|
|
return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName));
|
|
}
|
|
else if (name == ZoneNames::GRAVE)
|
|
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
|
|
else if (name == ZoneNames::EXILE)
|
|
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
|
|
else if (name == ZoneNames::SIDEBOARD)
|
|
switch (gc) {
|
|
case CaseLookAtZone:
|
|
return (theirOwn ? tr("their sideboard", "look at zone")
|
|
: tr("%1's sideboard", "look at zone").arg(ownerName));
|
|
case CaseNominative:
|
|
return (theirOwn ? tr("their sideboard", "nominative")
|
|
: tr("%1's sideboard", "nominative").arg(ownerName));
|
|
default:
|
|
break;
|
|
}
|
|
else {
|
|
return (theirOwn ? tr("their custom zone '%1'", "nominative").arg(name)
|
|
: tr("%1's custom zone '%2'", "nominative").arg(ownerName).arg(name));
|
|
}
|
|
return QString();
|
|
} |