From 11e506739a631618d98bcb4595655836a87b70fc Mon Sep 17 00:00:00 2001 From: GenevensiS <66968533+G-e-n-e-v-e-n-s-i-S@users.noreply.github.com> Date: Sun, 1 Feb 2026 17:39:33 +0100 Subject: [PATCH] linking refactor --- src/data/action/set.cpp | 169 ++++++------ src/data/action/set.hpp | 35 +-- src/data/action/value.cpp | 6 +- src/data/action/value.hpp | 4 +- src/data/card.cpp | 355 ++++++++++---------------- src/data/card.hpp | 65 +++-- src/data/game.cpp | 36 +-- src/data/game.hpp | 1 + src/gui/bulk_modification_window.cpp | 22 +- src/gui/bulk_modification_window.hpp | 2 +- src/gui/card_link_window.cpp | 66 ++++- src/gui/card_link_window.hpp | 1 + src/gui/control/card_list.cpp | 8 +- src/gui/control/card_list.hpp | 2 +- src/gui/print_window.cpp | 6 +- src/script/functions/basic.cpp | 32 +-- src/script/functions/construction.cpp | 4 +- src/util/action_stack.hpp | 4 +- 18 files changed, 411 insertions(+), 407 deletions(-) diff --git a/src/data/action/set.cpp b/src/data/action/set.cpp index 936bae6d..304fb895 100644 --- a/src/data/action/set.cpp +++ b/src/data/action/set.cpp @@ -22,66 +22,74 @@ AddCardAction::AddCardAction(Set& set) , action(ADD, make_intrusive(*set.game), set.cards) {} -AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const CardP& card) - : CardListAction(set) - , action(ar, card, set.cards) -{} - AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector& cards) : CardListAction(set) , action(ar, cards, set.cards) -{} +{ + // If we are adding cards, resolve any uid conflicts + // 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 all_existing_cards; + unordered_set 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 all_added_uids; + for (size_t pos = 0; pos < action.steps.size(); ++pos) { + CardP card = action.steps[pos].item; + all_added_uids.insert({ card->uid, card }); + } + // Cycle on the added cards + unordered_map all_added_uids_copy(all_added_uids); + FOR_EACH(added_pair, all_added_uids_copy) { + 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; + String new_uid = generate_uid(); + added_card->uid = new_uid; + all_added_uids.insert({ new_uid, added_card }); + // Update links on linked cards + LINK_PAIRS(linked_pairs, added_card); + FOR_EACH(linked_pair, linked_pairs) { + String& linked_uid = linked_pair.first.get(); + String& linked_relation = linked_pair.second.get(); + if (linked_uid.empty()) continue; + // If it's an added card, replace 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); + if (linked_index < 0) { + queue_message(MESSAGE_WARNING, _ERROR_1_("not enough free links", linked_card->identification())); + } + else { + String relation(linked_card->getLinkedRelation(linked_index)); + card_link_actions.push_back(make_intrusive(set, linked_card, new_uid, relation, linked_index)); + } + } + } + } +} String AddCardAction::getName(bool to_undo) const { return action.getName(); } void AddCardAction::perform(bool to_undo) { - // If we are adding cards, resolve any uid conflicts - // (If we are re-adding cards, from a remove undo, there shouldn't be any uid conflicts) - // 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 && !to_undo) { - // Tally existing unique ids - unordered_map all_existing_uids; - FOR_EACH(card, set.cards) { - all_existing_uids.insert({ card->uid, card }); - } - // Tally added unique ids - unordered_map all_added_uids; - for (size_t pos = 0; pos < action.steps.size(); ++pos) { - CardP card = action.steps[pos].item; - all_added_uids.insert({ card->uid, card }); - } - FOR_EACH(added_pair, all_added_uids) { - String old_uid = added_pair.first; - CardP added_card = added_pair.second; - // Assign new unique ids - if (all_existing_uids.find(old_uid) != all_existing_uids.end()) { - String new_uid = generate_uid(); - added_card->uid = new_uid; - all_added_uids.insert({ new_uid, added_card }); - // Update links on linked cards - OTHER_LINKED_PAIRS(linked_pairs, added_card); - FOR_EACH(linked_pair, linked_pairs) { - String& linked_uid = linked_pair.first.get(); - String& linked_relation = linked_pair.second.get(); - if (linked_uid.empty()) continue; - // If it's an added card, replace the link - if (all_added_uids.find(linked_uid) != all_added_uids.end()) { - all_added_uids.at(linked_uid)->updateLink(old_uid, new_uid); - } - // Otherwise, if it's an existing card, copy the link - else if (all_existing_uids.find(linked_uid) != all_existing_uids.end()) { - all_existing_uids.at(linked_uid)->copyLink(set, old_uid, new_uid); - } - } - } - } - } - // Add or remove cards action.perform(set.cards, to_undo); + // Update card links + for (int i = 0; i < card_link_actions.size(); ++i) { + card_link_actions[i]->perform(to_undo); + } } // ----------------------------------------------------------------------------- : Reorder cards @@ -108,39 +116,40 @@ void ReorderCardsAction::perform(bool to_undo) { // ----------------------------------------------------------------------------- : Link cards -LinkCardsAction::LinkCardsAction(Set& set, const CardP& selected_card, vector& linked_cards, const String& selected_relation, const String& linked_relation) - : CardListAction(set), selected_card(selected_card), linked_cards(linked_cards), selected_relation(selected_relation), linked_relation(linked_relation) -{} +OneWayLinkCardsAction::OneWayLinkCardsAction(Set& set, CardP& card, const String& uid, const String& relation, int index) + : CardListAction(set), card(card), uid(uid), relation(relation) +{ + switch (index) { + case 1: { + linked_uid = &card->linked_card_1; + linked_relation = &card->linked_relation_1; + return; + } + case 2: { + linked_uid = &card->linked_card_2; + linked_relation = &card->linked_relation_2; + return; + } + case 3: { + linked_uid = &card->linked_card_3; + linked_relation = &card->linked_relation_3; + return; + } + default: { + linked_uid = &card->linked_card_4; + linked_relation = &card->linked_relation_4; + return; + } + } +} -String LinkCardsAction::getName(bool to_undo) const { - return _("Link cards"); +String OneWayLinkCardsAction::getName(bool to_undo) const { + return _("Change link"); } -void LinkCardsAction::perform(bool to_undo) { - if (!to_undo) { - selected_card->link(set, linked_cards, selected_relation, linked_relation); - } else { - selected_card->unlink(linked_cards); - } -} - -UnlinkCardsAction::UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card) - : CardListAction(set), selected_card(selected_card), unlinked_card(unlinked_card) -{} - -String UnlinkCardsAction::getName(bool to_undo) const { - return _("Unlink card"); -} - -void UnlinkCardsAction::perform(bool to_undo) { - if (!to_undo) { - pair relations = selected_card->unlink(unlinked_card); - selected_relation = relations.first; - unlinked_relation = relations.second; - } - else { - selected_card->link(set, unlinked_card, selected_relation, unlinked_relation); - } +void OneWayLinkCardsAction::perform(bool to_undo) { + swap(*linked_uid, uid); + swap(*linked_relation, relation); } // ----------------------------------------------------------------------------- : Change stylesheet @@ -236,7 +245,7 @@ String ChangeCardUIDAction::getName(bool to_undo) const { } void ChangeCardUIDAction::perform(bool to_undo) { FOR_EACH(c, set.cards) { - c->updateLink(card->uid, uid); + c->updateLinkedUID(card->uid, uid); } swap(card->uid, uid); } diff --git a/src/data/action/set.hpp b/src/data/action/set.hpp index 0c53213d..b5d7bba7 100644 --- a/src/data/action/set.hpp +++ b/src/data/action/set.hpp @@ -41,13 +41,13 @@ class AddCardAction : public CardListAction { public: /// Add a newly allocated card AddCardAction(Set& set); - AddCardAction(AddingOrRemoving, Set& set, const CardP& card); AddCardAction(AddingOrRemoving, Set& set, const vector& cards); String getName(bool to_undo) const override; void perform(bool to_undo) override; - const GenericAddAction action; + const GenericAddAction action; + vector card_link_actions; }; // ----------------------------------------------------------------------------- : Reorder cards @@ -67,32 +67,19 @@ public: // ----------------------------------------------------------------------------- : Link cards /// Add a link between two or more cards -class LinkCardsAction : public CardListAction { +class OneWayLinkCardsAction : public CardListAction { public: - LinkCardsAction(Set& set, const CardP& selected_card, vector& linked_cards, const String& selected_relation, const String& linked_relation); - + OneWayLinkCardsAction(Set& set, CardP& card, const String& uid, const String& relation, int index); + String getName(bool to_undo) const override; void perform(bool to_undo) override; - + //private: - CardP selected_card; ///< The card currently selected in the cards tab - vector linked_cards; ///< The cards that will be linked to the selected card - String selected_relation; ///< The nature of the relation of the selected card - String linked_relation; ///< The nature of the relation of the linked cards -}; -/// Remove a link between two cards -class UnlinkCardsAction : public CardListAction { -public: - UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card); - - String getName(bool to_undo) const override; - void perform(bool to_undo) override; - - //private: - CardP selected_card; ///< The card currently selected in the cards tab - CardP unlinked_card; ///< The card that will be unlinked from the selected card - String selected_relation; ///< The nature of the relation of the selected card - String unlinked_relation; ///< The nature of the relation of the unlinked card + CardP card; ///< The card we change the link of. We hold a reference so it doesn't get destroyed before this. + String* linked_uid; ///< The uid slot we need to change + String* linked_relation; ///< The relation slot we need to change + String uid; ///< The uid we have to change the link to + String relation; ///< The relation we have to change the link to }; // ----------------------------------------------------------------------------- : Change stylesheet diff --git a/src/data/action/value.cpp b/src/data/action/value.cpp index 920c94a4..1d50d07f 100644 --- a/src/data/action/value.cpp +++ b/src/data/action/value.cpp @@ -212,12 +212,12 @@ void ScriptStyleEvent::perform(bool) { // ----------------------------------------------------------------------------- : Bulk action -BulkAction::BulkAction(const vector>& actions, const SetP& set, CardListBase* card_list_window) +BulkAction::BulkAction(const vector& actions, const SetP& set, CardListBase* card_list_window) : actions(actions), set(set), card_list_window(card_list_window) { if (actions.empty()) throw ScriptError(_("BulkAction created with no actions")); - name_do = actions.front()->getName(false) + _(" ") + _ACTION_("bulk"); - name_undo = actions.front()->getName(true) + _(" ") + _ACTION_("bulk"); + name_do = _ACTION_1_("bulk", actions.front()->getName(false)); + name_undo = _ACTION_1_("bulk", actions.front()->getName(true)); } BulkAction::~BulkAction() {} diff --git a/src/data/action/value.hpp b/src/data/action/value.hpp index 7f50f8da..c8938782 100644 --- a/src/data/action/value.hpp +++ b/src/data/action/value.hpp @@ -206,7 +206,7 @@ public: // An action that's just a list of other actions class BulkAction : public Action { public: - BulkAction(const vector>& actions, const SetP& set, CardListBase* card_list_window); + BulkAction(const vector& actions, const SetP& set, CardListBase* card_list_window); ~BulkAction() override; String getName(bool to_undo) const override; @@ -216,7 +216,7 @@ public: private: String name_do; String name_undo; - vector> actions; + vector actions; SetP set; CardListBase* card_list_window; }; diff --git a/src/data/card.cpp b/src/data/card.cpp index 9a11d643..a2d2a17a 100644 --- a/src/data/card.cpp +++ b/src/data/card.cpp @@ -63,205 +63,110 @@ bool Card::contains(QuickFilterPart const& query) const { } if (query.match(_("notes"), notes)) return true; return false; -} - -void Card::link(const Set& set, const vector& linked_cards, const String& selected_relation, const String& linked_relation) -{ - unlink(linked_cards); - - unordered_set all_existing_uids; - FOR_EACH(card, set.cards) { - all_existing_uids.insert(card->uid); - } - int free_link_count = 0; - THIS_LINKED_PAIRS(this_linked_pairs); - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - if ( - this_linked_uid.empty() || // Not a reference - all_existing_uids.find(this_linked_uid) == all_existing_uids.end() // Reference to nonexistent card - ) free_link_count++; - } - if (free_link_count < linked_cards.size()) { - queue_message(MESSAGE_WARNING, _ERROR_("not enough free links")); - return; - } - - vector all_missed_cards; - FOR_EACH(linked_card, linked_cards) { - bool written = false; - // Try to write to a free spot - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (this_linked_uid.empty()) { - this_linked_uid = linked_card->uid; - this_linked_relation = linked_relation; - written = true; - break; +} + +vector Card::findFreeLinks(vector& linked_uids, const unordered_set& all_existing_uids) { + vector freeIndexes; + int count = min(4, (int)linked_uids.size()); + LINK_PAIRS(linked_pairs, this); + + for (int i = 0; i < count; ++i) { + String& linked_uid = linked_uids[i]; + // Try to find an existing link + for (int j = 0; j < linked_pairs.size(); ++j) { + auto& linked_pair = linked_pairs[j]; + if (linked_pair.first.get() == linked_uid && + std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end() + ) { + freeIndexes.push_back(j); + goto continue_outer; + } + } + // Try to find a free spot + for (int j = 0; j < linked_pairs.size(); ++j) { + auto& linked_pair = linked_pairs[j]; + if (linked_pair.first.get().empty() && + std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end() + ) { + freeIndexes.push_back(j); + goto continue_outer; } } - // Try to write to an erasable spot - if (!written) { - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) { - this_linked_uid = linked_card->uid; - this_linked_relation = linked_relation; - written = true; - break; - } + // Try to find an erasable spot + for (int j = 0; j < linked_pairs.size(); ++j) { + auto& linked_pair = linked_pairs[j]; + if (all_existing_uids.find(linked_pair.first.get()) == all_existing_uids.end() && + std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end() + ) { + freeIndexes.push_back(j); + goto continue_outer; } - } - if (!written) { - // Should be impossible to end up here? - } + } + freeIndexes.push_back(-1); + continue_outer:; + } + return freeIndexes; +} +int Card::findFreeLink(const String& linked_uid, const unordered_set& all_existing_uids) { + vector linked_uids { linked_uid }; + return findFreeLinks(linked_uids, all_existing_uids)[0]; +} + +int Card::findUIDLink(const String& linked_uid) { + if (linked_card_1 == linked_uid) return 0; + if (linked_card_2 == linked_uid) return 1; + if (linked_card_3 == linked_uid) return 2; + if (linked_card_4 == linked_uid) return 3; + return -1; +} + +vector Card::findRelationLinks(const String& linked_relation) { + vector indexes; + if (linked_relation_1 == linked_relation) indexes.push_back(0); + if (linked_relation_2 == linked_relation) indexes.push_back(1); + if (linked_relation_3 == linked_relation) indexes.push_back(2); + if (linked_relation_4 == linked_relation) indexes.push_back(3); + return indexes; +} + +String& Card::getLinkedUID(int index) { + switch (index) { + case 3: return linked_card_4; + case 2: return linked_card_3; + case 1: return linked_card_2; + default: return linked_card_1; + } +} +String& Card::getLinkedRelation(int index) { + switch (index) { + case 3: return linked_relation_4; + case 2: return linked_relation_3; + case 1: return linked_relation_2; + default: return linked_relation_1; + } +} - OTHER_LINKED_PAIRS(linked_pairs, linked_card); - written = false; - // Try to write to a free spot - FOR_EACH(linked_pair, linked_pairs) { - String& linked_uid = linked_pair.first.get(); - String& linked_relation = linked_pair.second.get(); - if (linked_uid.empty()) { - linked_uid = uid; - linked_relation = selected_relation; - written = true; - break; - } - } - // Try to write to an erasable spot - if (!written) { - FOR_EACH(linked_pair, linked_pairs) { - String& linked_uid = linked_pair.first.get(); - String& linked_relation = linked_pair.second.get(); - if (all_existing_uids.find(linked_uid) == all_existing_uids.end()) { - linked_uid = uid; - linked_relation = selected_relation; - written = true; - break; - } - } - } - // Notify we couldn't write - if (!written) { - all_missed_cards.push_back(linked_card); - } - } - if (all_missed_cards.size() > 0) { - std::stringstream ss; - ss << _ERROR_("could not link"); - for (size_t pos = 0; pos < all_missed_cards.size(); ++pos) { - ss << all_missed_cards[pos]->identification(); - if (pos < all_missed_cards.size() - 1) ss << ", "; - }; - queue_message(MESSAGE_WARNING, wxString(ss.str().c_str())); - } +void Card::updateLinkedUID(const String& old_uid, const String& new_uid) { + int index = findUIDLink(old_uid); + if (index >= 0) getLinkedUID(index) = new_uid; } -void Card::link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) -{ - vector linked_cards { linked_card }; - link(set, linked_cards, selected_relation, linked_relation); +vector Card::getLinkedRelationCards(const vector& cards, const String& linked_relation, bool erase_if_no_card) { + vector other_cards; + vector indexes = findRelationLinks(linked_relation); + for (int 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; } - -void Card::unlink(const vector& unlinked_cards) -{ - for (size_t pos = 0; pos < unlinked_cards.size(); ++pos) { - CardP unlinked_card = unlinked_cards[pos]; - unlink(unlinked_card); - } -} - -pair Card::unlink(CardP& unlinked_card) -{ - String old_selected_relation = _(""); - THIS_LINKED_PAIRS(this_linked_pairs); - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (this_linked_uid == unlinked_card->uid) { - old_selected_relation = this_linked_relation; - this_linked_uid = _(""); - this_linked_relation = _(""); - } - } - String old_unlinked_relation = _(""); - OTHER_LINKED_PAIRS(unlinked_pairs, unlinked_card); - FOR_EACH(unlinked_pair, unlinked_pairs) { - String& unlinked_uid = unlinked_pair.first.get(); - String& unlinked_relation = unlinked_pair.second.get(); - if (unlinked_uid == uid) { - old_unlinked_relation = unlinked_relation; - unlinked_uid = _(""); - unlinked_relation = _(""); - } - } - return make_pair(old_selected_relation, old_unlinked_relation); -} - -void Card::copyLink(const Set& set, String old_uid, String new_uid) { - // Find what relation we need to copy - String relation_copy = _(""); - THIS_LINKED_PAIRS(this_linked_pairs); - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (this_linked_uid == old_uid) { - relation_copy = this_linked_relation; - break; - } - } - // Nothing to copy - if (relation_copy.empty()) { - return; - } - - // Try to copy to a free spot - bool written = false; - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (this_linked_uid.empty()) { - this_linked_uid = new_uid; - this_linked_relation = relation_copy; - written = true; - break; - } - } - // Try to copy to an erasable spot - if (!written) { - unordered_set all_existing_uids; - FOR_EACH(card, set.cards) { - all_existing_uids.insert(card->uid); - } - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) { - this_linked_uid = new_uid; - this_linked_relation = relation_copy; - written = true; - break; - } - } - } - // Notify we couldn't copy - if (!written) { - queue_message(MESSAGE_WARNING, _ERROR_("not enough free links for copy")); - } -} - -void Card::updateLink(String old_uid, String new_uid) { - THIS_LINKED_PAIRS(this_linked_pairs); - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - if (this_linked_uid == old_uid) { - this_linked_uid = new_uid; - return; - } - } +vector 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> Card::getLinkedCards(const vector& cards) { @@ -283,7 +188,7 @@ vector> Card::getLinkedCards(const Set& set) { return getLinkedCards(set.cards); } -CardP Card::getLinkedOtherFace(const vector& cards) { +CardP Card::getLinkedOtherFaceCard(const vector& cards) { unordered_set 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); @@ -294,39 +199,57 @@ CardP Card::getLinkedOtherFace(const vector& cards) { } return nullptr; } -CardP Card::getLinkedOtherFace(const Set& set) { - return getLinkedOtherFace(set.cards); +CardP Card::getLinkedOtherFaceCard(const Set& set) { + return getLinkedOtherFaceCard(set.cards); } -vector Card::getLinkedCardsFromLink(const vector& cards, const String& link, bool erase_if_no_card) { - vector other_cards; - THIS_LINKED_PAIRS(this_linked_pairs); - FOR_EACH(this_linked_pair, this_linked_pairs) { - String& this_linked_uid = this_linked_pair.first.get(); - String& this_linked_relation = this_linked_pair.second.get(); - if (this_linked_relation == link) { - CardP other_card = getCardFromUid(cards, this_linked_uid); - if (other_card) other_cards.push_back(other_card); - else if (erase_if_no_card) { - this_linked_relation = _(""); - this_linked_uid = _(""); - } - } +void Card::addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) { + unordered_set all_existing_uids; + FOR_EACH(card, set.cards) { + all_existing_uids.insert(card->uid); + } + + int index = findFreeLink(linked_card->uid, all_existing_uids); + if (index < 0) { + queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", identification())); + return; + } + getLinkedUID(index) = linked_card->uid; + getLinkedRelation(index) = selected_relation; + + index = linked_card->findFreeLink(uid, all_existing_uids); + if (index < 0) { + queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", linked_card->identification())); + } + else { + linked_card->getLinkedUID(index) = uid; + linked_card->getLinkedRelation(index) = linked_relation; + } +} + +void Card::removeLink(const CardP& linked_card) +{ + int index = findUIDLink(linked_card->uid); + if (index >= 0) { + getLinkedUID(index) = _(""); + getLinkedRelation(index) = _(""); + } + + index = linked_card->findUIDLink(uid); + if (index >= 0) { + linked_card->getLinkedUID(index) = _(""); + linked_card->getLinkedRelation(index) = _(""); } - return other_cards; -} -vector Card::getLinkedCardsFromLink(const Set& set, const String& link, bool erase_if_no_card) { - return getLinkedCardsFromLink(set.cards, link, erase_if_no_card); } -CardP Card::getCardFromUid(const vector& cards, const String& uid) { +CardP Card::getUIDCard(const vector& cards, const String& uid) { FOR_EACH(card, cards) { if (card->uid == uid) return card; } return nullptr; } -CardP Card::getCardFromUid(const Set& set, const String& uid) { - return getCardFromUid(set.cards, uid); +CardP Card::getUIDCard(const Set& set, const String& uid) { + return getUIDCard(set.cards, uid); } IndexMap& Card::extraDataFor(const StyleSheet& stylesheet) { diff --git a/src/data/card.hpp b/src/data/card.hpp index 71511d7d..06740a0d 100644 --- a/src/data/card.hpp +++ b/src/data/card.hpp @@ -13,6 +13,7 @@ #include #include #include // for Card::value +#include class Game; class Dependency; @@ -23,8 +24,7 @@ DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(StyleSheet); -#define THIS_LINKED_PAIRS(var) vector, reference_wrapper>> var { make_pair(ref(linked_card_1), ref(linked_relation_1)), make_pair(ref(linked_card_2), ref(linked_relation_2)), make_pair(ref(linked_card_3), ref(linked_relation_3)), make_pair(ref(linked_card_4), ref(linked_relation_4)) } -#define OTHER_LINKED_PAIRS(var, other_card) vector, reference_wrapper>> var { make_pair(ref(other_card->linked_card_1), ref(other_card->linked_relation_1)), make_pair(ref(other_card->linked_card_2), ref(other_card->linked_relation_2)), make_pair(ref(other_card->linked_card_3), ref(other_card->linked_relation_3)), make_pair(ref(other_card->linked_card_4), ref(other_card->linked_relation_4)) } +#define LINK_PAIRS(var, card) vector, reference_wrapper>> var { make_pair(ref(card->linked_card_1), ref(card->linked_relation_1)), make_pair(ref(card->linked_card_2), ref(card->linked_relation_2)), make_pair(ref(card->linked_card_3), ref(card->linked_relation_3)), make_pair(ref(card->linked_card_4), ref(card->linked_relation_4)) } // ----------------------------------------------------------------------------- : Card @@ -80,27 +80,6 @@ public: /// Does any field contains the given query string? bool contains(QuickFilterPart const& query) const; - /// Link or unlink other cards to this card - void link(const Set& set, const vector& linked_cards, const String& selected_relation, const String& linked_relation); - void link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation); - void unlink(const vector& linked_cards); - pair unlink(CardP& unlinked_card); // Returns the relations that were deleted, so we can undo - - void copyLink(const Set& set, String old_uid, String new_uid); - void updateLink(String old_uid, String new_uid); - - vector> getLinkedCards(const vector& cards); - vector> getLinkedCards(const Set& set); - - vector getLinkedCardsFromLink(const vector& cards, const String& link, bool erase_if_no_card); - vector getLinkedCardsFromLink(const Set& set, const String& link, bool erase_if_no_card); - - CardP getLinkedOtherFace(const vector& cards); - CardP getLinkedOtherFace(const Set& set); - - static CardP getCardFromUid(const vector& cards, const String& uid); - static CardP getCardFromUid(const Set& set, const String& uid); - /// Find a value in the data by name and type template T& value(const String& name) { for(IndexMap::iterator it = data.begin() ; it != data.end() ; ++it) { @@ -122,6 +101,46 @@ public: } throw InternalError(_("Expected a card field with name '")+name+_("'")); } + + /// 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& all_existing_uids); + vector findFreeLinks(vector& linked_uids, const unordered_set& 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); + /// Find all indexes of link slots that references the linked_relation. + vector findRelationLinks(const String& linked_relation); + + /// Get a reference to the linked uid slot. + String& getLinkedUID (int index); + /// Get a reference to the linked relation slot. + String& getLinkedRelation(int index); + + /// Make all links that point to old_uid point to new_uid instead. + void updateLinkedUID(const String& old_uid, const String& new_uid); + /// Make all links that have old_relation have new_relation instead. Not needed apparently. + //void updateLinkedRelation(const String& old_relation, const String& new_relation); + + /// Get the card with the given uid. + static CardP getUIDCard(const vector& 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 getLinkedRelationCards(const vector& cards, const String& linked_relation, bool erase_if_no_card = true); + vector getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card = true); + + /// Get all the cards linked to this card. + vector> getLinkedCards(const vector& cards); + vector> getLinkedCards(const Set& set); + + /// Get the back face or front face of this card. + CardP getLinkedOtherFaceCard(const vector& cards); + CardP getLinkedOtherFaceCard(const Set& set); + + /// Link a card to this card. + void addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation); + + /// Unlink a card from this card. + void removeLink(const CardP& linked_card); DECLARE_REFLECTION(); }; diff --git a/src/data/game.cpp b/src/data/game.cpp index 659aaad1..614096ae 100644 --- a/src/data/game.cpp +++ b/src/data/game.cpp @@ -67,6 +67,19 @@ IMPLEMENT_REFLECTION(Game) { REFLECT_NO_SCRIPT(auto_replaces); } +void Game::add_alt_name_or_warn(const String& alt_name, const String& field_name) +{ + String unified_name = unified_form(alt_name); + if (card_fields_alt_names.count(unified_name)) { + if (card_fields_alt_names[unified_name] != field_name) { + queue_message(MESSAGE_WARNING, _("Duplicate alt card field name ' ") + unified_name + _(" ' is both an alt for: ' ") + field_name + _(" ' and ' ") + card_fields_alt_names[unified_name] + _(" '.")); + } + } + else { + card_fields_alt_names.emplace(unified_name, field_name); + } +} + void Game::validate(Version v) { // check that we have at least one card field if (card_fields.size() < 1) { @@ -104,24 +117,13 @@ void Game::validate(Version v) { } // alternate card field names map for (auto it = card_fields.begin(); it != card_fields.end(); ++it) { - FieldP field = *it; - String unified_name = unified_form(field->name); - if (card_fields_alt_names.count(unified_name)) { - queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name); - } - else { - card_fields_alt_names.emplace(unified_name, field->name); - } - //String column_name = field->card_list_name.get(); - //card_fields_alt_names.emplace(unified_form(column_name), field->name); + FieldP field = *it; + String& field_name = field->name; + add_alt_name_or_warn(field_name, field_name); + add_alt_name_or_warn(field->card_list_name.default_, field_name); + add_alt_name_or_warn(field->card_list_name.get(), field_name); for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) { - unified_name = unified_form(*it2); - if (card_fields_alt_names.count(unified_name)) { - queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name); - } - else { - card_fields_alt_names.emplace(unified_name, field->name); - } + add_alt_name_or_warn(*it2, field_name); } } // front face/back face card link diff --git a/src/data/game.hpp b/src/data/game.hpp index a352d66f..620a89a1 100644 --- a/src/data/game.hpp +++ b/src/data/game.hpp @@ -78,6 +78,7 @@ public: bool isMagic() const; protected: + void add_alt_name_or_warn(const String& alt_name, const String& field_name); void validate(Version) override; DECLARE_REFLECTION_OVERRIDE(); diff --git a/src/gui/bulk_modification_window.cpp b/src/gui/bulk_modification_window.cpp index 5bdbc3fb..dc98a27e 100644 --- a/src/gui/bulk_modification_window.cpp +++ b/src/gui/bulk_modification_window.cpp @@ -204,7 +204,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { return; } // get the new script values - vector> actions; + vector actions; String field_name = field_type->GetString(field_type->GetSelection()); // stylesheet, notes or id change if (field_name == _("stylesheet") || field_name == _("notes") || field_name == _("id")) { @@ -226,15 +226,15 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { if (field_name == _("stylesheet")) { for (int i = 0; i < count; ++i) { StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, new_values[i]); - actions.push_back(make_shared(cards[i], stylesheet)); + actions.push_back(make_intrusive(cards[i], stylesheet)); } } else if (field_name == _("notes")) { for (int i = 0; i < count; ++i) { - actions.push_back(make_shared(cards[i], new_values[i])); + actions.push_back(make_intrusive(cards[i], new_values[i])); } } else if (field_name == _("id")) { for (int i = 0; i < count; ++i) { - actions.push_back(make_shared(*set, cards[i], new_values[i])); + actions.push_back(make_intrusive(*set, cards[i], new_values[i])); } } } @@ -265,7 +265,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { } TextValue* value = dynamic_cast(values[i]); TextValue::ValueType new_value = new_values[i]->toString(); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -280,7 +280,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { } MultipleChoiceValue* value = dynamic_cast(values[i]); MultipleChoiceValue::ValueType new_value = { new_values[i]->toString(), _("") }; - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -295,7 +295,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { } ChoiceValue* value = dynamic_cast(values[i]); ChoiceValue::ValueType new_value = new_values[i]->toString(); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -310,7 +310,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { } PackageChoiceValue* value = dynamic_cast(values[i]); PackageChoiceValue::ValueType new_value = new_values[i]->toString(); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -321,7 +321,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { try { ColorValue::ValueType new_value = new_values[i]->toColor(); ColorValue* value = dynamic_cast(values[i]); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -338,7 +338,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { if (ExternalImage* img = dynamic_cast(new_values[i].get())) { ImageValue* value = dynamic_cast(values[i]); ImageValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), ""); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } @@ -355,7 +355,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) { if (ExternalImage* img = dynamic_cast(new_values[i].get())) { SymbolValue* value = dynamic_cast(values[i]); SymbolValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), ""); - shared_ptr> action = make_shared>(value, new_value); + intrusive_ptr> action = make_intrusive>(value, new_value); action->setCard(cards[i]); actions.push_back(action); } diff --git a/src/gui/bulk_modification_window.hpp b/src/gui/bulk_modification_window.hpp index 6a5f8116..4fe8bafa 100644 --- a/src/gui/bulk_modification_window.hpp +++ b/src/gui/bulk_modification_window.hpp @@ -27,10 +27,10 @@ protected: wxStaticText* modification_description, *modification_errors, *predicate_description, *predicate_errors; wxTextCtrl* modification, *predicate; bool modification_parsed, predicate_parsed; + ScriptP modification_script, predicate_script; wxButton* ok_button; SetP set; Window* parent; - ScriptP modification_script, predicate_script; void onSelectionChange(wxCommandEvent&); void changeSelection(); diff --git a/src/gui/card_link_window.cpp b/src/gui/card_link_window.cpp index 486bd924..32cae5fd 100644 --- a/src/gui/card_link_window.cpp +++ b/src/gui/card_link_window.cpp @@ -9,17 +9,19 @@ #include #include #include +#include #include #include #include #include #include +#include // ----------------------------------------------------------------------------- : ExportCardSelectionChoice CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& selected_card, bool sizer) - : wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) - , set(set), selected_card(selected_card) + : wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxPoint(400,-1), wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , set(set), parent(parent), selected_card(selected_card) { // init controls selected_relation = new wxTextCtrl(this, wxID_ANY, _("")); @@ -39,7 +41,7 @@ CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& sel // init sizers if (sizer) { wxSizer* s = new wxBoxSizer(wxVERTICAL); - s->Add(new wxStaticText(this, -1, _LABEL_("linked cards relation")), 0, wxALL, 8); + s->Add(new wxStaticText(this, -1, _LABEL_1_("linked cards relation", selected_card->identification())), 0, wxALL, 8); s->Add(relation_type, 0, wxEXPAND | (wxALL & ~wxTOP), 8); s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("selected card")), 0, wxALL, 4); s->Add(selected_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8); @@ -95,19 +97,73 @@ void CardLinkWindow::onRelationTypeChange(wxCommandEvent&) { } void CardLinkWindow::onOk(wxCommandEvent&) { + wxBusyCursor wait; + // get the context + CardListBase* card_list_window = dynamic_cast(parent); + if (!card_list_window) { + queue_message(MESSAGE_ERROR, _("Bulk modification must be called from a card list window!")); + EndModal(wxID_ABORT); + return; + } // Perform the linking // The selected_card is the one selected on the main cards tab // The linked_cards are the ones selected in this dialogue window vector linked_cards; getSelection(linked_cards); + // Check that we are not linking to self + if (std::find(linked_cards.begin(), linked_cards.end(), selected_card) != linked_cards.end()) { + queue_message(MESSAGE_WARNING, _ERROR_("cant link to self")); + linked_cards.erase(std::remove(linked_cards.begin(), linked_cards.end(), selected_card), linked_cards.end()); + } + vector linked_uids; + for (int i = 0; i < linked_cards.size(); ++i) { + linked_uids.push_back(linked_cards[i]->uid); + } + // Find free links + unordered_set all_existing_uids; + FOR_EACH(card, set->cards) { + all_existing_uids.insert(card->uid); + } + vector free_link_indexes = selected_card->findFreeLinks(linked_uids, all_existing_uids); + int free_link_count = 0; + for (int i = 0; i < free_link_indexes.size(); ++i) { + if (free_link_indexes[i] >= 0) free_link_count++; + } + if (free_link_count < linked_cards.size()) { + wxMessageDialog dial = wxMessageDialog(this, _ERROR_1_("missing free links", wxString::Format(wxT("%i"), free_link_count))); + dial.ShowModal(); + return; + } + // Get the relations + String selected_relation_string; + String linked_relation_string; int index = relation_type->GetSelection(); if (index >= set->game->card_links.size()) { // Custom type - set->actions.addAction(make_unique(*set, selected_card, linked_cards, selected_relation->GetValue(), linked_relation->GetValue())); + selected_relation_string = selected_relation->GetValue(); + linked_relation_string = linked_relation->GetValue(); } else { CardLinkP link = set->game->card_links[index]; - set->actions.addAction(make_unique(*set, selected_card, linked_cards, link->selected.default_, link->linked.default_)); + selected_relation_string = link->selected.default_; + linked_relation_string = link->linked.default_; } + // Make the actions + vector actions; + for (int i = 0; i < free_link_indexes.size(); ++i) { + if (free_link_indexes[i] >= 0) { + actions.push_back(make_intrusive(*set, selected_card, linked_uids[i], selected_relation_string, free_link_indexes[i])); + } + } + // Find reciprocal free slots and make actions + String& selected_uid = selected_card->uid; + for (int i = 0; i < linked_cards.size(); ++i) { + int free_link_index = linked_cards[i]->findFreeLink(selected_uid, all_existing_uids); + if (free_link_index >= 0) { + actions.push_back(make_intrusive(*set, linked_cards[i], selected_uid, linked_relation_string, free_link_index)); + } + } + // Add action to set + set->actions.addAction(make_unique(actions, set, card_list_window), false); // Done EndModal(wxID_OK); } diff --git a/src/gui/card_link_window.hpp b/src/gui/card_link_window.hpp index 5c66a2a5..e07d1ad2 100644 --- a/src/gui/card_link_window.hpp +++ b/src/gui/card_link_window.hpp @@ -43,6 +43,7 @@ protected: SetP set; CardP selected_card; wxButton* sel_none; + Window* parent; void onRelationTypeChange(wxCommandEvent&); diff --git a/src/gui/control/card_list.cpp b/src/gui/control/card_list.cpp index 920e7fd5..665d449c 100644 --- a/src/gui/control/card_list.cpp +++ b/src/gui/control/card_list.cpp @@ -450,8 +450,12 @@ bool CardListBase::doLink() { } return false; } -bool CardListBase::doUnlink(CardP unlinked_card) { - set->actions.addAction(make_unique(*set, getCard(), unlinked_card)); +bool CardListBase::doUnlink(CardP linked_card) { + CardP selected_card = getCard(); + vector actions; + actions.emplace_back(make_intrusive(*set, selected_card, _(""), _(""), selected_card->findUIDLink(linked_card->uid))); + actions.emplace_back(make_intrusive(*set, linked_card, _(""), _(""), linked_card->findUIDLink(selected_card->uid))); + set->actions.addAction(make_unique(actions, set, this), false); return true; } diff --git a/src/gui/control/card_list.hpp b/src/gui/control/card_list.hpp index 057e66be..a5f672a1 100644 --- a/src/gui/control/card_list.hpp +++ b/src/gui/control/card_list.hpp @@ -105,7 +105,7 @@ public: bool canLink() const; bool doLink(); - bool doUnlink(CardP unlinked_card); + bool doUnlink(CardP linked_card); // --------------------------------------------------- : Set actions diff --git a/src/gui/print_window.cpp b/src/gui/print_window.cpp index 36cdcad5..bdad3cef 100644 --- a/src/gui/print_window.cpp +++ b/src/gui/print_window.cpp @@ -36,11 +36,11 @@ void PrintJob::measure_cards() { FOR_EACH(card, cards) { if (already_measured_cards.find(card) != already_measured_cards.end()) continue; already_measured_cards.emplace(card); - card_layouts.push_back(measure_card(card)); - CardP other_face = card->getLinkedOtherFace(cards); + card_layouts.emplace_back(measure_card(card)); + CardP other_face = card->getLinkedOtherFaceCard(cards); if (other_face && already_measured_cards.find(other_face) == already_measured_cards.end()) { already_measured_cards.emplace(other_face); - card_layouts.push_back(measure_card(other_face)); + card_layouts.emplace_back(measure_card(other_face)); int index = card_layouts.size()-1; card_layouts[index].other_face = index-1; card_layouts[index-1].other_face = index; diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 5bd850fa..7143e954 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -776,7 +776,7 @@ SCRIPT_FUNCTION(get_card_export_settings) { SCRIPT_FUNCTION(get_card_from_uid) { SCRIPT_PARAM_C(Set*, set); SCRIPT_PARAM_C(String, input); - SCRIPT_RETURN(Card::getCardFromUid(*set, input)); + SCRIPT_RETURN(Card::getUIDCard(*set, input)); } SCRIPT_FUNCTION(get_cards_from_link) { @@ -787,7 +787,7 @@ SCRIPT_FUNCTION(get_cards_from_link) { input_card = ic->getValue(); } else if (input->type() == SCRIPT_STRING) { - input_card = Card::getCardFromUid(*set, input->toString()); + input_card = Card::getUIDCard(*set, input->toString()); } if (!input_card) { queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); @@ -795,14 +795,14 @@ SCRIPT_FUNCTION(get_cards_from_link) { } SCRIPT_PARAM(String, linked_relation); ScriptCustomCollectionP ret(new ScriptCustomCollection()); - vector other_cards = input_card->getLinkedCardsFromLink(*set, linked_relation, true); + vector other_cards = input_card->getLinkedRelationCards(*set, linked_relation); if (other_cards.size() > 0) { FOR_EACH(other_card, other_cards) { ret->value.push_back(to_script(other_card)); } } else if (set->game->card_links_alt_names.find(linked_relation) != set->game->card_links_alt_names.end()) { - other_cards = input_card->getLinkedCardsFromLink(*set, set->game->card_links_alt_names[linked_relation], true); + other_cards = input_card->getLinkedRelationCards(*set, set->game->card_links_alt_names[linked_relation]); FOR_EACH(other_card, other_cards) { ret->value.push_back(to_script(other_card)); } @@ -818,13 +818,13 @@ SCRIPT_FUNCTION(get_front_face) { input_card = ic->getValue(); } else if (input->type() == SCRIPT_STRING) { - input_card = Card::getCardFromUid(*set, input->toString()); + input_card = Card::getUIDCard(*set, input->toString()); } if (!input_card) { queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); return script_nil; } - vector other_cards = input_card->getLinkedCardsFromLink(*set, "Front Face", true); + vector other_cards = input_card->getLinkedRelationCards(*set, "Front Face"); if (other_cards.size() == 0) return script_nil; if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple front faces", input_card->identification())); SCRIPT_RETURN(other_cards[0]); @@ -838,13 +838,13 @@ SCRIPT_FUNCTION(get_back_face) { input_card = ic->getValue(); } else if (input->type() == SCRIPT_STRING) { - input_card = Card::getCardFromUid(*set, input->toString()); + input_card = Card::getUIDCard(*set, input->toString()); } if (!input_card) { queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); return script_nil; } - vector other_cards = input_card->getLinkedCardsFromLink(*set, "Back Face", true); + vector other_cards = input_card->getLinkedRelationCards(*set, "Back Face"); if (other_cards.size() == 0) return script_nil; if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple back faces", input_card->identification())); SCRIPT_RETURN(other_cards[0]); @@ -858,7 +858,7 @@ SCRIPT_FUNCTION(add_link) { input_card = ic->getValue(); } else if (input->type() == SCRIPT_STRING) { - input_card = Card::getCardFromUid(*set, input->toString()); + input_card = Card::getUIDCard(*set, input->toString()); } if (!input_card) { queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); @@ -870,7 +870,7 @@ SCRIPT_FUNCTION(add_link) { other_card = c->getValue(); } else if (linked_card->type() == SCRIPT_STRING) { - other_card = Card::getCardFromUid(*set, linked_card->toString()); + other_card = Card::getUIDCard(*set, linked_card->toString()); } if (!other_card) { queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); @@ -878,7 +878,7 @@ SCRIPT_FUNCTION(add_link) { } SCRIPT_PARAM(String, selected_relation); SCRIPT_PARAM(String, linked_relation); - input_card->link(*set, other_card, selected_relation, linked_relation); + input_card->addLink(*set, other_card, selected_relation, linked_relation); SCRIPT_RETURN(other_card); } @@ -891,7 +891,7 @@ SCRIPT_FUNCTION(remove_links) { input_card = ic->getValue(); } else if (input->type() == SCRIPT_STRING) { - input_card = Card::getCardFromUid(*set, input->toString()); + input_card = Card::getUIDCard(*set, input->toString()); } if (!input_card) { queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); @@ -905,7 +905,7 @@ SCRIPT_FUNCTION(remove_links) { other_card = c->getValue(); } else if (linked_card->type() == SCRIPT_STRING) { - other_card = Card::getCardFromUid(*set, linked_card->toString()); + other_card = Card::getUIDCard(*set, linked_card->toString()); } if (!other_card) { queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); @@ -915,12 +915,12 @@ SCRIPT_FUNCTION(remove_links) { SCRIPT_PARAM_DEFAULT(ScriptValueP, linked_relation, script_nil); if (linked_relation != script_nil) { if (linked_relation->type() == SCRIPT_STRING) { - vector other_other_cards = input_card->getLinkedCardsFromLink(*set, linked_relation->toString(), true); + vector other_other_cards = input_card->getLinkedRelationCards(*set, linked_relation->toString()); other_cards.insert(other_cards.end(), other_other_cards.begin(), other_other_cards.end()); } } - input_card->unlink(other_cards); - FOR_EACH(other_card, other_cards) { + FOR_EACH(other_card, other_cards) { + input_card->removeLink(other_card); ret->value.push_back(to_script(other_card)); } return ret; diff --git a/src/script/functions/construction.cpp b/src/script/functions/construction.cpp index b64d4941..33856791 100644 --- a/src/script/functions/construction.cpp +++ b/src/script/functions/construction.cpp @@ -145,8 +145,8 @@ SCRIPT_FUNCTION(add_card_to_set) { Set& _set = *s->getValue(); ScriptObject* c = dynamic_cast*>(input.get()); if (c) { - CardP _card = c->getValue(); - _set.actions.addAction(make_unique(ADD, _set, _card)); + vector _cards { c->getValue() }; + _set.actions.addAction(make_unique(ADD, _set, _cards)); SCRIPT_RETURN(true); } if (input->type() == SCRIPT_COLLECTION) { diff --git a/src/util/action_stack.hpp b/src/util/action_stack.hpp index 082eee69..d10afd69 100644 --- a/src/util/action_stack.hpp +++ b/src/util/action_stack.hpp @@ -12,13 +12,15 @@ #include #include +DECLARE_POINTER_TYPE(Action); + // ----------------------------------------------------------------------------- : Action /// Base class for actions that can be stored in an ActionStack. /** An action is something that can be done to modify an object. * It must store the necessary information to also undo the action. */ -class Action { +class Action : public IntrusivePtrVirtualBase, public IntrusiveFromThis { public: virtual ~Action() {};