Add update_cards_scripts

This commit is contained in:
GenevensiS
2026-05-13 18:09:56 +02:00
parent f4fe9ab6b0
commit 151a04909a
26 changed files with 403 additions and 108 deletions
+25
View File
@@ -87,6 +87,31 @@ void AddCardAction::perform(bool to_undo) {
set.buildUIDMap();
}
UpdateCardAction::UpdateCardAction(Set& set, const vector<CardP>& cards_to_add, const vector<CardP>& cards_to_remove)
: CardListAction(set)
, action_add(ADD, set, cards_to_add)
, action_remove(REMOVE, set, cards_to_remove)
{}
String UpdateCardAction::getName(bool to_undo) const {
return _ACTION_("update card");
}
void UpdateCardAction::perform(bool to_undo) {
if (to_undo) {
action_remove.perform(to_undo);
set.actions.tellListeners(action_remove, to_undo);
action_add.perform(to_undo);
set.actions.tellListeners(action_add, to_undo);
}
else {
action_add.perform(to_undo);
set.actions.tellListeners(action_add, to_undo);
action_remove.perform(to_undo);
set.actions.tellListeners(action_remove, to_undo);
}
}
// ----------------------------------------------------------------------------- : Reorder cards
ReorderCardsAction::ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2)
+12
View File
@@ -50,6 +50,18 @@ public:
vector<ActionP> card_link_actions;
};
/// Update one or more cards from a set by replacing them with other cards
class UpdateCardAction : public CardListAction {
public:
UpdateCardAction(Set& set, const vector<CardP>& cards_to_add, const vector<CardP>& cards_to_remove);
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
AddCardAction action_add;
AddCardAction action_remove;
};
// ----------------------------------------------------------------------------- : Reorder cards
/// Change the position of a card in the card list by swapping two cards
+3 -1
View File
@@ -401,7 +401,9 @@ String BulkAction::getName(bool to_undo) const {
}
void BulkAction::perform(bool to_undo) {
FOR_EACH(action, actions) {
size_t size = actions.size();
for (size_t i = 0 ; i < size ; ++i) {
ActionP& action = actions[to_undo ? size - i - 1 : i];
action->perform(to_undo);
set->actions.tellListeners(*action, to_undo);
}
+8 -4
View File
@@ -26,17 +26,21 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(AddCardsScript) {
void AddCardsScript::perform(Set& set, vector<CardP>& out) {
// Perform script
Context& ctx = set.getContext();
if (enabled.hasBeenRead()) {
enabled.update(ctx);
if (!enabled()) return;
}
ScriptValueP result = script.invoke(ctx);
// Add cards to out
ScriptValueP it = result->makeIterator();
while (ScriptValueP item = it->next()) {
CardP card = from_script<CardP>(item);
CardP new_card = from_script<CardP>(item);
// is this a new card?
if (contains(set.cards,card) || contains(out,card)) {
if (contains(set.cards,new_card) || contains(out,new_card)) {
// make copy
card = make_intrusive<Card>(*card);
new_card = make_intrusive<Card>(&set, new_card);
}
out.push_back(card);
out.push_back(new_card);
}
}
+37 -1
View File
@@ -44,6 +44,37 @@ Card::Card(Game& game)
data.init(game.card_fields);
}
Card::Card(Set* set, const CardP& card)
: game(card->game)
, time_created (wxDateTime::Now())
, time_modified(wxDateTime::Now())
, notes(card->notes)
, uid(generate_uid())
, linked_card_1(card->linked_card_1)
, linked_card_2(card->linked_card_2)
, linked_card_3(card->linked_card_3)
, linked_card_4(card->linked_card_4)
, linked_relation_1(card->linked_relation_1)
, linked_relation_2(card->linked_relation_2)
, linked_relation_3(card->linked_relation_3)
, linked_relation_4(card->linked_relation_4)
, has_styling(card->has_styling)
, stylesheet_version(card->stylesheet_version)
, stylesheet(card->stylesheet)
{
if (!stylesheet && set) {
stylesheet = set->stylesheetForP(card);
}
if (has_styling) {
styling_data.cloneFrom(card->styling_data);
}
else {
if (stylesheet && set) styling_data.cloneFrom(set->stylingDataFor(*stylesheet));
}
data.cloneFrom(card->data);
extra_data.cloneFrom(card->extra_data);
}
String Card::identification() const {
// an identifying field
FOR_EACH_CONST(v, data) {
@@ -344,7 +375,12 @@ void reflect_version_check(GetDefaultMember& handler, const Char* key, intrusive
IMPLEMENT_REFLECTION(Card) {
REFLECT(stylesheet);
reflect_version_check(handler, _("stylesheet_version"), stylesheet);
if (Handler::isReading) {
REFLECT_NO_SCRIPT(stylesheet_version);
}
else {
reflect_version_check(handler, _("stylesheet_version"), stylesheet);
}
REFLECT(has_styling);
if (has_styling) {
if (stylesheet) {
+4
View File
@@ -35,6 +35,8 @@ public:
Card();
/// Creates a card using the given game
Card(Game& game);
/// Copy constructor, makes a deep copy
Card(Set* set, const CardP& card);
/// The game this card is made for
Game* game;
@@ -61,6 +63,8 @@ public:
/// Alternative style to use for this card
/** Optional; if not set use the card style from the set */
StyleSheetP stylesheet;
/// What version of the stylesheet was this card using when it was last saved?
Version stylesheet_version;
/// Alternative options to use for this card, for this card's stylesheet
/** Optional; if not set use the styling data from the set.
* If stylesheet is set then contains data for the this->stylesheet, otherwise for set->stylesheet
+4 -1
View File
@@ -16,6 +16,7 @@
#include <data/pack.hpp>
#include <data/word_list.hpp>
#include <data/add_cards_script.hpp>
#include <data/update_cards_script.hpp>
#include <util/io/package_manager.hpp>
#include <script/script.hpp>
@@ -55,7 +56,6 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT_NO_SCRIPT(json_paths);
REFLECT_NO_SCRIPT(statistics_dimensions);
REFLECT_NO_SCRIPT(statistics_categories);
REFLECT_COMPAT(<308, "pack_item", pack_types);
REFLECT_NO_SCRIPT(pack_types);
REFLECT_NO_SCRIPT(keyword_match_script);
REFLECT(has_keywords);
@@ -64,6 +64,7 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT_NO_SCRIPT(keywords);
REFLECT_NO_SCRIPT(word_lists);
REFLECT_NO_SCRIPT(add_cards_scripts);
REFLECT_NO_SCRIPT(update_cards_scripts);
REFLECT_NO_SCRIPT(auto_replaces);
}
@@ -206,6 +207,8 @@ void Game::validate(Version v) {
card_links_alt_names.emplace(linked_tr, linked_default);
}
}
// sort the update_cards_scripts from oldest to newest
std::sort(update_cards_scripts.begin(), update_cards_scripts.end());
}
void Game::initCardListColorScript() {
+23 -21
View File
@@ -26,6 +26,7 @@ DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword);
DECLARE_POINTER_TYPE(WordList);
DECLARE_POINTER_TYPE(AddCardsScript);
DECLARE_POINTER_TYPE(UpdateCardsScript);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : Game
@@ -38,27 +39,28 @@ class Game : public Packaged {
public:
Game();
OptionalScript init_script; ///< Script of variables available to other scripts in this game
vector<FieldP> set_fields; ///< Fields for set information
IndexMap<FieldP,StyleP> default_set_style; ///< Default style for the set fields, because it is often the same
vector<FieldP> card_fields; ///< Fields on each card
vector<CardLinkP> 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<String> json_paths; ///< Paths inside JSON files to find the card array
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
map<String,String> card_fields_alt_names; ///< Other names that fields might go by, for example in CSV files
map<String,String> 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<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
OptionalScript init_script; ///< Script of variables available to other scripts in this game
vector<FieldP> set_fields; ///< Fields for set information
IndexMap<FieldP,StyleP> default_set_style; ///< Default style for the set fields, because it is often the same
vector<FieldP> card_fields; ///< Fields on each card
vector<CardLinkP> 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<String> json_paths; ///< Paths inside JSON files to find the card array
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<UpdateCardsScriptP> update_cards_scripts; ///< Scripts for updating cards made with an earlier version of this game
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
map<String,String> card_fields_alt_names; ///< Other names that fields might go by, for example in CSV files
map<String,String> 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<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
Dependencies dependent_scripts_cards; ///< scripts that depend on the card list
Dependencies dependent_scripts_keywords; ///< scripts that depend on the keywords
-1
View File
@@ -85,7 +85,6 @@ bool Keyword::contains(QuickFilterPart const& query) const {
IMPLEMENT_REFLECTION(Keyword) {
REFLECT(keyword);
if (handler.formatVersion() < 301) read_compat(handler, this);
REFLECT(match);
REFLECT(reminder);
REFLECT(rules);
+54 -7
View File
@@ -14,6 +14,7 @@
#include <data/keyword.hpp>
#include <data/pack.hpp>
#include <data/field.hpp>
#include <data/update_cards_script.hpp>
#include <data/field/text.hpp> // for 0.2.7 fix
#include <data/field/information.hpp>
#include <data/field/image.hpp>
@@ -172,7 +173,7 @@ void fix_value_207(const ValueP& value) {
void Set::validate(Version file_app_version) {
Packaged::validate(file_app_version);
// are the
// are the game and stylesheet defined?
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("set")));
}
@@ -184,6 +185,8 @@ void Set::validate(Version file_app_version) {
throw Error(_ERROR_("stylesheet and set refer to different game"));
}
// We can probably retire this
/*
// This is our chance to fix version incompatabilities
if (file_app_version < 207) {
// Since 0.2.7 we use </tag> style close tags, in older versions it was </>
@@ -192,16 +195,52 @@ void Set::validate(Version file_app_version) {
FOR_EACH(v, c->data) fix_value_207(v);
}
FOR_EACH(v, data) fix_value_207(v);
/* FOR_EACH(s, styleData) {
FOR_EACH(s, styleData) {
FOR_EACH(v, s.second->data) fix_value_207(v);
}
*/ }
}
*/
// we want at least one card
if (cards.empty()) cards.push_back(make_intrusive<Card>(*game));
// update scripts
script_manager->updateAll();
// build uid map
buildUIDMap();
// update cards with game update_cards_scripts
for (int j = 0; j < game->update_cards_scripts.size(); ++j) {
UpdateCardsScriptP& script = game->update_cards_scripts[j];
if (game_version >= script->before_version) continue;
size_t size = cards.size();
for (int i = 0; i < size; ++i) {
CardP& card = cards[i];
vector<CardP> new_cards = script->perform(*this, card);
if (!new_cards.empty()) {
--i;
--size;
}
}
}
// update cards with stylesheet update_cards_scripts
for (int i = 0; i < cards.size(); ++i) {
CardP& card = cards[i];
StyleSheetP stylesheet = stylesheetForP(card);
for (int j = 0; j < stylesheet->update_cards_scripts.size(); ++j) {
UpdateCardsScriptP& script = stylesheet->update_cards_scripts[j];
if (card->stylesheet_version >= script->before_version) continue;
vector<CardP> new_cards = script->perform(*this, card);
if (!new_cards.empty()) {
FOR_EACH(new_card, new_cards) {
// Initialize the stylesheet_version if it wasn't defined, to prevent this script from applying again
if (stylesheet == stylesheetForP(new_card) && new_card->stylesheet_version < script->before_version) {
new_card->stylesheet_version = script->before_version;
}
}
--i;
break;
}
}
}
}
void reflect_version_check(Reader& handler, const Char* key, intrusive_ptr<Packaged> const& package) {
@@ -225,15 +264,23 @@ IMPLEMENT_REFLECTION(Set) {
REFLECT_IF_READING {
data.init(game->set_fields);
}
reflect_version_check(handler, _("game_version"), game);
if (Handler::isReading) {
REFLECT_NO_SCRIPT(game_version);
}
else {
reflect_version_check(handler, _("game_version"), game);
}
WITH_DYNAMIC_ARG(game_for_reading, game.get());
REFLECT(stylesheet);
REFLECT_COMPAT(<300, "style", stylesheet);
reflect_version_check(handler, _("stylesheet_version"), stylesheet);
if (Handler::isReading) {
REFLECT_NO_SCRIPT(stylesheet_version);
}
else {
reflect_version_check(handler, _("stylesheet_version"), stylesheet);
}
WITH_DYNAMIC_ARG(stylesheet_for_reading, stylesheet.get());
REFLECT_N("set_info", data);
if (stylesheet) {
REFLECT_COMPAT(<300, "extra_set_info", styling_data);
REFLECT_N("styling", styling_data);
}
// Experimental: save each card to a different file
+4
View File
@@ -61,6 +61,10 @@ public:
ActionStack actions; ///< Actions performed on this set and the cards in it
KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change
VCSP vcs; ///< The version control system to use
/// What version of the game was this set using when it was last saved?
Version game_version;
/// What version of the default stylesheet was this set using when it was last saved?
Version stylesheet_version;
/// A context for performing scripts
/** Should only be used from the main thread! */
+6
View File
@@ -10,6 +10,7 @@
#include <data/stylesheet.hpp>
#include <data/game.hpp>
#include <data/field.hpp>
#include <data/update_cards_script.hpp>
#include <util/io/package_manager.hpp>
#include <gui/new_window.hpp> // for selecting stylesheets on load error
@@ -76,6 +77,10 @@ void StyleSheet::validate(Version ver) {
}
// a stylesheet depends on the game it is made for
requireDependency(game.get());
// sort the update_cards_scripts from oldest to newest
std::sort(update_cards_scripts.begin(), update_cards_scripts.end(), [](const auto& a, const auto& b) {
return *a < *b;
});
}
@@ -125,6 +130,7 @@ IMPLEMENT_REFLECTION(StyleSheet) {
extra_card_style.init(extra_card_fields);
}
REFLECT(extra_card_style);
REFLECT_NO_SCRIPT(update_cards_scripts);
}
+8 -6
View File
@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(StyleSheet);
DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(CardRegion);
DECLARE_POINTER_TYPE(UpdateCardsScript);
// ----------------------------------------------------------------------------- : StyleSheet
@@ -33,12 +34,13 @@ class StyleSheet : public Packaged {
public:
StyleSheet();
GameP game; ///< The game this stylesheet is made for
OptionalScript init_script; ///< Script of variables available to other scripts in this stylesheet
double card_width; ///< The width of a card in pixels
double card_height; ///< The height of a card in pixels
double card_dpi; ///< The resolution of a card in dots per inch
Color card_background; ///< The background color of cards
GameP game; ///< The game this stylesheet is made for
OptionalScript init_script; ///< Script of variables available to other scripts in this stylesheet
vector<UpdateCardsScriptP> update_cards_scripts; ///< Scripts for updating cards made with an earlier version of this stylesheet
double card_width; ///< The width of a card in pixels
double card_height; ///< The height of a card in pixels
double card_dpi; ///< The resolution of a card in dots per inch
Color card_background; ///< The background color of cards
vector<CardRegionP> card_regions;
/// The styling for card fields
/** The indices should correspond to the card_fields in the Game */
-1
View File
@@ -178,7 +178,6 @@ IMPLEMENT_REFLECTION(SymbolInFont) {
REFLECT(draw_text);
REFLECT(text_font);
REFLECT(text_alignment);
REFLECT_COMPAT(<300,"text_align",text_alignment);
REFLECT(text_margin_left);
REFLECT(text_margin_right);
REFLECT(text_margin_top);
+57
View File
@@ -0,0 +1,57 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make card games |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/update_cards_script.hpp>
#include <data/action/set.hpp>
#include <data/set.hpp>
#include <data/card.hpp>
#include <data/stylesheet.hpp>
#include <data/action/value.hpp>
// ----------------------------------------------------------------------------- : UpdateCardsScript
IMPLEMENT_REFLECTION_NO_SCRIPT(UpdateCardsScript) {
REFLECT(before_version);
REFLECT(description);
REFLECT(enabled);
REFLECT(script);
}
void UpdateCardsScript::perform(Set& set, CardP& card, vector<CardP>& out) {
// Perform script
Context& ctx = set.getContext(card);
if (enabled.hasBeenRead()) {
enabled.update(ctx);
if (!enabled()) return;
}
ScriptValueP result = script.invoke(ctx);
// Add cards to out
ScriptValueP it = result->makeIterator();
while (ScriptValueP item = it->next()) {
CardP new_card = from_script<CardP>(item);
// is this a new card?
if (contains(set.cards,new_card) || contains(out,new_card)) {
// make copy
new_card = make_intrusive<Card>(&set, new_card);
}
out.push_back(new_card);
}
}
vector<CardP> UpdateCardsScript::perform(Set& set, CardP card) {
// Perform script
vector<CardP> cards;
perform(set, card, cards);
// Add to set
if (!cards.empty()) {
set.actions.addAction(make_unique<UpdateCardAction>(set, cards, vector<CardP>{card}), false);
}
return cards;
}
+39
View File
@@ -0,0 +1,39 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make card games |
//| 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 <util/prec.hpp>
#include <script/scriptable.hpp>
class Set;
DECLARE_POINTER_TYPE(Card);
// ----------------------------------------------------------------------------- : UpdateCardsScript
/// A script to add one or more cards to a set
class UpdateCardsScript : public IntrusivePtrBase<UpdateCardsScript> {
public:
Version before_version;
String description;
Scriptable<bool> enabled;
OptionalScript script;
bool operator < (const UpdateCardsScript& other) const {
return before_version < other.before_version;
}
/// Perform the script; return the cards (if any)
void perform(Set& set, CardP& card, vector<CardP>& out);
/// Perform the script; add cards to the set
vector<CardP> perform(Set& set, CardP card); // don't use CardP& here, because set.cards may get reallocated which would invalidate the ref
DECLARE_REFLECTION();
};
+2 -2
View File
@@ -376,12 +376,12 @@ bool CardListBase::parseText(String& text, vector<CardP>& out) {
if (ScriptCustomCollection* custom = dynamic_cast<ScriptCustomCollection*>(sv.get())) {
for (size_t i = 0; i < custom->value.size(); i++) {
if (ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(custom->value[i].get())) {
out.push_back(make_intrusive<Card>(*c->getValue()));
out.push_back(c->getValue());
}
}
}
} else if (ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(sv.get())) {
out.push_back(make_intrusive<Card>(*c->getValue()));
out.push_back(c->getValue());
}
} catch (...) {}
+5 -3
View File
@@ -26,11 +26,13 @@
SCRIPT_FUNCTION(new_card) {
SCRIPT_PARAM(GameP, game);
ScriptValueP set_ = ctx.getVariableOpt(BOOST_PP_CAT(L, "set")); // this is just SCRIPT_OPTIONAL_PARAM_(Set*, set) but the macro fails
Set* set = set_ && set_ != script_nil ? from_script<Set*>(set_, BOOST_PP_CAT(L, "set")) : nullptr;
SCRIPT_OPTIONAL_PARAM_(bool, ignore_field_not_found);
// create a new card object
CardP new_card = make_intrusive<Card>(*game);
// iterate on the given key/value pairs
SCRIPT_OPTIONAL_PARAM_(CardP, copy_from);
SCRIPT_PARAM(ScriptValueP, input);
// create a new card object, or a copy
CardP new_card = copy_from ? make_intrusive<Card>(set, copy_from) : make_intrusive<Card>(*game);
// check for a stylesheet first, since other things depend on it
ScriptValueP it = input->makeIterator();
ScriptValueP key;
+13 -3
View File
@@ -94,7 +94,10 @@ inline static bool set_stylesheet_container(const Game& game, CardP& card, Scrip
if (key_name == _("style") || key_name == _("stylesheet") || key_name == _("template")) {
if (!trim(value->toString()).empty()) {
card->stylesheet = StyleSheet::byGameAndName(game, value->toString());
if (card->stylesheet) card->styling_data.init(card->stylesheet->styling_fields);
if (card->stylesheet) {
card->styling_data.init(card->stylesheet->styling_fields);
card->extraDataFor(*card->stylesheet).init(card->stylesheet->extra_card_fields);
}
}
return true;
}
@@ -104,7 +107,14 @@ inline static bool set_stylesheet_container(const Game& game, CardP& card, Scrip
inline static bool set_builtin_container(const Game& game, CardP& card, ScriptValueP& value, String key_name, bool ignore_field_not_found) {
// check if the given value is for a built-in field, if found set it and return true
key_name = unified_form(key_name);
if (key_name == _("card_notes") || key_name == _("notes") || key_name == _("note")) {
if (key_name == _("style") || key_name == _("stylesheet") || key_name == _("template")) {
return true; // we already took care of this
}
else if (key_name == _("style_version") || key_name == _("stylesheet_version") || key_name == _("template_version")) {
card->stylesheet_version = Version::fromString(value->toString());
return true;
}
else if (key_name == _("card_notes") || key_name == _("notes") || key_name == _("note")) {
card->notes = value->toString();
return true;
}
@@ -237,7 +247,7 @@ inline static bool cards_from_table(SetP& set, vector<String>& headers, std::vec
// is this a new card?
if (contains(set->cards, card) || contains(cards_out, card)) {
// make copy
card = make_intrusive<Card>(*card);
card = make_intrusive<Card>(set.get(), card);
}
cards_out.push_back(card);
}
+2 -1
View File
@@ -256,6 +256,7 @@ CardP json_to_mse_card(boost::json::object& jv, Set* set) {
StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, String::FromUTF8(stylesheet_view.data(), stylesheet_view.size()));
if (!stylesheet) continue;
IndexMap<FieldP, ValueP>& stylesheet_data = card->extraDataFor(*stylesheet);
stylesheet_data.init(stylesheet->extra_card_fields);
boost::json::object stylesheet_datav = it->value().as_object();
for (auto stylesheet_it = stylesheet_datav.begin(); stylesheet_it != stylesheet_datav.end(); ++stylesheet_it) {
boost::json::string_view key_view = stylesheet_it->key();
@@ -404,7 +405,7 @@ ScriptValueP json_to_mse(const String& string, Set* set) {
options.allow_invalid_utf8 = true;
wxScopedCharBuffer buffer = string.ToUTF8();
boost::json::value jv = boost::json::parse(boost::json::string_view(buffer.data(), buffer.length()), ec, {}, options);
if(ec && buffer.length() > 0) queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message());
//if(ec && buffer.length() > 0) queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message());
if(ec) return script_nil;
return json_to_mse(jv, set);
}
+15
View File
@@ -32,6 +32,21 @@ IndexMap<Key,Value>& DelayedIndexMaps<Key,Value>::get(const String& name, const
return item->read_data;
}
/// Initialize this map with cloned values from another map
template <typename Key, typename Value>
void DelayedIndexMaps<Key,Value>::cloneFrom(const DelayedIndexMaps<Key,Value>& values) {
clear();
for (const auto& [name, src_ptr] : values.data) {
if (!src_ptr) continue;
auto dst_ptr = make_intrusive<DelayedIndexMapsData<Key,Value>>();
dst_ptr->unread_data = src_ptr->unread_data;
if (src_ptr->unread_data.empty()) {
dst_ptr->read_data.cloneFrom(src_ptr->read_data);
}
data[name] = dst_ptr;
}
}
template <typename Key, typename Value>
void DelayedIndexMaps<Key,Value>::clear() {
data.clear();
+3 -1
View File
@@ -60,7 +60,7 @@ public:
}
return true;
}
/// Initialize this map with cloned values from another list
/// Initialize this map with cloned values from another map
void cloneFrom(const IndexMap<Key,Value>& values) {
if (this->size() == values.size()) return;
this->reserve(values.size());
@@ -147,6 +147,8 @@ class DelayedIndexMaps {
public:
/// Get the data for a specific name. Initialize the map with init_with (if it is not alread initialized)
IndexMap<Key,Value>& get(const String& name, const vector<Key>& init_with);
/// Initialize this map with cloned values from another map
void cloneFrom(const DelayedIndexMaps<Key,Value>& values);
/// Clear the delayed index map
void clear();
map<String, intrusive_ptr<DelayedIndexMapsData<Key,Value>>> data;
+1 -1
View File
@@ -30,7 +30,7 @@ Reader::Reader(wxInputStream& input, Packaged* package, const String& filename,
handleAppVersion();
}
void Reader::handleIgnore(int end_version, const Char* a) {
void Reader::handleIgnore(Version end_version, const Char* a) {
if (file_app_version < end_version) {
if (enterBlock(a)) exitBlock();
}
+2 -2
View File
@@ -49,7 +49,7 @@ public:
/// Is the thing currently being read 'complex', i.e. does it have children
inline bool isCompound() const { return indent != expected_indent - 1 || value.empty(); }
/// Ignore old keys
void handleIgnore(int, const Char*);
void handleIgnore(Version, const Char*);
/// Get the version of the format we are reading
inline Version formatVersion() const { return file_app_version; }
@@ -189,7 +189,7 @@ private:
value = key == _("include_localized_file") ? addLocale(value) : key == _("include_dark_file") ? addDark(value) : value;
auto [stream, include_package] = openFileFromPackage(package, value);
Reader sub_reader(*stream, include_package, value, ignore_invalid);
if (sub_reader.file_app_version == 0) {
if (sub_reader.file_app_version.isZero()) {
// in an included file, use the app version of the parent if there is none
sub_reader.file_app_version = file_app_version;
}
+25 -24
View File
@@ -13,30 +13,31 @@
// ----------------------------------------------------------------------------- : Version
UInt Version::toNumber() const { return version; }
UInt Version::toNumber() const { return major * 10000 + minor * 100 + revision; }
String Version::toString() const {
if (version > 20000000) {
// major > 2000, the version is a date, use ISO notation
if (major >= 2000) {
// the version is a date, use ISO notation
return String::Format(_("%04d-%02d-%02d"),
(version / 10000) ,
(version / 100) % 100,
(version / 1) % 100);
major,
minor,
revision);
} else {
return String::Format(_("%d.%d.%d"),
(version / 10000) ,
(version / 100) % 100,
(version / 1) % 100);
major,
minor,
revision);
}
}
Version Version::fromString(const String& version) {
UInt major = 0, minor = 0, build = 0;
if (wxSscanf(version, _("%u.%u.%u"), &major, &minor, &build)<=1) // a.b.c style
wxSscanf(version, _("%u-%u-%u"), &major, &minor, &build); // date style
return Version(major * 10000 + minor * 100 + build);
UInt major = 0, minor = 0, revision = 0;
if (wxSscanf(version, _("%u.%u.%u"), &major, &minor, &revision)<=1) // a.b.c style
wxSscanf(version, _("%u-%u-%u"), &major, &minor, &revision); // date style
return Version(major, minor, revision);
}
bool Version::isZero() const { return revision == 0u && minor == 0u && major == 0u; }
template <> void Reader::handle(Version& v) {
v = Version::fromString(getValue());
@@ -51,7 +52,7 @@ template <> void GetDefaultMember::handle(const Version& v) {
// ----------------------------------------------------------------------------- : Versions
// NOTE: Don't use leading zeroes, they mean octal
const Version app_version = 10000 * MSE_VERSION_MAJOR + 100 * MSE_VERSION_MINOR + MSE_VERSION_PATCH;
const Version app_version = Version(MSE_VERSION_MAJOR, MSE_VERSION_MINOR, MSE_VERSION_PATCH);
#if defined UNOFFICIAL_BUILD
const Char* version_suffix = _(" (Unofficial)");
@@ -88,13 +89,13 @@ const Char* version_suffix = _(" (ascii build)");
* 2.0.0 : bugfix release mostly, added error console
* 2.0.2 : store game and stylesheet version numbers
*/
const Version file_version_locale = 20002; // 2.0.2
const Version file_version_set = 20002; // 2.0.2
const Version file_version_game = 308; // 0.3.8
const Version file_version_stylesheet = 308; // 0.3.8
const Version file_version_symbol_font = 306; // 0.3.6
const Version file_version_export_template = 307; // 0.3.7
const Version file_version_installer = 307; // 0.3.7
const Version file_version_symbol = 305; // 0.3.5
const Version file_version_clipboard = 20002; // 2.0.2
const Version file_version_script = 307; // 0.3.7
const Version file_version_locale = Version(2u, 0u, 2u);
const Version file_version_set = Version(2u, 0u, 2u);
const Version file_version_game = Version(0u, 3u, 8u);
const Version file_version_stylesheet = Version(0u, 3u, 8u);
const Version file_version_symbol_font = Version(0u, 3u, 6u);
const Version file_version_export_template = Version(0u, 3u, 7u);
const Version file_version_installer = Version(0u, 3u, 7u);
const Version file_version_symbol = Version(0u, 3u, 5u);
const Version file_version_clipboard = Version(2u, 6u, 0u);
const Version file_version_script = Version(0u, 3u, 7u);
+32 -9
View File
@@ -21,15 +21,33 @@
/// A version number
struct Version {
public:
Version() : version(0) {}
Version(UInt version) : version(version) {}
Version() : major(0u), minor(0u), revision(0u) {}
Version(UInt major, UInt minor, UInt revision) : major(major), minor(minor), revision(revision) {}
inline bool operator == (Version v) const { return version == v.version; }
inline bool operator != (Version v) const { return version != v.version; }
inline bool operator < (Version v) const { return version < v.version; }
inline bool operator <= (Version v) const { return version <= v.version; }
inline bool operator > (Version v) const { return version > v.version; }
inline bool operator >= (Version v) const { return version >= v.version; }
bool operator==(const Version& v) const {
return std::tie(major, minor, revision)
== std::tie(v.major, v.minor, v.revision);
}
bool operator!=(const Version& v) const {
return std::tie(major, minor, revision)
!= std::tie(v.major, v.minor, v.revision);
}
bool operator<(const Version& v) const {
return std::tie(major, minor, revision)
< std::tie(v.major, v.minor, v.revision);
}
bool operator>(const Version& v) const {
return std::tie(major, minor, revision)
> std::tie(v.major, v.minor, v.revision);
}
bool operator<=(const Version& v) const {
return std::tie(major, minor, revision)
<= std::tie(v.major, v.minor, v.revision);
}
bool operator>=(const Version& v) const {
return std::tie(major, minor, revision)
>= std::tie(v.major, v.minor, v.revision);
}
/// Convert a version number to a string
String toString() const;
@@ -39,8 +57,13 @@ public:
/// Convert a string to a version number
static Version fromString(const String& version);
/// Did this version get properly initialized?
bool isZero() const;
private:
UInt version; ///< Version number encoded as aabbcc, where a=major, b=minor, c=revision
UInt major = 0;
UInt minor = 0;
UInt revision = 0;
};
// ----------------------------------------------------------------------------- : Versions