Commit 9857a30b authored by twanvl's avatar twanvl

Experimental:

 * use ScriptValueP, wrapped in AnyValue, instead of the specialized value types
 * serialize these values using script syntax.
 * However: #ifdefed out for now, because I first wanted to commit the following change:

This requires a change to handling of last_change of MultipleChoiceValue. Instead of storing it in the field, it is now just part of the action. The action therefore has to be passed to Value::update.
parent 66091cd7
...@@ -39,8 +39,15 @@ void ValueAction::isOnCard(Card* card) { ...@@ -39,8 +39,15 @@ void ValueAction::isOnCard(Card* card) {
// ----------------------------------------------------------------------------- : Simple // ----------------------------------------------------------------------------- : Simple
/// Swap the value in a Value object with a new one /// Swap the value in a Value object with a new one
#if !USE_SCRIPT_VALUE_CHOICE
inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); }
#endif
#if !USE_SCRIPT_VALUE_COLOR
inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); }
#endif
#if USE_SCRIPT_VALUE_VALUE
inline void swap_value(AnyValue& a, AnyValue ::ValueType& b) { swap(a.value, b); }
#endif
inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); } inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); }
...@@ -80,15 +87,40 @@ class SimpleValueAction : public ValueAction { ...@@ -80,15 +87,40 @@ class SimpleValueAction : public ValueAction {
typename T::ValueType new_value; typename T::ValueType new_value;
}; };
#if !USE_SCRIPT_VALUE_CHOICE
ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<ChoiceValue, true> (value, new_value); } ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<ChoiceValue, true> (value, new_value); }
#endif
#if !USE_SCRIPT_VALUE_COLOR
ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value) { return new SimpleValueAction<ColorValue, true> (value, new_value); } ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value) { return new SimpleValueAction<ColorValue, true> (value, new_value); }
#endif
#if USE_SCRIPT_VALUE_VALUE
ValueAction* value_action(const AnyValueP& value, const ScriptValueP& new_value) { return new SimpleValueAction<AnyValue, false>(value, new_value); }
#endif
ValueAction* value_action(const ImageValueP& value, const FileName& new_value) { return new SimpleValueAction<ImageValue, false>(value, new_value); } ValueAction* value_action(const ImageValueP& value, const FileName& new_value) { return new SimpleValueAction<ImageValue, false>(value, new_value); }
ValueAction* value_action(const SymbolValueP& value, const FileName& new_value) { return new SimpleValueAction<SymbolValue, false>(value, new_value); } ValueAction* value_action(const SymbolValueP& value, const FileName& new_value) { return new SimpleValueAction<SymbolValue, false>(value, new_value); }
ValueAction* value_action(const PackageChoiceValueP& value, const String& new_value) { return new SimpleValueAction<PackageChoiceValue, false>(value, new_value); } ValueAction* value_action(const PackageChoiceValueP& value, const String& new_value) { return new SimpleValueAction<PackageChoiceValue, false>(value, new_value); }
// ----------------------------------------------------------------------------- : MultipleChoice
/*
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) { ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) {
MultipleChoiceValue::ValueType v = { new_value, last_change }; MultipleChoiceValue::ValueType v = { new_value, last_change };
return new SimpleValueAction<MultipleChoiceValue, false>(value, v); return new SimpleValueAction<MultipleChoiceValue, false>(value, v);
} }
*/
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) {
return new MultipleChoiceValueAction(value,new_value,last_change);
}
// copy paste of SimpleValueAction :(
// TODO: do this in a better way
void MultipleChoiceValueAction::perform(bool to_undo) {
ValueAction::perform(to_undo);
swap_value(static_cast<MultipleChoiceValue&>(*valueP), new_value);
valueP->onAction(*this, to_undo); // notify value
}
// ----------------------------------------------------------------------------- : Text // ----------------------------------------------------------------------------- : Text
......
...@@ -24,12 +24,17 @@ DECLARE_POINTER_TYPE(Set); ...@@ -24,12 +24,17 @@ DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(Style); DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(TextValue); DECLARE_POINTER_TYPE(TextValue);
#if !USE_SCRIPT_VALUE_CHOICE
DECLARE_POINTER_TYPE(ChoiceValue); DECLARE_POINTER_TYPE(ChoiceValue);
#endif
DECLARE_POINTER_TYPE(MultipleChoiceValue); DECLARE_POINTER_TYPE(MultipleChoiceValue);
#if !USE_SCRIPT_VALUE_COLOR
DECLARE_POINTER_TYPE(ColorValue); DECLARE_POINTER_TYPE(ColorValue);
#endif
DECLARE_POINTER_TYPE(ImageValue); DECLARE_POINTER_TYPE(ImageValue);
DECLARE_POINTER_TYPE(SymbolValue); DECLARE_POINTER_TYPE(SymbolValue);
DECLARE_POINTER_TYPE(PackageChoiceValue); DECLARE_POINTER_TYPE(PackageChoiceValue);
DECLARE_POINTER_TYPE(AnyValue);
// ----------------------------------------------------------------------------- : ValueAction (based class) // ----------------------------------------------------------------------------- : ValueAction (based class)
...@@ -55,12 +60,34 @@ class ValueAction : public Action { ...@@ -55,12 +60,34 @@ class ValueAction : public Action {
// ----------------------------------------------------------------------------- : Simple // ----------------------------------------------------------------------------- : Simple
/// Action that updates a Value to a new value /// Action that updates a Value to a new value
#if !USE_SCRIPT_VALUE_CHOICE
ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value); ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value);
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change); #endif
#if !USE_SCRIPT_VALUE_COLOR
ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value); ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value);
#endif
ValueAction* value_action(const ImageValueP& value, const FileName& new_value); ValueAction* value_action(const ImageValueP& value, const FileName& new_value);
ValueAction* value_action(const SymbolValueP& value, const FileName& new_value); ValueAction* value_action(const SymbolValueP& value, const FileName& new_value);
ValueAction* value_action(const PackageChoiceValueP& value, const String& new_value); ValueAction* value_action(const PackageChoiceValueP& value, const String& new_value);
#if USE_SCRIPT_VALUE_VALUE
ValueAction* value_action(const AnyValueP& value, const ScriptValueP& new_value);
#endif
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
// ----------------------------------------------------------------------------- : MultipleChoice
class MultipleChoiceValueAction : public ValueAction {
public:
inline MultipleChoiceValueAction(const ValueP& value, const Defaultable<String>& new_value, const String& changed_choice)
: ValueAction(value), new_value(new_value), changed_choice(changed_choice)
{}
virtual void perform(bool to_undo);
const String changed_choice; ///< What choice was toggled by this action (if any)
private:
Defaultable<String> new_value;
};
// ----------------------------------------------------------------------------- : Text // ----------------------------------------------------------------------------- : Text
......
...@@ -275,7 +275,7 @@ bool Value::equals(const Value* that) { ...@@ -275,7 +275,7 @@ bool Value::equals(const Value* that) {
return this == that; return this == that;
} }
bool Value::update(Context& ctx) { bool Value::update(Context& ctx, const Action*) {
updateAge(); updateAge();
updateSortValue(ctx); updateSortValue(ctx);
return false; return false;
...@@ -304,23 +304,15 @@ void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& ...@@ -304,23 +304,15 @@ void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String&
} }
} }
// ----------------------------------------------------------------------------- : AnyValue
ValueP AnyValue::clone() const {
return intrusive(new AnyValue(fieldP,value));
}
String AnyValue::toString() const {
return value->toString();
}
void AnyValue::reflect(GetDefaultMember& reflector) { // ----------------------------------------------------------------------------- : AnyValue : reflecting ScriptValues
reflector.handle(value); // TODO: move this to a more sensible location
}
void AnyValue::reflect(GetMember& reflector) {
// nothing
}
ScriptValueP parse_script_value(String const& str) { DECLARE_DYNAMIC_ARG(Field const*, field_for_reading);
IMPLEMENT_DYNAMIC_ARG(Field const*, field_for_reading, nullptr);
ScriptValueP parse_script_value(String::const_iterator& it, String::const_iterator const& end) {
// possible values: // possible values:
// * "quoted string" # a string // * "quoted string" # a string
// * 123.456 # a number // * 123.456 # a number
...@@ -328,54 +320,138 @@ ScriptValueP parse_script_value(String const& str) { ...@@ -328,54 +320,138 @@ ScriptValueP parse_script_value(String const& str) {
// * rgb(123,123,123) # a color // * rgb(123,123,123) # a color
// * nil # nil // * nil # nil
// * true/false # a boolean // * true/false # a boolean
return script_nil; // * mark_default(value) # a value marked as being default
throw "TODO";
// reader.warning();
} }
ScriptValueP parse_script_value(String const& str) {
String::const_iterator it = str.begin();
ScriptValueP val = parse_script_value(it, str.end());
// stuff after the value
while (it != str.end()) {
if (*it == _('#')) {
// comment until end of line
while (it != str.end() && *it != _('\n')) ++it;
} else if (isSpace(*it)) {
// ignore whitespace
++it;
} else {
//reader.warning(_(""));
break;
}
}
return val;
}
void parse_errors_to_reader_warnings(Reader& reader, vector<ScriptParseError> const& errors);
/// A filename in the current set void Reader::handle(ScriptValueP& value) {
/// functions differently wrt. garbage collection Field const* field = field_for_reading();
/*class ScriptLocalFileName : public ScriptString { if (formatVersion() < 20001 && field) {
public:
ScriptLocalFileName(String const& filename) : ScriptString(filename) {}
// TODO!
};*/
void AnyValue::reflect(Reader& reflector) {
if (reflector.formatVersion() < 200001) {
// in older versions, the format was based on the type of the field // in older versions, the format was based on the type of the field
Field* field = fieldP.get(); if (dynamic_cast<BooleanField const*>(field)) {
if (dynamic_cast<BooleanField*>(field)) {
// boolean field: boolean "yes" or "no" // boolean field: boolean "yes" or "no"
bool x; bool x;
reflector.handle(x); handle(x);
value = to_script(x); value = to_script(x);
} else if (dynamic_cast<TextField*>(field) || dynamic_cast<ChoiceField*>(field)) { } else if (dynamic_cast<TextField const*>(field) || dynamic_cast<ChoiceField const*>(field)) {
// text, choice fields: string // text, choice fields: string
String str; String str;
reflector.handle(str); handle(str);
value = to_script(str); value = to_script(str);
} else if (dynamic_cast<ColorField*>(field)) { } else if (dynamic_cast<ColorField const*>(field)) {
// color field: color // color field: color
Color x; Color x;
reflector.handle(x); handle(x);
value = to_script(x); value = to_script(x);
} else if (dynamic_cast<ImageField*>(field)) { } else if (dynamic_cast<ImageField const*>(field)) {
// image, symbol fields: string that is a filename in the set // image, symbol fields: string that is a filename in the set
String str; String str;
reflector.handle(str); handle(str);
//value = intrusive(new ScriptLocalFileName(str)); //Packaged* package = package_for_reading();
Packaged* package = nullptr; // TODO
value = intrusive(new ScriptLocalFileName(package,str));
throw "TODO"; throw "TODO";
} else if (dynamic_cast<InfoField*>(field)) { } else if (dynamic_cast<InfoField const*>(field)) {
// this should never happen, since info fields were not saved // this should never happen, since info fields were not saved
String str;
handle(str);
} }
} else { } else {
// in the new system, the type is stored in the file. // in the new system, the type is stored in the file.
String str; String unparsed;
reflector.handle(str); vector<ScriptParseError> errors;
value = parse_script_value(str); handle(unparsed);
value = parse_value(unparsed, this->getPackage(), errors);
if (!value) {
value = script_nil;
}
parse_errors_to_reader_warnings(*this, errors);
}
}
void Writer::handle(ScriptValueP const& value) {
// TODO: Make a distinction in which values can be saved?
handle(value->toCode());
}
// ----------------------------------------------------------------------------- : AnyField
#if USE_SCRIPT_VALUE_VALUE
ScriptValueP script_default_nil() { static ScriptValueP x(new ScriptDefault(script_nil)); return x; }
AnyField::AnyField() : initial(script_default_nil()) {}
void AnyField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(AnyField) {
REFLECT_BASE(Field);
REFLECT(script);
REFLECT_N("default", default_script);
WITH_DYNAMIC_ARG(field_for_reading, this);
REFLECT(initial);
}
// ----------------------------------------------------------------------------- : AnyValue
AnyValue::AnyValue(AnyFieldP const& field)
: Value(field), value(field->initial)
{}
AnyValue::AnyValue(AnyFieldP const& field, ScriptValueP const& value)
: Value(field), value(value)
{}
/*// TODO: conflict with ColorValue::clone, from IMPLEMENT_FIELD
ValueP AnyValue::clone() const {
return intrusive(new AnyValue(*this));
}*/
String AnyValue::toString() const {
return value->toString();
}
bool AnyValue::update(Context& ctx, const Action* act) {
bool change = false;
if (ScriptDefault const* dv = dynamic_cast<ScriptDefault*>(value.get())) {
ScriptValueP dvv = dv->value;
change = field().default_script.invokeOn(ctx, dvv);
change |= field().script.invokeOn(ctx, dvv);
if (change) value = intrusive(new ScriptDefault(dvv));
} else {
change = field().script.invokeOn(ctx, value);
} }
change |= Value::update(ctx, act);
return change;
} }
void AnyValue::reflect(Writer& reflector) {
if (!fieldP->save_value) return;
// TODO IMPLEMENT_REFLECTION_NAMELESS(AnyValue) {
reflector.handle(value->toString()); if (reflector.isWriting() && !fieldP->save_value) return;
if (reflector.isWriting() && is_default(value)) return;
WITH_DYNAMIC_ARG(field_for_reading, fieldP.get());
REFLECT_NAMELESS(value);
} }
#endif
...@@ -34,6 +34,11 @@ DECLARE_POINTER_TYPE(ValueEditor); ...@@ -34,6 +34,11 @@ DECLARE_POINTER_TYPE(ValueEditor);
// Value for which script updates are being run // Value for which script updates are being run
DECLARE_DYNAMIC_ARG(Value*, value_being_updated); DECLARE_DYNAMIC_ARG(Value*, value_being_updated);
// experimental: use ScriptValue to store any kind of value
#define USE_SCRIPT_VALUE_VALUE 0
#define USE_SCRIPT_VALUE_COLOR 0
#define USE_SCRIPT_VALUE_CHOICE 0
// ----------------------------------------------------------------------------- : Field // ----------------------------------------------------------------------------- : Field
/// Information on how to store a value /// Information on how to store a value
...@@ -217,7 +222,9 @@ class Value : public IntrusivePtrVirtualBase { ...@@ -217,7 +222,9 @@ class Value : public IntrusivePtrVirtualBase {
/// Convert this value to a string for use in tables /// Convert this value to a string for use in tables
virtual String toString() const = 0; virtual String toString() const = 0;
/// Apply scripts to this value, return true if the value has changed /// Apply scripts to this value, return true if the value has changed
virtual bool update(Context& ctx); /** Optionally, the action that causes the update is also passed.
*/
virtual bool update(Context& ctx, const Action* = nullptr);
/// This value has been updated by an action /// This value has been updated by an action
/** Does nothing for most Values, only FakeValues can update underlying data */ /** Does nothing for most Values, only FakeValues can update underlying data */
virtual void onAction(Action& a, bool undone) {} virtual void onAction(Action& a, bool undone) {}
...@@ -300,23 +307,43 @@ inline String type_name(const Value&) { ...@@ -300,23 +307,43 @@ inline String type_name(const Value&) {
void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& name, const Dependency& dep); void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& name, const Dependency& dep);
#if USE_SCRIPT_VALUE_VALUE
// ----------------------------------------------------------------------------- : Value (new style) // ----------------------------------------------------------------------------- : Value (new style)
/// Base class for fields whos values can be scripted
class AnyField : public Field {
public:
AnyField();
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
ScriptValueP initial; ///< Initial choice of a new value, if not set the first choice is used
virtual void initDependencies(Context&, const Dependency&) const;
DECLARE_REFLECTION_VIRTUAL();
};
DECLARE_POINTER_TYPE(AnyField)
/// A Value object that can hold something of 'any' type /// A Value object that can hold something of 'any' type
class AnyValue : public Value { class AnyValue : public Value {
public: public:
inline AnyValue(FieldP const& field, ScriptValueP const& value = script_nil) AnyValue(AnyFieldP const& field);
: Value(field), value(value) AnyValue(AnyFieldP const& field, ScriptValueP const& value);
{}
/// The actual value /// The actual value
typedef ScriptValueP ValueType;
ScriptValueP value; ScriptValueP value;
virtual ValueP clone() const; virtual ValueP clone() const;
virtual String toString() const; virtual String toString() const;
typedef ScriptValueP ValueType; virtual bool update(Context& ctx, const Action* = nullptr);
DECLARE_HAS_FIELD(Any)
DECLARE_REFLECTION_VIRTUAL(); DECLARE_REFLECTION_VIRTUAL();
}; };
DECLARE_POINTER_TYPE(AnyValue)
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -19,23 +19,31 @@ DECLARE_TYPEOF(map<String COMMA ScriptableImage>); ...@@ -19,23 +19,31 @@ DECLARE_TYPEOF(map<String COMMA ScriptableImage>);
ChoiceField::ChoiceField() ChoiceField::ChoiceField()
: choices((Choice*)new Choice) : choices((Choice*)new Choice)
#if !USE_SCRIPT_VALUE_CHOICE
, default_name(_("Default")) , default_name(_("Default"))
#endif
{} {}
IMPLEMENT_FIELD_TYPE(Choice, "choice"); IMPLEMENT_FIELD_TYPE(Choice, "choice");
#if !USE_SCRIPT_VALUE_CHOICE
void ChoiceField::initDependencies(Context& ctx, const Dependency& dep) const { void ChoiceField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep); Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep); script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep); default_script.initDependencies(ctx, dep);
} }
#endif
IMPLEMENT_REFLECTION(ChoiceField) { IMPLEMENT_REFLECTION(ChoiceField) {
#if USE_SCRIPT_VALUE_CHOICE
REFLECT_BASE(AnyField);
#else
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT_N("choices", choices->choices);
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(initial); REFLECT(initial);
#endif
REFLECT_N("choices", choices->choices);
REFLECT(default_name); REFLECT(default_name);
REFLECT_IF_READING { REFLECT_IF_READING {
choices->initIds(); choices->initIds();
...@@ -283,6 +291,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { ...@@ -283,6 +291,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
reflect_content(reflector, *this); reflect_content(reflector, *this);
} }
#if !USE_SCRIPT_VALUE_CHOICE
// ----------------------------------------------------------------------------- : ChoiceValue // ----------------------------------------------------------------------------- : ChoiceValue
ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice) ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice)
...@@ -296,7 +305,7 @@ ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice) ...@@ -296,7 +305,7 @@ ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice)
String ChoiceValue::toString() const { String ChoiceValue::toString() const {
return value(); return value();
} }
bool ChoiceValue::update(Context& ctx) { bool ChoiceValue::update(Context& ctx, const Action*) {
bool change = field().default_script.invokeOnDefault(ctx, value) bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value); | field(). script.invokeOn(ctx, value);
Value::update(ctx); Value::update(ctx);
...@@ -308,3 +317,4 @@ IMPLEMENT_REFLECTION_NAMELESS(ChoiceValue) { ...@@ -308,3 +317,4 @@ IMPLEMENT_REFLECTION_NAMELESS(ChoiceValue) {
} }
INSTANTIATE_REFLECTION_NAMELESS(ChoiceValue) INSTANTIATE_REFLECTION_NAMELESS(ChoiceValue)
#endif USE_SCRIPT_VALUE_CHOICE
...@@ -22,10 +22,16 @@ ...@@ -22,10 +22,16 @@
DECLARE_POINTER_TYPE(ChoiceField); DECLARE_POINTER_TYPE(ChoiceField);
DECLARE_POINTER_TYPE(ChoiceStyle); DECLARE_POINTER_TYPE(ChoiceStyle);
#if !USE_SCRIPT_VALUE_CHOICE
DECLARE_POINTER_TYPE(ChoiceValue); DECLARE_POINTER_TYPE(ChoiceValue);
#endif
/// A field that contains a list of choices /// A field that contains a list of choices
#if USE_SCRIPT_VALUE_CHOICE
class ChoiceField : public AnyField {
#else
class ChoiceField : public Field { class ChoiceField : public Field {
#endif
public: public:
ChoiceField(); ChoiceField();
DECLARE_FIELD_TYPE(Choice); DECLARE_FIELD_TYPE(Choice);
...@@ -34,14 +40,16 @@ class ChoiceField : public Field { ...@@ -34,14 +40,16 @@ class ChoiceField : public Field {
typedef intrusive_ptr<Choice> ChoiceP; typedef intrusive_ptr<Choice> ChoiceP;
ChoiceP choices; ///< A choice group of possible choices ChoiceP choices; ///< A choice group of possible choices
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
String initial; ///< Initial choice of a new value, or ""
String default_name; ///< Name of "default" value String default_name; ///< Name of "default" value
map<String,Color> choice_colors; ///< Colors for the various choices (when color_cardlist) map<String,Color> choice_colors; ///< Colors for the various choices (when color_cardlist)
map<String,Color> choice_colors_cardlist; ///< Colors for the various choices, for in the card list map<String,Color> choice_colors_cardlist; ///< Colors for the various choices, for in the card list
#if !USE_SCRIPT_VALUE_CHOICE
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
String initial; ///< Initial choice of a new value, or ""
#endif
}; };
...@@ -164,6 +172,10 @@ class ChoiceStyle : public Style { ...@@ -164,6 +172,10 @@ class ChoiceStyle : public Style {
// ----------------------------------------------------------------------------- : ChoiceValue // ----------------------------------------------------------------------------- : ChoiceValue
#if USE_SCRIPT_VALUE_CHOICE
typedef AnyValue ChoiceValue;
typedef AnyValueP ChoiceValueP;
#else
/// The Value in a ChoiceField /// The Value in a ChoiceField
class ChoiceValue : public Value { class ChoiceValue : public Value {
public: public:
...@@ -176,8 +188,9 @@ class ChoiceValue : public Value { ...@@ -176,8 +188,9 @@ class ChoiceValue : public Value {
ValueType value; /// The name of the selected choice ValueType value; /// The name of the selected choice
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -21,18 +21,23 @@ ColorField::ColorField() ...@@ -21,18 +21,23 @@ ColorField::ColorField()
IMPLEMENT_FIELD_TYPE(Color, "color"); IMPLEMENT_FIELD_TYPE(Color, "color");
#if !USE_SCRIPT_VALUE_COLOR
void ColorField::initDependencies(Context& ctx, const Dependency& dep) const { void ColorField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep); Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep); script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep); default_script.initDependencies(ctx, dep);
} }
#endif
IMPLEMENT_REFLECTION(ColorField) { IMPLEMENT_REFLECTION(ColorField) {
#if USE_SCRIPT_VALUE_COLOR
REFLECT_BASE(AnyField);
#else
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(initial); REFLECT(initial);
#endif
REFLECT(default_name); REFLECT(default_name);
REFLECT(allow_custom); REFLECT(allow_custom);
REFLECT(choices); REFLECT(choices);
...@@ -76,6 +81,8 @@ int ColorStyle::update(Context& ctx) { ...@@ -76,6 +81,8 @@ int ColorStyle::update(Context& ctx) {
// ----------------------------------------------------------------------------- : ColorValue // ----------------------------------------------------------------------------- : ColorValue
#if !USE_SCRIPT_VALUE_COLOR
ColorValue::ColorValue(const ColorFieldP& field) ColorValue::ColorValue(const ColorFieldP& field)
: Value(field) : Value(field)
, value( !field->initial.isDefault() ? field->initial() , value( !field->initial.isDefault() ? field->initial()
...@@ -92,7 +99,7 @@ String ColorValue::toString() const { ...@@ -92,7 +99,7 @@ String ColorValue::toString() const {
} }
return _("<color>"); return _("<color>");
} }
bool ColorValue::update(Context& ctx) { bool ColorValue::update(Context& ctx, const Action*) {
bool change = field().default_script.invokeOnDefault(ctx, value) bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value); | field(). script.invokeOn(ctx, value);
Value::update(ctx); Value::update(ctx);
...@@ -102,3 +109,6 @@ bool ColorValue::update(Context& ctx) { ...@@ -102,3 +109,6 @@ bool ColorValue::update(Context& ctx) {
IMPLEMENT_REFLECTION_NAMELESS(ColorValue) { IMPLEMENT_REFLECTION_NAMELESS(ColorValue) {
if (fieldP->save_value || !reflector.isWriting()) REFLECT_NAMELESS(value); if (fieldP->save_value || !reflector.isWriting()) REFLECT_NAMELESS(value);
} }
#endif
...@@ -19,10 +19,16 @@ ...@@ -19,10 +19,16 @@
DECLARE_POINTER_TYPE(ColorField); DECLARE_POINTER_TYPE(ColorField);
DECLARE_POINTER_TYPE(ColorStyle); DECLARE_POINTER_TYPE(ColorStyle);
#if !USE_SCRIPT_VALUE_COLOR
DECLARE_POINTER_TYPE(ColorValue); DECLARE_POINTER_TYPE(ColorValue);
#endif
/// A field for color values, it contains a list of choices for colors /// A field for color values, it contains a list of choices for colors
#if USE_SCRIPT_VALUE_COLOR
class ColorField : public AnyField {
#else
class ColorField : public Field { class ColorField : public Field {
#endif
public: public:
ColorField(); ColorField();
DECLARE_FIELD_TYPE(Color); DECLARE_FIELD_TYPE(Color);
...@@ -30,14 +36,16 @@ class ColorField : public Field { ...@@ -30,14 +36,16 @@ class ColorField : public Field {
class Choice; class Choice;
typedef intrusive_ptr<Choice> ChoiceP; typedef intrusive_ptr<Choice> ChoiceP;
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
vector<ChoiceP> choices; ///< Color choices available vector<ChoiceP> choices; ///< Color choices available
bool allow_custom; ///< Are colors not in the list of choices allowed? bool allow_custom; ///< Are colors not in the list of choices allowed?
Defaultable<Color> initial; ///< Initial choice of a new value, if not set the first choice is used
String default_name; ///< Name of "default" value String default_name; ///< Name of "default" value
#if !USE_SCRIPT_VALUE_COLOR
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
Defaultable<Color> initial; ///< Initial choice of a new value, or ""
#endif
}; };
/// A color that can be chosen for this field /// A color that can be chosen for this field
...@@ -69,6 +77,10 @@ class ColorStyle : public Style { ...@@ -69,6 +77,10 @@ class ColorStyle : public Style {
// ----------------------------------------------------------------------------- : ColorValue // ----------------------------------------------------------------------------- : ColorValue
#if USE_SCRIPT_VALUE_CHOICE
typedef AnyValue ColorValue;
typedef AnyValueP ColorValueP;
#else
/// The Value in a ColorField /// The Value in a ColorField
class ColorValue : public Value { class ColorValue : public Value {
public: public:
...@@ -77,9 +89,9 @@ class ColorValue : public Value { ...@@ -77,9 +89,9 @@ class ColorValue : public Value {
ValueType value; ///< The value ValueType value; ///< The value
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -62,7 +62,7 @@ IMPLEMENT_REFLECTION(InfoStyle) { ...@@ -62,7 +62,7 @@ IMPLEMENT_REFLECTION(InfoStyle) {
String InfoValue::toString() const { String InfoValue::toString() const {
return value; return value;
} }
bool InfoValue::update(Context& ctx) { bool InfoValue::update(Context& ctx, const Action*) {
if (value.empty()) value = field().caption; if (value.empty()) value = field().caption;
bool change = field().script.invokeOn(ctx, value); bool change = field().script.invokeOn(ctx, value);
Value::update(ctx); Value::update(ctx);
......
...@@ -62,7 +62,7 @@ class InfoValue : public Value { ...@@ -62,7 +62,7 @@ class InfoValue : public Value {
ValueType value; ValueType value;
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <data/field/multiple_choice.hpp> #include <data/field/multiple_choice.hpp>
#include <data/action/value.hpp>
// ----------------------------------------------------------------------------- : MultipleChoiceField // ----------------------------------------------------------------------------- : MultipleChoiceField
...@@ -51,10 +52,13 @@ IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) { ...@@ -51,10 +52,13 @@ IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) {
REFLECT_BASE(ChoiceValue); REFLECT_BASE(ChoiceValue);
} }
bool MultipleChoiceValue::update(Context& ctx) { bool MultipleChoiceValue::update(Context& ctx, const Action* act) {
String old_value = value(); String old_value = value();
ctx.setVariable(_("last_change"), to_script(last_change)); if (const MultipleChoiceValueAction* mvca = dynamic_cast<const MultipleChoiceValueAction*>(act)) {
ChoiceValue::update(ctx); ctx.setVariable(_("last_change"), to_script(mvca->changed_choice));
}
// ctx.setVariable(_("last_change"), to_script(last_change));
ChoiceValue::update(ctx,act);
normalForm(); normalForm();
return value() != old_value; return value() != old_value;
} }
......
...@@ -65,7 +65,7 @@ class MultipleChoiceValue : public ChoiceValue { ...@@ -65,7 +65,7 @@ class MultipleChoiceValue : public ChoiceValue {
/// Splits the value, stores the selected choices in the out parameter /// Splits the value, stores the selected choices in the out parameter
void get(vector<String>& out) const; void get(vector<String>& out) const;
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
private: private:
DECLARE_REFLECTION(); DECLARE_REFLECTION();
......
...@@ -61,9 +61,9 @@ PackagedP PackageChoiceValue::getPackage() const { ...@@ -61,9 +61,9 @@ PackagedP PackageChoiceValue::getPackage() const {
else return package_manager.openAny(package_name, true); else return package_manager.openAny(package_name, true);
} }
bool PackageChoiceValue::update(Context& ctx) { bool PackageChoiceValue::update(Context& ctx, const Action* act) {
bool change = field().script.invokeOn(ctx, package_name); bool change = field().script.invokeOn(ctx, package_name);
Value::update(ctx); Value::update(ctx,act);
return change; return change;
} }
......
...@@ -63,7 +63,7 @@ class PackageChoiceValue : public Value { ...@@ -63,7 +63,7 @@ class PackageChoiceValue : public Value {
/// Get the package (if it is set) /// Get the package (if it is set)
PackagedP getPackage() const; PackagedP getPackage() const;
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -135,7 +135,7 @@ IMPLEMENT_REFLECTION(TextStyle) { ...@@ -135,7 +135,7 @@ IMPLEMENT_REFLECTION(TextStyle) {
String TextValue::toString() const { String TextValue::toString() const {
return untag_hide_sep(value()); return untag_hide_sep(value());
} }
bool TextValue::update(Context& ctx) { bool TextValue::update(Context& ctx, const Action*) {
updateAge(); updateAge();
WITH_DYNAMIC_ARG(last_update_age, last_update.get()); WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this); WITH_DYNAMIC_ARG(value_being_updated, this);
......
...@@ -92,7 +92,7 @@ class TextValue : public Value { ...@@ -92,7 +92,7 @@ class TextValue : public Value {
ValueType value; ///< The text of this value ValueType value; ///< The text of this value
Age last_update; ///< When was the text last changed? Age last_update; ///< When was the text last changed?
virtual bool update(Context&); virtual bool update(Context&, const Action* = nullptr);
}; };
// ----------------------------------------------------------------------------- : TextValue // ----------------------------------------------------------------------------- : TextValue
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
DECLARE_TYPEOF_COLLECTION(CardP); DECLARE_TYPEOF_COLLECTION(CardP);
DECLARE_TYPEOF_COLLECTION(FieldP); DECLARE_TYPEOF_COLLECTION(FieldP);
DECLARE_POINTER_TYPE(ChoiceValue); //%DECLARE_POINTER_TYPE(ChoiceValue);
DECLARE_TYPEOF(map<int COMMA FieldP>); DECLARE_TYPEOF(map<int COMMA FieldP>);
DECLARE_TYPEOF_NO_REV(IndexMap<FieldP COMMA StyleP>); DECLARE_TYPEOF_NO_REV(IndexMap<FieldP COMMA StyleP>);
DECLARE_TYPEOF_COLLECTION(CardListBase*); DECLARE_TYPEOF_COLLECTION(CardListBase*);
......
...@@ -43,8 +43,14 @@ SCRIPT_FUNCTION(new_card) { ...@@ -43,8 +43,14 @@ SCRIPT_FUNCTION(new_card) {
cvalue->value = v->toString(); cvalue->value = v->toString();
} else if (PackageChoiceValue* pvalue = dynamic_cast<PackageChoiceValue*>(value)) { } else if (PackageChoiceValue* pvalue = dynamic_cast<PackageChoiceValue*>(value)) {
pvalue->package_name = v->toString(); pvalue->package_name = v->toString();
#if !USE_SCRIPT_VALUE_COLOR
} else if (ColorValue* cvalue = dynamic_cast<ColorValue*>(value)) { } else if (ColorValue* cvalue = dynamic_cast<ColorValue*>(value)) {
cvalue->value = v->toColor(); cvalue->value = v->toColor();
#endif
#if USE_SCRIPT_VALUE_VALUE
} else if (AnyValue* avalue = dynamic_cast<AnyValue*>(value)) {
avalue->value = v;
#endif
} else { } else {
throw ScriptError(format_string(_("Can not set value '%s', it is not of the right type"),name)); throw ScriptError(format_string(_("Can not set value '%s', it is not of the right type"),name));
} }
......
...@@ -155,7 +155,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) { ...@@ -155,7 +155,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) {
TYPE_CASE(action, ValueAction) { TYPE_CASE(action, ValueAction) {
if (action.card) { if (action.card) {
// we can just turn the Card* into a CardP // we can just turn the Card* into a CardP
updateValue(*action.valueP, intrusive_from_existing(const_cast<Card*>(action.card))); updateValue(*action.valueP, intrusive_from_existing(const_cast<Card*>(action.card)), &action);
return; return;
} else { } else {
// is it a keyword's fake value? // is it a keyword's fake value?
...@@ -173,7 +173,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) { ...@@ -173,7 +173,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) {
return; return;
} }
// a set or styling value // a set or styling value
updateValue(*action.valueP, CardP()); updateValue(*action.valueP, CardP(), &action);
} }
} }
TYPE_CASE_(action, ScriptValueEvent) { TYPE_CASE_(action, ScriptValueEvent) {
...@@ -186,7 +186,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) { ...@@ -186,7 +186,7 @@ void SetScriptManager::onAction(const Action& action, bool undone) {
const CardP& card = step.item; const CardP& card = step.item;
Context& ctx = getContext(card); Context& ctx = getContext(card);
FOR_EACH(v, card->data) { FOR_EACH(v, card->data) {
v->update(ctx); v->update(ctx,&action);
} }
} }
} }
...@@ -259,11 +259,11 @@ void SetScriptManager::updateDelayed() { ...@@ -259,11 +259,11 @@ void SetScriptManager::updateDelayed() {
delay = 0; delay = 0;
} }
void SetScriptManager::updateValue(Value& value, const CardP& card) { void SetScriptManager::updateValue(Value& value, const CardP& card, Action const* action) {
Age starting_age; // the start of the update process Age starting_age; // the start of the update process
deque<ToUpdate> to_update; deque<ToUpdate> to_update;
// execute script for initial changed value // execute script for initial changed value
value.update(getContext(card)); value.update(getContext(card), action);
#ifdef LOG_UPDATES #ifdef LOG_UPDATES
wxLogDebug(_("Start: %s"), value.fieldP->name); wxLogDebug(_("Start: %s"), value.fieldP->name);
#endif #endif
......
...@@ -81,8 +81,10 @@ class SetScriptManager : public SetScriptContext, public ActionListener { ...@@ -81,8 +81,10 @@ class SetScriptManager : public SetScriptContext, public ActionListener {
/// Update a map of styles /// Update a map of styles
void updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>& styles, bool only_content_dependent); void updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>& styles, bool only_content_dependent);
/// Updates scripts, starting at some value /// Updates scripts, starting at some value
/** if the value changes any dependend values are updated as well */ /** if the value changes any dependend values are updated as well
void updateValue(Value& value, const CardP& card); * optionally: action that causes this update
*/
void updateValue(Value& value, const CardP& card, const Action* action);
// Update all values with a specific dependency // Update all values with a specific dependency
void updateAllDependend(const vector<Dependency>& dependent_scripts, const CardP& card = CardP()); void updateAllDependend(const vector<Dependency>& dependent_scripts, const CardP& card = CardP());
......
...@@ -52,9 +52,7 @@ ScriptValueP OptionalScript::invoke(Context& ctx, bool open_scope) const { ...@@ -52,9 +52,7 @@ ScriptValueP OptionalScript::invoke(Context& ctx, bool open_scope) const {
} }
} }
void OptionalScript::parse(Reader& reader, bool string_mode) { void parse_errors_to_reader_warnings(Reader& reader, vector<ScriptParseError> const& errors) {
vector<ScriptParseError> errors;
script = ::parse(unparsed, reader.getPackage(), string_mode, errors);
// show parse errors as warnings // show parse errors as warnings
String include_warnings; String include_warnings;
for (size_t i = 0 ; i < errors.size() ; ++i) { for (size_t i = 0 ; i < errors.size() ; ++i) {
...@@ -73,6 +71,12 @@ void OptionalScript::parse(Reader& reader, bool string_mode) { ...@@ -73,6 +71,12 @@ void OptionalScript::parse(Reader& reader, bool string_mode) {
} }
} }
void OptionalScript::parse(Reader& reader, bool string_mode) {
vector<ScriptParseError> errors;
script = ::parse(unparsed, reader.getPackage(), string_mode, errors);
parse_errors_to_reader_warnings(reader,errors);
}
void OptionalScript::initDependencies(Context& ctx, const Dependency& dep) const { void OptionalScript::initDependencies(Context& ctx, const Dependency& dep) const {
if (script) { if (script) {
ctx.dependencies(dep, *script); ctx.dependencies(dep, *script);
......
...@@ -16,6 +16,7 @@ template <typename T> class Defaultable; ...@@ -16,6 +16,7 @@ template <typename T> class Defaultable;
template <typename T> class Scriptable; template <typename T> class Scriptable;
DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(Game);
DECLARE_POINTER_TYPE(StyleSheet); DECLARE_POINTER_TYPE(StyleSheet);
DECLARE_POINTER_TYPE(ScriptValue);
class Packaged; class Packaged;
// ----------------------------------------------------------------------------- : Reader // ----------------------------------------------------------------------------- : Reader
...@@ -95,11 +96,10 @@ class Reader { ...@@ -95,11 +96,10 @@ class Reader {
template <typename K, typename V> void handle(IndexMap<K,V>&); template <typename K, typename V> void handle(IndexMap<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&); template <typename K, typename V> void handle(DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&); template <typename K, typename V> void handle(DelayedIndexMapsData<K,V>&);
/// Reads a Defaultable from the input stream
template <typename T> void handle(Defaultable<T>&); template <typename T> void handle(Defaultable<T>&);
/// Reads a Scriptable from the input stream
template <typename T> void handle(Scriptable<T>&); template <typename T> void handle(Scriptable<T>&);
// special behaviour void handle(ScriptValueP&);
// special behaviour: only write the name of the package
void handle(GameP&); void handle(GameP&);
void handle(StyleSheetP&); void handle(StyleSheetP&);
......
...@@ -61,10 +61,9 @@ class Writer { ...@@ -61,10 +61,9 @@ class Writer {
template <typename K, typename V> void handle(const IndexMap<K,V>&); template <typename K, typename V> void handle(const IndexMap<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&); template <typename K, typename V> void handle(const DelayedIndexMaps<K,V>&);
template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&); template <typename K, typename V> void handle(const DelayedIndexMapsData<K,V>&);
/// Write an object of type Defaultable<T> to the output stream
template <typename T> void handle(const Defaultable<T>&); template <typename T> void handle(const Defaultable<T>&);
/// Write an object of type Scriptable<T> to the output stream
template <typename T> void handle(const Scriptable<T>&); template <typename T> void handle(const Scriptable<T>&);
void handle(const ScriptValueP&);
// special behaviour // special behaviour
void handle(const GameP&); void handle(const GameP&);
void handle(const StyleSheetP&); void handle(const StyleSheetP&);
......
...@@ -81,14 +81,16 @@ const Char* version_suffix = _(" (ascii build)"); ...@@ -81,14 +81,16 @@ const Char* version_suffix = _(" (ascii build)");
* - store time created,modified for cards -> changes set and clipboard format * - store time created,modified for cards -> changes set and clipboard format
* 0.3.9 : bugfix release mostly, a few new script functions * 0.3.9 : bugfix release mostly, a few new script functions
* 2.0.0 : bugfix release mostly, added error console * 2.0.0 : bugfix release mostly, added error console
* 2.0.1 : values are stored as ScriptValues, i.e. as parsable code.
* previously they were stored as unescaped strings, where the field type determined how the value would be parsed.
*/ */
const Version file_version_locale = 20000; // 2.0.0 const Version file_version_locale = 20000; // 2.0.0
const Version file_version_set = 308; // 0.3.8 const Version file_version_set = 20001; // 2.0.1
const Version file_version_game = 308; // 0.3.8 const Version file_version_game = 20001; // 2.0.1
const Version file_version_stylesheet = 308; // 0.3.8 const Version file_version_stylesheet = 20001; // 2.0.1
const Version file_version_symbol_font = 306; // 0.3.6 const Version file_version_symbol_font = 20001; // 2.0.1
const Version file_version_export_template = 307; // 0.3.7 const Version file_version_export_template = 307; // 0.3.7
const Version file_version_installer = 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_symbol = 305; // 0.3.5
const Version file_version_clipboard = 308; // 0.3.8 const Version file_version_clipboard = 308; // 0.3.8
const Version file_version_script = 307; // 0.3.7 const Version file_version_script = 20001; // 2.0.1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment