diff --git a/Makefile.am b/Makefile.am index f8a69730..04c33cb3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,6 +78,8 @@ magicseteditor_SOURCES += ./src/gui/symbol/select_editor.cpp magicseteditor_SOURCES += ./src/gui/symbol/editor.cpp magicseteditor_SOURCES += ./src/gui/symbol/basic_shape_editor.cpp magicseteditor_SOURCES += ./src/gui/symbol/window.cpp +magicseteditor_SOURCES += ./src/gui/symbol/symmetry_editor.cpp +magicseteditor_SOURCES += ./src/gui/symbol/selection.cpp magicseteditor_SOURCES += ./src/gui/about_window.cpp magicseteditor_SOURCES += ./src/gui/new_window.cpp magicseteditor_SOURCES += ./src/gui/drop_down_list.cpp diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index e8d87496..6791542d 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -1,4 +1,4 @@ -mse version: 0.3.4 +mse version: 0.3.4 short name: Magic full name: Magic the Gathering icon: card-back.png @@ -138,32 +138,50 @@ init script: if count >= 2 then "yes" else "no" } + is_null_cost := { + input == "" or input == "0" + }; + is_color := { + name := color_name(); + contains(object.casting_cost, match:input) or (chosen(choice: name, object.card_color) and is_null_cost(object.casting_cost)) + }; + monocolor_sort := { + if is_color(object:input, "W") then "A" + else if is_color(object:input, "U") then "B" + else if is_color(object:input, "B") then "C" + else if is_color(object:input, "R") then "D" + else if is_color(object:input, "G") then "E" + else false + }; sort_index := { - if card.casting_cost_2 !="" and card.card_color != card.card_color_2 then "H" # splits that aren't the same color on both sides - else if is_land (card.card_color) then (if card.rarity != "basic land" then "J" else "K") # lands, basic and non-basic - else if contains(card.casting_cost,match:"/") then "G" # hybrid cards, hybrid artifacts - else if is_multicolor_cost(card.casting_cost) == "yes" then "F" # multicolor cards, multicolor artifacts - else if contains(card.casting_cost,match:"W") then "A" # white cards, white artifacts - else if contains(card.casting_cost,match:"U") then "B" # blue cards, blue artifacts - else if contains(card.casting_cost,match:"B") then "C" # black cards, black artifacts - else if contains(card.casting_cost,match:"R") then "D" # red cards, red artifacts - else if contains(card.casting_cost,match:"G") then "E" # green cards, green artifacts - else if is_multicolor(card.card_color) and card.casting_cost == "0" then "F" # free multicolor cards - else if is_multicolor(card.card_color) and card.casting_cost == "" then "F" # costless multicolor cards - else if is_hybrid (card.card_color) and card.casting_cost == "0" then "G" # free hybrid cards - else if is_hybrid (card.card_color) and card.casting_cost == "" then "G" # costless hybrid cards - else if chosen(choice:"white", card.card_color) and card.casting_cost== "0" then "A" # free white cards - else if chosen(choice:"white", card.card_color) and card.casting_cost == "" then "A" # costless white cards - else if chosen(choice:"blue", card.card_color) and card.casting_cost == "0" then "B" # free blue cards - else if chosen(choice:"blue", card.card_color) and card.casting_cost == "" then "B" # costless blue cards - else if chosen(choice:"black", card.card_color) and card.casting_cost == "0" then "C" # free black cards - else if chosen(choice:"black", card.card_color) and card.casting_cost == "" then "C" # costless black cards - else if chosen(choice:"red", card.card_color) and card.casting_cost == "0" then "D" # free red cards - else if chosen(choice:"red", card.card_color) and card.casting_cost == "" then "D" # costless red cards - else if chosen(choice:"green", card.card_color) and card.casting_cost == "0" then "E" # free green cards - else if chosen(choice:"green", card.card_color) and card.casting_cost == "" then "E" # costless green cards - else "I" # colorless, artifacts + if input.casting_cost_2 != "" and input.card_color != input.card_color_2 then "H" # multicolor splits + else if is_land (input.card_color) then (if input.rarity != "basic land" then "J" else "K") # lands, both basic and non-basic + else if contains(input.casting_cost, match:"/") then "G" # generic multicolor cards + else if is_multicolor_cost(input.casting_cost) == "yes" then "F" # multicolors (including artifacts) + else if is_multicolor(input.card_color) and is_null_cost(input.casting_cost) then "F" # costless multicolor cards + else if is_hybrid (input.card_color) and is_null_cost(input.casting_cost) then "G" # costless hybrid cards + else if monocolor_sort() != false then monocolor_sort() # regular monocolor cards + }; + rarity_sort := { + if set.special_rarity_sort == "normally" or input.rarity != "special" then sort_index() + else "Z" + sort_index() + }; + collation_set := { + if set.special_rarity_sort != "separate numbering" then filter := { true } + else if card.rarity == "special" then filter := { input.rarity == "special" } + else filter := {input.rarity != "special"} + filter_list (set, filter: filter) + }; + + card_number := { + sort_set := sort_list (collation_set(), order_by: {rarity_sort() + input.name} ) + position ( + of: card + in: sort_set + ) + 1 + + "/" + + number_of_items(in: sort_set) }; # The color of a card @@ -243,7 +261,7 @@ init script: |[ ][^ .,]*$ # still typing... |[ ]( or | and | in | less | more | to ) # or next word is ... ) - ) + ) | # keyword argument that is declared as mana | [ ]* # keyword argument that is declared as cost | , # keyword argument that is declared as cost @@ -382,7 +400,7 @@ init script: "(?ix) # case insensitive, ignore whitespace (^|[[:space:]\"(]) # start of a word ( (^)(A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|Æ) - ) + ) "; # Used in FPM and Future Sight artist_line_filter := @@ -523,6 +541,14 @@ set field: choice: no description: Use gradients on multicolor cards by default, you can always change it be clicking on the card border. initial: no +set field: + type: choice + name: special rarity sort + description: Determines how cards with special rarity are sorted. + choice: normally + choice: last + choice: separate numbering + initial: last ############################# Default style @@ -670,7 +696,6 @@ card field: name: casting cost icon: stats/casting_cost.png script: mana_filter(value) - move cursor with sort: true card list visible: true card list column: 2 card list alignment: right @@ -802,13 +827,9 @@ card field: name: card number save value: false script: - position( - of: card - in: set - order_by: { sort_index() + card.name } - ) + 1 - + "/" + - number_of_items(in: set) + card_number() + sort script: + rarity_sort(card) + card.name card list visible: true card list column: 10 card list width: 50 @@ -901,7 +922,6 @@ card field: name: casting cost 2 icon: stats/casting_cost.png script: mana_filter(value) - move cursor with sort: true card list alignment: right card list width: 50 card list name: CC diff --git a/src/data/field.cpp b/src/data/field.cpp index d89de9f4..58f58719 100644 --- a/src/data/field.cpp +++ b/src/data/field.cpp @@ -55,6 +55,7 @@ IMPLEMENT_REFLECTION(Field) { REFLECT(card_list_visible); REFLECT(card_list_allow); REFLECT(card_list_name); + REFLECT(sort_script); REFLECT_IF_READING if(card_list_name.empty()) card_list_name = name; REFLECT_N("card_list_alignment", card_list_align); REFLECT(tab_index); @@ -215,7 +216,8 @@ bool Value::equals(const Value* that) { } void init_object(const FieldP& field, ValueP& value) { - if (!value) value = field->newValue(field); + if (!value) + value = field->newValue(field); } template <> ValueP read_new(Reader&) { throw InternalError(_("IndexMap contains nullptr ValueP the application should have crashed already")); diff --git a/src/data/field.hpp b/src/data/field.hpp index 1543db9f..3776578d 100644 --- a/src/data/field.hpp +++ b/src/data/field.hpp @@ -54,6 +54,7 @@ class Field : public IntrusivePtrVirtualBase { bool card_list_allow; ///< Is this field allowed to appear in the card list? String card_list_name; ///< Alternate name to use in card list. Alignment card_list_align; ///< Alignment of the card list colummn. + OptionalScript sort_script; ///< The script to use when sorting this, if not the value. int tab_index; ///< Tab index in editor Dependencies dependent_scripts; ///< Scripts that depend on values of this field @@ -67,7 +68,9 @@ class Field : public IntrusivePtrVirtualBase { virtual String typeName() const = 0; /// Add the given dependency to the dependet_scripts list for the variables this field depends on - virtual void initDependencies(Context&, const Dependency&) const {} + inline virtual void initDependencies(Context& ctx, const Dependency& dep) const { + sort_script.initDependencies(ctx, dep); + } private: DECLARE_REFLECTION_VIRTUAL(); @@ -183,6 +186,7 @@ class Value : public IntrusivePtrVirtualBase { const FieldP fieldP; ///< Field this value is for, should have the right type! Age last_script_update; ///< When where the scripts last updated? (by calling update) + ScriptValueP sortValue; ///< How this should be sorted. /// Get a copy of this value virtual ValueP clone() const = 0; @@ -190,7 +194,11 @@ class Value : public IntrusivePtrVirtualBase { /// Convert this value to a string for use in tables virtual String toString() const = 0; /// Apply scripts to this value, return true if the value has changed - virtual bool update(Context&) { last_script_update.update(); return false; } + inline virtual bool update(Context& ctx) { + sortValue = fieldP->sort_script.invoke(ctx); + last_script_update.update(); + return false; + } /// This value has been updated by an action /** Does nothing for most Values, only FakeValues can update underlying data */ virtual void onAction(Action& a, bool undone) {} @@ -199,6 +207,11 @@ class Value : public IntrusivePtrVirtualBase { * In that case, afterwards this becomes equal to that if they use the same underlying object. */ virtual bool equals(const Value* that); + + /// Get the sort key for this value. + inline String getSortKey () const { + return sortValue == script_nil ? *sortValue : toString(); + } private: DECLARE_REFLECTION_VIRTUAL(); diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index f9227187..c6c5cb15 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP); DECLARE_TYPEOF(map); diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp index fb6473dc..e36b1af1 100644 --- a/src/gfx/generated_image.hpp +++ b/src/gfx/generated_image.hpp @@ -65,6 +65,7 @@ class BlankImage : public GeneratedImage { public: virtual Image generate(const Options&) const; virtual bool operator == (const GeneratedImage& that) const; + virtual bool threadSafe() const { return false; }; }; // ----------------------------------------------------------------------------- : LinearBlendImage diff --git a/src/gui/control/card_list.cpp b/src/gui/control/card_list.cpp index a977fb2a..b29e3100 100644 --- a/src/gui/control/card_list.cpp +++ b/src/gui/control/card_list.cpp @@ -176,7 +176,7 @@ bool CardListBase::compareItems(void* a, void* b) const { ValueP va = reinterpret_cast(a)->data[sort_field]; ValueP vb = reinterpret_cast(b)->data[sort_field]; if (!va || !vb) return va < vb; // got to do something, compare pointers - return smart_less( va->toString() , vb->toString() ); + return smart_less( va->getSortKey(), vb->getSortKey() ); } void CardListBase::rebuild() { diff --git a/src/gui/symbol/part_list.hpp b/src/gui/symbol/part_list.hpp index 17ab0981..f83f367c 100644 --- a/src/gui/symbol/part_list.hpp +++ b/src/gui/symbol/part_list.hpp @@ -11,6 +11,7 @@ #include #include +#include class SymbolPartsSelection; diff --git a/src/gui/symbol/symmetry_editor.hpp b/src/gui/symbol/symmetry_editor.hpp index 409561b7..cc88f586 100644 --- a/src/gui/symbol/symmetry_editor.hpp +++ b/src/gui/symbol/symmetry_editor.hpp @@ -11,6 +11,7 @@ #include #include +#include class SymmetryMoveAction; diff --git a/src/render/symbol/viewer.cpp b/src/render/symbol/viewer.cpp index 716e971f..528448ff 100644 --- a/src/render/symbol/viewer.cpp +++ b/src/render/symbol/viewer.cpp @@ -50,7 +50,7 @@ typedef shared_ptr MemoryDCP; // Return a temporary DC with the same size as the parameter MemoryDCP getTempDC(DC& dc) { wxSize s = dc.GetSize(); - Bitmap buffer(s.GetWidth(), s.GetHeight(), 1); + Bitmap buffer(s.GetWidth(), s.GetHeight(), 24); MemoryDCP newDC(new wxMemoryDC); newDC->SelectObject(buffer); clearDC(*newDC, *wxBLACK_BRUSH); @@ -161,10 +161,10 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint // Matrix2D rot(cos(a),-sin(a), sin(a),cos(a)); // // ref * rot - // /cos b sin b\ /cos a -sin a\ - // = \sin b -cos b/ \sin a cos a/ - // = /cos(a+b) sin(a+b)\ - // \sin(a+b) -cos(a+b)/ + // /cos b sin b\ /cos a -sin a\; + // = \sin b -cos b/ \sin a cos a/;s + // = /cos(a+b) sin(a+b)\; + // \sin(a+b) -cos(a+b)/; Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b)); multiply.mx = rot.mx * old_m; multiply.my = rot.my * old_m; diff --git a/src/render/symbol/viewer.hpp b/src/render/symbol/viewer.hpp index ee40dc9c..a7c0d098 100644 --- a/src/render/symbol/viewer.hpp +++ b/src/render/symbol/viewer.hpp @@ -67,7 +67,7 @@ class SymbolViewer : public SymbolView { int in_symmetry; /// Combine a symbol part with the dc - void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC); + void combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC); /// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly /** directB/directI are true if the border/interior is the screen dc, false if it diff --git a/src/script/script_manager.cpp b/src/script/script_manager.cpp index be96c492..fb0aefec 100644 --- a/src/script/script_manager.cpp +++ b/src/script/script_manager.cpp @@ -229,7 +229,7 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap& s->tellListeners(only_content_dependent); } } catch (const ScriptError& e) { - throw ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'")); + handle_error(ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'"))); } } } @@ -267,7 +267,7 @@ void SetScriptManager::updateAll() { try { v->update(ctx); } catch (const ScriptError& e) { - throw ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")); + handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'"))); } } // update card data of all cards @@ -277,7 +277,7 @@ void SetScriptManager::updateAll() { try { v->update(ctx); } catch (const ScriptError& e) { - throw ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")); + handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'"))); } } } @@ -311,7 +311,7 @@ void SetScriptManager::updateToUpdate(const ToUpdate& u, deque& to_upd try { changes = u.value->update(ctx); } catch (const ScriptError& e) { - throw ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'")); + handle_error(ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'"))); } if (changes) { // changed, send event