add card uid map

This commit is contained in:
GenevensiS
2026-05-04 02:21:33 +02:00
parent 9a5be16e4e
commit 8cc17abecc
9 changed files with 138 additions and 90 deletions
+8 -12
View File
@@ -30,13 +30,6 @@ AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>&
// We always assume uid conflicts occur because a card was copy-pasted into the same set,
// and never because two different cards randomly got assigned the same uid
if (!action.adding) return;
// Tally existing unique ids
unordered_map<String, CardP> all_existing_cards;
unordered_set<String> all_existing_uids;
FOR_EACH(card, set.cards) {
all_existing_cards.insert({ card->uid, card });
all_existing_uids.insert(card->uid);
}
// Tally added unique ids
unordered_map<String, CardP> all_added_uids;
for (size_t pos = 0; pos < action.steps.size(); ++pos) {
@@ -49,7 +42,7 @@ AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>&
String old_uid = added_pair.first;
CardP added_card = added_pair.second;
// Assign new unique id if necessary
if (all_existing_cards.find(old_uid) == all_existing_cards.end()) continue;
if (set.card_uids.find(old_uid) == set.card_uids.end()) continue;
String new_uid = generate_uid();
added_card->uid = new_uid;
all_added_uids.insert({ new_uid, added_card });
@@ -58,14 +51,14 @@ AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>&
FOR_EACH(linked_pair, linked_pairs) {
String& linked_uid = linked_pair.first.get();
if (linked_uid.empty()) continue;
// If it's an added card, replace the link
// If it's an added card, update the link
if (all_added_uids.find(linked_uid) != all_added_uids.end()) {
all_added_uids.at(linked_uid)->updateLinkedUID(old_uid, new_uid);
}
// Otherwise, if it's an existing card, create an action to copy the link
else if (all_existing_cards.find(linked_uid) != all_existing_cards.end()) {
CardP linked_card = all_existing_cards.at(linked_uid);
int linked_index = linked_card->findFreeLink(new_uid, all_existing_uids);
else if (set.card_uids.find(linked_uid) != set.card_uids.end()) {
CardP linked_card = set.card_uids.at(linked_uid);
int linked_index = linked_card->findFreeLink(new_uid, set.card_uids);
if (linked_index < 0) {
queue_message(MESSAGE_WARNING, _ERROR_1_("not enough free links", linked_card->identification()));
}
@@ -89,6 +82,8 @@ void AddCardAction::perform(bool to_undo) {
for (size_t i = 0; i < card_link_actions.size(); ++i) {
card_link_actions[i]->perform(to_undo);
}
// Update uid map
set.buildUidMap();
}
// ----------------------------------------------------------------------------- : Reorder cards
@@ -250,6 +245,7 @@ void ChangeCardUIDAction::perform(bool to_undo) {
c->updateLinkedUID(card->uid, uid);
}
swap(card->uid, uid);
set.buildUidMap();
}
// ----------------------------------------------------------------------------- : Pack types
+87 -44
View File
@@ -67,7 +67,7 @@ bool Card::contains(QuickFilterPart const& query) const {
return false;
}
vector<int> Card::findFreeLinks(vector<String>& linked_uids, const unordered_set<String>& all_existing_uids) {
vector<int> Card::findFreeLinks(vector<String>& linked_uids, const unordered_map<String, CardP>& all_existing_uids) {
vector<int> freeIndexes;
int count = min(4, (int)linked_uids.size());
LINK_PAIRS(linked_pairs, this);
@@ -109,7 +109,7 @@ vector<int> Card::findFreeLinks(vector<String>& linked_uids, const unordered_set
}
return freeIndexes;
}
int Card::findFreeLink(const String& linked_uid, const unordered_set<String>& all_existing_uids) {
int Card::findFreeLink(const String& linked_uid, const unordered_map<String, CardP>& all_existing_uids) {
vector<String> linked_uids { linked_uid };
return findFreeLinks(linked_uids, all_existing_uids)[0];
}
@@ -155,12 +155,26 @@ void Card::updateLinkedUID(const String& old_uid, const String& new_uid) {
if (index >= 0) getLinkedUID(index) = new_uid;
}
vector<CardP> Card::getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card) {
//vector<CardP> Card::getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card) {
// vector<CardP> other_cards;
// vector<int> indexes = findRelationLinks(linked_relation);
// for (size_t i = 0; i < indexes.size(); ++i) {
// String& linked_uid = getLinkedUID(indexes[i]);
// CardP other_card = getUIDCard(cards, linked_uid);
// if (other_card) other_cards.push_back(other_card);
// else if (erase_if_no_card) {
// linked_uid = _("");
// getLinkedRelation(indexes[i]) = _("");
// }
// }
// return other_cards;
//}
vector<CardP> Card::getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card) {
vector<CardP> other_cards;
vector<int> indexes = findRelationLinks(linked_relation);
for (size_t i = 0; i < indexes.size(); ++i) {
String& linked_uid = getLinkedUID(indexes[i]);
CardP other_card = getUIDCard(cards, linked_uid);
CardP other_card = getUIDCard(set, linked_uid);
if (other_card) other_cards.push_back(other_card);
else if (erase_if_no_card) {
linked_uid = _("");
@@ -169,51 +183,76 @@ vector<CardP> Card::getLinkedRelationCards(const vector<CardP>& cards, const Str
}
return other_cards;
}
vector<CardP> Card::getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card) {
return getLinkedRelationCards(set.cards, linked_relation, erase_if_no_card);
}
vector<pair<CardP, String>> Card::getLinkedCards(const vector<CardP>& cards) {
unordered_map<String, String> links{
{ linked_card_1, linked_relation_1 },
{ linked_card_2, linked_relation_2 },
{ linked_card_3, linked_relation_3 },
{ linked_card_4, linked_relation_4 }
};
//vector<pair<CardP, String>> Card::getLinkedCards(const vector<CardP>& cards) {
// unordered_map<String, String> links{
// { linked_card_1, linked_relation_1 },
// { linked_card_2, linked_relation_2 },
// { linked_card_3, linked_relation_3 },
// { linked_card_4, linked_relation_4 }
// };
// vector<pair<CardP, String>> linked_cards;
// FOR_EACH(other_card, cards) {
// if (links.find(other_card->uid) != links.end()) {
// linked_cards.push_back(make_pair(other_card, links.at(other_card->uid)));
// }
// }
// return linked_cards;
//}
vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
vector<pair<CardP, String>> linked_cards;
FOR_EACH(other_card, cards) {
if (links.find(other_card->uid) != links.end()) {
linked_cards.push_back(make_pair(other_card, links.at(other_card->uid)));
CardP other_card_1 = getUIDCard(set, linked_card_1);
if (other_card_1) {
linked_cards.push_back(make_pair(other_card_1, linked_relation_1));
}
CardP other_card_2 = getUIDCard(set, linked_card_2);
if (other_card_2) {
linked_cards.push_back(make_pair(other_card_2, linked_relation_2));
}
CardP other_card_3 = getUIDCard(set, linked_card_3);
if (other_card_3) {
linked_cards.push_back(make_pair(other_card_3, linked_relation_3));
}
CardP other_card_4 = getUIDCard(set, linked_card_4);
if (other_card_4) {
linked_cards.push_back(make_pair(other_card_4, linked_relation_4));
}
return linked_cards;
}
vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
return getLinkedCards(set.cards);
}
CardP Card::getLinkedOtherFaceCard(const vector<CardP>& cards) {
unordered_set<String> faces;
if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) faces.emplace(linked_card_1);
if (linked_relation_2 == _("Front Face") || linked_relation_2 == _("Back Face")) faces.emplace(linked_card_2);
if (linked_relation_3 == _("Front Face") || linked_relation_3 == _("Back Face")) faces.emplace(linked_card_3);
if (linked_relation_4 == _("Front Face") || linked_relation_4 == _("Back Face")) faces.emplace(linked_card_4);
FOR_EACH(other_card, cards) {
if (faces.find(other_card->uid) != faces.end()) return other_card;
//CardP Card::getLinkedOtherFaceCard(const vector<CardP>& cards) {
// unordered_set<String> faces;
// if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) faces.emplace(linked_card_1);
// if (linked_relation_2 == _("Front Face") || linked_relation_2 == _("Back Face")) faces.emplace(linked_card_2);
// if (linked_relation_3 == _("Front Face") || linked_relation_3 == _("Back Face")) faces.emplace(linked_card_3);
// if (linked_relation_4 == _("Front Face") || linked_relation_4 == _("Back Face")) faces.emplace(linked_card_4);
// FOR_EACH(other_card, cards) {
// if (faces.find(other_card->uid) != faces.end()) return other_card;
// }
// return nullptr;
//}
CardP Card::getLinkedOtherFaceCard(const Set& set) {
if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) {
CardP other_card_1 = getUIDCard(set, linked_card_1);
if (other_card_1) return other_card_1;
}
if (linked_relation_2 == _("Front Face") || linked_relation_2 == _("Back Face")) {
CardP other_card_2 = getUIDCard(set, linked_card_2);
if (other_card_2) return other_card_2;
}
if (linked_relation_3 == _("Front Face") || linked_relation_3 == _("Back Face")) {
CardP other_card_3 = getUIDCard(set, linked_card_3);
if (other_card_3) return other_card_3;
}
if (linked_relation_4 == _("Front Face") || linked_relation_4 == _("Back Face")) {
CardP other_card_4 = getUIDCard(set, linked_card_4);
if (other_card_4) return other_card_4;
}
return nullptr;
}
CardP Card::getLinkedOtherFaceCard(const Set& set) {
return getLinkedOtherFaceCard(set.cards);
}
void Card::addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) {
unordered_set<String> all_existing_uids;
FOR_EACH(card, set.cards) {
all_existing_uids.insert(card->uid);
}
int index = findFreeLink(linked_card->uid, all_existing_uids);
int index = findFreeLink(linked_card->uid, set.card_uids);
if (index < 0) {
queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", identification()));
return;
@@ -221,7 +260,7 @@ void Card::addLink(const Set& set, CardP& linked_card, const String& selected_re
getLinkedUID(index) = linked_card->uid;
getLinkedRelation(index) = linked_relation;
index = linked_card->findFreeLink(uid, all_existing_uids);
index = linked_card->findFreeLink(uid, set.card_uids);
if (index < 0) {
queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", linked_card->identification()));
}
@@ -246,15 +285,19 @@ void Card::removeLink(const CardP& linked_card)
}
}
CardP Card::getUIDCard(const vector<CardP>& cards, const String& uid) {
FOR_EACH(card, cards) {
if (card->uid == uid) return card;
//CardP Card::getUIDCard(const vector<CardP>& cards, const String& uid) {
// FOR_EACH(card, cards) {
// if (card->uid == uid) return card;
// }
// return nullptr;
//}
CardP Card::getUIDCard(const Set& set, const String& uid) {
auto it = set.card_uids.find(uid);
if (it != set.card_uids.end()) {
return it->second;
}
return nullptr;
}
CardP Card::getUIDCard(const Set& set, const String& uid) {
return getUIDCard(set.cards, uid);
}
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
+6 -6
View File
@@ -106,8 +106,8 @@ public:
}
/// Find the index of a free link slot to write to. Returns -1 if not found.
int findFreeLink (const String& linked_uid, const unordered_set<String>& all_existing_uids);
vector<int> findFreeLinks(vector<String>& linked_uids, const unordered_set<String>& all_existing_uids);
int findFreeLink (const String& linked_uid, const unordered_map<String, CardP>& all_existing_uids);
vector<int> findFreeLinks(vector<String>& linked_uids, const unordered_map<String, CardP>& all_existing_uids);
/// Find the index of a link slot that references the linked_uid. Returns -1 if not found.
int findUIDLink(const String& linked_uid);
@@ -125,18 +125,18 @@ public:
//void updateLinkedRelation(const String& old_relation, const String& new_relation);
/// Get the card with the given uid.
static CardP getUIDCard(const vector<CardP>& cards, const String& uid);
//static CardP getUIDCard(const vector<CardP>& cards, const String& uid);
static CardP getUIDCard(const Set& set, const String& uid);
/// Get all the cards linked to this card with the given relation.
vector<CardP> getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card = true);
//vector<CardP> getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card = true);
vector<CardP> getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card = true);
/// Get all the cards linked to this card.
vector<pair<CardP, String>> getLinkedCards(const vector<CardP>& cards);
//vector<pair<CardP, String>> getLinkedCards(const vector<CardP>& cards);
vector<pair<CardP, String>> getLinkedCards(const Set& set);
/// Get the back face or front face of this card.
CardP getLinkedOtherFaceCard(const vector<CardP>& cards);
//CardP getLinkedOtherFaceCard(const vector<CardP>& cards);
CardP getLinkedOtherFaceCard(const Set& set);
/// Link a card to this card.
+13
View File
@@ -22,6 +22,7 @@
#include <util/tagged_string.hpp> // for 0.2.7 fix
#include <util/order_cache.hpp>
#include <util/delayed_index_maps.hpp>
#include <util/uid.hpp>
#include <script/script_manager.hpp>
#include <script/profiler.hpp>
#include <wx/sstream.h>
@@ -67,6 +68,16 @@ void Set::updateStyles(const CardP& card, bool only_content_dependent) {
void Set::updateDelayed() {
script_manager->updateDelayed();
}
void Set::buildUidMap() {
card_uids.clear();
FOR_EACH(c, cards) {
while (card_uids.find(c->uid) != card_uids.end()) {
queue_message(MESSAGE_WARNING, _("Multiple cards found with same uid:\n") + c->identification() + _("\n") + card_uids[c->uid]->identification() + _("\nPlease notify someone on the Discord server."));
c->uid = generate_uid();
}
card_uids[c->uid] = c;
}
}
Context& Set::getContextForThumbnails() {
assert(!wxThread::IsMain());
@@ -189,6 +200,8 @@ void Set::validate(Version file_app_version) {
if (cards.empty()) cards.push_back(make_intrusive<Card>(*game));
// update scripts
script_manager->updateAll();
// build uid map
buildUidMap();
}
void reflect_version_check(Reader& handler, const Char* key, intrusive_ptr<Packaged> const& package) {
+3
View File
@@ -53,6 +53,7 @@ public:
/// Extra values for specific stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> styling_data;
vector<CardP> cards; ///< The cards in the set
unordered_map<String, CardP> card_uids; ///< The uids of the cards in the set
vector<KeywordP> keywords; ///< Additional keywords used in this set
vector<PackTypeP> pack_types; ///< Additional/replacement pack types
String apprentice_code; ///< Code to use for apprentice (magic only)
@@ -71,6 +72,8 @@ public:
void updateStyles(const CardP& card, bool only_content_dependent);
/// Update scripts that were delayed
void updateDelayed();
/// Update uid map
void buildUidMap();
/// A context for performing scripts
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails();
+2 -6
View File
@@ -126,11 +126,7 @@ void CardLinkWindow::onOk(wxCommandEvent&) {
linked_uids.push_back(linked_cards[i]->uid);
}
// Find free links
unordered_set<String> all_existing_uids;
FOR_EACH(card, set->cards) {
all_existing_uids.insert(card->uid);
}
vector<int> free_link_indexes = selected_card->findFreeLinks(linked_uids, all_existing_uids);
vector<int> free_link_indexes = selected_card->findFreeLinks(linked_uids, set->card_uids);
int free_link_count = 0;
for (size_t i = 0; i < free_link_indexes.size(); ++i) {
if (free_link_indexes[i] >= 0) free_link_count++;
@@ -163,7 +159,7 @@ void CardLinkWindow::onOk(wxCommandEvent&) {
// Find reciprocal free slots and make actions
String& selected_uid = selected_card->uid;
for (size_t i = 0; i < linked_cards.size(); ++i) {
int free_link_index = linked_cards[i]->findFreeLink(selected_uid, all_existing_uids);
int free_link_index = linked_cards[i]->findFreeLink(selected_uid, set->card_uids);
if (free_link_index >= 0) {
actions.push_back(make_intrusive<OneWayLinkCardsAction>(linked_cards[i], selected_uid, selected_relation_string, free_link_index));
}
+1 -5
View File
@@ -447,12 +447,8 @@ bool CardListBase::canLink() const {
vector<CardP> selected_cards;
getSelection(selected_cards);
if (selected_cards.size() != 1) return false;
unordered_set<String> all_existing_uids;
FOR_EACH(card, set->cards) {
all_existing_uids.insert(card->uid);
}
CardP card = selected_cards[0];
return card->findFreeLink(card->uid, all_existing_uids) >= 0;
return card->findFreeLink(card->uid, set->card_uids) >= 0;
}
bool CardListBase::doLink() {
CardLinkWindow wnd(this, set, getCard());
+1 -1
View File
@@ -37,7 +37,7 @@ void PrintJob::measure_cards() {
if (already_measured_cards.find(card) != already_measured_cards.end()) continue;
already_measured_cards.emplace(card);
card_layouts.emplace_back(measure_card(card));
CardP other_face = card->getLinkedOtherFaceCard(cards);
CardP other_face = card->getLinkedOtherFaceCard(*set); // we assume that if you want to print one side, you also want to print the other, so we look for it in the entire set instead of just the given cards
if (other_face && already_measured_cards.find(other_face) == already_measured_cards.end()) {
already_measured_cards.emplace(other_face);
card_layouts.emplace_back(measure_card(other_face));
+1
View File
@@ -309,6 +309,7 @@ void SetWindow::onChangeSet() {
// make sure there is always at least one card
// some things need this
if (set->cards.empty()) set->cards.push_back(make_intrusive<Card>(*set->game));
set->buildUidMap();
// all panels view the same set
FOR_EACH(p, panels) {
p->setSet(set);