From aeed54e4ba8472c0e2683c9bf470fe7d0069da61 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: Tue, 16 Dec 2025 19:05:33 +0100 Subject: [PATCH] localize card links, add_link, remove_links --- doc/function/add_link.txt | 18 ++ doc/function/get_cards_from_link.txt | 8 +- doc/function/has_link.txt | 8 +- doc/function/remove_links.txt | 16 ++ src/data/card.cpp | 40 +++- src/data/card.hpp | 13 +- src/data/card_link.cpp | 23 +++ src/data/card_link.hpp | 30 +++ src/data/game.cpp | 57 +++++- src/data/game.hpp | 4 +- src/gui/card_link_window.cpp | 24 ++- src/gui/print_window.cpp | 2 +- src/script/functions/basic.cpp | 271 +++++++++++++-------------- src/util/string.cpp | 2 +- src/util/string.hpp | 2 +- 15 files changed, 352 insertions(+), 166 deletions(-) create mode 100644 doc/function/add_link.txt create mode 100644 doc/function/remove_links.txt create mode 100644 src/data/card_link.cpp create mode 100644 src/data/card_link.hpp diff --git a/doc/function/add_link.txt b/doc/function/add_link.txt new file mode 100644 index 00000000..dcd9e96a --- /dev/null +++ b/doc/function/add_link.txt @@ -0,0 +1,18 @@ +Function: add_link + +--Usage-- +> add_link(input: card, selected_relation: "link type", linked_card: other_card, linked_relation: "link type") + +Links the input card and the linked_card together. The link is reciprocal if possible. + +(Adds the linked_card to the input card's links, and gives it the linked_relation. Adds the input card to the linked_card's links, and gives it the selected_relation.) + +Returns the linked_card, or nil if the linked_card was not found. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:card]] or [[type:string]] One of the two cards we will link, or the uid of a card. +| @selected_relation@ [[type:string]] The type of link we will give to the input card. +| @linked_card@ [[type:card]] or [[type:string]] The other of the two cards we will link, or the uid of the other card. +| @linked_relation@ [[type:string]] The type of link we will give to the linked card. +| @set@ [[type:set]] The set in which to look for the other card. This can be omitted since 'set' is a predefined variable. diff --git a/doc/function/get_cards_from_link.txt b/doc/function/get_cards_from_link.txt index 31c0f91c..81802ee9 100644 --- a/doc/function/get_cards_from_link.txt +++ b/doc/function/get_cards_from_link.txt @@ -1,13 +1,13 @@ Function: get_cards_from_link --Usage-- -> get_cards_from_link(card: card, input: "link type") +> get_cards_from_link(input: card, linked_relation: "link type") Inspects a given [[type:card]]'s links to find those of the given type, and returns an array containing the linked cards. Returns an empty array if no link of the given type, or no card was found. --Parameters-- ! Parameter Type Description -| @input@ [[type:string]] The type of link we want to find. -| @card@ [[type:card]] The card whose links we'll inspect. -| @set@ [[type:set]] The set in which to look. This can be omited since 'set' is a predefined variable. +| @input@ [[type:card]] The card whose links we'll inspect. +| @linked_relation@ [[type:string]] The type of link we want to find. +| @set@ [[type:set]] The set in which to look. This can be omitted since 'set' is a predefined variable. diff --git a/doc/function/has_link.txt b/doc/function/has_link.txt index 99af105c..c9787ac4 100644 --- a/doc/function/has_link.txt +++ b/doc/function/has_link.txt @@ -1,14 +1,14 @@ Function: has_link --Usage-- -> has_link(card: card, "link type") +> has_link(input: card, linked_relation: "link type") Inspects a given [[type:card]]'s links to find one of the given type. Returns true if such a link was found, false otherwise. -Note that this function does not check if the linked card exists in the set. For that, use get_card_from_link. +Note that this function does not check if the linked card exists in the set. For that, use get_cards_from_link. --Parameters-- ! Parameter Type Description -| @input@ [[type:string]] The type of link we want to find. -| @card@ [[type:card]] The card whose links we'll inspect. +| @input@ [[type:card]] The card whose links we'll inspect. +| @linked_relation@ [[type:string]] The type of link we want to find. diff --git a/doc/function/remove_links.txt b/doc/function/remove_links.txt new file mode 100644 index 00000000..ef8a8208 --- /dev/null +++ b/doc/function/remove_links.txt @@ -0,0 +1,16 @@ +Function: remove_links + +--Usage-- +> remove_links(input: card, linked_card: other card) +> remove_links(input: card, linked_relation: "link type") + +Breaks the link between the input card and the linked_card. If a linked_relation is given, breaks the link between the input card and all cards with the linked_relation. + +Returns an array containing all the cards that were unlinked. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:card]] or [[type:string]] One of the two cards we will unlink, or the uid of a card. +| @linked_card@ [[type:card]] or [[type:string]] The other of the two cards we will unlink, or the uid of the other card. +| @linked_relation@ [[type:string]] The type of link we will break. +| @set@ [[type:set]] The set in which to look for the other card. This can be omitted since 'set' is a predefined variable. diff --git a/src/data/card.cpp b/src/data/card.cpp index f90a17ed..16f424aa 100644 --- a/src/data/card.cpp +++ b/src/data/card.cpp @@ -156,8 +156,7 @@ void Card::link(const Set& set, const vector& linked_cards, const String& ss << all_missed_cards[pos]->identification(); if (pos < all_missed_cards.size() - 1) ss << ", "; }; - String wxString(ss.str().c_str(), wxConvUTF8); - queue_message(MESSAGE_WARNING, wxString); + queue_message(MESSAGE_WARNING, wxString(ss.str().c_str())); } } @@ -284,7 +283,7 @@ vector> Card::getLinkedCards(const Set& set) { return getLinkedCards(set.cards); } -CardP Card::getOtherFace(const vector& cards) { +CardP Card::getLinkedOtherFace(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); @@ -295,8 +294,39 @@ CardP Card::getOtherFace(const vector& cards) { } return nullptr; } -CardP Card::getOtherFace(const Set& set) { - return getOtherFace(set.cards); +CardP Card::getLinkedOtherFace(const Set& set) { + return getLinkedOtherFace(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 = _(""); + } + } + } + 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) { + 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); } IndexMap& Card::extraDataFor(const StyleSheet& stylesheet) { diff --git a/src/data/card.hpp b/src/data/card.hpp index 92fb9f83..38276a98 100644 --- a/src/data/card.hpp +++ b/src/data/card.hpp @@ -90,9 +90,16 @@ public: void updateLink(String old_uid, String new_uid); vector> getLinkedCards(const vector& cards); - vector> getLinkedCards(const Set& set); - CardP getOtherFace(const vector& cards); - CardP getOtherFace(const Set& set); + 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) { diff --git a/src/data/card_link.cpp b/src/data/card_link.cpp new file mode 100644 index 00000000..375e43e6 --- /dev/null +++ b/src/data/card_link.cpp @@ -0,0 +1,23 @@ +//+----------------------------------------------------------------------------+ +//| Description: Magic Set Editor - Program to make Magic (tm) cards | +//| Copyright: (C) Twan van Laarhoven and the other MSE developers | +//| License: GNU General Public License 2 or later (see file COPYING) | +//+----------------------------------------------------------------------------+ + +// ----------------------------------------------------------------------------- : Includes + +#include +#include + +// ----------------------------------------------------------------------------- : CardLink + +CardLink::CardLink() {} + +String CardLink::name() { + return selected.get() + _(" // ") + linked.get(); +} + +IMPLEMENT_REFLECTION_NO_GET_MEMBER(CardLink) { + REFLECT_LOCALIZED(selected); + REFLECT_LOCALIZED(linked); +} diff --git a/src/data/card_link.hpp b/src/data/card_link.hpp new file mode 100644 index 00000000..fb122db6 --- /dev/null +++ b/src/data/card_link.hpp @@ -0,0 +1,30 @@ +//+----------------------------------------------------------------------------+ +//| Description: Magic Set Editor - Program to make Magic (tm) cards | +//| Copyright: (C) Twan van Laarhoven and the other MSE developers | +//| License: GNU General Public License 2 or later (see file COPYING) | +//+----------------------------------------------------------------------------+ + +#pragma once + +// ----------------------------------------------------------------------------- : Includes + +#include +#include +#include + +DECLARE_POINTER_TYPE(CardLink); + +// ----------------------------------------------------------------------------- : Card Link + +/// Information on a link between two cards in a set +class CardLink : public IntrusivePtrBase { +public: + CardLink(); + + LocalizedString selected; ///< Type of link for the selected card + LocalizedString linked; ///< Type of link for the linked card + + String name(); +private: + DECLARE_REFLECTION(); +}; diff --git a/src/data/game.cpp b/src/data/game.cpp index 9fb6996b..6edd4d79 100644 --- a/src/data/game.cpp +++ b/src/data/game.cpp @@ -1,4 +1,4 @@ -//+----------------------------------------------------------------------------+ +//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) Twan van Laarhoven and the other MSE developers | //| License: GNU General Public License 2 or later (see file COPYING) | @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,60 @@ void Game::validate(Version v) { card_fields_alt_names.emplace(unified_name, field->name); } } + } + // front face/back face card link + card_links.insert(card_links.begin(), make_intrusive()); + card_links[0]->selected.default_ = _("Front Face"); + card_links[0]->selected.translations = std::unordered_map{ + {_("ch-s"), _("卡片正面")}, + {_("ch-t"), _("卡片正面")}, + {_("da"), _("Forside")}, + {_("de"), _("Vorderseite")}, + {_("en"), _("Front Face")}, + {_("es"), _("Cara frontal")}, + {_("fr"), _("Face Avant")}, + {_("it"), _("Fronte")}, + {_("jp"), _("カードの表面")}, + {_("ko"), _("카드 앞면")}, + {_("pl"), _("Przód")}, + {_("pt-br"), _("Frente")}, + {_("ru"), _("Лицевая сторона")} + }; + card_links[0]->linked.default_ = _("Back Face"); + card_links[0]->linked.translations = std::unordered_map{ + {_("ch-s"), _("卡片背面")}, + {_("ch-t"), _("卡片背面")}, + {_("da"), _("Bagside")}, + {_("de"), _("Rückseite")}, + {_("en"), _("Back Face")}, + {_("es"), _("Cara posterior")}, + {_("fr"), _("Face Arrière")}, + {_("it"), _("Retro")}, + {_("jp"), _("カードの裏面")}, + {_("ko"), _("카드 뒷면")}, + {_("pl"), _("Tył")}, + {_("pt-br"), _("Verso")}, + {_("ru"), _("Обратная сторона")} + }; + // localized card link names map + for (auto it = card_links.begin(); it != card_links.end(); ++it) { + CardLinkP link = *it; + String selected_default = link->selected.default_; + for (auto selected_it = link->selected.translations.begin(); selected_it != link->selected.translations.end(); selected_it++) { + String selected_tr = unified_form(selected_it->second); + if (card_links_alt_names.find(selected_tr) != card_links_alt_names.end() && card_links_alt_names[selected_tr] != selected_default) { + queue_message(MESSAGE_WARNING, _ERROR_3_("link duplicate", selected_tr, card_links_alt_names[selected_tr], selected_default)); + } + card_links_alt_names.emplace(selected_tr, selected_default); + } + String linked_default = link->linked.default_; + for (auto linked_it = link->linked.translations.begin(); linked_it != link->linked.translations.end(); linked_it++) { + String linked_tr = unified_form(linked_it->second); + if (card_links_alt_names.find(linked_tr) != card_links_alt_names.end() && card_links_alt_names[linked_tr] != linked_default) { + queue_message(MESSAGE_WARNING, _ERROR_3_("link duplicate", linked_tr, card_links_alt_names[linked_tr], linked_default)); + } + card_links_alt_names.emplace(linked_tr, linked_default); + } } } diff --git a/src/data/game.hpp b/src/data/game.hpp index 5b422573..f0c96bb9 100644 --- a/src/data/game.hpp +++ b/src/data/game.hpp @@ -19,6 +19,7 @@ DECLARE_POINTER_TYPE(Style); DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(StatsDimension); DECLARE_POINTER_TYPE(StatsCategory); +DECLARE_POINTER_TYPE(CardLink); DECLARE_POINTER_TYPE(PackType); DECLARE_POINTER_TYPE(KeywordParam); DECLARE_POINTER_TYPE(KeywordMode); @@ -41,7 +42,7 @@ public: vector set_fields; ///< Fields for set information IndexMap default_set_style; ///< Default style for the set fields, because it is often the same vector card_fields; ///< Fields on each card - vector card_links; ///< Possible links between cards + vector card_links; ///< Possible links between cards OptionalScript card_list_color_script; ///< Script that determines the color of items in the card list OptionalScript import_script; ///< Script applied as the last step of the new_card function vector json_paths; ///< Paths inside JSON files to find the card array @@ -52,6 +53,7 @@ public: vector add_cards_scripts; ///< Scripts for adding multiple cards to the set vector auto_replaces; ///< Things to autoreplace in textboxes map card_fields_alt_names; ///< Other names that fields might go by, for example in CSV files + map card_links_alt_names; ///< Localized names that card links go by bool has_keywords; ///< Does this game use keywords? OptionalScript keyword_match_script; ///< For the keyword editor vector keyword_parameter_types;///< Types of keyword parameters diff --git a/src/gui/card_link_window.cpp b/src/gui/card_link_window.cpp index f17fb00b..25a41441 100644 --- a/src/gui/card_link_window.cpp +++ b/src/gui/card_link_window.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -25,9 +26,8 @@ CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& sel linked_relation = new wxTextCtrl(this, wxID_ANY, _("")); relation_type = new wxChoice(this, ID_CARD_LINK_TYPE, wxDefaultPosition, wxDefaultSize, 0, nullptr); relation_type->Clear(); - relation_type->Append("Front Face // Back Face"); FOR_EACH(link, set->game->card_links) { - relation_type->Append(link); + relation_type->Append(link->name()); } relation_type->Append(_LABEL_("custom link")); relation_type->SetSelection(0); @@ -69,19 +69,18 @@ void CardLinkWindow::setSelection(const vector& cards) { list->setSelection(cards); } void CardLinkWindow::setRelationType() { - int sel = relation_type->GetSelection(); - if (sel == relation_type->GetCount() - 1) { // Custom type + int index = relation_type->GetSelection(); + if (index >= set->game->card_links.size()) { // Custom type selected_relation->ChangeValue(_LABEL_("custom link selected")); selected_relation->Enable(); linked_relation->ChangeValue(_LABEL_("custom link linked")); linked_relation->Enable(); } else { - String relation = relation_type->GetString(sel); - int delimiter_pos = relation.find("//"); - selected_relation->ChangeValue(relation.substr(0, delimiter_pos).Trim().Trim(false)); + CardLinkP link = set->game->card_links[index]; + selected_relation->ChangeValue(link->selected.get()); selected_relation->Enable(false); - linked_relation->ChangeValue(delimiter_pos + 2 < relation.Length() ? relation.substr(delimiter_pos + 2).Trim().Trim(false) : _LABEL_("custom link undefined")); + linked_relation->ChangeValue(link->linked.get()); linked_relation->Enable(false); } } @@ -100,7 +99,14 @@ void CardLinkWindow::onOk(wxCommandEvent&) { // The linked_cards are the ones selected in this dialogue window vector linked_cards; getSelection(linked_cards); - set->actions.addAction(make_unique(*set, selected_card, linked_cards, selected_relation->GetValue(), linked_relation->GetValue())); + 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())); + } + else { + CardLinkP link = set->game->card_links[index]; + set->actions.addAction(make_unique(*set, selected_card, linked_cards, link->selected.default_, link->linked.default_)); + } // Done EndModal(wxID_OK); } diff --git a/src/gui/print_window.cpp b/src/gui/print_window.cpp index 27d1fa2b..bda7d822 100644 --- a/src/gui/print_window.cpp +++ b/src/gui/print_window.cpp @@ -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.push_back(measure_card(card)); - CardP other_face = card->getOtherFace(cards); + CardP other_face = card->getLinkedOtherFace(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)); diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 01625cc1..4ca5c8d3 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -778,168 +778,165 @@ SCRIPT_FUNCTION(get_card_export_settings) { SCRIPT_FUNCTION(get_card_from_uid) { SCRIPT_PARAM_C(Set*, set); SCRIPT_PARAM_C(String, input); - FOR_EACH(other_card, set->cards) { - if (other_card->uid == input) SCRIPT_RETURN(other_card); - } - return script_nil; + SCRIPT_RETURN(Card::getCardFromUid(*set, input)); } SCRIPT_FUNCTION(get_cards_from_link) { SCRIPT_PARAM_C(Set*, set); - SCRIPT_PARAM_C(CardP, card); - SCRIPT_PARAM_C(String, input); - ScriptCustomCollectionP ret(new ScriptCustomCollection()); - String trimmed_input = input.Trim().Trim(false); - if (card->linked_relation_1 == trimmed_input) { - String uid = card->linked_card_1; - bool found = false; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) { - ret->value.push_back(to_script(other_card)); - found = true; - break; - } - } - if (!found) { - card->linked_relation_1 = _(""); - card->linked_card_1 = _(""); - } + SCRIPT_PARAM_C(ScriptValueP, input); + CardP input_card = nullptr; + if (ScriptObject* ic = dynamic_cast*>(input.get())) { + input_card = ic->getValue(); } - if (card->linked_relation_2 == trimmed_input) { - String uid = card->linked_card_2; - bool found = false; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) { - ret->value.push_back(to_script(other_card)); - found = true; - break; - } - } - if (!found) { - card->linked_relation_2 = _(""); - card->linked_card_2 = _(""); - } + else if (input->type() == SCRIPT_STRING) { + input_card = Card::getCardFromUid(*set, input->toString()); } - if (card->linked_relation_3 == trimmed_input) { - String uid = card->linked_card_3; - bool found = false; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) { - ret->value.push_back(to_script(other_card)); - found = true; - break; - } - } - if (!found) { - card->linked_relation_3 = _(""); - card->linked_card_3 = _(""); - } - } - if (card->linked_relation_4 == trimmed_input) { - String uid = card->linked_card_4; - bool found = false; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) { - ret->value.push_back(to_script(other_card)); - found = true; - break; - } - } - if (!found) { - card->linked_relation_4 = _(""); - card->linked_card_4 = _(""); + if (!input_card) { + queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); + return script_nil; + } + SCRIPT_PARAM(String, linked_relation); + ScriptCustomCollectionP ret(new ScriptCustomCollection()); + vector other_cards = input_card->getLinkedCardsFromLink(*set, linked_relation, true); + 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); + FOR_EACH(other_card, other_cards) { + ret->value.push_back(to_script(other_card)); + } + } return ret; -} +} SCRIPT_FUNCTION(get_front_face) { SCRIPT_PARAM_C(Set*, set); - SCRIPT_PARAM_C(CardP, input); - if (input->linked_relation_1 == _("Front Face")) { - String uid = input->linked_card_1; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_1 = _(""); - input->linked_card_1 = _(""); + SCRIPT_PARAM_C(ScriptValueP, input); + CardP input_card = nullptr; + if (ScriptObject* ic = dynamic_cast*>(input.get())) { + input_card = ic->getValue(); } - if (input->linked_relation_2 == _("Front Face")) { - String uid = input->linked_card_2; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_2 = _(""); - input->linked_card_2 = _(""); + else if (input->type() == SCRIPT_STRING) { + input_card = Card::getCardFromUid(*set, input->toString()); } - if (input->linked_relation_3 == _("Front Face")) { - String uid = input->linked_card_3; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_3 = _(""); - input->linked_card_3 = _(""); + if (!input_card) { + queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); + return script_nil; } - if (input->linked_relation_4 == _("Front Face")) { - String uid = input->linked_card_4; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_4 = _(""); - input->linked_card_4 = _(""); - } - return script_nil; + vector other_cards = input_card->getLinkedCardsFromLink(*set, "Front Face", true); + 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]); } SCRIPT_FUNCTION(get_back_face) { SCRIPT_PARAM_C(Set*, set); - SCRIPT_PARAM_C(CardP, input); - if (input->linked_relation_1 == _("Back Face")) { - String uid = input->linked_card_1; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_1 = _(""); - input->linked_card_1 = _(""); + SCRIPT_PARAM_C(ScriptValueP, input); + CardP input_card = nullptr; + if (ScriptObject* ic = dynamic_cast*>(input.get())) { + input_card = ic->getValue(); } - if (input->linked_relation_2 == _("Back Face")) { - String uid = input->linked_card_2; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_2 = _(""); - input->linked_card_2 = _(""); + else if (input->type() == SCRIPT_STRING) { + input_card = Card::getCardFromUid(*set, input->toString()); } - if (input->linked_relation_3 == _("Back Face")) { - String uid = input->linked_card_3; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_3 = _(""); - input->linked_card_3 = _(""); + if (!input_card) { + queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); + return script_nil; } - if (input->linked_relation_4 == _("Back Face")) { - String uid = input->linked_card_4; - FOR_EACH(other_card, set->cards) { - if (other_card->uid == uid) SCRIPT_RETURN(other_card); - } - input->linked_relation_4 = _(""); - input->linked_card_4 = _(""); + vector other_cards = input_card->getLinkedCardsFromLink(*set, "Back Face", true); + 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]); +} + +SCRIPT_FUNCTION(add_link) { + SCRIPT_PARAM_C(Set*, set); + SCRIPT_PARAM_C(ScriptValueP, input); + CardP input_card = nullptr; + if (ScriptObject* ic = dynamic_cast*>(input.get())) { + input_card = ic->getValue(); + } + else if (input->type() == SCRIPT_STRING) { + input_card = Card::getCardFromUid(*set, input->toString()); + } + if (!input_card) { + queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); + return script_nil; } - return script_nil; + SCRIPT_PARAM(ScriptValueP, linked_card); + CardP other_card = nullptr; + if (ScriptObject* c = dynamic_cast*>(linked_card.get())) { + other_card = c->getValue(); + } + else if (linked_card->type() == SCRIPT_STRING) { + other_card = Card::getCardFromUid(*set, linked_card->toString()); + } + if (!other_card) { + queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); + return script_nil; + } + SCRIPT_PARAM(String, selected_relation); + SCRIPT_PARAM(String, linked_relation); + input_card->link(*set, other_card, selected_relation, linked_relation); + SCRIPT_RETURN(other_card); +} + +SCRIPT_FUNCTION(remove_links) { + SCRIPT_PARAM_C(Set*, set); + ScriptCustomCollectionP ret(new ScriptCustomCollection()); + SCRIPT_PARAM_C(ScriptValueP, input); + CardP input_card = nullptr; + if (ScriptObject* ic = dynamic_cast*>(input.get())) { + input_card = ic->getValue(); + } + else if (input->type() == SCRIPT_STRING) { + input_card = Card::getCardFromUid(*set, input->toString()); + } + if (!input_card) { + queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); + return ret; + } + vector other_cards; + SCRIPT_PARAM_DEFAULT(ScriptValueP, linked_card, script_nil); + if (linked_card != script_nil) { + CardP other_card = nullptr; + if (ScriptObject* c = dynamic_cast*>(linked_card.get())) { + other_card = c->getValue(); + } + else if (linked_card->type() == SCRIPT_STRING) { + other_card = Card::getCardFromUid(*set, linked_card->toString()); + } + if (!other_card) { + queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); + } + else other_cards.push_back(other_card); + } + 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); + 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) { + ret->value.push_back(to_script(other_card)); + } + return ret; } SCRIPT_FUNCTION(has_link) { - SCRIPT_PARAM_C(CardP, card); - SCRIPT_PARAM_C(String, input); - String trimmed_input = input.Trim().Trim(false); - if ( - card->linked_relation_1 == trimmed_input || - card->linked_relation_2 == trimmed_input || - card->linked_relation_3 == trimmed_input || - card->linked_relation_4 == trimmed_input - ) SCRIPT_RETURN(true); - SCRIPT_RETURN(false); + SCRIPT_PARAM_C(CardP, input); + SCRIPT_PARAM(String, linked_relation); + SCRIPT_RETURN( + input->linked_relation_1 == linked_relation || + input->linked_relation_2 == linked_relation || + input->linked_relation_3 == linked_relation || + input->linked_relation_4 == linked_relation + ); } // ----------------------------------------------------------------------------- : Keywords @@ -1045,6 +1042,8 @@ void init_script_basic_functions(Context& ctx) { ctx.setVariable(_("get_back_face"), script_get_back_face); ctx.setVariable(_("get_front_face"), script_get_front_face); ctx.setVariable(_("has_link"), script_has_link); + ctx.setVariable(_("add_link"), script_add_link); + ctx.setVariable(_("remove_links"), script_remove_links); // math ctx.setVariable(_("abs"), script_abs); ctx.setVariable(_("random_real"), script_random_real); diff --git a/src/util/string.cpp b/src/util/string.cpp index 832ff227..576c4e24 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -137,7 +137,7 @@ void uncanonical_name_form_in_place(String& str) { } } -String unified_form(String& str) { +String unified_form(String str) { str = trim(str); for (String::iterator it = str.begin(); it != str.end(); ++it) { if (*it == ' ') *it = '_'; diff --git a/src/util/string.hpp b/src/util/string.hpp index 199f5315..8c282616 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -244,7 +244,7 @@ inline String uncanonical_name_form(String s) { } /// Convert a field name to canonical form, then to lower case, then trim it -String unified_form(String&); +String unified_form(String); /// Convert a field name to a string that can be shown to the user String name_to_caption(const String&);