diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index e745c528..69b1bae1 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -386,9 +386,13 @@ label: ############################################################## Buttons/checkboxes/choices in the GUI button: - # Set window + # Style panel use for all cards: Use for &all cards + # Keywords panel + insert parameter: Insert Parameter... + refer parameter: Use Parameter... + # Welcome new set: New set open set: Open set diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index bf83e53f..c44c1203 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -898,36 +898,35 @@ keyword parameter type: # By jimrandomh keyword parameter type: name: cost - #insert as: word match: [XYZ0-9WUBRGS/]+|[^(.,\n]|([XYZ0-9WUBRGS/]+,)?[^(.,\n]* - #script: "{mana_sort()}" # TODO : DEBUG keyword parameter type: name: number match: [XYZ0-9]+ -keyword parameter type: - name: number (one, two, ...) - #insert as: number: one, two - match: [XYZ0-9]+ -keyword parameter type: - name: number (a, two, ...) - #insert as: number: a, two - match: [XYZ0-9]+ -keyword parameter type: - name: number (, two, ...) - #insert as: number: , two - match: [XYZ0-9]+ + refer script: + name: normal + description: (1,2,3) + script: \{{input}\} + refer script: + name: as words + description: (one, two, three) + script: \{english_number({input})\} + refer script: + name: as words, use "a" for 1 + description: (a, two, three) + script: \{english_number_a({input})\} + refer script: + name: as words, use "" for 1 + description: (, two, three) + script: \{english_number_multiple({input})\} keyword parameter type: name: action - #insert as: word match: [^(.,\n]+ keyword parameter type: name: name - #insert as: word match: [^(.,\n]+ keyword parameter type: - name: prefix + name: land description: Prefix for things like "walk" - placeholder: land optional: false match: [A-Z][a-z]* example: Forest @@ -1015,7 +1014,7 @@ keyword: reminder: At the beginning of your next upkeep after this permanent comes under your control, sacrifice it unless you pay its mana cost. keyword: keyword: Landcycling - match: prefixcycling cost + match: landcycling cost mode: expert reminder: {param2}, Discard this card: Search your library for a {param1} card, reveal it, and put it into your hand. Then shuffle your library. keyword: @@ -1093,10 +1092,9 @@ keyword: reminder: The removed card is imprinted on this artifact. keyword: keyword: Modular - separator: whitespace [ ] - parameter: number (a, two, ...) + match: Modular number mode: expert - reminder: This comes into play with {param1} +1/+1 counter(s) on it. When it’s put into a graveyard, you may put its +1/+1 counters on target artifact creature. + reminder: This comes into play with {english_number_a(param1)} +1/+1 counter(s) on it. When it’s put into a graveyard, you may put its +1/+1 counters on target artifact creature. keyword: keyword: Scry match: Scry number @@ -1156,9 +1154,9 @@ keyword: keyword: keyword: Bloodthirst separator: whitespace [ ] - parameter: number (a, two, ...) + parameter: number mode: expert - reminder: If an opponent was dealt damage this turn, this creature comes into play with {param1} +1/+1 counter(s) on it. + reminder: If an opponent was dealt damage this turn, this creature comes into play with {english_number_a(param1)} +1/+1 counter(s) on it. keyword: keyword: Replicate separator: whitespace [ ] @@ -1183,7 +1181,7 @@ keyword: reminder: If defending player is wearing any clothing made of denim, this creature is unblockable. keyword: keyword: Landwalk - match: prefixwalk + match: landwalk mode: core reminder: This creature is unblockable as long as defending player controls a {param1}. keyword: diff --git a/src/data/action/keyword.cpp b/src/data/action/keyword.cpp index d250ef0c..e9276730 100644 --- a/src/data/action/keyword.cpp +++ b/src/data/action/keyword.cpp @@ -18,8 +18,9 @@ DECLARE_TYPEOF_COLLECTION(KeywordModeP); // ----------------------------------------------------------------------------- : Add Keyword -AddKeywordAction::AddKeywordAction(Set& set) - : KeywordListAction(set), keyword(new Keyword()) +AddKeywordAction::AddKeywordAction(Adding, Set& set) + : KeywordListAction(set), adding(true), keyword(new Keyword()) + , keyword_id(set.keywords.size()) { // find default mode FOR_EACH(mode, set.game->keyword_modes) { @@ -29,28 +30,8 @@ AddKeywordAction::AddKeywordAction(Set& set) } } } - -AddKeywordAction::AddKeywordAction(Set& set, const KeywordP& keyword) - : KeywordListAction(set), keyword(keyword) -{} - -String AddKeywordAction::getName(bool to_undo) const { - return _("Add keyword"); -} - -void AddKeywordAction::perform(bool to_undo) { - if (!to_undo) { - set.keywords.push_back(keyword); - } else { - assert(!set.keywords.empty()); - set.keywords.pop_back(); - } -} - -// ----------------------------------------------------------------------------- : Remove Keyword - -RemoveKeywordAction::RemoveKeywordAction(Set& set, const KeywordP& keyword) - : KeywordListAction(set), keyword(keyword) +AddKeywordAction::AddKeywordAction(Removing, Set& set, const KeywordP& keyword) + : KeywordListAction(set), adding(false), keyword(keyword) // find the keyword_id of the keyword we want to remove , keyword_id(find(set.keywords.begin(), set.keywords.end(), keyword) - set.keywords.begin()) { @@ -59,18 +40,19 @@ RemoveKeywordAction::RemoveKeywordAction(Set& set, const KeywordP& keyword) } } -String RemoveKeywordAction::getName(bool to_undo) const { - return _("Remove keyword"); +String AddKeywordAction::getName(bool to_undo) const { + return adding ? _("Add keyword") : _("Remove keyword"); } -void RemoveKeywordAction::perform(bool to_undo) { - if (!to_undo) { - assert(keyword_id < set.keywords.size()); - set.keywords.erase(set.keywords.begin() + keyword_id); - } else { +void AddKeywordAction::perform(bool to_undo) { + if (adding != to_undo) { assert(keyword_id <= set.keywords.size()); set.keywords.insert(set.keywords.begin() + keyword_id, keyword); + } else { + assert(keyword_id < set.keywords.size()); + set.keywords.erase(set.keywords.begin() + keyword_id); } + set.keyword_db.clear(); } // ----------------------------------------------------------------------------- : Changing keywords diff --git a/src/data/action/keyword.hpp b/src/data/action/keyword.hpp index 755bd713..05026772 100644 --- a/src/data/action/keyword.hpp +++ b/src/data/action/keyword.hpp @@ -34,32 +34,22 @@ class KeywordListAction : public Action { // therefore we don't need a smart pointer }; -/// Adding a new keyword to a set +enum Adding {ADD}; +enum Removing {REMOVE}; + +/// Adding or removing a keyword from a set class AddKeywordAction : public KeywordListAction { public: - AddKeywordAction(Set& set); - AddKeywordAction(Set& set, const KeywordP& keyword); + AddKeywordAction(Adding, Set& set); + AddKeywordAction(Removing, Set& set, const KeywordP& keyword); virtual String getName(bool to_undo) const; virtual void perform(bool to_undo); //private: - const KeywordP keyword; ///< The new keyword -}; - -// ----------------------------------------------------------------------------- : Remove Keyword - -/// Removing a keyword from a set -class RemoveKeywordAction : public KeywordListAction { - public: - RemoveKeywordAction(Set& set, const KeywordP& keyword); - - virtual String getName(bool to_undo) const; - virtual void perform(bool to_undo); - - //private: - const KeywordP keyword; ///< The removed keyword - const size_t keyword_id; ///< Position of the keyword in the set + const bool adding; ///< Was a keyword added? (as opposed to removed) + const KeywordP keyword; ///< The new/removed keyword + const size_t keyword_id; ///< Position of the keyword in the set }; // ----------------------------------------------------------------------------- : Changing keywords diff --git a/src/data/keyword.cpp b/src/data/keyword.cpp index 3932f589..f9ae6a70 100644 --- a/src/data/keyword.cpp +++ b/src/data/keyword.cpp @@ -23,6 +23,12 @@ KeywordParam::KeywordParam() : optional(true) {} +IMPLEMENT_REFLECTION(ParamReferenceType) { + REFLECT(name); + REFLECT(description); + REFLECT(script); +} + IMPLEMENT_REFLECTION(KeywordParam) { REFLECT(name); REFLECT(description); @@ -31,6 +37,7 @@ IMPLEMENT_REFLECTION(KeywordParam) { REFLECT(match); REFLECT(script); REFLECT(example); + REFLECT(refer_scripts); } IMPLEMENT_REFLECTION(KeywordMode) { REFLECT(name); diff --git a/src/data/keyword.hpp b/src/data/keyword.hpp index a3bd41a4..801589e6 100644 --- a/src/data/keyword.hpp +++ b/src/data/keyword.hpp @@ -22,9 +22,10 @@ class KeywordTrie; // ----------------------------------------------------------------------------- : Keyword parameters class ParamReferenceType { + public: String name; ///< Name of the parameter reference type String description; ///< Description (for status bar) - StringScript code; ///< Code to insert into the reminder text script, input is the actual parameter name + StringScript script; ///< Code to insert into the reminder text script, input is the actual parameter name DECLARE_REFLECTION(); }; @@ -40,7 +41,7 @@ class KeywordParam { String match; ///< Regular expression to match OptionalScript script; ///< Transformation of the value for showing in the reminder text String example; ///< Example for the keyword editor - vector refer_script;///< Way to refer to a parameter from the reminder text script + vector refer_scripts;///< Way to refer to a parameter from the reminder text script DECLARE_REFLECTION(); }; @@ -110,6 +111,8 @@ class KeywordDatabase { void add(const vector&); /// Add a keyword to be matched void add(const Keyword&); + /// Remove a keyword from the database + void remove(const Keyword&); /// Prepare the parameters and match regex for a list of keywords static void prepare_parameters(const vector&, const vector&); diff --git a/src/gui/control/keyword_list.cpp b/src/gui/control/keyword_list.cpp index f276eb03..d0ae1ac5 100644 --- a/src/gui/control/keyword_list.cpp +++ b/src/gui/control/keyword_list.cpp @@ -30,7 +30,7 @@ KeywordList::KeywordList(Window* parent, int id, long additional_style) InsertColumn(0, _LABEL_("keyword"), wxLIST_FORMAT_LEFT, 0); InsertColumn(1, _LABEL_("match"), wxLIST_FORMAT_LEFT, 200); InsertColumn(2, _LABEL_("mode"), wxLIST_FORMAT_LEFT, 60); - InsertColumn(3, _LABEL_("uses"), wxLIST_FORMAT_RIGHT, 80); + InsertColumn(3, _LABEL_("uses"), wxLIST_FORMAT_RIGHT, 50); InsertColumn(4, _LABEL_("reminder"), wxLIST_FORMAT_LEFT, 300); } @@ -50,28 +50,10 @@ void KeywordList::onChangeSet() { void KeywordList::onAction(const Action& action, bool undone) { TYPE_CASE(action, AddKeywordAction) { - if (undone) { - long pos = selected_item_pos; - refreshList(); - if (action.keyword == selected_item) { - // select the next keyword, if not possible, select the last - if (pos + 1 < GetItemCount()) { - selectItemPos(pos, true); - } else { - selectItemPos(GetItemCount() - 1, true); - } - } - } else { + if (action.adding != undone) { // select the new keyword selectItem(action.keyword, false /*list will be refreshed anyway*/, true); refreshList(); - } - } - TYPE_CASE(action, RemoveKeywordAction) { - if (undone) { - // select the re-added keyword - selectItem(action.keyword, false /*list will be refreshed anyway*/, true); - refreshList(); } else { long pos = selected_item_pos; refreshList(); @@ -143,7 +125,7 @@ String KeywordList::OnGetItemText (long pos, long col) const { case 0: return kw.keyword; case 1: return match_string(kw); case 2: return kw.mode; - case 3: return _("TODO"); + case 3: return _("?"); case 4: return kw.reminder.getUnparsed(); default: return wxEmptyString; } diff --git a/src/gui/set/keywords_panel.cpp b/src/gui/set/keywords_panel.cpp index eeb28ab6..d4b8cfbe 100644 --- a/src/gui/set/keywords_panel.cpp +++ b/src/gui/set/keywords_panel.cpp @@ -22,6 +22,7 @@ #include #include +DECLARE_TYPEOF_COLLECTION(ParamReferenceTypeP); DECLARE_TYPEOF_COLLECTION(KeywordParamP); DECLARE_TYPEOF_COLLECTION(KeywordModeP); @@ -39,8 +40,10 @@ KeywordsPanel::KeywordsPanel(Window* parent, int id) reminder = new TextCtrl(panel, wxID_ANY, true); // allow multiline for wordwrap rules = new TextCtrl(panel, wxID_ANY, true); errors = new wxStaticText(panel, wxID_ANY, _("")); + errors->SetForegroundColour(*wxRED); mode = new wxChoice(panel, ID_KEYWORD_MODE, wxDefaultPosition, wxDefaultSize, 0, nullptr); - add_param = new wxButton(panel, ID_KEYWORD_ADD_PARAM, _("Insert Parameter")); + add_param = new wxButton(panel, ID_KEYWORD_ADD_PARAM, _BUTTON_("insert parameter")); + ref_param = new wxButton(panel, ID_KEYWORD_REF_PARAM, _BUTTON_("refer parameter")); // warning about fixed keywords fixedL = new wxStaticText(panel, wxID_ANY, _("")); wxStaticBitmap* fixedI = new wxStaticBitmap(panel, wxID_ANY, wxArtProvider::GetBitmap(wxART_WARNING)); @@ -64,15 +67,15 @@ KeywordsPanel::KeywordsPanel(Window* parent, int id) wxSizer* s2 = new wxBoxSizer(wxVERTICAL); s2->Add(new wxStaticText(panel, wxID_ANY, _("Match:")), 0); s2->Add(match, 0, wxEXPAND | wxTOP, 2); - s2->Add(new wxStaticText(panel, wxID_ANY, _("Parameters:")), 0, wxTOP, 6); s2->Add(add_param, 0, wxALIGN_LEFT | wxTOP, 2); sp->Add(s2, 0, wxEXPAND | wxLEFT, 2); sp->Add(new wxStaticLine(panel), 0, wxEXPAND | wxTOP | wxBOTTOM, 8); wxSizer* s3 = new wxBoxSizer(wxVERTICAL); s3->Add(new wxStaticText(panel, wxID_ANY, _("Reminder:")), 0); s3->Add(reminder, 1, wxEXPAND | wxTOP, 2); + s3->Add(ref_param, 0, wxALIGN_LEFT | wxTOP, 2); s3->Add(errors, 0, wxEXPAND | wxTOP, 4); - s3->Add(new wxStaticText(panel, wxID_ANY, _("Example:")), 0, wxTOP, 6); + //s3->Add(new wxStaticText(panel, wxID_ANY, _("Example:")), 0, wxTOP, 6); sp->Add(s3, 1, wxEXPAND | wxLEFT, 2); sp->Add(new wxStaticLine(panel), 0, wxEXPAND | wxTOP | wxBOTTOM, 8); wxSizer* s4 = new wxBoxSizer(wxVERTICAL); @@ -149,28 +152,62 @@ void KeywordsPanel::onCommand(int id) { list->selectNext(); break; case ID_KEYWORD_ADD: - set->actions.add(new AddKeywordAction(*set)); + set->actions.add(new AddKeywordAction(ADD, *set)); break; case ID_KEYWORD_REMOVE: if (!list->getKeyword()->fixed) { // only remove set keywords - set->actions.add(new RemoveKeywordAction(*set, list->getKeyword())); + set->actions.add(new AddKeywordAction(REMOVE, *set, list->getKeyword())); } break; case ID_KEYWORD_ADD_PARAM: { wxMenu param_menu; int id = ID_PARAM_TYPE_MIN; FOR_EACH(p, set->game->keyword_parameter_types) { - param_menu.Append(id++, p->name); + param_menu.Append(id++, p->name, p->description); } add_param->PopupMenu(¶m_menu, 0, add_param->GetSize().y); break; } + case ID_KEYWORD_REF_PARAM: { + wxMenu ref_menu; + int id = ID_PARAM_REF_MIN; + int param = 0; + FOR_EACH(p, list->getKeyword()->parameters) { + String item = String() << ++param << _(". ") << LEFT_ANGLE_BRACKET << p->name << RIGHT_ANGLE_BRACKET; + if (p->refer_scripts.empty()) { + ref_menu.Append(id++, item); + } else { + wxMenu* submenu = new wxMenu(); + FOR_EACH(r, p->refer_scripts) { + submenu->Append(id++, r->name, r->description); + } + ref_menu.Append(wxID_ANY, item, submenu); + } + } + ref_param->PopupMenu(&ref_menu, 0, ref_param->GetSize().y); + break; + } default: if (id >= ID_PARAM_TYPE_MIN && id < ID_PARAM_TYPE_MAX) { // add parameter KeywordParamP param = set->game->keyword_parameter_types.at(id - ID_PARAM_TYPE_MIN); - + String to_insert = _("") + param->name + _(""); + // TODO + } else if (id >= ID_PARAM_REF_MIN && id < ID_PARAM_REF_MAX) { + /* + int i = ID_PARAM_REF_MIN; + FOR_EACH(p, list->getKeyword()->parameters) { + if (p->refer_scripts.empty()) { + if (i == id) { + // found it + } else { + } + } + } + String to_insert = list->getKeyword()->run_ref_script(id - ID_PARAM_REF_MIN, set->getContext()); + */ + // TODO } } } @@ -201,6 +238,7 @@ void KeywordsPanel::onChangeSet() { rules ->setSet(set); // parameter & mode lists add_param->Enable(false); + ref_param->Enable(false); mode->Clear(); FOR_EACH(m, set->game->keyword_modes) { mode->Append(m->name); @@ -237,6 +275,7 @@ void KeywordsPanel::onKeywordSelect(KeywordSelectEvent& ev) { reminder->setValue(reminder_value); errors->SetLabel(reminder_value->errors); add_param->Enable(!kw.fixed && !set->game->keyword_parameter_types.empty()); + ref_param->Enable(!kw.fixed && !kw.parameters.empty()); mode ->Enable(!kw.fixed && !set->game->keyword_modes.empty()); mode->SetSelection((int)kw.findMode(set->game->keyword_modes)); sp->Layout(); @@ -246,6 +285,7 @@ void KeywordsPanel::onKeywordSelect(KeywordSelectEvent& ev) { rules ->setValue(nullptr); reminder->setValue(nullptr); add_param->Enable(false); + ref_param->Enable(false); mode ->Enable(false); } } diff --git a/src/gui/set/keywords_panel.hpp b/src/gui/set/keywords_panel.hpp index 80ec9b92..43d07aac 100644 --- a/src/gui/set/keywords_panel.hpp +++ b/src/gui/set/keywords_panel.hpp @@ -54,13 +54,7 @@ class KeywordsPanel : public SetWindowPanel { wxStaticText* errors; wxChoice* mode; wxButton* add_param; - /// Controls to edit a parameter - struct ParamEditor { - wxStaticText* label; - wxChoice* type; - bool shown; - }; - vector params; + wxButton* ref_param; // --------------------------------------------------- : Events void onKeywordSelect(KeywordSelectEvent& ev); diff --git a/src/script/script_manager.cpp b/src/script/script_manager.cpp index 13442571..37f1fa1f 100644 --- a/src/script/script_manager.cpp +++ b/src/script/script_manager.cpp @@ -135,6 +135,11 @@ void SetScriptManager::onAction(const Action& action, bool undone) { // is it a keyword's fake value? KeywordTextValue* value = dynamic_cast(action.valueP.get()); if (value) { + if (value->underlying == &value->keyword.match) { + // changed the 'match' string of a keyword, rebuild database and regex so matching is correct + value->keyword.prepare(set.game->keyword_parameter_types, true); + set.keyword_db.clear(); + } updateAllDependend(set.game->dependent_scripts_keywords); return; } diff --git a/src/util/window_id.hpp b/src/util/window_id.hpp index 26358982..b7fd1cc0 100644 --- a/src/util/window_id.hpp +++ b/src/util/window_id.hpp @@ -160,6 +160,8 @@ enum ChildMenuID { , ID_KEYWORD_MODE , ID_PARAM_TYPE_MIN = 3101 , ID_PARAM_TYPE_MAX = 3199 +, ID_PARAM_REF_MIN = 3201 +, ID_PARAM_REF_MAX = 3299 // Statistics panel , ID_FIELD_LIST = 3031