From 3cc0627948c7e16291c06ae3a7f9860f99b849ee Mon Sep 17 00:00:00 2001 From: gallantron <25206391+gallantron@users.noreply.github.com> Date: Mon, 11 Nov 2024 00:11:17 +0100 Subject: [PATCH 1/2] consistent deck sorting --- gframe/client_card.cpp | 97 +++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/gframe/client_card.cpp b/gframe/client_card.cpp index 25b69f71b..ec4d845a9 100644 --- a/gframe/client_card.cpp +++ b/gframe/client_card.cpp @@ -126,23 +126,90 @@ void ClientCard::ClearTarget() { ownerTarget.clear(); } bool ClientCard::client_card_sort(ClientCard* c1, ClientCard* c2) { - uint8_t cp1 = c1->overlayTarget ? c1->overlayTarget->controler : c1->controler; - uint8_t cp2 = c2->overlayTarget ? c2->overlayTarget->controler : c2->controler; - if(cp1 != cp2) - return cp1 < cp2; - if(c1->location != c2->location) - return c1->location < c2->location; - if(c1->location & LOCATION_OVERLAY) { - if(c1->overlayTarget != c2->overlayTarget) - return c1->overlayTarget->sequence < c2->overlayTarget->sequence; + // attached cards are sorted alongside the thing they are attached to + ClientCard* e1 = (c1->overlayTarget != nullptr) ? c1->overlayTarget : c1; + ClientCard* e2 = (c2->overlayTarget != nullptr) ? c2->overlayTarget : c2; + // if they are attached to the same thing, shortcut: + if (e1 == e2) { + if ((c1->overlayTarget != nullptr) != (c2->overlayTarget != nullptr)) { + // if only one is attached, the non-attached card comes first + return c1->overlayTarget == nullptr; + } + // if both are attached, order by sequence return c1->sequence < c2->sequence; } - if((c1->location & (LOCATION_DECK | LOCATION_GRAVE | LOCATION_REMOVED | LOCATION_EXTRA)) == 0) - return c1->sequence < c2->sequence; - for(const auto& chain : mainGame->dField.chains) { - if(c1 == chain.chain_card || chain.target.find(c1) != chain.target.end()) - return true; + // player cards go before opponent cards + if (e1->controler != e2->controler) + return e1->controler < e2->controler; + // cards are grouped by location + if (e1->location != e2->location) + return e1->location < e2->location; + + // sorting behavior differs for each location + if (e1->location & (LOCATION_DECK | LOCATION_EXTRA)) { + // face-up cards go before face-down cards + auto fu1 = e1->is_reversed; + auto fu2 = e2->is_reversed; + if (fu1 != fu2) + return fu1; + else if (fu1) { + // face-up cards stay in (reverse) order + return e1->sequence > e2->sequence; + } else { + CardDataC const* data1 = gDataManager->GetCardData(e1->code); + CardDataC const* data2 = gDataManager->GetCardData(e2->code); + if (data1 && data2) { + auto basetype = [](uint32_t t) { + return std::make_pair( + t & (TYPE_MONSTER | TYPE_SPELL | TYPE_TRAP), + t & (TYPE_NORMAL | TYPE_EFFECT | TYPE_RITUAL | TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ | TYPE_LINK | + TYPE_QUICKPLAY | TYPE_CONTINUOUS | TYPE_EQUIP | TYPE_FIELD | TYPE_COUNTER)); }; + auto [base1, extra1] = basetype(data1->type); + auto [base2, extra2] = basetype(data2->type); + // first, group by monster/spell/trap + if (base1 != base2) + return base1 < base2; + // then, group by normal/effect/etc. + if (extra1 != extra2) + return extra1 < extra2; + // then level, atk, def + if (data1->level != data2->level) + return data1->level < data2->level; + if (data1->attack != data2->attack) + return data1->attack < data2->attack; + if (data1->defense != data2->defense) + return data1->defense < data2->defense; + } + // finally fall back to card code & sequence + if (e1->code != e2->code) + return e1->code < e2->code; + return e1->sequence > e2->sequence; + } + } else if (e1->location & (LOCATION_GRAVE | LOCATION_REMOVED)) { + // any cards involved in ongoing chain links are sorted to the top + auto chainOrder = [](ClientCard* c) { + for (size_t i = 0; i < mainGame->dField.chains.size(); ++i) { + ChainInfo const& it = mainGame->dField.chains[i]; + // card whose effect was activated is sorted... + if (c == it.chain_card) + return (i * 2) + 1; + // ...before the chain link's target + if (it.target.find(c) != it.target.end()) + return (i * 2) + 2; + } + return 0u; + }; + size_t const o1 = chainOrder(e1); + size_t const o2 = chainOrder(e2); + // more recent chain links come first + if (o1 != o2) + return o1 > o2; + // GY/banish cards are shown reversed (highest index first) + return e1->sequence > e2->sequence; + } else { + // other locations (field) + return e1->sequence < e2->sequence; } - return c1->sequence > c2->sequence; } + } From d501aa15d78d4a26b4d1f00fb54c4188668be300 Mon Sep 17 00:00:00 2001 From: gallantron <25206391+gallantron@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:55:05 +0200 Subject: [PATCH 2/2] make lambda deduction happy --- gframe/client_card.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gframe/client_card.cpp b/gframe/client_card.cpp index ec4d845a9..8572d8737 100644 --- a/gframe/client_card.cpp +++ b/gframe/client_card.cpp @@ -197,7 +197,7 @@ bool ClientCard::client_card_sort(ClientCard* c1, ClientCard* c2) { if (it.target.find(c) != it.target.end()) return (i * 2) + 2; } - return 0u; + return size_t{ 0u }; }; size_t const o1 = chainOrder(e1); size_t const o2 = chainOrder(e2);