mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
98 Commits
experiment
...
v5.3.179
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f6539626d3 | ||
![]() |
e0df9b0ff3 | ||
![]() |
0b50231027 | ||
![]() |
94c9924a88 | ||
![]() |
581cc5837a | ||
![]() |
ff77d107b2 | ||
![]() |
3a5198cb34 | ||
![]() |
c92025549d | ||
![]() |
600b307e7e | ||
![]() |
297fade0bd | ||
![]() |
84d7500eab | ||
![]() |
f99c4ab948 | ||
![]() |
5fe4f23c83 | ||
![]() |
8820bd45e4 | ||
![]() |
5e16968f37 | ||
![]() |
6b6179ff22 | ||
![]() |
94bcd87a9f | ||
![]() |
356a1974ef | ||
![]() |
345bad9876 | ||
![]() |
0b8d843a73 | ||
![]() |
9f0c987ec7 | ||
![]() |
0e79209d2b | ||
![]() |
f991c09c39 | ||
![]() |
6e24dfa9b8 | ||
![]() |
a3cd00dc94 | ||
![]() |
300c011151 | ||
![]() |
230d410469 | ||
![]() |
402e54f706 | ||
![]() |
dc29c27272 | ||
![]() |
462bb84c51 | ||
![]() |
a775e79bae | ||
![]() |
bc8c867f23 | ||
![]() |
f34bf2b7b4 | ||
![]() |
e2d482e1aa | ||
![]() |
0724ae34d9 | ||
![]() |
2fc1c2fd86 | ||
![]() |
9a42ae11ad | ||
![]() |
32773e06f6 | ||
![]() |
00508df014 | ||
![]() |
c3cf5b7002 | ||
![]() |
df7fab84b8 | ||
![]() |
95a0012c5e | ||
![]() |
4bf644f9f2 | ||
![]() |
79cad1da3a | ||
![]() |
ffc6f5786b | ||
![]() |
07df65f8c2 | ||
![]() |
61f8f0ed66 | ||
![]() |
c24e2e5ace | ||
![]() |
25c72a6d1f | ||
![]() |
6153b23e7d | ||
![]() |
1b7ccfded3 | ||
![]() |
ec57a0a80d | ||
![]() |
95a9e37aba | ||
![]() |
787274f7fa | ||
![]() |
ceb24f0edb | ||
![]() |
028bf7a70d | ||
![]() |
08471d0356 | ||
![]() |
921add6665 | ||
![]() |
dabff06891 | ||
![]() |
967033c407 | ||
![]() |
0809749ddc | ||
![]() |
8a27801355 | ||
![]() |
20cba9d7ed | ||
![]() |
3878b93c94 | ||
![]() |
645d5331cb | ||
![]() |
91db750632 | ||
![]() |
740485b54a | ||
![]() |
e960fe7c5b | ||
![]() |
0633c7b474 | ||
![]() |
704bfd40cb | ||
![]() |
310abe7a13 | ||
![]() |
b0f9bed273 | ||
![]() |
766a62ad9b | ||
![]() |
ec6f669d94 | ||
![]() |
36a2408be0 | ||
![]() |
1343210a2b | ||
![]() |
1e76fedd7b | ||
![]() |
3923641988 | ||
![]() |
a7d488626c | ||
![]() |
33f20b9bde | ||
![]() |
6c4cd6343f | ||
![]() |
f1c04df0ee | ||
![]() |
9b466ff574 | ||
![]() |
2b0969600e | ||
![]() |
7ed1eee8f9 | ||
![]() |
9b9b729b42 | ||
![]() |
530969d4b0 | ||
![]() |
11ef87b9ee | ||
![]() |
e1857e2e1e | ||
![]() |
d1385e5fc2 | ||
![]() |
9298026179 | ||
![]() |
913e367f6f | ||
![]() |
1bc27bdd8b | ||
![]() |
4d8b39df95 | ||
![]() |
e6970319b9 | ||
![]() |
cdf21ebd4c | ||
![]() |
dc014e2ab0 | ||
![]() |
25509e1c7b |
@@ -46,10 +46,6 @@ void EffectsCodeGenerator::GenerateEffectsIncludeFiles(
|
||||
// TODO Add unit tests on this function.
|
||||
|
||||
// TODO Merge with UsedExtensionsFinder.
|
||||
// Default lights rely on the fact that UsedExtensionsFinder doesn't find
|
||||
// extension usages for effects. This has the happy side effect of not
|
||||
// including Three.js when no 3D object are in the scenes.
|
||||
// We need to make something explicit to avoid future bugs.
|
||||
|
||||
// See also gd::Project::ExposeResources for a method that traverse the whole
|
||||
// project (this time for resources) and
|
||||
|
@@ -69,8 +69,36 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
}
|
||||
}
|
||||
|
||||
return callStartString + "(" + argumentsStr + ") " + relationalOperator +
|
||||
" " + rhs;
|
||||
auto lhs = callStartString + "(" + argumentsStr + ")";
|
||||
return GenerateRelationalOperation(relationalOperator, lhs, rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a relational operation
|
||||
*
|
||||
* @param relationalOperator the operator
|
||||
* @param lhs the left hand operand
|
||||
* @param rhs the right hand operand
|
||||
* @return gd::String
|
||||
*/
|
||||
gd::String EventsCodeGenerator::GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs) {
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " + rhs;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=" &&
|
||||
operatorString != "startsWith" && operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,18 +666,6 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
@@ -674,7 +690,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
argOutput =
|
||||
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
|
||||
} else if (metadata.GetType() == "relationalOperator") {
|
||||
argOutput += GenerateRelationalOperatorCodes(parameter.GetPlainString());
|
||||
argOutput += parameter.GetPlainString();
|
||||
argOutput = "\"" + argOutput + "\"";
|
||||
} else if (metadata.GetType() == "operator") {
|
||||
argOutput += parameter.GetPlainString();
|
||||
@@ -1212,13 +1228,13 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesConta
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
return "getProperty" + property.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
return "getParameter" + parameter.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
|
@@ -12,8 +12,8 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
class Expression;
|
||||
@@ -58,8 +58,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Construct a code generator for the specified
|
||||
* objects/groups and platform
|
||||
*/
|
||||
EventsCodeGenerator(const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
EventsCodeGenerator(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
|
||||
/**
|
||||
@@ -472,10 +473,15 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdForEventsList();
|
||||
|
||||
virtual gd::String GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs);
|
||||
|
||||
protected:
|
||||
virtual const gd::String GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Generate the code for a single parameter.
|
||||
*
|
||||
@@ -546,7 +552,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
};
|
||||
|
||||
virtual gd::String GenerateVariableValueAs(const gd::String& type) {
|
||||
return type == "string" ? ".getAsString()" : ".getAsNumber()";
|
||||
return type == "number|string" ? ".getAsNumberOrString()"
|
||||
: type == "string" ? ".getAsString()"
|
||||
: ".getAsNumber()";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,14 +585,16 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
return "fakeObjectListOf_" + objectName;
|
||||
}
|
||||
|
||||
virtual gd::String GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
virtual gd::String GeneratePropertyGetter(
|
||||
const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
virtual gd::String GenerateParameterGetter(
|
||||
const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
/**
|
||||
* \brief Generate the code to reference an object which is
|
||||
@@ -665,7 +675,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* The default implementation generates C-style code : It wraps the predicate
|
||||
* inside parenthesis and add a !.
|
||||
*/
|
||||
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
|
||||
virtual gd::String GenerateNegatedPredicate(
|
||||
const gd::String& predicate) const {
|
||||
return "!(" + predicate + ")";
|
||||
};
|
||||
|
||||
@@ -726,6 +737,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument = 0);
|
||||
|
||||
gd::String GenerateOperatorCall(const gd::InstructionMetadata& instrInfos,
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
|
@@ -103,7 +103,7 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
|
||||
// This "translation" from the type to an enum could be avoided
|
||||
// if all types were moved to an enum.
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -191,7 +191,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
return;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
|
||||
ExpressionCodeGenerator generator("number|string", "", codeGenerator, context);
|
||||
node.expression->Visit(generator);
|
||||
output +=
|
||||
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
|
||||
@@ -200,7 +200,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -271,7 +271,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -502,7 +502,7 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
@@ -511,7 +511,7 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
|
||||
ObjectFunctionNameNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
|
@@ -115,21 +115,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
|
||||
.AddExpression(
|
||||
"GetArgumentAsNumber",
|
||||
_("Get function parameter value"),
|
||||
_("Get function parameter (also called \"argument\") value."),
|
||||
_("Get function parameter (also called \"argument\") value. You don't need this most of the time as you can simply write the parameter name in an expression."),
|
||||
"",
|
||||
"res/function16.png")
|
||||
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
|
||||
.SetRelevantForFunctionEventsOnly();
|
||||
.SetRelevantForFunctionEventsOnly()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddStrExpression(
|
||||
"GetArgumentAsString",
|
||||
_("Get function parameter text"),
|
||||
_("Get function parameter (also called \"argument\") text."),
|
||||
_("Get function parameter (also called \"argument\") text. You don't need this most of the time as you can simply write the parameter name in an expression."),
|
||||
"",
|
||||
"res/function16.png")
|
||||
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
|
||||
.SetRelevantForFunctionEventsOnly();
|
||||
.SetRelevantForFunctionEventsOnly()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
|
@@ -49,6 +49,7 @@ class GD_CORE_API BuiltinExtensionsImplementer {
|
||||
static void ImplementsAnimatableExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsEffectExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsOpacityExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsTextContainerExtension(gd::PlatformExtension& extension);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -396,7 +396,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Z order"),
|
||||
_("Modify the Z-order of an object"),
|
||||
_("the z-order"),
|
||||
_("Z order"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/planicon24.png",
|
||||
"res/actions/planicon.png")
|
||||
|
||||
@@ -550,7 +550,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Z-order"),
|
||||
_("Compare the Z-order of the specified object."),
|
||||
_("the Z-order"),
|
||||
_("Z-order"),
|
||||
_("Layer"),
|
||||
"res/conditions/planicon24.png",
|
||||
"res/conditions/planicon.png")
|
||||
|
||||
@@ -617,6 +617,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"number", ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("AngleOfDisplacement",
|
||||
_("Angle of movement (using forces)"),
|
||||
_("Compare the angle of movement of an object according to "
|
||||
@@ -626,7 +627,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Movement using forces"),
|
||||
"res/conditions/vitesse24.png",
|
||||
"res/conditions/vitesse.png")
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Tolerance, in degrees"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
obj.AddCondition("IsTotalForceAngleAround",
|
||||
_("Angle of movement (using forces)"),
|
||||
_("Compare the angle of movement of an object according to "
|
||||
"the forces applied on it."),
|
||||
_("Angle of movement of _PARAM0_ is _PARAM1_ ± _PARAM2_°"),
|
||||
_("Movement using forces"),
|
||||
"res/conditions/vitesse24.png",
|
||||
"res/conditions/vitesse.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Tolerance, in degrees"))
|
||||
@@ -1137,7 +1151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
obj.AddExpression("ZOrder",
|
||||
_("Z-order"),
|
||||
_("Z-order of an object"),
|
||||
_("Visibility"),
|
||||
"",
|
||||
"res/actions/planicon.png")
|
||||
.AddParameter("object", _("Object"));
|
||||
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation index")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["Index"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"string",
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"objectAnimationName", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation name")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Name"].SetGroup("");
|
||||
|
||||
aut.AddScopedAction("PauseAnimation",
|
||||
_("Pause the animation"),
|
||||
@@ -107,6 +111,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Speed scale")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["SpeedScale"].SetGroup("");
|
||||
|
||||
aut.AddScopedCondition("IsAnimationPaused",
|
||||
_("Animation paused"),
|
||||
@@ -130,6 +135,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
"ElapsedTime",
|
||||
_("Animation elapsed time"),
|
||||
_("the elapsed time from the beginning of the animation (in seconds)"),
|
||||
_("the animation elapsed time"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.UseStandardParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Elapsed time (in seconds)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["ElapsedTime"].SetGroup("");
|
||||
|
||||
aut.AddExpression(
|
||||
"Duration",
|
||||
_("Animation duration"),
|
||||
_("Return the current animation duration (in seconds)."),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
|
||||
@@ -54,6 +56,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
_("Opacity (0-255)")))
|
||||
.SetFunctionName("setOpacity")
|
||||
.SetGetter("getOpacity");
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
|
||||
@@ -44,7 +46,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale"),
|
||||
_("the scale of the object (default scale is 1)"),
|
||||
_("the scale"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scale24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -60,7 +63,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale on X axis"),
|
||||
_("the scale on X axis of the object (default scale is 1)"),
|
||||
_("the scale on X axis"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scaleWidth24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["X"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -76,7 +80,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale on Y axis"),
|
||||
_("the scale on Y axis of the object (default scale is 1)"),
|
||||
_("the scale on Y axis"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scaleHeight24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -85,6 +89,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Y"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the GNU Lesser General Public
|
||||
* License.
|
||||
*/
|
||||
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/BehaviorsSharedData.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("TextContainerCapability",
|
||||
_("Text capability"),
|
||||
_("Animate objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
|
||||
.SetIcon("res/conditions/text24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"TextContainerBehavior",
|
||||
_("Text capability"),
|
||||
"Text",
|
||||
_("Access objects text."),
|
||||
"",
|
||||
"res/conditions/text24_black.png",
|
||||
"TextContainerBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"string",
|
||||
"Value",
|
||||
_("Text"),
|
||||
_("the text"),
|
||||
_("the text"),
|
||||
"",
|
||||
"res/conditions/text24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TextContainerBehavior")
|
||||
.UseStandardParameters(
|
||||
"string", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Text")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -38,6 +38,8 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter("expression", _("Y position of the origin"), "", true)
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter("expression", _("Z position of the origin"), "", true)
|
||||
.SetDefaultValue("0")
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.AddObject<SpriteObject>("Sprite",
|
||||
_("Sprite"),
|
||||
_("Animated object which can be used for "
|
||||
"most elements of a game"),
|
||||
"most elements of a game."),
|
||||
"CppPlatform/Extensions/spriteicon.png")
|
||||
.SetCategoryFullName(_("General"))
|
||||
.AddDefaultBehavior("EffectCapability::EffectBehavior")
|
||||
@@ -408,6 +408,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.SetHidden()
|
||||
.MarkAsSimple();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("ScaleWidth",
|
||||
_("Scale on X axis"),
|
||||
_("Compare the scale of the width of an object."),
|
||||
@@ -415,7 +416,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Size"),
|
||||
"res/conditions/scaleWidth24_black.png",
|
||||
"res/conditions/scaleWidth_black.png")
|
||||
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number",
|
||||
@@ -423,6 +424,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("ScaleHeight",
|
||||
_("Scale on Y axis"),
|
||||
_("Compare the scale of the height of an object."),
|
||||
@@ -430,7 +432,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Size"),
|
||||
"res/conditions/scaleHeight24_black.png",
|
||||
"res/conditions/scaleHeight_black.png")
|
||||
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number",
|
||||
|
@@ -303,12 +303,24 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Return true if the instruction must be hidden in the IDE.
|
||||
* \brief Return true if the object must be hidden in the IDE.
|
||||
*/
|
||||
bool IsHidden() const { return hidden; }
|
||||
|
||||
/**
|
||||
* \brief Declare a usage of the 3D renderer.
|
||||
*/
|
||||
ObjectMetadata &MarkAsRenderedIn3D() {
|
||||
isRenderedIn3D = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the object uses the 3D renderer.
|
||||
*/
|
||||
bool IsRenderedIn3D() const { return isRenderedIn3D; }
|
||||
|
||||
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
|
||||
@@ -329,6 +341,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
gd::String categoryFullName;
|
||||
std::set<gd::String> defaultBehaviorTypes;
|
||||
bool hidden = false;
|
||||
bool isRenderedIn3D = false;
|
||||
|
||||
std::shared_ptr<gd::ObjectConfiguration>
|
||||
blueprintObject; ///< The "blueprint" object to be copied when a new
|
||||
|
@@ -60,10 +60,10 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
void ParameterMetadataTools::ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
@@ -71,7 +71,7 @@ void ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName().find(prefix) == 0) cb(parameterMetadata);
|
||||
if (parameterMetadata.GetName().FindCaseInsensitive(search) != gd::String::npos) cb(parameterMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,9 +27,9 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
|
||||
static void ForEachParameterWithPrefix(
|
||||
static void ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb);
|
||||
|
||||
static bool Has(
|
||||
|
@@ -58,7 +58,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -88,7 +88,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(projectScopedContainers, node.identifierName);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
|
@@ -81,8 +81,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -115,7 +114,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
@@ -217,8 +216,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -250,7 +248,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
|
@@ -395,12 +395,10 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch("", type);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
@@ -409,12 +407,10 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// No completions.
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch("", type);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
@@ -488,18 +484,25 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
const gd::VariablesContainer* variablesContainer = nullptr;
|
||||
if (type == "globalvar") {
|
||||
variablesContainer =
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(
|
||||
*variablesContainer, node.name, node.nameLocation);
|
||||
}
|
||||
} else if (type == "scenevar") {
|
||||
variablesContainer =
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(
|
||||
*variablesContainer, node.name, node.nameLocation);
|
||||
}
|
||||
} else if (type == "objectvar") {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
@@ -508,17 +511,12 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
variablesContainer =
|
||||
objectsContainersList.GetObjectOrGroupVariablesContainer(
|
||||
objectName);
|
||||
}
|
||||
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(
|
||||
*variablesContainer, node.name, node.nameLocation);
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList, objectName, node.name, node.nameLocation);
|
||||
}
|
||||
} else {
|
||||
AddCompletionsForObjectsAndVariablesWithPrefix(
|
||||
AddCompletionsForObjectsAndVariablesMatchingSearch(
|
||||
node.name, type, node.nameLocation);
|
||||
}
|
||||
}
|
||||
@@ -533,21 +531,30 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
// Only show completions of objects if an object is required.
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.identifierName, type, node.location);
|
||||
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
const gd::VariablesContainer* variablesContainer = nullptr;
|
||||
if (type == "globalvar") {
|
||||
variablesContainer =
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation);
|
||||
}
|
||||
} else if (type == "scenevar") {
|
||||
variablesContainer =
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation);
|
||||
}
|
||||
} else if (type == "objectvar") {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
@@ -556,21 +563,18 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
variablesContainer =
|
||||
objectsContainersList.GetObjectOrGroupVariablesContainer(
|
||||
objectName);
|
||||
}
|
||||
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(*variablesContainer,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation);
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList,
|
||||
objectName,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation);
|
||||
}
|
||||
} else {
|
||||
// Object function, behavior name, variable, object variable.
|
||||
if (IsCaretOn(node.identifierNameLocation)) {
|
||||
// Is this the proper position?
|
||||
AddCompletionsForAllIdentifiersWithPrefix(
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
node.identifierName, type, node.identifierNameLocation);
|
||||
if (!node.identifierNameDotLocation.IsValid()) {
|
||||
completions.push_back(
|
||||
@@ -585,16 +589,11 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const gd::String& objectName = node.identifierName;
|
||||
|
||||
// Might be an object variable, object behavior or object expression:
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(objectName);
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(
|
||||
*variablesContainer,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation);
|
||||
}
|
||||
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList,
|
||||
objectName,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForBehaviorWithPrefix(
|
||||
node.childIdentifierName,
|
||||
@@ -612,16 +611,14 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (!node.behaviorFunctionName.empty() ||
|
||||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
|
||||
// Behavior function (or behavior function being written, with the
|
||||
// function name missing)
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
@@ -645,7 +642,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else {
|
||||
// Object function or behavior name
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
@@ -666,17 +663,15 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
|
||||
IsCaretOn(node.closingParenthesisLocation);
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
// Behavior function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.behaviorNameLocation)) {
|
||||
@@ -700,7 +695,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else if (!node.objectName.empty()) {
|
||||
// Object function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else {
|
||||
// Add completions for behaviors, because we could imagine that the user
|
||||
@@ -738,12 +733,10 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix(node.text, type, node.location);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(node.text, type, node.location);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type,
|
||||
@@ -762,12 +755,12 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
(inclusive && searchedPosition <= location.GetEndPosition())));
|
||||
}
|
||||
|
||||
void AddCompletionsForVariablesWithPrefix(
|
||||
void AddCompletionsForVariablesMatchingSearch(
|
||||
const gd::VariablesContainer& variablesContainer,
|
||||
const gd::String& prefix,
|
||||
const gd::String& search,
|
||||
const ExpressionParserLocation& location) {
|
||||
variablesContainer.ForEachVariableWithPrefix(
|
||||
prefix,
|
||||
variablesContainer.ForEachVariableMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Variable,
|
||||
@@ -779,12 +772,31 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
const gd::ObjectsContainersList& objectsContainersList,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
const ExpressionParserLocation& location) {
|
||||
objectsContainersList.ForEachObjectOrGroupVariableMatchingSearch(
|
||||
objectOrGroupName,
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Variable,
|
||||
location.GetStartPosition(),
|
||||
location.GetEndPosition());
|
||||
description.SetCompletion(variableName);
|
||||
description.SetVariableType(variable.GetType());
|
||||
completions.push_back(description);
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.GetObjectsContainersList().ForEachNameWithPrefix(
|
||||
prefix,
|
||||
projectScopedContainers.GetObjectsContainersList().ForEachNameMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
@@ -798,12 +810,12 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectsAndVariablesWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void AddCompletionsForObjectsAndVariablesMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.ForEachIdentifierWithPrefix(
|
||||
prefix,
|
||||
projectScopedContainers.ForEachIdentifierMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& objectName,
|
||||
const ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
@@ -832,20 +844,20 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForAllIdentifiersWithPrefix(const gd::String& prefix,
|
||||
void AddCompletionsForAllIdentifiersMatchingSearch(const gd::String& search,
|
||||
const gd::String& type) {
|
||||
AddCompletionsForAllIdentifiersWithPrefix(
|
||||
prefix,
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
search,
|
||||
type,
|
||||
ExpressionParserLocation(searchedPosition + 1, searchedPosition + 1));
|
||||
}
|
||||
|
||||
void AddCompletionsForAllIdentifiersWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.ForEachIdentifierWithPrefix(
|
||||
prefix,
|
||||
projectScopedContainers.ForEachIdentifierMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& objectName,
|
||||
const ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
|
@@ -14,13 +14,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -41,10 +40,10 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
* operations.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionLeftSideTypeFinder typeFinder(
|
||||
platform, objectsContainersList);
|
||||
platform, projectScopedContainers);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -53,9 +52,9 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
|
||||
protected:
|
||||
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_)
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
type("unknown") {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
@@ -67,6 +66,14 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
if (type == "unknown" || type == "number|string") {
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
@@ -83,7 +90,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
type = "unknown";
|
||||
}
|
||||
@@ -93,12 +100,99 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
type = "unknown";
|
||||
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
}, [&]() {
|
||||
// This is a property with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is a parameter with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
type = "unknown";
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName,
|
||||
[&]() {
|
||||
// It's an object variable.
|
||||
if (projectScopedContainers.GetObjectsContainersList()
|
||||
.HasObjectOrGroupWithVariableNamed(
|
||||
node.identifierName, node.childIdentifierName)
|
||||
== ObjectsContainersList::VariableExistence::DoesNotExist) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
auto variableType =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetTypeOfObjectOrGroupVariable(node.identifierName,
|
||||
node.childIdentifierName);
|
||||
ReadTypeFromVariable(variableType);
|
||||
},
|
||||
[&]() {
|
||||
// It's a variable.
|
||||
const auto& variable =
|
||||
projectScopedContainers.GetVariablesContainersList().Get(
|
||||
node.identifierName);
|
||||
|
||||
if (node.childIdentifierName.empty()) {
|
||||
ReadTypeFromVariable(variable.GetType());
|
||||
} else {
|
||||
if (!variable.HasChild(node.childIdentifierName)) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
ReadTypeFromVariable(
|
||||
variable.GetChild(node.childIdentifierName).GetType());
|
||||
}
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(node.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
type = "number";
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
type = "string";
|
||||
}
|
||||
}, [&]() {
|
||||
// It's a parameter.
|
||||
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
type = "number";
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
type = "string";
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
type = "unknown";
|
||||
}
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
type = "unknown";
|
||||
@@ -108,10 +202,18 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
|
||||
private:
|
||||
void ReadTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
type = "number";
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
type = "string";
|
||||
}
|
||||
}
|
||||
|
||||
gd::String type;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -15,13 +15,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -51,11 +50,11 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
* sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionTypeFinder typeFinder(
|
||||
platform, objectsContainersList, rootType);
|
||||
platform, projectScopedContainers, rootType);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -64,10 +63,10 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
|
||||
protected:
|
||||
ExpressionTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
type(ExpressionTypeFinder::unknownType),
|
||||
child(nullptr) {};
|
||||
@@ -113,8 +112,12 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
|
||||
// If we can infer a definitive number or string type, use it.
|
||||
// Otherwise, we only know that it's number or string, and this can even
|
||||
// be used as is at runtime.
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
type = leftSideType;
|
||||
@@ -126,7 +129,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (child == nullptr) {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
VisitParent(node);
|
||||
}
|
||||
@@ -138,7 +141,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
gd::MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers.GetObjectsContainersList(),
|
||||
node,
|
||||
*child);
|
||||
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
|
||||
@@ -159,7 +162,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
@@ -183,7 +186,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
ExpressionNode *child;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -89,20 +89,43 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
|
||||
// Unless we find something precise (like a variable or property or parameter with a known type),
|
||||
// we consider this node will be of the type required by the parent.
|
||||
childType = parentType;
|
||||
|
||||
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
if (identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("An object variable or expression should be entered."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
if (!objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName)) {
|
||||
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
|
||||
|
||||
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
|
||||
RaiseTypeError(_("This variable does not exist on this object or group."),
|
||||
identifier.identifierNameLocation);
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
|
||||
RaiseTypeError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
|
||||
RaiseTypeError(_("This group is empty. Add an object to this group first."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(variableType);
|
||||
|
||||
return true; // We found a variable.
|
||||
}, [&]() {
|
||||
@@ -110,7 +133,6 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
|
||||
// Try to identify a declared variable with the name (and maybe the child
|
||||
// variable).
|
||||
|
||||
const gd::Variable& variable =
|
||||
variablesContainersList.Get(identifier.identifierName);
|
||||
|
||||
@@ -118,6 +140,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
// Just the root variable is accessed, check it can be used in an
|
||||
// expression.
|
||||
validateVariableTypeForExpression(variable.GetType());
|
||||
ReadChildTypeFromVariable(variable.GetType());
|
||||
|
||||
return true; // We found a variable.
|
||||
} else {
|
||||
@@ -131,6 +154,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
|
||||
const gd::Variable& childVariable =
|
||||
variable.GetChild(identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(childVariable.GetType());
|
||||
return true; // We found a variable.
|
||||
}
|
||||
}, [&]() {
|
||||
@@ -138,23 +162,44 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We found a property, even if the child is not allowed.
|
||||
}
|
||||
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(identifier.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
childType = Type::Number;
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
childType = Type::String;
|
||||
}
|
||||
|
||||
return true; // We found a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We found a parameter, even if the child is not allowed.
|
||||
}
|
||||
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifier.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (!valueTypeMetadata.IsNumber() && !valueTypeMetadata.IsString() && !valueTypeMetadata.IsBoolean()) {
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
childType = Type::Number;
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
childType = Type::String;
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
RaiseTypeError(_("This parameter is not a string, number or boolean - it can't be used in an expression."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We found a parameter, even though the type is incompatible.
|
||||
}
|
||||
|
||||
|
@@ -103,9 +103,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
|
||||
}
|
||||
else if (node.op != '+') {
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
|
||||
}
|
||||
} else if (leftType == Type::Object) {
|
||||
@@ -124,7 +124,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
node.rightHandSide->Visit(*this);
|
||||
const Type rightType = childType;
|
||||
|
||||
childType = leftType;
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
childType = (leftType == Type::Unknown || leftType == Type::NumberOrString) ? leftType : rightType;
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -309,8 +313,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
|
||||
node.location);
|
||||
childType = parentType;
|
||||
} else {
|
||||
childType = parentType;
|
||||
}
|
||||
childType = parentType;
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -379,6 +385,16 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseError("invalid_operator", message, location);
|
||||
}
|
||||
|
||||
void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
childType = Type::Number;
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
childType = Type::String;
|
||||
} else {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
}
|
||||
}
|
||||
|
||||
static Type StringToType(const gd::String &type);
|
||||
static const gd::String &TypeToString(Type type);
|
||||
static const gd::String unknownTypeString;
|
||||
|
@@ -25,6 +25,9 @@ const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& projec
|
||||
void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
project.GetCurrentPlatform(), object.GetType());
|
||||
if (metadata.GetMetadata().IsRenderedIn3D()) {
|
||||
result.MarkAsHaving3DObjects();
|
||||
}
|
||||
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
|
||||
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
|
||||
result.GetUsedIncludeFiles().insert(includeFile);
|
||||
@@ -110,7 +113,7 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
|
||||
result.GetUsedExtensions().insert("BuiltinVariables");
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -154,7 +157,7 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
|
||||
// Add extensions bound to Objects/Behaviors/Functions
|
||||
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) ||
|
||||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
|
||||
// An object or object variable is used.
|
||||
|
@@ -44,6 +44,13 @@ public:
|
||||
return usedRequiredFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true when at least 1 object uses the 3D renderer.
|
||||
*/
|
||||
bool Has3DObjects() const {
|
||||
return has3DObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extensions used by the project (or part of it).
|
||||
*/
|
||||
@@ -59,10 +66,15 @@ public:
|
||||
*/
|
||||
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
|
||||
|
||||
void MarkAsHaving3DObjects() {
|
||||
has3DObjects = true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<gd::String> usedExtensions;
|
||||
std::set<gd::String> usedIncludeFiles;
|
||||
std::set<gd::String> usedRequiredFiles;
|
||||
bool has3DObjects = false;
|
||||
};
|
||||
|
||||
class GD_CORE_API UsedExtensionsFinder
|
||||
|
@@ -293,8 +293,8 @@ void ResourceWorkerInObjectsWorker::DoVisitBehavior(gd::Behavior &behavior){
|
||||
gd::ResourceWorkerInObjectsWorker
|
||||
GetResourceWorkerOnObjects(const gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
gd::ResourceWorkerInObjectsWorker eventsWorker(project, worker);
|
||||
return eventsWorker;
|
||||
gd::ResourceWorkerInObjectsWorker resourcesWorker(project, worker);
|
||||
return resourcesWorker;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
19
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.cpp
Normal file
19
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "ObjectsUsingResourceCollector.h"
|
||||
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void ObjectsUsingResourceCollector::DoVisitObject(gd::Object& object) {
|
||||
gd::ResourceNameMatcher resourceNameMatcher(resourceName);
|
||||
|
||||
object.GetConfiguration().ExposeResources(resourceNameMatcher);
|
||||
if (resourceNameMatcher.AnyResourceMatches()) {
|
||||
objectNames.push_back(object.GetName());
|
||||
}
|
||||
};
|
||||
|
||||
ObjectsUsingResourceCollector::~ObjectsUsingResourceCollector() {}
|
||||
|
||||
} // namespace gd
|
89
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Normal file
89
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef ProjectObjectsUsingResourceCollector_H
|
||||
#define ProjectObjectsUsingResourceCollector_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Object;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API ObjectsUsingResourceCollector
|
||||
: public ArbitraryObjectsWorker {
|
||||
public:
|
||||
ObjectsUsingResourceCollector(const gd::String& resourceName_)
|
||||
: resourceName(resourceName_){};
|
||||
virtual ~ObjectsUsingResourceCollector();
|
||||
|
||||
std::vector<gd::String>& GetObjectNames() { return objectNames; }
|
||||
|
||||
private:
|
||||
void DoVisitObject(gd::Object& object) override;
|
||||
|
||||
std::vector<gd::String> objectNames;
|
||||
gd::String resourceName;
|
||||
};
|
||||
|
||||
class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourceNameMatcher(const gd::String& resourceName_)
|
||||
: resourceName(resourceName_), matchesResourceName(false){};
|
||||
virtual ~ResourceNameMatcher(){};
|
||||
|
||||
bool AnyResourceMatches() { return matchesResourceName; }
|
||||
void Reset() { matchesResourceName = false; }
|
||||
|
||||
private:
|
||||
virtual void ExposeFile(gd::String& resource) override{
|
||||
/*Don't care, we just read resource names*/
|
||||
};
|
||||
virtual void ExposeImage(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeAudio(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeFont(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeJson(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeTilemap(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeTileset(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeVideo(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeModel3D(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
|
||||
void MatchResourceName(gd::String& otherResourceName) {
|
||||
if (otherResourceName == resourceName) matchesResourceName = true;
|
||||
}
|
||||
|
||||
gd::String resourceName;
|
||||
bool matchesResourceName;
|
||||
};
|
||||
|
||||
}; // namespace gd
|
||||
|
||||
#endif // ProjectObjectsUsingResourceCollector_H
|
@@ -140,7 +140,7 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
globalObjectsAndGroups, objectsAndGroups);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsFunctionsExtension));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
@@ -183,7 +183,7 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetSharedPropertyDescriptors());
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedBehavior.GetEventsFunctions()));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedObject.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedObject.GetEventsFunctions()));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ EventsBasedObject::EventsBasedObject()
|
||||
}
|
||||
|
||||
EventsBasedObject::~EventsBasedObject() {}
|
||||
|
||||
|
||||
EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObject)
|
||||
: AbstractEventsBasedEntity(_eventBasedObject) {
|
||||
// TODO Add a copy constructor in ObjectsContainer.
|
||||
@@ -27,17 +27,26 @@ EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObj
|
||||
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("defaultName", defaultName);
|
||||
if (isRenderedIn3D) {
|
||||
element.SetBoolAttribute("is3D", true);
|
||||
}
|
||||
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
}
|
||||
|
||||
void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
const SerializerElement& element) {
|
||||
defaultName = element.GetStringAttribute("defaultName");
|
||||
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
|
||||
|
||||
AbstractEventsBasedEntity::UnserializeFrom(project, element);
|
||||
UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -72,6 +72,19 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Declare a usage of the 3D renderer.
|
||||
*/
|
||||
EventsBasedObject& MarkAsRenderedIn3D(bool isRenderedIn3D_) {
|
||||
isRenderedIn3D = isRenderedIn3D_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the object uses the 3D renderer.
|
||||
*/
|
||||
bool IsRenderedIn3D() const { return isRenderedIn3D; }
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
void UnserializeFrom(gd::Project& project,
|
||||
@@ -79,6 +92,7 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
|
||||
|
||||
private:
|
||||
gd::String defaultName;
|
||||
bool isRenderedIn3D;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -19,7 +19,7 @@ Layer::Layer()
|
||||
isLocked(false),
|
||||
isLightingLayer(false),
|
||||
followBaseLayerCamera(false),
|
||||
camera3DNearPlaneDistance(0.1),
|
||||
camera3DNearPlaneDistance(3),
|
||||
camera3DFarPlaneDistance(10000),
|
||||
camera3DFieldOfView(45),
|
||||
ambientLightColorR(200),
|
||||
|
@@ -294,6 +294,7 @@ void Layout::SerializeTo(SerializerElement& element) const {
|
||||
GetVariables().SerializeTo(element.AddChild("variables"));
|
||||
GetInitialInstances().SerializeTo(element.AddChild("instances"));
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
|
||||
@@ -353,6 +354,11 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
project, GetEvents(), element.GetChild("events", 0, "Events"));
|
||||
|
||||
UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
|
||||
initialInstances.UnserializeFrom(
|
||||
element.GetChild("instances", 0, "Positions"));
|
||||
variables.UnserializeFrom(element.GetChild("variables", 0, "Variables"));
|
||||
@@ -444,13 +450,15 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
|
||||
bool searchInGroups) {
|
||||
gd::String type;
|
||||
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(name))
|
||||
type = layout.GetObject(name).GetType();
|
||||
else if (project.HasObjectNamed(name))
|
||||
type = project.GetObject(name).GetType();
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
else if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
@@ -523,7 +531,7 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
const gd::ObjectsContainer &project, const gd::ObjectsContainer &layout,
|
||||
const gd::String &objectOrGroupName, const gd::String &behaviorType,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
auto behaviorNames = object.GetAllBehaviorNames();
|
||||
@@ -542,7 +550,9 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
return behaviorNames;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -554,12 +564,14 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
std::vector<gd::String> behaviorNames;
|
||||
return behaviorNames;
|
||||
}
|
||||
|
||||
// Compute the intersection of the behaviors of all objects.
|
||||
auto behaviorNames = GetBehaviorNamesInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorType, false);
|
||||
for (size_t i = 1; i < groupsObjects.size(); i++) {
|
||||
@@ -587,7 +599,7 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
return layout.GetObject(objectOrGroupName).HasBehaviorNamed(behaviorName);
|
||||
}
|
||||
@@ -599,7 +611,9 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -610,10 +624,12 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all objects have the behavior.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
|
||||
@@ -629,7 +645,7 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
gd::String objectOrGroupName,
|
||||
gd::String behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) &&
|
||||
@@ -645,7 +661,9 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -656,10 +674,12 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all objects have the same type.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!IsDefaultBehavior(project, layout, object, behaviorName,
|
||||
@@ -675,7 +695,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) ?
|
||||
@@ -691,7 +711,9 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
return "";
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -702,10 +724,12 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Check that all objects have the behavior with the same type.
|
||||
auto behaviorType = GetTypeOfBehaviorInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorName, false);
|
||||
@@ -767,6 +791,8 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
|
@@ -40,7 +40,6 @@ void Object::Init(const gd::Object& object) {
|
||||
name = object.name;
|
||||
assetStoreId = object.assetStoreId;
|
||||
objectVariables = object.objectVariables;
|
||||
tags = object.tags;
|
||||
effectsContainer = object.effectsContainer;
|
||||
|
||||
behaviors.clear();
|
||||
@@ -134,7 +133,6 @@ void Object::UnserializeFrom(gd::Project& project,
|
||||
SetType(element.GetStringAttribute("type"));
|
||||
assetStoreId = element.GetStringAttribute("assetStoreId");
|
||||
name = element.GetStringAttribute("name", name, "nom");
|
||||
tags = element.GetStringAttribute("tags");
|
||||
|
||||
objectVariables.UnserializeFrom(
|
||||
element.GetChild("variables", 0, "Variables"));
|
||||
@@ -207,7 +205,6 @@ void Object::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("assetStoreId", GetAssetStoreId());
|
||||
element.SetAttribute("type", GetType());
|
||||
element.SetAttribute("tags", GetTags());
|
||||
objectVariables.SerializeTo(element.AddChild("variables"));
|
||||
effectsContainer.SerializeTo(element.AddChild("effects"));
|
||||
|
||||
|
@@ -120,14 +120,6 @@ class GD_CORE_API Object {
|
||||
*/
|
||||
const gd::String& GetType() const { return configuration->GetType(); }
|
||||
|
||||
/** \brief Change the tags of the object.
|
||||
*/
|
||||
void SetTags(const gd::String& tags_) { tags = tags_; }
|
||||
|
||||
/** \brief Return the tags of the object.
|
||||
*/
|
||||
const gd::String& GetTags() const { return tags; }
|
||||
|
||||
/** \brief Shortcut to check if the object is a 3D object.
|
||||
*/
|
||||
bool Is3DObject() const { return configuration->Is3DObject(); }
|
||||
@@ -268,7 +260,6 @@ class GD_CORE_API Object {
|
||||
///< object.
|
||||
gd::VariablesContainer
|
||||
objectVariables; ///< List of the variables of the object
|
||||
gd::String tags; ///< Comma-separated list of tags
|
||||
gd::EffectsContainer
|
||||
effectsContainer; ///< The effects container for the object.
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
|
248
Core/GDCore/Project/ObjectFolderOrObject.cpp
Normal file
248
Core/GDCore/Project/ObjectFolderOrObject.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
ObjectFolderOrObject ObjectFolderOrObject::badObjectFolderOrObject;
|
||||
|
||||
ObjectFolderOrObject::ObjectFolderOrObject()
|
||||
: folderName("__NULL"), object(nullptr) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::String folderName_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: folderName(folderName_), parent(parent_), object(nullptr) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::Object* object_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: object(object_), parent(parent_) {}
|
||||
ObjectFolderOrObject::~ObjectFolderOrObject() {}
|
||||
|
||||
bool ObjectFolderOrObject::HasObjectNamed(const gd::String& name) {
|
||||
if (IsFolder()) {
|
||||
return std::any_of(
|
||||
children.begin(),
|
||||
children.end(),
|
||||
[&name](
|
||||
std::unique_ptr<gd::ObjectFolderOrObject>& objectFolderOrObject) {
|
||||
return objectFolderOrObject->HasObjectNamed(name);
|
||||
});
|
||||
}
|
||||
if (!object) return false;
|
||||
return object->GetName() == name;
|
||||
}
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetObjectNamed(
|
||||
const gd::String& name) {
|
||||
if (object && object->GetName() == name) {
|
||||
return *this;
|
||||
}
|
||||
if (IsFolder()) {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
ObjectFolderOrObject& foundInChild = children[j]->GetObjectNamed(name);
|
||||
if (&(foundInChild) != &badObjectFolderOrObject) {
|
||||
return foundInChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::SetFolderName(const gd::String& name) {
|
||||
if (!IsFolder()) return;
|
||||
folderName = name;
|
||||
}
|
||||
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
const ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) const {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetObjectChild(
|
||||
const gd::String& name) {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
if (!children[j]->IsFolder()) {
|
||||
if (children[j]->GetObject().GetName() == name) return *children[j];
|
||||
};
|
||||
}
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::InsertObject(gd::Object* insertedObject,
|
||||
std::size_t position) {
|
||||
auto objectFolderOrObject =
|
||||
gd::make_unique<ObjectFolderOrObject>(insertedObject, this);
|
||||
if (position < children.size()) {
|
||||
children.insert(children.begin() + position,
|
||||
std::move(objectFolderOrObject));
|
||||
} else {
|
||||
children.push_back(std::move(objectFolderOrObject));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ObjectFolderOrObject::GetChildPosition(
|
||||
const ObjectFolderOrObject& child) const {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
if (children[j].get() == &child) return j;
|
||||
}
|
||||
return gd::String::npos;
|
||||
}
|
||||
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::InsertNewFolder(
|
||||
const gd::String& newFolderName, std::size_t position) {
|
||||
auto newFolderPtr =
|
||||
gd::make_unique<ObjectFolderOrObject>(newFolderName, this);
|
||||
gd::ObjectFolderOrObject& newFolder = *(*(children.insert(
|
||||
position < children.size() ? children.begin() + position : children.end(),
|
||||
std::move(newFolderPtr))));
|
||||
return newFolder;
|
||||
};
|
||||
|
||||
void ObjectFolderOrObject::RemoveRecursivelyObjectNamed(
|
||||
const gd::String& name) {
|
||||
if (IsFolder()) {
|
||||
children.erase(
|
||||
std::remove_if(children.begin(),
|
||||
children.end(),
|
||||
[&name](std::unique_ptr<gd::ObjectFolderOrObject>&
|
||||
objectFolderOrObject) {
|
||||
return !objectFolderOrObject->IsFolder() &&
|
||||
objectFolderOrObject->GetObject().GetName() ==
|
||||
name;
|
||||
}),
|
||||
children.end());
|
||||
for (auto& it : children) {
|
||||
it->RemoveRecursivelyObjectNamed(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool ObjectFolderOrObject::IsADescendantOf(
|
||||
const ObjectFolderOrObject& otherObjectFolderOrObject) {
|
||||
if (parent == nullptr) return false;
|
||||
if (&(*parent) == &otherObjectFolderOrObject) return true;
|
||||
return parent->IsADescendantOf(otherObjectFolderOrObject);
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::MoveChild(std::size_t oldIndex,
|
||||
std::size_t newIndex) {
|
||||
if (!IsFolder()) return;
|
||||
if (oldIndex >= children.size() || newIndex >= children.size()) return;
|
||||
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> objectFolderOrObject =
|
||||
std::move(children[oldIndex]);
|
||||
children.erase(children.begin() + oldIndex);
|
||||
children.insert(children.begin() + newIndex, std::move(objectFolderOrObject));
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::RemoveFolderChild(
|
||||
const ObjectFolderOrObject& childToRemove) {
|
||||
if (!IsFolder() || !childToRemove.IsFolder() ||
|
||||
childToRemove.GetChildrenCount() > 0) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::unique_ptr<gd::ObjectFolderOrObject>>::iterator it = find_if(
|
||||
children.begin(),
|
||||
children.end(),
|
||||
[&childToRemove](std::unique_ptr<gd::ObjectFolderOrObject>& child) {
|
||||
return child.get() == &childToRemove;
|
||||
});
|
||||
if (it == children.end()) return;
|
||||
|
||||
children.erase(it);
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::MoveObjectFolderOrObjectToAnotherFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition) {
|
||||
if (!newParentFolder.IsFolder()) return;
|
||||
if (newParentFolder.IsADescendantOf(objectFolderOrObject)) return;
|
||||
|
||||
std::vector<std::unique_ptr<gd::ObjectFolderOrObject>>::iterator it =
|
||||
find_if(children.begin(),
|
||||
children.end(),
|
||||
[&objectFolderOrObject](std::unique_ptr<gd::ObjectFolderOrObject>&
|
||||
childObjectFolderOrObject) {
|
||||
return childObjectFolderOrObject.get() == &objectFolderOrObject;
|
||||
});
|
||||
if (it == children.end()) return;
|
||||
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> objectFolderOrObjectPtr =
|
||||
std::move(*it);
|
||||
children.erase(it);
|
||||
|
||||
objectFolderOrObjectPtr->parent = &newParentFolder;
|
||||
newParentFolder.children.insert(
|
||||
newPosition < newParentFolder.children.size()
|
||||
? newParentFolder.children.begin() + newPosition
|
||||
: newParentFolder.children.end(),
|
||||
std::move(objectFolderOrObjectPtr));
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::SerializeTo(SerializerElement& element) const {
|
||||
if (IsFolder()) {
|
||||
element.SetAttribute("folderName", GetFolderName());
|
||||
if (children.size() > 0) {
|
||||
SerializerElement& childrenElement = element.AddChild("children");
|
||||
childrenElement.ConsiderAsArrayOf("objectFolderOrObject");
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
children[j]->SerializeTo(
|
||||
childrenElement.AddChild("objectFolderOrObject"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
element.SetAttribute("objectName", GetObject().GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::UnserializeFrom(
|
||||
gd::Project& project,
|
||||
const SerializerElement& element,
|
||||
gd::ObjectsContainer& objectsContainer) {
|
||||
children.clear();
|
||||
gd::String potentialFolderName = element.GetStringAttribute("folderName", "");
|
||||
|
||||
if (!potentialFolderName.empty()) {
|
||||
object = nullptr;
|
||||
folderName = potentialFolderName;
|
||||
|
||||
if (element.HasChild("children")) {
|
||||
const SerializerElement& childrenElements =
|
||||
element.GetChild("children", 0);
|
||||
childrenElements.ConsiderAsArrayOf("objectFolderOrObject");
|
||||
for (std::size_t i = 0; i < childrenElements.GetChildrenCount(); ++i) {
|
||||
std::unique_ptr<ObjectFolderOrObject> childObjectFolderOrObject =
|
||||
make_unique<ObjectFolderOrObject>();
|
||||
childObjectFolderOrObject->UnserializeFrom(
|
||||
project, childrenElements.GetChild(i), objectsContainer);
|
||||
childObjectFolderOrObject->parent = this;
|
||||
children.push_back(std::move(childObjectFolderOrObject));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
folderName = "";
|
||||
gd::String objectName = element.GetStringAttribute("objectName");
|
||||
if (objectsContainer.HasObjectNamed(objectName)) {
|
||||
object = &objectsContainer.GetObject(objectName);
|
||||
} else {
|
||||
gd::LogError("Object with name " + objectName +
|
||||
" not found in objects container.");
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
203
Core/GDCore/Project/ObjectFolderOrObject.h
Normal file
203
Core/GDCore/Project/ObjectFolderOrObject.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_OBJECTFOLDEROROBJECT_H
|
||||
#define GDCORE_OBJECTFOLDEROROBJECT_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Object;
|
||||
class SerializerElement;
|
||||
class ObjectsContainer;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Class representing a folder structure in order to organize objects
|
||||
* in folders (to be used with an ObjectsContainer.)
|
||||
*
|
||||
* \see gd::ObjectsContainer
|
||||
*/
|
||||
class GD_CORE_API ObjectFolderOrObject {
|
||||
public:
|
||||
/**
|
||||
* \brief Default constructor creating an empty instance. Useful for the null
|
||||
* object pattern.
|
||||
*/
|
||||
ObjectFolderOrObject();
|
||||
virtual ~ObjectFolderOrObject();
|
||||
/**
|
||||
* \brief Constructor for creating an instance representing a folder.
|
||||
*/
|
||||
ObjectFolderOrObject(gd::String folderName_,
|
||||
ObjectFolderOrObject* parent_ = nullptr);
|
||||
/**
|
||||
* \brief Constructor for creating an instance representing an object.
|
||||
*/
|
||||
ObjectFolderOrObject(gd::Object* object_,
|
||||
ObjectFolderOrObject* parent_ = nullptr);
|
||||
|
||||
/**
|
||||
* \brief Returns the object behind the instance.
|
||||
*/
|
||||
gd::Object& GetObject() const { return *object; }
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance represents a folder.
|
||||
*/
|
||||
bool IsFolder() const { return !folderName.empty(); }
|
||||
/**
|
||||
* \brief Returns the name of the folder.
|
||||
*/
|
||||
const gd::String& GetFolderName() const { return folderName; }
|
||||
|
||||
/**
|
||||
* \brief Set the folder name. Does nothing if called on an instance not
|
||||
* representing a folder.
|
||||
*/
|
||||
void SetFolderName(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance represents the object with the given
|
||||
* name or if any of the children does (recursive search).
|
||||
*/
|
||||
bool HasObjectNamed(const gd::String& name);
|
||||
/**
|
||||
* \brief Returns the child instance holding the object with the given name
|
||||
* (recursive search).
|
||||
*/
|
||||
ObjectFolderOrObject& GetObjectNamed(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns the number of children. Returns 0 if the instance represents
|
||||
* an object.
|
||||
*/
|
||||
std::size_t GetChildrenCount() const {
|
||||
if (IsFolder()) return children.size();
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject at the given index.
|
||||
*/
|
||||
ObjectFolderOrObject& GetChildAt(std::size_t index);
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject at the given index.
|
||||
*/
|
||||
const ObjectFolderOrObject& GetChildAt(std::size_t index) const;
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject that represents the object
|
||||
* with the given name. To use only if sure that the instance holds the object
|
||||
* in its direct children (no recursive search).
|
||||
*
|
||||
* \note The equivalent method to get a folder by its name cannot be
|
||||
* implemented because there is no unicity enforced on the folder name.
|
||||
*/
|
||||
ObjectFolderOrObject& GetObjectChild(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns the parent of the instance. If the instance has no parent
|
||||
* (root folder), the null object is returned.
|
||||
*/
|
||||
ObjectFolderOrObject& GetParent() {
|
||||
if (parent == nullptr) {
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
return *parent;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance is a root folder (that's to say it
|
||||
* has no parent).
|
||||
*/
|
||||
bool IsRootFolder() { return !object && !parent; }
|
||||
|
||||
/**
|
||||
* \brief Moves a child from a position to a new one.
|
||||
*/
|
||||
void MoveChild(std::size_t oldIndex, std::size_t newIndex);
|
||||
/**
|
||||
* \brief Removes the given child from the instance's children. If the given
|
||||
* child contains children of its own, does nothing.
|
||||
*/
|
||||
void RemoveFolderChild(const ObjectFolderOrObject& childToRemove);
|
||||
/**
|
||||
* \brief Removes the child representing the object with the given name from
|
||||
* the instance children and recursively does it for every folder children.
|
||||
*/
|
||||
void RemoveRecursivelyObjectNamed(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Inserts an instance representing the given object at the given
|
||||
* position.
|
||||
*/
|
||||
void InsertObject(gd::Object* insertedObject,
|
||||
std::size_t position = (size_t)-1);
|
||||
/**
|
||||
* \brief Inserts an instance representing a folder with the given name at the
|
||||
* given position.
|
||||
*/
|
||||
ObjectFolderOrObject& InsertNewFolder(const gd::String& newFolderName,
|
||||
std::size_t position);
|
||||
/**
|
||||
* \brief Returns true if the instance is a descendant of the given instance
|
||||
* of ObjectFolderOrObject.
|
||||
*/
|
||||
bool IsADescendantOf(const ObjectFolderOrObject& otherObjectFolderOrObject);
|
||||
|
||||
/**
|
||||
* \brief Returns the position of the given instance of ObjectFolderOrObject
|
||||
* in the instance's children.
|
||||
*/
|
||||
std::size_t GetChildPosition(const ObjectFolderOrObject& child) const;
|
||||
/**
|
||||
* \brief Moves the given child ObjectFolderOrObject to the given folder at
|
||||
* the given position.
|
||||
*/
|
||||
void MoveObjectFolderOrObjectToAnotherFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition);
|
||||
|
||||
/** \name Saving and loading
|
||||
* Members functions related to saving and loading the objects of the class.
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the ObjectFolderOrObject instance.
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the ObjectFolderOrObject instance.
|
||||
*/
|
||||
void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element,
|
||||
ObjectsContainer& objectsContainer);
|
||||
///@}
|
||||
|
||||
private:
|
||||
static gd::ObjectFolderOrObject badObjectFolderOrObject;
|
||||
|
||||
gd::ObjectFolderOrObject*
|
||||
parent; // nullptr if root folder, points to the parent folder otherwise.
|
||||
|
||||
// Representing an object:
|
||||
gd::Object* object; // nullptr if folderName is set.
|
||||
|
||||
// or representing a folder:
|
||||
gd::String folderName; // Empty if object is set.
|
||||
std::vector<std::unique_ptr<ObjectFolderOrObject>>
|
||||
children; // Folder children.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_OBJECTFOLDEROROBJECT_H
|
@@ -162,10 +162,12 @@ void ObjectGroupsContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
|
||||
objectGroups.insert(objectGroups.begin() + newIndex, std::move(objectGroup));
|
||||
}
|
||||
|
||||
void ObjectGroupsContainer::ForEachNameWithPrefix(const gd::String& prefix,
|
||||
std::function<void(const gd::String& name)> fn) const {
|
||||
for (const auto& objectGroup: objectGroups) {
|
||||
if (objectGroup->GetName().find(prefix) == 0) fn(objectGroup->GetName());
|
||||
void ObjectGroupsContainer::ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name)> fn) const {
|
||||
for (const auto& objectGroup : objectGroups) {
|
||||
if (objectGroup->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(objectGroup->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -118,9 +118,9 @@ class GD_CORE_API ObjectGroupsContainer {
|
||||
inline void Clear() { objectGroups.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each group name starting with the prefix passed in parameter.
|
||||
* \brief Call the callback for each group name matching the specified search.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name)> fn) const;
|
||||
void ForEachNameMatchingSearch(const gd::String& search, std::function<void(const gd::String& name)> fn) const;
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
|
@@ -9,12 +9,15 @@
|
||||
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
ObjectsContainer::ObjectsContainer() {}
|
||||
ObjectsContainer::ObjectsContainer() {
|
||||
rootFolder = gd::make_unique<gd::ObjectFolderOrObject>("__ROOT");
|
||||
}
|
||||
|
||||
ObjectsContainer::~ObjectsContainer() {}
|
||||
|
||||
@@ -24,6 +27,22 @@ void ObjectsContainer::SerializeObjectsTo(SerializerElement& element) const {
|
||||
initialObjects[j]->SerializeTo(element.AddChild("object"));
|
||||
}
|
||||
}
|
||||
void ObjectsContainer::SerializeFoldersTo(SerializerElement& element) const {
|
||||
rootFolder->SerializeTo(element);
|
||||
}
|
||||
|
||||
void ObjectsContainer::UnserializeFoldersFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
rootFolder->UnserializeFrom(project, element, *this);
|
||||
}
|
||||
|
||||
void ObjectsContainer::AddMissingObjectsInRootFolder() {
|
||||
for (std::size_t i = 0; i < initialObjects.size(); ++i) {
|
||||
if (!rootFolder->HasObjectNamed(initialObjects[i]->GetName())) {
|
||||
rootFolder->InsertObject(&(*initialObjects[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainer::UnserializeObjectsFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
@@ -48,17 +67,23 @@ void ObjectsContainer::UnserializeObjectsFrom(
|
||||
bool ObjectsContainer::HasObjectNamed(const gd::String& name) const {
|
||||
return (find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)) != initialObjects.end());
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}) != initialObjects.end());
|
||||
}
|
||||
gd::Object& ObjectsContainer::GetObject(const gd::String& name) {
|
||||
return *(*find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}));
|
||||
}
|
||||
const gd::Object& ObjectsContainer::GetObject(const gd::String& name) const {
|
||||
return *(*find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}));
|
||||
}
|
||||
gd::Object& ObjectsContainer::GetObject(std::size_t index) {
|
||||
return *initialObjects[index];
|
||||
@@ -84,6 +109,22 @@ gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
|
||||
: initialObjects.end(),
|
||||
project.CreateObject(objectType, name))));
|
||||
|
||||
rootFolder->InsertObject(&newlyCreatedObject);
|
||||
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
gd::Object& ObjectsContainer::InsertNewObjectInFolder(
|
||||
const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
std::size_t position) {
|
||||
gd::Object& newlyCreatedObject = *(*(initialObjects.insert(
|
||||
initialObjects.end(), project.CreateObject(objectType, name))));
|
||||
|
||||
objectFolderOrObject.InsertObject(&newlyCreatedObject, position);
|
||||
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
@@ -97,16 +138,6 @@ gd::Object& ObjectsContainer::InsertObject(const gd::Object& object,
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
void ObjectsContainer::SwapObjects(std::size_t firstObjectIndex,
|
||||
std::size_t secondObjectIndex) {
|
||||
if (firstObjectIndex >= initialObjects.size() ||
|
||||
secondObjectIndex >= initialObjects.size())
|
||||
return;
|
||||
|
||||
std::iter_swap(initialObjects.begin() + firstObjectIndex,
|
||||
initialObjects.begin() + secondObjectIndex);
|
||||
}
|
||||
|
||||
void ObjectsContainer::MoveObject(std::size_t oldIndex, std::size_t newIndex) {
|
||||
if (oldIndex >= initialObjects.size() || newIndex >= initialObjects.size())
|
||||
return;
|
||||
@@ -120,30 +151,59 @@ void ObjectsContainer::RemoveObject(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
|
||||
find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(ObjectHasName(), name));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
});
|
||||
if (objectIt == initialObjects.end()) return;
|
||||
|
||||
rootFolder->RemoveRecursivelyObjectNamed(name);
|
||||
|
||||
initialObjects.erase(objectIt);
|
||||
}
|
||||
|
||||
void ObjectsContainer::MoveObjectToAnotherContainer(
|
||||
const gd::String& name,
|
||||
void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectsContainer& newContainer,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition) {
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
|
||||
find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(ObjectHasName(), name));
|
||||
if (objectFolderOrObject.IsFolder() || !newParentFolder.IsFolder()) return;
|
||||
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt = find_if(
|
||||
initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
[&objectFolderOrObject](std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == objectFolderOrObject.GetObject().GetName();
|
||||
});
|
||||
if (objectIt == initialObjects.end()) return;
|
||||
|
||||
std::unique_ptr<gd::Object> object = std::move(*objectIt);
|
||||
initialObjects.erase(objectIt);
|
||||
|
||||
newContainer.initialObjects.insert(
|
||||
newPosition < newContainer.initialObjects.size()
|
||||
? newContainer.initialObjects.begin() + newPosition
|
||||
: newContainer.initialObjects.end(),
|
||||
std::move(object));
|
||||
newContainer.initialObjects.push_back(std::move(object));
|
||||
|
||||
objectFolderOrObject.GetParent().MoveObjectFolderOrObjectToAnotherFolder(
|
||||
objectFolderOrObject, newParentFolder, newPosition);
|
||||
}
|
||||
|
||||
std::vector<const ObjectFolderOrObject*>
|
||||
ObjectsContainer::GetAllObjectFolderOrObjects() const {
|
||||
std::vector<const ObjectFolderOrObject*> results;
|
||||
|
||||
std::function<void(const ObjectFolderOrObject& folder)> addChildrenOfFolder =
|
||||
[&](const ObjectFolderOrObject& folder) {
|
||||
for (size_t i = 0; i < folder.GetChildrenCount(); ++i) {
|
||||
const auto& child = folder.GetChildAt(i);
|
||||
results.push_back(&child);
|
||||
|
||||
if (child.IsFolder()) {
|
||||
addChildrenOfFolder(child);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addChildrenOfFolder(*rootFolder);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,11 +9,12 @@
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
namespace gd {
|
||||
class Object;
|
||||
class Project;
|
||||
class SerializerElement;
|
||||
}
|
||||
} // namespace gd
|
||||
#undef GetObject // Disable an annoying macro
|
||||
|
||||
namespace gd {
|
||||
@@ -98,6 +99,19 @@ class GD_CORE_API ObjectsContainer {
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
std::size_t position);
|
||||
/**
|
||||
* \brief Add a new empty object of type \a objectType called \a name in the
|
||||
* given folder at the specified position.<br>
|
||||
*
|
||||
* \note The object is created using the project's current platform.
|
||||
* \return A reference to the object in the list.
|
||||
*/
|
||||
gd::Object& InsertNewObjectInFolder(
|
||||
const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
std::size_t position);
|
||||
|
||||
/**
|
||||
* \brief Add a new object to the list
|
||||
@@ -125,18 +139,18 @@ class GD_CORE_API ObjectsContainer {
|
||||
void MoveObject(std::size_t oldIndex, std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Swap the position of the specified objects.
|
||||
*/
|
||||
void SwapObjects(std::size_t firstObjectIndex, std::size_t secondObjectIndex);
|
||||
|
||||
/**
|
||||
* Move the specified object to another container, removing it from the current one
|
||||
* and adding it to the new one at the specified position.
|
||||
* Move the specified object to another container, removing it from the
|
||||
* current one and adding it to the new one at the specified position in the
|
||||
* given folder.
|
||||
*
|
||||
* \note This does not invalidate the references to the object (object is not moved in memory,
|
||||
* as referenced by smart pointers internally).
|
||||
* \note This does not invalidate the references to the object (object is not
|
||||
* moved in memory, as referenced by smart pointers internally).
|
||||
*/
|
||||
void MoveObjectToAnotherContainer(const gd::String& name, gd::ObjectsContainer & newContainer, std::size_t newPosition);
|
||||
void MoveObjectFolderOrObjectToAnotherContainerInFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectsContainer& newContainer,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition);
|
||||
|
||||
/**
|
||||
* Provide a raw access to the vector containing the objects
|
||||
@@ -153,20 +167,43 @@ class GD_CORE_API ObjectsContainer {
|
||||
}
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Returns a vector containing all object and folders in this container.
|
||||
* Only use this for checking if you hold a valid `ObjectFolderOrObject` -
|
||||
* don't use this for rendering or anything else.
|
||||
*/
|
||||
std::vector<const ObjectFolderOrObject*> GetAllObjectFolderOrObjects() const;
|
||||
|
||||
gd::ObjectFolderOrObject& GetRootFolder() {
|
||||
return *rootFolder;
|
||||
}
|
||||
|
||||
void AddMissingObjectsInRootFolder();
|
||||
|
||||
/** \name Saving and loading
|
||||
* Members functions related to saving and loading the objects of the class.
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize instances container.
|
||||
* \brief Serialize the objects container.
|
||||
*/
|
||||
void SerializeObjectsTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the instances container.
|
||||
* \brief Unserialize the objects container.
|
||||
*/
|
||||
void UnserializeObjectsFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
/**
|
||||
* \brief Serialize folder structure.
|
||||
*/
|
||||
void SerializeFoldersTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize folder structure.
|
||||
*/
|
||||
void UnserializeFoldersFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
/** \name Objects groups management
|
||||
@@ -190,6 +227,9 @@ class GD_CORE_API ObjectsContainer {
|
||||
std::vector<std::unique_ptr<gd::Object> >
|
||||
initialObjects; ///< Objects contained.
|
||||
gd::ObjectGroupsContainer objectGroups;
|
||||
|
||||
private:
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> rootFolder;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -51,17 +51,62 @@ bool ObjectsContainersList::HasObjectNamed(const gd::String& name) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectOrGroupWithVariableNamed(
|
||||
ObjectsContainersList::VariableExistence
|
||||
ObjectsContainersList::HasObjectOrGroupWithVariableNamed(
|
||||
const gd::String& objectOrGroupName, const gd::String& variableName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
return variables.Has(variableName);
|
||||
return variables.Has(variableName) ? VariableExistence::Exists
|
||||
: VariableExistence::DoesNotExist;
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that a group has a variable if all objects of the
|
||||
// group have it:
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
if (objectNames.empty()) return VariableExistence::GroupIsEmpty;
|
||||
|
||||
bool existsOnAtLeastOneObject = false;
|
||||
bool missingOnAtLeastOneObject = false;
|
||||
for (const auto& objectName : objectNames) {
|
||||
if (!HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
missingOnAtLeastOneObject = true;
|
||||
if (existsOnAtLeastOneObject) {
|
||||
return VariableExistence::ExistsOnlyOnSomeObjectsOfTheGroup;
|
||||
}
|
||||
} else {
|
||||
existsOnAtLeastOneObject = true;
|
||||
if (missingOnAtLeastOneObject) {
|
||||
return VariableExistence::ExistsOnlyOnSomeObjectsOfTheGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingOnAtLeastOneObject) {
|
||||
return VariableExistence::DoesNotExist;
|
||||
}
|
||||
|
||||
return VariableExistence::Exists;
|
||||
}
|
||||
}
|
||||
|
||||
return VariableExistence::DoesNotExist;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectWithVariableNamed(
|
||||
const gd::String& objectName, const gd::String& variableName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
const auto& variables = (*it)->GetObject(objectName).GetVariables();
|
||||
return variables.Has(variableName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +124,7 @@ bool ObjectsContainersList::HasVariablesContainer(
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// This would allow handling the renaming of variables of an object group.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,25 +141,133 @@ ObjectsContainersList::GetObjectOrGroupVariablesContainer(
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// This would allow handling the renaming of variables of an object group.
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachNameWithPrefix(
|
||||
const gd::String& prefix,
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectOrGroupVariable(
|
||||
const gd::String& objectOrGroupName, const gd::String& variableName) const {
|
||||
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
|
||||
return variables.Get(variableName).GetType();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that the first object having the variable will
|
||||
// define its type.
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
|
||||
for (const auto& objectName : objectNames) {
|
||||
if (HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
return GetTypeOfObjectVariable(objectName, variableName);
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const {
|
||||
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectName).GetVariables();
|
||||
|
||||
return variables.Get(variableName).GetType();
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObjectOrGroupVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
variables.ForEachVariableMatchingSearch(search, fn);
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that a group has a variable if all objects of the
|
||||
// group have it:
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
|
||||
if (objectNames.empty()) return;
|
||||
const auto& firstObjectName = objectNames.front();
|
||||
ForEachObjectVariableMatchingSearch(
|
||||
firstObjectName,
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
for (const auto& objectName : objectGroup.GetAllObjectsNames()) {
|
||||
if (!HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
return; // This variable is not shared by all objects of the
|
||||
// group.
|
||||
}
|
||||
}
|
||||
|
||||
// This variable is shared by all objects in the group. Note that
|
||||
// other objects can have it with a different type - we allow this.
|
||||
fn(variableName, variable);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObjectVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
variables.ForEachVariableMatchingSearch(search, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration)> fn)
|
||||
const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
for (const auto& object : (*it)->GetObjects()) {
|
||||
if (object->GetName().find(prefix) == 0)
|
||||
if (object->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(object->GetName(), &object->GetConfiguration());
|
||||
}
|
||||
(*it)->GetObjectGroups().ForEachNameWithPrefix(
|
||||
prefix, [&](const gd::String& name) { fn(name, nullptr); });
|
||||
(*it)->GetObjectGroups().ForEachNameMatchingSearch(
|
||||
search, [&](const gd::String& name) { fn(name, nullptr); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +383,10 @@ gd::String ObjectsContainersList::GetTypeOfBehavior(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
return gd::GetTypeOfBehavior(
|
||||
*objectsContainers[0], *objectsContainers[1], behaviorName, searchInGroups);
|
||||
return gd::GetTypeOfBehavior(*objectsContainers[0],
|
||||
*objectsContainers[1],
|
||||
behaviorName,
|
||||
searchInGroups);
|
||||
}
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
|
||||
|
@@ -42,12 +42,20 @@ class GD_CORE_API ObjectsContainersList {
|
||||
*/
|
||||
bool HasObjectOrGroupNamed(const gd::String& name) const;
|
||||
|
||||
enum VariableExistence {
|
||||
DoesNotExist,
|
||||
Exists,
|
||||
GroupIsEmpty,
|
||||
ExistsOnlyOnSomeObjectsOfTheGroup
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object or group has the specified variable in
|
||||
* its declared variables.
|
||||
*/
|
||||
bool HasObjectOrGroupWithVariableNamed(const gd::String& objectOrGroupName,
|
||||
const gd::String& variableName) const;
|
||||
VariableExistence HasObjectOrGroupWithVariableNamed(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& variableName) const;
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object or group has the specified variables
|
||||
@@ -64,6 +72,11 @@ class GD_CORE_API ObjectsContainersList {
|
||||
const gd::VariablesContainer* GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group variable.
|
||||
*/
|
||||
gd::Variable::Type GetTypeOfObjectOrGroupVariable(const gd::String& objectOrGroupName, const gd::String& variableName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group name.
|
||||
* \note If a group contains only objects of a same type, then the group has
|
||||
@@ -81,18 +94,21 @@ class GD_CORE_API ObjectsContainersList {
|
||||
const gd::String& behaviorName) const;
|
||||
|
||||
/**
|
||||
* \brief Get the type of a behavior if an object or all objects of a group has it.
|
||||
*/
|
||||
gd::String GetTypeOfBehaviorInObjectOrGroup(const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorName,
|
||||
bool searchInGroups = true) const;
|
||||
* \brief Get the type of a behavior if an object or all objects of a group
|
||||
* has it.
|
||||
*/
|
||||
gd::String GetTypeOfBehaviorInObjectOrGroup(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from a behavior name
|
||||
* @return Type of the behavior.
|
||||
* @deprecated - Use GetTypeOfBehaviorInObjectOrGroup instead.
|
||||
*/
|
||||
gd::String GetTypeOfBehavior(const gd::String& behaviorName, bool searchInGroups = true) const;
|
||||
gd::String GetTypeOfBehavior(const gd::String& behaviorName,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get behaviors of an object/group
|
||||
@@ -101,9 +117,8 @@ class GD_CORE_API ObjectsContainersList {
|
||||
*
|
||||
* @return Vector containing names of behaviors
|
||||
*/
|
||||
std::vector<gd::String>
|
||||
GetBehaviorsOfObject(const gd::String& objectName,
|
||||
bool searchInGroups = true) const;
|
||||
std::vector<gd::String> GetBehaviorsOfObject(
|
||||
const gd::String& objectName, bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Return a list containing all objects refered to by the group.
|
||||
@@ -121,9 +136,24 @@ class GD_CORE_API ObjectsContainersList {
|
||||
void ForEachObject(std::function<void(const gd::Object& object)> fn) const;
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each object or group name starting with the prefix passed in parameter.
|
||||
* \brief Call the callback for each object or group name matching the
|
||||
* search passed in parameter.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::ObjectConfiguration* objectConfiguration)> fn) const;
|
||||
void ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration)>
|
||||
fn) const;
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable of the object (or group)
|
||||
* matching the search passed in parameter.
|
||||
*/
|
||||
void ForEachObjectOrGroupVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
@@ -132,6 +162,17 @@ class GD_CORE_API ObjectsContainersList {
|
||||
private:
|
||||
bool HasObjectNamed(const gd::String& name) const;
|
||||
|
||||
bool HasObjectWithVariableNamed(const gd::String& objectName,
|
||||
const gd::String& variableName) const;
|
||||
|
||||
gd::Variable::Type GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const;
|
||||
|
||||
void ForEachObjectVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const;
|
||||
|
||||
void Add(const gd::ObjectsContainer& objectsContainer) {
|
||||
objectsContainers.push_back(&objectsContainer);
|
||||
};
|
||||
|
@@ -48,11 +48,6 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
// By default, disallow unicode in identifiers, but this can be set to true
|
||||
// by the IDE. In the future, this will be set to true by default, keeping backward compatibility.
|
||||
// We keep it disabled by default to progressively ask users to test it in real projects.
|
||||
bool Project::allowUsageOfUnicodeIdentifierNames = false;
|
||||
|
||||
Project::Project()
|
||||
: name(_("Project")),
|
||||
version("1.0.0"),
|
||||
@@ -86,26 +81,24 @@ Project::~Project() {}
|
||||
|
||||
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
|
||||
|
||||
std::unique_ptr<gd::Object>
|
||||
Project::CreateObject(const gd::String &objectType, const gd::String &name) const {
|
||||
std::unique_ptr<gd::Object> object =
|
||||
gd::make_unique<Object>(name, objectType, CreateObjectConfiguration(objectType));
|
||||
std::unique_ptr<gd::Object> Project::CreateObject(
|
||||
const gd::String& objectType, const gd::String& name) const {
|
||||
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
|
||||
name, objectType, CreateObjectConfiguration(objectType));
|
||||
|
||||
auto &platform = GetCurrentPlatform();
|
||||
auto &project = *this;
|
||||
auto addDefaultBehavior =
|
||||
[&platform,
|
||||
&project,
|
||||
&object,
|
||||
&objectType](const gd::String& behaviorType) {
|
||||
auto &behaviorMetadata =
|
||||
auto& platform = GetCurrentPlatform();
|
||||
auto& project = *this;
|
||||
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
|
||||
const gd::String& behaviorType) {
|
||||
auto& behaviorMetadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
|
||||
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
|
||||
gd::LogWarning("Object: " + objectType + " has an unknown default behavior: " + behaviorType);
|
||||
gd::LogWarning("Object: " + objectType +
|
||||
" has an unknown default behavior: " + behaviorType);
|
||||
return;
|
||||
}
|
||||
auto* behavior = object->AddNewBehavior(project, behaviorType,
|
||||
behaviorMetadata.GetDefaultName());
|
||||
auto* behavior = object->AddNewBehavior(
|
||||
project, behaviorType, behaviorMetadata.GetDefaultName());
|
||||
behavior->SetDefaultBehavior(true);
|
||||
};
|
||||
|
||||
@@ -114,18 +107,17 @@ Project::CreateObject(const gd::String &objectType, const gd::String &name) cons
|
||||
addDefaultBehavior("ResizableCapability::ResizableBehavior");
|
||||
addDefaultBehavior("ScalableCapability::ScalableBehavior");
|
||||
addDefaultBehavior("FlippableCapability::FlippableBehavior");
|
||||
}
|
||||
else {
|
||||
auto &objectMetadata = gd::MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
} else {
|
||||
auto& objectMetadata =
|
||||
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
if (MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
|
||||
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
|
||||
}
|
||||
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
|
||||
for (auto& behaviorType : objectMetadata.GetDefaultBehaviors()) {
|
||||
addDefaultBehavior(behaviorType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return std::move(object);
|
||||
}
|
||||
|
||||
@@ -849,6 +841,11 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
resourcesManager.UnserializeFrom(
|
||||
element.GetChild("resources", 0, "Resources"));
|
||||
UnserializeObjectsFrom(*this, element.GetChild("objects", 0, "Objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(*this, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
|
||||
GetVariables().UnserializeFrom(element.GetChild("variables", 0, "Variables"));
|
||||
|
||||
scenes.clear();
|
||||
@@ -1000,6 +997,7 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
resourcesManager.SerializeTo(element.AddChild("resources"));
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
|
||||
GetVariables().SerializeTo(element.AddChild("variables"));
|
||||
|
||||
@@ -1038,28 +1036,18 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
externalSourceFilesElement.AddChild("sourceFile"));
|
||||
}
|
||||
|
||||
void Project::AllowUsageOfUnicodeIdentifierNames(bool enable) {
|
||||
allowUsageOfUnicodeIdentifierNames = enable;
|
||||
}
|
||||
|
||||
bool Project::IsNameSafe(const gd::String& name) {
|
||||
if (name.empty()) return false;
|
||||
|
||||
if (isdigit(name[0])) return false;
|
||||
|
||||
if (!allowUsageOfUnicodeIdentifierNames) {
|
||||
gd::String legacyAllowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
return !(name.find_first_not_of(legacyAllowedCharacters) != gd::String::npos);
|
||||
} else {
|
||||
for (auto character : name) {
|
||||
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
|
||||
return false;
|
||||
}
|
||||
for (auto character : name) {
|
||||
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String Project::GetSafeName(const gd::String& name) {
|
||||
@@ -1069,18 +1057,13 @@ gd::String Project::GetSafeName(const gd::String& name) {
|
||||
|
||||
if (isdigit(name[0])) newName = "_" + newName;
|
||||
|
||||
gd::String legacyAllowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
|
||||
for (size_t i = 0;i < newName.size();++i) {
|
||||
// Note that iterating on the characters is not super efficient (O(n^2), which
|
||||
// could be avoided with an iterator), but this function is not critical for performance
|
||||
// (only used to generate a name when a user creates a new entity or rename one).
|
||||
for (size_t i = 0; i < newName.size(); ++i) {
|
||||
// Note that iterating on the characters is not super efficient (O(n^2),
|
||||
// which could be avoided with an iterator), but this function is not
|
||||
// critical for performance (only used to generate a name when a user
|
||||
// creates a new entity or rename one).
|
||||
auto character = newName[i];
|
||||
bool isAllowed =
|
||||
allowUsageOfUnicodeIdentifierNames
|
||||
? GrammarTerminals::IsAllowedInIdentifier(character)
|
||||
: legacyAllowedCharacters.find(character) != gd::String::npos;
|
||||
bool isAllowed = GrammarTerminals::IsAllowedInIdentifier(character);
|
||||
|
||||
// Replace all unallowed letters by an underscore.
|
||||
if (!isAllowed) {
|
||||
|
@@ -11,12 +11,12 @@
|
||||
|
||||
#include "GDCore/Project/ExtensionProperties.h"
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/Watermark.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/PlatformSpecificAssets.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/Project/Watermark.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Platform;
|
||||
@@ -82,7 +82,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Change the project description
|
||||
*/
|
||||
void SetDescription(const gd::String& description_) { description = description_; };
|
||||
void SetDescription(const gd::String& description_) {
|
||||
description = description_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the project description
|
||||
@@ -124,7 +126,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Get the author usernames of the project.
|
||||
*/
|
||||
const std::vector<gd::String>& GetAuthorUsernames() const { return authorUsernames; };
|
||||
const std::vector<gd::String>& GetAuthorUsernames() const {
|
||||
return authorUsernames;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the author usernames of the project, to modify them (non-const).
|
||||
@@ -135,7 +139,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable with a keyboard.
|
||||
* \param enable True to define the project as playable with a keyboard.
|
||||
*/
|
||||
void SetPlayableWithKeyboard(bool playable = true) { isPlayableWithKeyboard = playable; }
|
||||
void SetPlayableWithKeyboard(bool playable = true) {
|
||||
isPlayableWithKeyboard = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable with a keyboard.
|
||||
@@ -146,7 +152,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable with a gamepad.
|
||||
* \param enable True to define the project as playable with a gamepad.
|
||||
*/
|
||||
void SetPlayableWithGamepad(bool playable = true) { isPlayableWithGamepad = playable; }
|
||||
void SetPlayableWithGamepad(bool playable = true) {
|
||||
isPlayableWithGamepad = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable with a gamepad.
|
||||
@@ -157,7 +165,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable on a mobile.
|
||||
* \param enable True to define the project as playable on a mobile.
|
||||
*/
|
||||
void SetPlayableWithMobile(bool playable = true) { isPlayableWithMobile = playable; }
|
||||
void SetPlayableWithMobile(bool playable = true) {
|
||||
isPlayableWithMobile = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable on a mobile.
|
||||
@@ -391,17 +401,23 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* Set the antialiasing mode used by the game ("none" or "MSAA").
|
||||
*/
|
||||
void SetAntialiasingMode(const gd::String& antialiasingMode_) { antialiasingMode = antialiasingMode_; }
|
||||
void SetAntialiasingMode(const gd::String& antialiasingMode_) {
|
||||
antialiasingMode = antialiasingMode_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if antialising is enabled on mobiles.
|
||||
*/
|
||||
bool IsAntialisingEnabledOnMobile() const { return isAntialisingEnabledOnMobile; }
|
||||
bool IsAntialisingEnabledOnMobile() const {
|
||||
return isAntialisingEnabledOnMobile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether antialising is enabled on mobiles or not.
|
||||
*/
|
||||
void SetAntialisingEnabledOnMobile(bool enable) { isAntialisingEnabledOnMobile = enable; }
|
||||
void SetAntialisingEnabledOnMobile(bool enable) {
|
||||
isAntialisingEnabledOnMobile = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return if the project should set 0 as Z-order for objects created
|
||||
@@ -905,7 +921,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Return the events based object with a given type.
|
||||
*/
|
||||
const gd::EventsBasedObject& GetEventsBasedObject(const gd::String& type) const;
|
||||
const gd::EventsBasedObject& GetEventsBasedObject(
|
||||
const gd::String& type) const;
|
||||
|
||||
/**
|
||||
* \brief Check if events based behavior with a given type exists.
|
||||
@@ -920,7 +937,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Return the events based behavior with a given type.
|
||||
*/
|
||||
const gd::EventsBasedBehavior& GetEventsBasedBehavior(const gd::String& type) const;
|
||||
const gd::EventsBasedBehavior& GetEventsBasedBehavior(
|
||||
const gd::String& type) const;
|
||||
|
||||
///@}
|
||||
|
||||
@@ -969,20 +987,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Check if unicode names are allowed in identifier names.
|
||||
* \see IsNameSafe
|
||||
* \see GetSafeName
|
||||
*/
|
||||
static bool IsUsageOfUnicodeIdentifierNamesAllowed() { return allowUsageOfUnicodeIdentifierNames; };
|
||||
|
||||
/**
|
||||
* Set if unicode names are allowed in identifier names.
|
||||
* \see IsNameSafe
|
||||
* \see GetSafeName
|
||||
*/
|
||||
static void AllowUsageOfUnicodeIdentifierNames(bool enable);
|
||||
|
||||
/**
|
||||
* Return true if \a name is valid (can be used safely for an object,
|
||||
* behavior, events function name, etc...).
|
||||
@@ -990,8 +994,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
static bool IsNameSafe(const gd::String& name);
|
||||
|
||||
/**
|
||||
* Return a name, based on the one passed in parameter, that can be safely used
|
||||
* for an object, behavior, events function name, etc...
|
||||
* Return a name, based on the one passed in parameter, that can be safely
|
||||
* used for an object, behavior, events function name, etc...
|
||||
*/
|
||||
static gd::String GetSafeName(const gd::String& name);
|
||||
///@}
|
||||
@@ -1068,8 +1072,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
|
||||
///< to the window size at runtime
|
||||
gd::String
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
gd::String antialiasingMode;
|
||||
bool isAntialisingEnabledOnMobile;
|
||||
gd::String projectUuid; ///< UUID useful to identify the game in online
|
||||
@@ -1095,19 +1099,18 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
externalSourceFiles; ///< List of external source files used.
|
||||
gd::String author; ///< Game author name, for publishing purpose.
|
||||
std::vector<gd::String>
|
||||
authorIds; ///< Game author ids, from GDevelop users DB.
|
||||
authorIds; ///< Game author ids, from GDevelop users DB.
|
||||
std::vector<gd::String>
|
||||
authorUsernames; ///< Game author usernames, from GDevelop users DB.
|
||||
std::vector<gd::String>
|
||||
categories; ///< Game categories
|
||||
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String templateSlug; ///< The slug of the template from which the game is
|
||||
///< created.
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
authorUsernames; ///< Game author usernames, from GDevelop users DB.
|
||||
std::vector<gd::String> categories; ///< Game categories
|
||||
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String templateSlug; ///< The slug of the template from which the game is
|
||||
///< created.
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
bool
|
||||
folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String
|
||||
@@ -1128,8 +1131,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
|
||||
static bool allowUsageOfUnicodeIdentifierNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -97,8 +97,8 @@ class ProjectScopedContainers {
|
||||
return notFoundCallback();
|
||||
};
|
||||
|
||||
void ForEachIdentifierWithPrefix(
|
||||
const gd::String &prefix,
|
||||
void ForEachIdentifierMatchingSearch(
|
||||
const gd::String &search,
|
||||
std::function<void(const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration)>
|
||||
objectCallback,
|
||||
@@ -110,8 +110,8 @@ class ProjectScopedContainers {
|
||||
parameterCallback) const {
|
||||
std::set<gd::String> namesAlreadySeen;
|
||||
|
||||
objectsContainersList.ForEachNameWithPrefix(
|
||||
prefix,
|
||||
objectsContainersList.ForEachNameMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
@@ -119,24 +119,24 @@ class ProjectScopedContainers {
|
||||
objectCallback(name, objectConfiguration);
|
||||
}
|
||||
});
|
||||
variablesContainersList.ForEachVariableWithPrefix(
|
||||
prefix, [&](const gd::String &name, const gd::Variable &variable) {
|
||||
variablesContainersList.ForEachVariableMatchingSearch(
|
||||
search, [&](const gd::String &name, const gd::Variable &variable) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
namesAlreadySeen.insert(name);
|
||||
variableCallback(name, variable);
|
||||
}
|
||||
});
|
||||
gd::ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
gd::ParameterMetadataTools::ForEachParameterMatchingSearch(
|
||||
parametersVectorsList,
|
||||
prefix,
|
||||
search,
|
||||
[&](const gd::ParameterMetadata ¶meter) {
|
||||
if (namesAlreadySeen.count(parameter.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(parameter.GetName());
|
||||
parameterCallback(parameter);
|
||||
}
|
||||
});
|
||||
propertiesContainersList.ForEachPropertyWithPrefix(
|
||||
prefix, [&](const gd::NamedPropertyDescriptor &property) {
|
||||
propertiesContainersList.ForEachPropertyMatchingSearch(
|
||||
search, [&](const gd::NamedPropertyDescriptor &property) {
|
||||
if (namesAlreadySeen.count(property.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(property.GetName());
|
||||
propertyCallback(property);
|
||||
|
@@ -31,9 +31,13 @@ class PropertiesContainer
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ForEachPropertyWithPrefix(const gd::String& prefix, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const {
|
||||
for (const auto& property: elements) {
|
||||
if (property->GetName().find(prefix) == 0) fn(*property);
|
||||
void ForEachPropertyMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::NamedPropertyDescriptor& property)> fn)
|
||||
const {
|
||||
for (const auto& property : elements) {
|
||||
if (property->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(*property);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -55,12 +55,12 @@ bool PropertiesContainersList::HasPropertiesContainer(const gd::PropertiesContai
|
||||
return false;
|
||||
}
|
||||
|
||||
void PropertiesContainersList::ForEachPropertyWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void PropertiesContainersList::ForEachPropertyMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const {
|
||||
for (auto it = propertiesContainers.rbegin(); it != propertiesContainers.rend();
|
||||
++it) {
|
||||
(*it)->ForEachPropertyWithPrefix(prefix, fn);
|
||||
(*it)->ForEachPropertyMatchingSearch(search, fn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -67,9 +67,9 @@ class GD_CORE_API PropertiesContainersList {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each property having a name starting with the specified prefix.
|
||||
* \brief Call the callback for each property having a name matching the specified search.
|
||||
*/
|
||||
void ForEachPropertyWithPrefix(const gd::String& prefix, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const;
|
||||
void ForEachPropertyMatchingSearch(const gd::String& search, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
|
@@ -162,11 +162,13 @@ void VariablesContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
|
||||
variables.insert(variables.begin() + newIndex, nameAndVariable);
|
||||
}
|
||||
|
||||
void VariablesContainer::ForEachVariableWithPrefix(
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const {
|
||||
for (const auto& nameAndVariable: variables) {
|
||||
if (nameAndVariable.first.find(prefix) == 0) fn(nameAndVariable.first, *nameAndVariable.second);
|
||||
void VariablesContainer::ForEachVariableMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name, const gd::Variable& variable)>
|
||||
fn) const {
|
||||
for (const auto& nameAndVariable : variables) {
|
||||
if (nameAndVariable.first.FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(nameAndVariable.first, *nameAndVariable.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +201,7 @@ void VariablesContainer::UnserializeFrom(const SerializerElement& element) {
|
||||
|
||||
VariablesContainer& VariablesContainer::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
for(auto& variable: variables) {
|
||||
for (auto& variable : variables) {
|
||||
variable.second->ResetPersistentUuid();
|
||||
}
|
||||
|
||||
@@ -208,7 +210,7 @@ VariablesContainer& VariablesContainer::ResetPersistentUuid() {
|
||||
|
||||
VariablesContainer& VariablesContainer::ClearPersistentUuid() {
|
||||
persistentUuid = "";
|
||||
for(auto& variable: variables) {
|
||||
for (auto& variable : variables) {
|
||||
variable.second->ClearPersistentUuid();
|
||||
}
|
||||
|
||||
|
@@ -139,9 +139,9 @@ class GD_CORE_API VariablesContainer {
|
||||
inline void Clear() { variables.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable with a name starting with the specified prefix.
|
||||
* \brief Call the callback for each variable with a name matching the specified search.
|
||||
*/
|
||||
void ForEachVariableWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
|
@@ -52,12 +52,12 @@ bool VariablesContainersList::HasVariablesContainer(const gd::VariablesContainer
|
||||
return false;
|
||||
}
|
||||
|
||||
void VariablesContainersList::ForEachVariableWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void VariablesContainersList::ForEachVariableMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const {
|
||||
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
|
||||
++it) {
|
||||
(*it)->ForEachVariableWithPrefix(prefix, fn);
|
||||
(*it)->ForEachVariableMatchingSearch(search, fn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -66,9 +66,9 @@ class GD_CORE_API VariablesContainersList {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable having a name starting with the specified prefix.
|
||||
* \brief Call the callback for each variable having a name matching the specified search.
|
||||
*/
|
||||
void ForEachVariableWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a temporary. */
|
||||
VariablesContainersList() {};
|
||||
|
@@ -22,12 +22,19 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
|
||||
// Add some variables and objects:
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 1);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2", 2).GetChild("MyChild");
|
||||
project.GetVariables().InsertNew("MyGlobalNumberVariable").SetValue(1234);
|
||||
project.GetVariables().InsertNew("MyGlobalStringVariable").SetString("TestGlobal");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneStringVariable").SetString("MyString");
|
||||
layout1.GetVariables().InsertNew("MySceneBooleanVariable").SetBool(true);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2").GetChild("MyChild");
|
||||
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
auto &mySpriteObject = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
mySpriteObject.GetVariables().InsertNew("MyNumberVariable").SetValue(123);
|
||||
mySpriteObject.GetVariables().InsertNew("MyStringVariable").SetString("Test");
|
||||
mySpriteObject.GetVariables().InsertNew("MyStructureVariable").GetChild("MyStringChild").SetString("Test");
|
||||
layout1.InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyOtherSpriteObject", 1);
|
||||
layout1.InsertNewObject(project,
|
||||
@@ -48,6 +55,10 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
group.AddObject("MyOtherSpriteObject");
|
||||
group.AddObject("FakeObjectWithDefaultBehavior");
|
||||
|
||||
auto &spriteGroup = layout1.GetObjectGroups().InsertNew("MySpriteObjects");
|
||||
spriteGroup.AddObject("MySpriteObject");
|
||||
spriteGroup.AddObject("MyOtherSpriteObject");
|
||||
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
unsigned int maxDepth = 0;
|
||||
@@ -428,7 +439,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + 1");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyPropertyAsnumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
@@ -440,7 +451,56 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + getPropertyMyProperty2()");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyPropertyAsnumber() + getPropertyMyProperty2Asnumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Properties (1 level, number|string)") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithProperties);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyNumberPropertyAsnumber()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyStringPropertyAsstring()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyBooleanProperty");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyBooleanPropertyAsnumber|string()");
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level)") {
|
||||
@@ -469,7 +529,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + 1");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1Asnumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
@@ -481,7 +541,64 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + getParameterMyParameter2()");
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1Asnumber() + getParameterMyParameter2Asnumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level, number|string)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyNumberParameter");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyStringParameter");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyBooleanParameter");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithParameters);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyNumberParameterAsnumber()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyStringParameterAsstring()");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyBooleanParameter");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyBooleanParameterAsnumber|string()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (1 level)") {
|
||||
@@ -550,7 +667,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor)") {
|
||||
SECTION("Scene variables (2 levels with bracket accessor, string)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"MyChild\"] + 1");
|
||||
@@ -576,6 +693,144 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, number)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[3] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[3] + MySceneStructureVariable2[3]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(3).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a number variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a string variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStringVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a non string/number variable as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneBooleanVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a number as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + 2] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 2).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a string as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + \"Test\"] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \"Test\").getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index) (expression type: number|string)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number|string",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumberOrString()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor, using a number variable casted to string as index)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"\" + MySceneVariable] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"\" + getLayoutVariable(MySceneVariable).getAsString()).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Object variable with non existing object (invalid)") {
|
||||
auto node =
|
||||
parser.ParseExpression("MyNonExistingSpriteObject.MyVariable");
|
||||
@@ -614,6 +869,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getAsNumber() + getVariableForObject(MySpriteObject, MyVariable2).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (1 level, object group)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObjects.MyVariable + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObjects, MyVariable).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (conflict with a scene variable)") {
|
||||
{
|
||||
auto node =
|
||||
@@ -739,7 +1008,20 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"fakeBadVariable");
|
||||
}
|
||||
}
|
||||
SECTION("Valid variables") {
|
||||
SECTION("Valid variables (upcoming, new 'variable' type working for any variable)") {
|
||||
// When implemented, copy the test cases from the next section, like this:
|
||||
// SECTION("simple variable") {
|
||||
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
// codeGenerator, context, "variable", "MySceneVariable", "")
|
||||
// == "getLayoutVariable(MySceneVariable)");
|
||||
// }
|
||||
// SECTION("simple (global) variable") {
|
||||
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
// codeGenerator, context, "variable", "MyGlobalNumberVariable", "")
|
||||
// == "getProjectVariable(MyGlobalNumberVariable)");
|
||||
// }
|
||||
}
|
||||
SECTION("Valid variables (legacy, pre-scoped variables)") {
|
||||
SECTION("simple variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable", "")
|
||||
@@ -761,13 +1043,63 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"\"world\" ]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")");
|
||||
}
|
||||
SECTION("object variable (legacy)") {
|
||||
SECTION("bracket access (using a string object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyNumberVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyNumberVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a string variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a string global variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MyGlobalStringVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("bracket access (using a number global variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MyGlobalNumberVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalNumberVariable).getAsNumber())");
|
||||
}
|
||||
SECTION("bracket access (using a boolean variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneBooleanVariable]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString())");
|
||||
}
|
||||
SECTION("bracket access (using a structure variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "scenevar", "myVariable[MySceneStructureVariable.MyChild.SubChild]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"SubChild\").getAsNumberOrString())");
|
||||
}
|
||||
SECTION("object variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable)");
|
||||
}
|
||||
SECTION("object variable with bracket access (using a structure variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable[MySceneStringVariable]", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())");
|
||||
}
|
||||
SECTION("object variable with bracket access (using an object variable inside)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable[MySpriteObject.MyStructureVariable.MyChild]", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable).getChild(getVariableForObject(MySpriteObject, MyStructureVariable).getChild(\"MyChild\").getAsNumberOrString())");
|
||||
}
|
||||
}
|
||||
SECTION("Valid function calls with variables") {
|
||||
SECTION("Valid function calls with variables (legacy, pre-scoped)") {
|
||||
SECTION("Simple access") {
|
||||
SECTION("Scene variable") {
|
||||
auto node = parser.ParseExpression(
|
||||
|
@@ -56,6 +56,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
@@ -67,6 +68,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
@@ -78,6 +80,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number|string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number|string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
@@ -92,6 +95,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
@@ -112,6 +116,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
@@ -200,6 +205,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, unknown, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("unknown", "My", 9, 10).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
|
@@ -22,27 +22,42 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 1);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable");
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2").GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneNumberVariable").SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneStringVariable").SetString("Test");
|
||||
layout1.GetVariables().InsertNew("MySceneBooleanVariable").SetBool(true);
|
||||
|
||||
// Create an instance of BuiltinObject.
|
||||
// This is not possible in practice.
|
||||
auto &myObject = layout1.InsertNewObject(project, "", "MyObject", 0);
|
||||
myObject.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior");
|
||||
|
||||
auto &myGroup = layout1.GetObjectGroups().InsertNew("MyGroup", 0);
|
||||
auto &myGroup = layout1.GetObjectGroups().InsertNew("MyGroup");
|
||||
myGroup.AddObject(myObject.GetName());
|
||||
|
||||
layout1.GetObjectGroups().InsertNew("EmptyGroup");
|
||||
|
||||
auto &mySpriteObject = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 1);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable", 0);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable2", 1);
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable");
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable2");
|
||||
mySpriteObject.GetVariables().InsertNew("MyVariable3");
|
||||
mySpriteObject.GetVariables().InsertNew("MyNumberVariable").SetValue(123);
|
||||
mySpriteObject.GetVariables().InsertNew("MyStringVariable").SetString("Test");
|
||||
auto &mySpriteObject2 = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject2", 1);
|
||||
mySpriteObject2.GetVariables().InsertNew("MyVariable", 0);
|
||||
mySpriteObject2.GetVariables().InsertNew("MyVariable2", 1);
|
||||
layout1.InsertNewObject(project,
|
||||
"MyExtension::FakeObjectWithDefaultBehavior",
|
||||
"FakeObjectWithDefaultBehavior",
|
||||
2);
|
||||
|
||||
auto &mySpriteGroup = layout1.GetObjectGroups().InsertNew("MySpriteObjects", 0);
|
||||
mySpriteGroup.AddObject("MySpriteObject");
|
||||
mySpriteGroup.AddObject("MySpriteObject2");
|
||||
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
@@ -54,7 +69,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", emptyNode);
|
||||
platform, projectScopedContainers, "string", emptyNode);
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -69,7 +84,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", emptyNode);
|
||||
platform, projectScopedContainers, "number", emptyNode);
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -84,7 +99,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "object", emptyNode);
|
||||
platform, projectScopedContainers, "object", emptyNode);
|
||||
REQUIRE(type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
|
||||
@@ -101,7 +116,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", emptyNode);
|
||||
platform, projectScopedContainers, "string", emptyNode);
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -110,7 +125,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", emptyNode);
|
||||
platform, projectScopedContainers, "number", emptyNode);
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -119,7 +134,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &emptyNode = dynamic_cast<gd::EmptyNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "object", emptyNode);
|
||||
platform, projectScopedContainers, "object", emptyNode);
|
||||
REQUIRE(type == "object");
|
||||
REQUIRE(emptyNode.text == "");
|
||||
}
|
||||
@@ -475,7 +490,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", operatorNode);
|
||||
platform, projectScopedContainers, "number", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &leftNumberNode =
|
||||
@@ -494,7 +509,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", operatorNode);
|
||||
platform, projectScopedContainers, "string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "string");
|
||||
auto &leftTextNode =
|
||||
@@ -516,7 +531,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", operatorNode);
|
||||
platform, projectScopedContainers, "number|string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &leftNumberNode =
|
||||
@@ -535,7 +550,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &operatorNode = dynamic_cast<gd::OperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", operatorNode);
|
||||
platform, projectScopedContainers, "number|string", operatorNode);
|
||||
REQUIRE(operatorNode.op == '+');
|
||||
REQUIRE(type == "string");
|
||||
auto &leftTextNode =
|
||||
@@ -557,7 +572,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -573,7 +588,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -589,7 +604,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -607,7 +622,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -623,7 +638,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '+');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -639,7 +654,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &unaryOperatorNode = dynamic_cast<gd::UnaryOperatorNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", unaryOperatorNode);
|
||||
platform, projectScopedContainers, "number|string", unaryOperatorNode);
|
||||
REQUIRE(unaryOperatorNode.op == '-');
|
||||
REQUIRE(type == "number");
|
||||
auto &numberNode =
|
||||
@@ -904,6 +919,242 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 19);
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a known variable type first)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneNumberVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 24);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStringVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 24);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneNumberVariable + MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 49);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStringVariable + MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 49);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyNumberVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyStringVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a parameter first)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberParameter + MyBooleanParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringParameter + MyBooleanParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with a property first)") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyNumberProperty + MyBooleanProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyStringProperty + MyBooleanProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches ('number|string' type, with an unknown variable type first)") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + 123 + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + \"hello world\" + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + 123 + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a text, but a number was expected.");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + \"hello world\" + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"You entered a number, but a text was expected (in quotes).");
|
||||
}
|
||||
}
|
||||
SECTION("Numbers and texts mismatches with parenthesis") {
|
||||
{
|
||||
auto node =
|
||||
@@ -1283,6 +1534,25 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid object variables (object group, 1 level)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObjects.MyVariable");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObjects.MyVariable + MySpriteObjects.MyVariable2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid object variables (2 levels)") {
|
||||
{
|
||||
auto node =
|
||||
@@ -1323,6 +1593,45 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid object variables (object group, non existing variable)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObjects.MyNonExistingVariable");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"This variable does not exist on this object or group.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid object variables (object group, partially existing variable)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObjects.MyVariable3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"This variable only exists on some objects of the group. It must be declared for all objects.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid object variables (empty object group)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("EmptyGroup.MyVariable");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
|
||||
"This group is empty. Add an object to this group first.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid object variables (2 levels, bracket accessor)") {
|
||||
{
|
||||
auto node =
|
||||
@@ -1383,21 +1692,78 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid property") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyProperty2").SetType("String");
|
||||
propertiesContainer.InsertNew("MyProperty3").SetType("Boolean");
|
||||
|
||||
{
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty");
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
propertiesContainer.InsertNew("MyProperty");
|
||||
propertiesContainer.InsertNew("MyProperty2");
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number|string");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + MyProperty2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + MyProperty2 + MyProperty3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithProperties, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1417,7 +1783,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must wrap your text inside double quotes (example: \"Hello world\").");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1461,7 +1827,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid parameter") {
|
||||
{
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
@@ -1469,18 +1834,75 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
gd::ParameterMetadata param3;
|
||||
param3.SetName("MyParameter3");
|
||||
param3.SetType("yesorno");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
parameters.push_back(param3);
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number|string");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + MyParameter2");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + MyParameter2 + MyParameter3");
|
||||
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 0);
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1530,7 +1952,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainersWithParameters, "number|string");
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetFatalErrors().size() == 1);
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name.");
|
||||
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You must enter a number.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,7 +2012,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", functionNode);
|
||||
platform, projectScopedContainers, "number|string", functionNode);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::GetNumber");
|
||||
REQUIRE(type == "number");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
@@ -1606,7 +2028,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", functionNode);
|
||||
platform, projectScopedContainers, "number|string", functionNode);
|
||||
REQUIRE(functionNode.functionName == "MyExtension::ToString");
|
||||
REQUIRE(type == "string");
|
||||
REQUIRE(functionNode.objectName == "");
|
||||
@@ -1741,7 +2163,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", objectFunctionCall);
|
||||
platform, projectScopedContainers, "string", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "string");
|
||||
@@ -1752,7 +2174,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", objectFunctionCall);
|
||||
platform, projectScopedContainers, "number", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "number");
|
||||
@@ -1765,7 +2187,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", objectFunctionCall);
|
||||
platform, projectScopedContainers, "number|string", objectFunctionCall);
|
||||
REQUIRE(objectFunctionCall.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionCall.functionName == "");
|
||||
REQUIRE(type == "number|string");
|
||||
@@ -1786,7 +2208,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", objectFunctionName);
|
||||
platform, projectScopedContainers, "string", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1798,7 +2220,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", objectFunctionName);
|
||||
platform, projectScopedContainers, "number", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1813,7 +2235,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", objectFunctionName);
|
||||
platform, projectScopedContainers, "number|string", objectFunctionName);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.functionName == "");
|
||||
@@ -1825,7 +2247,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "string", freeFunctionCall);
|
||||
platform, projectScopedContainers, "string", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "string");
|
||||
@@ -1836,7 +2258,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number", freeFunctionCall);
|
||||
platform, projectScopedContainers, "number", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "number");
|
||||
@@ -1848,7 +2270,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
REQUIRE(node != nullptr);
|
||||
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", freeFunctionCall);
|
||||
platform, projectScopedContainers, "number|string", freeFunctionCall);
|
||||
REQUIRE(freeFunctionCall.objectName == "");
|
||||
REQUIRE(freeFunctionCall.functionName == "fun");
|
||||
REQUIRE(type == "number|string");
|
||||
@@ -2204,21 +2626,21 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
auto node = parser.ParseExpression("123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("123 + MyExtension::GetNumber()");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("\"Hello\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
@@ -2226,10 +2648,160 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
"\"Hello\" + MyExtension::ToString(3)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, "number|string", *node.get());
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with an known variable first") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneNumberVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStringVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneNumberVariable + MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStringVariable + MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with an unknown variable first") {
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneBooleanVariable + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MySceneStructureVariable.MyChild.UnknownSubChild + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with a property first") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyNumberProperty").SetType("Number");
|
||||
propertiesContainer.InsertNew("MyStringProperty").SetType("String");
|
||||
propertiesContainer.InsertNew("MyBooleanProperty").SetType("Boolean");
|
||||
{
|
||||
auto node = parser.ParseExpression("MyNumberProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyStringProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanProperty + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanProperty + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainers, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
}
|
||||
SECTION("Valid type inferred from expressions with type 'number|string', with a parameter first") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyNumberParameter");
|
||||
param.SetType("number");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyStringParameter");
|
||||
param.SetType("string");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
{
|
||||
gd::ParameterMetadata param;
|
||||
param.SetName("MyBooleanParameter");
|
||||
param.SetType("yesorno");
|
||||
parameters.push_back(param);
|
||||
}
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
{
|
||||
auto node = parser.ParseExpression("MyNumberParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyStringParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanParameter + \"hello world\"");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "string");
|
||||
}
|
||||
{
|
||||
auto node = parser.ParseExpression("MyBooleanParameter + 123");
|
||||
REQUIRE(node != nullptr);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, projectScopedContainersWithParameters, "number|string", *node.get());
|
||||
REQUIRE(type == "number");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid function call with object variable") {
|
||||
{
|
||||
|
@@ -107,7 +107,7 @@ module.exports = {
|
||||
_('Scale on Z axis'),
|
||||
_("the scale on Z axis of an object (default scale is 1)"),
|
||||
_("the scale on Z axis scale"),
|
||||
_('Scale'),
|
||||
_('Size'),
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D object'))
|
||||
@@ -244,17 +244,18 @@ module.exports = {
|
||||
.addObject(
|
||||
'Model3DObject',
|
||||
_('3D Model'),
|
||||
_('A 3D model.'),
|
||||
_('An animated 3D model.'),
|
||||
'JsPlatform/Extensions/3d_box.svg',
|
||||
new gd.Model3DObjectConfiguration()
|
||||
)
|
||||
.setCategoryFullName(_('3D'))
|
||||
.setCategoryFullName(_('General'))
|
||||
// Effects are unsupported because the object is not rendered with PIXI.
|
||||
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
|
||||
.addDefaultBehavior('ScalableCapability::ScalableBehavior')
|
||||
.addDefaultBehavior('FlippableCapability::FlippableBehavior')
|
||||
.addDefaultBehavior('AnimatableCapability::AnimatableBehavior')
|
||||
.addDefaultBehavior('Scene3D::Base3DBehavior')
|
||||
.markAsRenderedIn3D()
|
||||
.setIncludeFile('Extensions/3D/A_RuntimeObject3D.js')
|
||||
.addIncludeFile('Extensions/3D/A_RuntimeObject3DRenderer.js')
|
||||
.addIncludeFile('Extensions/3D/Model3DRuntimeObject.js')
|
||||
@@ -1125,16 +1126,17 @@ module.exports = {
|
||||
.addObject(
|
||||
'Cube3DObject',
|
||||
_('3D Box'),
|
||||
_('A 3D box.'),
|
||||
_('A box with images for each face'),
|
||||
'JsPlatform/Extensions/3d_box.svg',
|
||||
Cube3DObject
|
||||
)
|
||||
.setCategoryFullName(_('3D'))
|
||||
.setCategoryFullName(_('General'))
|
||||
// Effects are unsupported because the object is not rendered with PIXI.
|
||||
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
|
||||
.addDefaultBehavior('ScalableCapability::ScalableBehavior')
|
||||
.addDefaultBehavior('FlippableCapability::FlippableBehavior')
|
||||
.addDefaultBehavior('Scene3D::Base3DBehavior')
|
||||
.markAsRenderedIn3D()
|
||||
.setIncludeFile('Extensions/3D/A_RuntimeObject3D.js')
|
||||
.addIncludeFile('Extensions/3D/A_RuntimeObject3DRenderer.js')
|
||||
.addIncludeFile('Extensions/3D/Cube3DRuntimeObject.js')
|
||||
|
@@ -221,6 +221,9 @@ namespace gdjs {
|
||||
const animation = this._animations[animationIndex];
|
||||
this._currentAnimationIndex = animationIndex;
|
||||
this._renderer.playAnimation(animation.source, animation.loop);
|
||||
if (this._animationPaused) {
|
||||
this._renderer.pauseAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,12 +275,12 @@ namespace gdjs {
|
||||
|
||||
pauseAnimation() {
|
||||
this._animationPaused = true;
|
||||
return this._renderer.pauseAnimation();
|
||||
this._renderer.pauseAnimation();
|
||||
}
|
||||
|
||||
resumeAnimation() {
|
||||
this._animationPaused = false;
|
||||
return this._renderer.resumeAnimation();
|
||||
this._renderer.resumeAnimation();
|
||||
}
|
||||
|
||||
getAnimationSpeedScale() {
|
||||
@@ -288,6 +291,20 @@ namespace gdjs {
|
||||
this._animationSpeedScale = ratio;
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this._renderer.getAnimationElapsedTime();
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
this._renderer.setAnimationElapsedTime(time);
|
||||
}
|
||||
|
||||
getAnimationDuration(): float {
|
||||
return this._renderer.getAnimationDuration(
|
||||
this._animations[this._currentAnimationIndex].source
|
||||
);
|
||||
}
|
||||
|
||||
getCenterX(): float {
|
||||
const centerPoint = this._renderer.getCenterPoint();
|
||||
return this.getWidth() * centerPoint[0];
|
||||
|
@@ -348,6 +348,24 @@ namespace gdjs {
|
||||
// Make sure the first frame is displayed.
|
||||
this._animationMixer.update(0);
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this._action ? this._action.time : 0;
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
if (this._action) {
|
||||
this._action.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
getAnimationDuration(animationName: string): float {
|
||||
const clip = THREE.AnimationClip.findByName(
|
||||
this._originalModel.animations,
|
||||
animationName
|
||||
);
|
||||
return clip ? clip.duration : 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;
|
||||
|
@@ -126,6 +126,10 @@ namespace gdjs {
|
||||
getHeight(): float {
|
||||
return this._pixiObject.height;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this._pixiObject.destroy(true);
|
||||
}
|
||||
}
|
||||
|
||||
export const BBTextRuntimeObjectRenderer = BBTextRuntimeObjectPixiRenderer;
|
||||
|
@@ -132,6 +132,7 @@ namespace gdjs {
|
||||
|
||||
onDestroyFromScene(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
super.onDestroyFromScene(instanceContainer);
|
||||
this._renderer.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -176,10 +176,12 @@ module.exports = {
|
||||
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
|
||||
)
|
||||
.setCategoryFullName(_('Text'))
|
||||
.addDefaultBehavior("TextContainerCapability::TextContainerBehavior")
|
||||
.addDefaultBehavior('EffectCapability::EffectBehavior')
|
||||
.addDefaultBehavior('OpacityCapability::OpacityBehavior')
|
||||
.addDefaultBehavior('ScalableCapability::ScalableBehavior');
|
||||
|
||||
// Deprecated
|
||||
object
|
||||
.addExpressionAndConditionAndAction(
|
||||
'string',
|
||||
@@ -190,11 +192,23 @@ module.exports = {
|
||||
'',
|
||||
'res/conditions/text24_black.png'
|
||||
)
|
||||
.setHidden()
|
||||
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
|
||||
.useStandardParameters('string', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setText')
|
||||
.setGetter('getText');
|
||||
|
||||
object
|
||||
.addStrExpression(
|
||||
'Text',
|
||||
_('Text'),
|
||||
_('Return the text.'),
|
||||
'',
|
||||
'res/conditions/text24_black.png'
|
||||
)
|
||||
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
|
||||
.setFunctionName('getText');
|
||||
|
||||
// Deprecated
|
||||
object
|
||||
.addExpressionAndConditionAndAction(
|
||||
@@ -282,6 +296,7 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setTint');
|
||||
|
||||
// Deprecated
|
||||
object
|
||||
.addAction(
|
||||
'SetBitmapFontAndTextureAtlasResourceName',
|
||||
@@ -294,6 +309,7 @@ module.exports = {
|
||||
'res/actions/font24.png',
|
||||
'res/actions/font.png'
|
||||
)
|
||||
.setHidden()
|
||||
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
|
||||
.addParameter(
|
||||
'bitmapFontResource',
|
||||
@@ -311,6 +327,34 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
|
||||
|
||||
object
|
||||
.addAction(
|
||||
'SetBitmapFontAndTextureAtlasResourceName2',
|
||||
_('Bitmap files resources'),
|
||||
_('Change the Bitmap Font and/or the atlas image used by the object.'),
|
||||
_(
|
||||
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
|
||||
),
|
||||
'',
|
||||
'res/actions/font24.png',
|
||||
'res/actions/font.png'
|
||||
)
|
||||
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
|
||||
.addParameter(
|
||||
'bitmapFontResource',
|
||||
_('Bitmap font resource name'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.addParameter(
|
||||
'imageResource',
|
||||
_('Texture atlas resource name'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
|
||||
|
||||
object
|
||||
.addExpressionAndCondition(
|
||||
'string',
|
||||
|
@@ -37,7 +37,7 @@ namespace gdjs {
|
||||
*/
|
||||
export class BitmapTextRuntimeObject
|
||||
extends gdjs.RuntimeObject
|
||||
implements gdjs.OpacityHandler, gdjs.Scalable {
|
||||
implements gdjs.TextContainer, gdjs.OpacityHandler, gdjs.Scalable {
|
||||
_opacity: float;
|
||||
_text: string;
|
||||
/** color in format [r, g, b], where each component is in the range [0, 255] */
|
||||
|
@@ -725,6 +725,44 @@ module.exports = {
|
||||
.setType('number')
|
||||
.setDescription(_('Padding for the visual effect area'));
|
||||
|
||||
const hslAdjustmentEffect = extension
|
||||
.addEffect('HslAdjustment')
|
||||
.setFullName(_('HSL Adjustment'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Adjust hue, saturation and lightness.'
|
||||
)
|
||||
)
|
||||
.markAsOnlyWorkingFor2D()
|
||||
.addIncludeFile('Extensions/Effects/pixi-filters/filter-hsl-adjustment.js')
|
||||
.addIncludeFile('Extensions/Effects/hsl-adjustment-pixi-filter.js');
|
||||
const hslAdjustmentProperties = hslAdjustmentEffect.getProperties();
|
||||
hslAdjustmentProperties
|
||||
.getOrCreate('hue')
|
||||
.setValue('0')
|
||||
.setLabel(_('Hue in degrees (between -180 and 180)'))
|
||||
.setType('number');
|
||||
hslAdjustmentProperties
|
||||
.getOrCreate('saturation')
|
||||
.setValue('0')
|
||||
.setLabel(_('Saturation (between -1 and 1)'))
|
||||
.setType('number');
|
||||
hslAdjustmentProperties
|
||||
.getOrCreate('lightness')
|
||||
.setValue('0')
|
||||
.setLabel(_('Lightness (between -1 and 1)'))
|
||||
.setType('number');
|
||||
hslAdjustmentProperties
|
||||
.getOrCreate('colorize')
|
||||
.setValue('false')
|
||||
.setLabel(_('Colorize from the grayscale image'))
|
||||
.setType('boolean');
|
||||
hslAdjustmentProperties
|
||||
.getOrCreate('alpha')
|
||||
.setValue('1')
|
||||
.setLabel(_('Alpha (between 0 and 1, 0 is transparent)'))
|
||||
.setType('number');
|
||||
|
||||
const kawaseBlurEffect = extension
|
||||
.addEffect('KawaseBlur')
|
||||
.setFullName(_('Blur (Kawase, fast)'))
|
||||
@@ -775,6 +813,38 @@ module.exports = {
|
||||
.setLabel(_('Opacity (between 0 and 1)'))
|
||||
.setType('number');
|
||||
|
||||
const motionBlurEffect = extension
|
||||
.addEffect('MotionBlur')
|
||||
.setFullName(_('Motion Blur'))
|
||||
.setDescription(
|
||||
_('Blur the rendered image to give a feeling of speed.')
|
||||
)
|
||||
.markAsOnlyWorkingFor2D()
|
||||
.addIncludeFile('Extensions/Effects/pixi-filters/filter-motion-blur.js')
|
||||
.addIncludeFile('Extensions/Effects/motion-blur-pixi-filter.js');
|
||||
const motionBlurProperties = motionBlurEffect.getProperties();
|
||||
motionBlurProperties
|
||||
.getOrCreate('velocityX')
|
||||
.setValue('0')
|
||||
.setLabel(_('Velocity on X axis'))
|
||||
.setType('number');
|
||||
motionBlurProperties
|
||||
.getOrCreate('velocityY')
|
||||
.setValue('0')
|
||||
.setLabel(_('Velocity on Y axis'))
|
||||
.setType('number');
|
||||
motionBlurProperties
|
||||
.getOrCreate('kernelSize')
|
||||
.setValue('5')
|
||||
.setLabel(_('Kernel size (odd number between 3 and 25)'))
|
||||
.setType('number')
|
||||
.setDescription(_('Quality of the blur.'));
|
||||
motionBlurProperties
|
||||
.getOrCreate('offset')
|
||||
.setValue('0')
|
||||
.setLabel(_('Offset'))
|
||||
.setType('number');
|
||||
|
||||
const nightEffect = extension
|
||||
.addEffect('Night')
|
||||
.setFullName(_('Dark Night'))
|
||||
@@ -1089,6 +1159,59 @@ module.exports = {
|
||||
.setLabel(_('Opacity (between 0 and 1)'))
|
||||
.setType('number');
|
||||
|
||||
const shockwaveEffect = extension
|
||||
.addEffect('Shockwave')
|
||||
.setFullName(_('Shockwave'))
|
||||
.setDescription(
|
||||
_('Deform the image the way a drop deforms a water surface.')
|
||||
)
|
||||
.markAsOnlyWorkingFor2D()
|
||||
.addIncludeFile('Extensions/Effects/pixi-filters/filter-shockwave.js')
|
||||
.addIncludeFile('Extensions/Effects/shockwave-pixi-filter.js');
|
||||
const shockwaveEffectProperties = shockwaveEffect.getProperties();
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('time')
|
||||
.setValue('0')
|
||||
.setLabel(_('Elapsed time'))
|
||||
.setType('number')
|
||||
.setDescription('It can be set back to 0 to play the shockwave animation again.');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('speed')
|
||||
.setValue('500')
|
||||
.setLabel(_('Spreading speed (in pixels per second)'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('amplitude')
|
||||
.setValue('50')
|
||||
.setLabel(_('Amplitude'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('wavelength')
|
||||
.setValue('200')
|
||||
.setLabel(_('Wavelength'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('brightness')
|
||||
.setValue('1')
|
||||
.setLabel(_('Brightness'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('radius')
|
||||
.setValue('0')
|
||||
.setLabel(_('Maximum radius (0 for infinity)'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('centerX')
|
||||
.setValue('0.5')
|
||||
.setLabel(_('Center on X axis'))
|
||||
.setType('number');
|
||||
shockwaveEffectProperties
|
||||
.getOrCreate('centerY')
|
||||
.setValue('0.5')
|
||||
.setLabel(_('Center on Y axis'))
|
||||
.setType('number')
|
||||
.setDescription('(0,0) is the top-left and (1,1) is the bottom right.');
|
||||
|
||||
const tiltShiftEffect = extension
|
||||
.addEffect('TiltShift')
|
||||
.setFullName(_('Tilt shift'))
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Adjustment',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const adjustmentFilter = new PIXI.filters.AdjustmentFilter();
|
||||
return adjustmentFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
|
||||
if (parameterName === 'gamma') {
|
||||
adjustmentFilter.gamma = value;
|
||||
@@ -27,8 +31,16 @@ namespace gdjs {
|
||||
adjustmentFilter.alpha = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'AdvancedBloom',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const advancedBloomFilter = new PIXI.filters.AdvancedBloomFilter();
|
||||
return advancedBloomFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
|
||||
if (parameterName === 'threshold') {
|
||||
advancedBloomFilter.threshold = value;
|
||||
@@ -23,8 +27,16 @@ namespace gdjs {
|
||||
advancedBloomFilter.padding = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,19 +2,31 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Ascii',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const asciiFilter = new PIXI.filters.AsciiFilter();
|
||||
return asciiFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
|
||||
if (parameterName === 'size') {
|
||||
asciiFilter.size = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Bevel',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const bevelFilter = new PIXI.filters.BevelFilter();
|
||||
return bevelFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
|
||||
if (parameterName === 'rotation') {
|
||||
bevelFilter.rotation = value;
|
||||
@@ -22,7 +26,11 @@ namespace gdjs {
|
||||
bevelFilter.shadowAlpha = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {
|
||||
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
|
||||
if (parameterName === 'lightColor') {
|
||||
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
@@ -35,7 +43,11 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,21 +2,33 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'BlackAndWhite',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const colorMatrix = new PIXI.ColorMatrixFilter();
|
||||
colorMatrix.blackAndWhite(false);
|
||||
return colorMatrix;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
|
||||
if (parameterName !== 'opacity') {
|
||||
return;
|
||||
}
|
||||
colorMatrix.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'BlendingMode',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const blendingModeFilter = new PIXI.AlphaFilter();
|
||||
return blendingModeFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
|
||||
if (parameterName === 'alpha') {
|
||||
blendingModeFilter.alpha = value;
|
||||
@@ -15,8 +19,16 @@ namespace gdjs {
|
||||
blendingModeFilter.blendMode = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Blur',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const blur = new PIXI.BlurFilter();
|
||||
return blur;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (
|
||||
parameterName !== 'blur' &&
|
||||
parameterName !== 'quality' &&
|
||||
@@ -21,8 +25,16 @@ namespace gdjs {
|
||||
}
|
||||
filter[parameterName] = value;
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,13 +2,17 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Brightness',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const brightness = new PIXI.ColorMatrixFilter();
|
||||
brightness.brightness(1, false);
|
||||
return brightness;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
|
||||
if (parameterName !== 'brightness') {
|
||||
return;
|
||||
@@ -18,8 +22,16 @@ namespace gdjs {
|
||||
false
|
||||
);
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'BulgePinch',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const bulgePinchFilter = new PIXI.filters.BulgePinchFilter();
|
||||
return bulgePinchFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
|
||||
if (parameterName === 'centerX') {
|
||||
bulgePinchFilter.center[0] = value;
|
||||
@@ -23,8 +27,16 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'ColorMap',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const colorMapTexture = target
|
||||
.getRuntimeScene()
|
||||
.getGame()
|
||||
@@ -19,8 +19,12 @@ namespace gdjs {
|
||||
);
|
||||
return colorMapFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
|
||||
if (parameterName === 'mix') {
|
||||
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
|
||||
@@ -30,8 +34,16 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
|
||||
if (parameterName === 'nearest') {
|
||||
colorMapFilter.nearest = value;
|
||||
|
@@ -2,18 +2,26 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'ColorReplace',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const colorReplaceFilter = new PIXI.filters.ColorReplaceFilter();
|
||||
return colorReplaceFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
|
||||
if (parameterName === 'epsilon') {
|
||||
colorReplaceFilter.epsilon = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {
|
||||
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
|
||||
if (parameterName === 'originalColor') {
|
||||
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
@@ -25,7 +33,11 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ namespace gdjs {
|
||||
crtFilter._animationTimer = 0;
|
||||
return crtFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
|
||||
if (filter.animationSpeed !== 0) {
|
||||
// Multiply by 10 so that the default value is a sensible speed
|
||||
filter.time +=
|
||||
@@ -23,7 +23,11 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName === 'lineWidth') {
|
||||
filter.lineWidth = value;
|
||||
} else if (parameterName === 'lineContrast') {
|
||||
@@ -48,8 +52,16 @@ namespace gdjs {
|
||||
filter.padding = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
if (parameterName === 'verticalLine') {
|
||||
filter.verticalLine = value;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Displacement',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const displacementMapTexture = target
|
||||
.getRuntimeScene()
|
||||
.getGame()
|
||||
@@ -15,8 +15,12 @@ namespace gdjs {
|
||||
);
|
||||
return displacementFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
|
||||
if (parameterName === 'scaleX') {
|
||||
displacementFilter.scale.x = value;
|
||||
@@ -25,8 +29,16 @@ namespace gdjs {
|
||||
displacementFilter.scale.y = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Dot',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const dotFilter = new PIXI.filters.DotFilter();
|
||||
return dotFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
|
||||
if (parameterName === 'scale') {
|
||||
dotFilter.scale = value;
|
||||
@@ -15,8 +19,16 @@ namespace gdjs {
|
||||
dotFilter.angle = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'DropShadow',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const dropShadowFilter = new PIXI.filters.DropShadowFilter();
|
||||
return dropShadowFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
|
||||
if (parameterName === 'blur') {
|
||||
dropShadowFilter.blur = value;
|
||||
@@ -23,7 +27,11 @@ namespace gdjs {
|
||||
dropShadowFilter.padding = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {
|
||||
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
|
||||
if (parameterName === 'color') {
|
||||
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
@@ -31,7 +39,11 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(filter, parameterName, value) {
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
|
||||
if (parameterName === 'shadowOnly') {
|
||||
dropShadowFilter.shadowOnly = value;
|
||||
|
@@ -9,7 +9,7 @@ namespace gdjs {
|
||||
glitchFilter._animationTimer = 0;
|
||||
return glitchFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
|
||||
if (filter.animationFrequency !== 0) {
|
||||
filter._animationTimer += target.getElapsedTime() / 1000;
|
||||
if (filter._animationTimer >= 1 / filter.animationFrequency) {
|
||||
@@ -18,7 +18,11 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName === 'slices') {
|
||||
filter.slices = value;
|
||||
} else if (parameterName === 'offset') {
|
||||
@@ -47,8 +51,16 @@ namespace gdjs {
|
||||
filter.animationFrequency = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
if (parameterName === 'average') {
|
||||
filter.average = value;
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Glow',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const glowFilter = new PIXI.filters.GlowFilter();
|
||||
return glowFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
|
||||
if (parameterName === 'innerStrength') {
|
||||
glowFilter.innerStrength = value;
|
||||
@@ -18,13 +22,21 @@ namespace gdjs {
|
||||
glowFilter.distance = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {
|
||||
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
|
||||
if (parameterName === 'color') {
|
||||
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -8,13 +8,17 @@ namespace gdjs {
|
||||
const godrayFilter = new PIXI.filters.GodrayFilter();
|
||||
return godrayFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
|
||||
if (filter.animationSpeed !== 0) {
|
||||
filter.time +=
|
||||
(target.getElapsedTime() / 1000) * filter.animationSpeed;
|
||||
}
|
||||
}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName === 'lacunarity') {
|
||||
filter.lacunarity = value;
|
||||
} else if (parameterName === 'angle') {
|
||||
@@ -33,8 +37,16 @@ namespace gdjs {
|
||||
filter.padding = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
if (parameterName === 'parallel') {
|
||||
filter.parallel = value;
|
||||
}
|
||||
|
43
Extensions/Effects/hsl-adjustment-pixi-filter.ts
Normal file
43
Extensions/Effects/hsl-adjustment-pixi-filter.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'HslAdjustment',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const hslAdjustmentFilter = new PIXI.filters.HslAdjustmentFilter();
|
||||
return hslAdjustmentFilter;
|
||||
}
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const hslAdjustmentFilter = filter as PIXI.filters.HslAdjustmentFilter;
|
||||
if (parameterName === 'hue') {
|
||||
hslAdjustmentFilter.hue = value;
|
||||
} else if (parameterName === 'saturation') {
|
||||
hslAdjustmentFilter.saturation = value;
|
||||
} else if (parameterName === 'lightness') {
|
||||
hslAdjustmentFilter.lightness = value;
|
||||
} else if (parameterName === 'alpha') {
|
||||
hslAdjustmentFilter.alpha = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {
|
||||
const hslAdjustmentFilter = filter as PIXI.filters.HslAdjustmentFilter;
|
||||
if (parameterName === 'colorize') {
|
||||
hslAdjustmentFilter.colorize = value;
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
@@ -2,27 +2,37 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'KawaseBlur',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const kawaseBlurFilter = new PIXI.filters.KawaseBlurFilter();
|
||||
return kawaseBlurFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
|
||||
if (parameterName === 'pixelizeX') {
|
||||
// @ts-ignore: fix these wrong parameters
|
||||
kawaseBlurFilter.pixelizeX = value;
|
||||
kawaseBlurFilter.pixelSize[0] = value;
|
||||
} else if (parameterName === 'pixelizeY') {
|
||||
// @ts-ignore: fix these wrong parameters
|
||||
kawaseBlurFilter.pixelizeY = value;
|
||||
kawaseBlurFilter.pixelSize[1] = value;
|
||||
} else if (parameterName === 'blur') {
|
||||
kawaseBlurFilter.blur = value;
|
||||
} else if (parameterName === 'quality') {
|
||||
kawaseBlurFilter.quality = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -24,19 +24,31 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'LightNight',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const filter = new gdjs.LightNightPixiFilter();
|
||||
return filter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName !== 'opacity') {
|
||||
return;
|
||||
}
|
||||
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
40
Extensions/Effects/motion-blur-pixi-filter.ts
Normal file
40
Extensions/Effects/motion-blur-pixi-filter.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'MotionBlur',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const motionBlurFilter = new PIXI.filters.MotionBlurFilter([0, 0]);
|
||||
return motionBlurFilter;
|
||||
}
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const motionBlurFilter = filter as PIXI.filters.MotionBlurFilter;
|
||||
if (parameterName === 'velocityX') {
|
||||
// @ts-ignore Using the private member avoids to instantiate Arrays.
|
||||
motionBlurFilter._velocity.x = value;
|
||||
} else if (parameterName === 'velocityY') {
|
||||
// @ts-ignore Using the private member avoids to instantiate Arrays.
|
||||
motionBlurFilter._velocity.y = value;
|
||||
} else if (parameterName === 'kernelSize') {
|
||||
motionBlurFilter.kernelSize = value;
|
||||
} else if (parameterName === 'offset') {
|
||||
motionBlurFilter.offset = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
@@ -28,12 +28,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Night',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const filter = new gdjs.NightPixiFilter();
|
||||
return filter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName !== 'intensity' && parameterName !== 'opacity') {
|
||||
return;
|
||||
}
|
||||
@@ -43,8 +47,16 @@ namespace gdjs {
|
||||
1
|
||||
);
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,20 +2,32 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Noise',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const noiseFilter = new PIXI.NoiseFilter();
|
||||
return noiseFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
|
||||
if (parameterName !== 'noise') {
|
||||
return;
|
||||
}
|
||||
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ namespace gdjs {
|
||||
oldFilmFilter._animationTimer = 0;
|
||||
return oldFilmFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
|
||||
if (filter.animationFrequency !== 0) {
|
||||
filter._animationTimer += target.getElapsedTime() / 1000;
|
||||
if (filter._animationTimer >= 1 / filter.animationFrequency) {
|
||||
@@ -18,7 +18,11 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
if (parameterName === 'sepia') {
|
||||
filter.sepia = value;
|
||||
} else if (parameterName === 'noise') {
|
||||
@@ -41,8 +45,16 @@ namespace gdjs {
|
||||
filter.animationFrequency = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Outline',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const outlineFilter = new PIXI.filters.OutlineFilter();
|
||||
return outlineFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
|
||||
if (parameterName === 'thickness') {
|
||||
outlineFilter.thickness = value;
|
||||
@@ -15,7 +19,11 @@ namespace gdjs {
|
||||
outlineFilter.padding = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {
|
||||
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
|
||||
if (parameterName === 'color') {
|
||||
outlineFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
@@ -23,7 +31,11 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -2,21 +2,33 @@ namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Pixelate',
|
||||
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
|
||||
makePIXIFilter(target, effectData) {
|
||||
makePIXIFilter(target: EffectsTarget, effectData) {
|
||||
const pixelateFilter = new PIXI.filters.PixelateFilter(
|
||||
effectData.doubleParameters.size
|
||||
);
|
||||
return pixelateFilter;
|
||||
}
|
||||
updatePreRender(filter, target) {}
|
||||
updateDoubleParameter(filter, parameterName, value) {
|
||||
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
|
||||
updateDoubleParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: number
|
||||
) {
|
||||
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter;
|
||||
if (parameterName === 'size') {
|
||||
pixelateFilter.size = value;
|
||||
}
|
||||
}
|
||||
updateStringParameter(filter, parameterName, value) {}
|
||||
updateBooleanParameter(filter, parameterName, value) {}
|
||||
updateStringParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: string
|
||||
) {}
|
||||
updateBooleanParameter(
|
||||
filter: PIXI.Filter,
|
||||
parameterName: string,
|
||||
value: boolean
|
||||
) {}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// This filter is probably made useless by advanced-bloom.
|
||||
/*!
|
||||
* @pixi/filter-bloom - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// TODO This filter can be interesting to make a gradient shadow over sprites.
|
||||
// It can work with only 2 colors but maybe it would be better to wait that property list are handled.
|
||||
/*!
|
||||
* @pixi/filter-color-gradient - v5.2.0
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// It doesn't seem very useful.
|
||||
/*!
|
||||
* @pixi/filter-color-overlay - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// It's probably too complicated to use.
|
||||
/*!
|
||||
* @pixi/filter-convolution - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// This filter is maybe too much specific.
|
||||
/*!
|
||||
* @pixi/filter-cross-hatch - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// It probably needs to be blend to be useful.
|
||||
/*!
|
||||
* @pixi/filter-emboss - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// There is already a black-and-white effect that has more features.
|
||||
/*!
|
||||
* @pixi/filter-grayscale - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// This filter doesn't seem useful and need property list.
|
||||
/*!
|
||||
* @pixi/filter-multi-color-replace - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// This filter is probably not very useful.
|
||||
/*!
|
||||
* @pixi/filter-simple-lightmap - v5.1.1
|
||||
* Compiled Thu, 31 Aug 2023 09:18:38 UTC
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user