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
+10 -14
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()));
}
@@ -88,7 +81,9 @@ void AddCardAction::perform(bool to_undo) {
// Update card links
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
@@ -249,7 +244,8 @@ void ChangeCardUIDAction::perform(bool to_undo) {
FOR_EACH(c, set.cards) {
c->updateLinkedUID(card->uid, uid);
}
swap(card->uid, uid);
swap(card->uid, uid);
set.buildUidMap();
}
// ----------------------------------------------------------------------------- : Pack types
+88 -45
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.
+14 -1
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());
@@ -188,7 +199,9 @@ void Set::validate(Version file_app_version) {
// we want at least one card
if (cards.empty()) cards.push_back(make_intrusive<Card>(*game));
// update scripts
script_manager->updateAll();
script_manager->updateAll();
// build uid map
buildUidMap();
}
void reflect_version_check(Reader& handler, const Char* key, intrusive_ptr<Packaged> const& package) {
+14 -11
View File
@@ -45,21 +45,22 @@ public:
Set(const StyleSheetP& stylesheet);
~Set();
GameP game; ///< The game this set uses
StyleSheetP stylesheet; ///< The default stylesheet
GameP game; ///< The game this set uses
StyleSheetP stylesheet; ///< The default stylesheet
/// The values on the fields of the set
/** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, ValueP> data;
IndexMap<FieldP, ValueP> data;
/// Extra values for specific stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> styling_data;
vector<CardP> cards; ///< 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)
ActionStack actions; ///< Actions performed on this set and the cards in it
KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change
VCSP vcs; ///< The version control system to use
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)
ActionStack actions; ///< Actions performed on this set and the cards in it
KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change
VCSP vcs; ///< The version control system to use
/// A context for performing scripts
/** Should only be used from the main thread! */
@@ -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();