Compare commits

...

71 Commits

Author SHA1 Message Date
D8H
402e54f706 Fix the Ease expression of the Tween extension (#5810)
Don't show in changelog
2023-10-20 11:07:19 +02:00
github-actions[bot]
dc29c27272 Update translations [skip ci] (#5803) 2023-10-20 09:01:47 +02:00
Florian Rival
462bb84c51 Fix exporting not shown on iOS (#5808) 2023-10-19 17:26:25 +02:00
AlexandreS
a775e79bae Catch instance rendering process in scene editor (#5806)
Don't show in changelog
2023-10-19 16:32:57 +02:00
Florian Rival
bc8c867f23 Fix parameters not accessible in expressions for 'Action With Operator' functions (#5805) 2023-10-19 15:55:55 +02:00
AlexandreS
f34bf2b7b4 Focus confirm delete button (#5804)
Don't show in changelog
2023-10-19 15:52:18 +02:00
Florian Rival
e2d482e1aa Fix long formulas/expressions going out of the screen in the events sheet 2023-10-19 14:51:21 +02:00
AlexandreS
0724ae34d9 Use electron remote shell to open project folder (#5801)
Don't show in changelog
2023-10-19 14:47:31 +02:00
github-actions[bot]
2fc1c2fd86 Update translations [skip ci] (#5774) 2023-10-19 12:19:58 +02:00
D8H
9a42ae11ad Tween extension rework (#5710)
Fix bugs and change implementation for a better maintainability.
2023-10-19 11:24:32 +02:00
Florian Rival
32773e06f6 Improve error message when exception at runtime (#5800) 2023-10-19 10:20:08 +02:00
Florian Rival
00508df014 Don't show an undeclared variable as an error in action/condition editor 2023-10-18 17:22:07 +02:00
Florian Rival
c3cf5b7002 Display an error message in game when an exception/crash is detected (#5799) 2023-10-18 17:19:11 +02:00
AlexandreS
df7fab84b8 Bump newIDE version (#5798) 2023-10-18 16:39:32 +02:00
AlexandreS
95a0012c5e Load texture in runtime even if filename does not have an extension (#5797)
Don't show in changelog
2023-10-18 16:38:25 +02:00
AlexandreS
4bf644f9f2 Add "Help > About GDevelop" menu option for non-Mac users (#5796)
---------

Co-authored-by: Tristan Rhodes <tristan.rhodes@gmail.com>
2023-10-18 13:01:02 +02:00
AlexandreS
79cad1da3a Fix error message not cleared after choosing a color in a color picker (#5794) 2023-10-18 12:47:57 +02:00
AlexandreS
ffc6f5786b Do not clear selection when panning with middle mouse button (#5795) 2023-10-18 12:24:08 +02:00
Florian Rival
07df65f8c2 Fix a potential crash by ensuring selection of instances does not contain deleted global objects (#5791)
- Also keep the selection of objects/folders when changing between scene tabs (unless they have been deleted)
2023-10-18 11:12:19 +02:00
D8H
61f8f0ed66 Fix Bitmap text resource action parameter type (#5793) 2023-10-18 10:55:20 +02:00
D8H
c24e2e5ace Use similar wording for angle conditions (#5792)
* Don't show in changelog
2023-10-17 18:45:17 +02:00
D8H
25c72a6d1f Optimize evaluation of several forces on 1 object (#5789) 2023-10-17 17:09:27 +02:00
AlexandreS
6153b23e7d Improve data sent to automatic error report (#5790)
Also, attempt to fix a crash at GDevelop editor opening.
2023-10-17 16:48:47 +02:00
Florian Rival
1b7ccfded3 Fix warning 2023-10-17 14:00:37 +02:00
Florian Rival
ec57a0a80d Improve the Share/Publish dialog: larger window, clearer sections and wording (#5785) 2023-10-17 11:28:29 +02:00
D8H
95a9e37aba Add animation time control for sprites and 3D models (#5777) 2023-10-17 10:02:49 +02:00
D8H
787274f7fa Fix 3D distance sorting issues by using a higher default near plane (#5784) 2023-10-17 09:55:23 +02:00
D8H
ceb24f0edb Unify text expressions conditions and actions between objects (#5778) 2023-10-16 18:17:50 +02:00
D8H
028bf7a70d Fix Physics behavior forces effect under 60 fps (#5783) 2023-10-16 17:50:52 +02:00
AlexandreS
08471d0356 Use componentWillReceiveProps rather than componentWillUpdate (#5782)
Don't show in changelog
2023-10-16 17:15:27 +02:00
AlexandreS
921add6665 Organize your objects into folders (#5655)
You can now create folders as a means to organize your objects when editing a scene or an external layout.

Thanks to @Silver-Streak, Matt Pin, @arthuro555, SnailMail, @TheGemDev, Peeble, Rasterisko for all the help designing and testing the feature.

Note that object tags have been dropped in this version since they were not practical to use thus not widespread used.
2023-10-16 17:04:20 +02:00
AlexandreS
dabff06891 Add error message when trying to login without connection to internet (#5780) 2023-10-16 11:37:43 +02:00
AlexandreS
967033c407 Fix scene loading for video resources (#5772)
Listen error event on base texture loading
Also makes file watcher listen on addition and removal of files.
2023-10-13 15:03:57 +02:00
Florian Rival
0809749ddc Hide expressions GetArgumentAsString and GetArgumentAsNumber as they are now replaced by just being able to write the parameter name in an expression (#5773) 2023-10-13 10:14:28 +02:00
github-actions[bot]
8a27801355 Update translations [skip ci] (#5766)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-10-12 18:30:18 +02:00
AlexandreS
20cba9d7ed Fix linter and types (#5770)
Don't show in changelog
2023-10-12 18:00:31 +02:00
AlexandreS
3878b93c94 Fix: Prevent loading of forbidden GIF file from crashing a game loading screen (#5769) 2023-10-12 17:01:10 +02:00
D8H
645d5331cb Add a setting to show warnings about deprecated instructions (#5755) 2023-10-12 16:55:02 +02:00
AlexandreS
91db750632 Reload resources in the editor when the resource changes (#5631)
GDevelop now refreshes resources (images, videos, 3D models, fonts, etc.) when a change is detected on the filesystem so that you can immediately see the changes.

This allows to efficiently use GDevelop alongside other tools (Tiled or LDtk for instance).

Notes:
- This logically only affects the desktop version for projects saved on the filesystem;
- You can deactivate it in the preferences.
2023-10-12 16:01:01 +02:00
Clément Pasteau
740485b54a Activate support for unicode characters (including emojis) for all names (objects, groups, variables, functions, parameters) on all projects (#5703) 2023-10-12 15:25:37 +02:00
Florian Rival
e960fe7c5b Fix unicode/emojis not supported in function parameter names (#5768) 2023-10-12 15:20:12 +02:00
AlexandreS
0633c7b474 Fix: Prevent root variable from having white spaces (#5767) 2023-10-12 15:13:18 +02:00
AlexandreS
704bfd40cb Fix example opening wrongfully opening "Create from scratch" dialog (#5765) 2023-10-12 14:34:24 +02:00
github-actions[bot]
310abe7a13 Update translations [skip ci] (#5759)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-10-12 14:11:12 +02:00
Daniel R
b0f9bed273 Add string condition operators: starts with, ends with and contains (#4970)
- Thanks @danired
2023-10-12 13:33:32 +02:00
Clément Pasteau
766a62ad9b Invalidate cache when updating Panel Sprite opacity (#5764)
* This fixes a bug where creating a panel sprite with low opacity would prevent changing its opacity afterwards
2023-10-12 13:27:08 +02:00
D8H
ec6f669d94 Add HSL adjustment, motion blur and shockwave effects (#5760) 2023-10-12 10:49:29 +02:00
D8H
36a2408be0 Fix particles angle when the speed is negative (#5762) 2023-10-12 10:48:23 +02:00
Clément Pasteau
1343210a2b Fix "Locate file" for resource to open the folder in front of the app (#5758) 2023-10-11 15:52:27 +02:00
github-actions[bot]
1e76fedd7b Update translations [skip ci] (#5757)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-11 15:15:25 +02:00
Clément Pasteau
3923641988 Bump to 5.2.176 (#5756) 2023-10-11 10:42:18 +02:00
github-actions[bot]
a7d488626c Update translations [skip ci] (#5751)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-11 10:42:04 +02:00
Clément Pasteau
33f20b9bde Fix adding multiple assets after navigating into folder, correctly taking the assets into account (#5752) 2023-10-11 10:23:03 +02:00
AlexandreS
6c4cd6343f Fix Ctrl+Z closing project on Azerty keyboards (#5753)
Use event which property rather than code property when possible
2023-10-11 10:03:56 +02:00
Clément Pasteau
f1c04df0ee Do not sort animations by name in the Animations Dropdown for consistency (#5754) 2023-10-10 18:49:44 +02:00
D8H
9b466ff574 Fix particle emitter rotation speed (#5747) 2023-10-10 18:42:28 +02:00
Clément Pasteau
2b0969600e Deactivate accessibility in the editor too (#5750)
Do not show in changelog
2023-10-10 17:19:32 +02:00
github-actions[bot]
7ed1eee8f9 Update translations [skip ci] (#5748)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-10 17:11:17 +02:00
Clément Pasteau
9b9b729b42 Improve error message and component when crash (#5749)
Do not show in changelog
2023-10-10 15:27:23 +02:00
github-actions[bot]
530969d4b0 Update translations [skip ci] (#5735)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-10-10 14:19:42 +02:00
Clément Pasteau
11ef87b9ee Force window events to not be passive, so they can be prevented on mobile. (#5745)
This fixes a bug where mouse events where triggered on mobile, introducing unwanted interactions.
2023-10-10 14:17:16 +02:00
Clément Pasteau
e1857e2e1e Deactivate PixiJS accessibility plugin (#5746)
* For developer changelog
* Better remove it, as this isn't used and adds unnecessary elements in the Dom.
2023-10-10 14:16:57 +02:00
Florian Rival
d1385e5fc2 Fix completions in expressions not being case insensitive and limited for objects/variables/properties/parameters (#5742) 2023-10-10 10:48:33 +02:00
AlexandreS
9298026179 Add possibility to set Z offset when creating instances from external layout (#5704) 2023-10-10 09:40:10 +02:00
Florian Rival
913e367f6f Allow to properly use variables/properties/parameters in brackets to access a structure variable (#5738)
* This means expressions like `MyStructure[SomeIndexVariable]` will work properly for a structure if `SomeIndexVariable` is a string variable. Gdevelop now properly uses the type of the variable/property/parameter (to avoid considering what's inside the brackets as a number if it's a string). Remember to declare your variables in the variables editor so that you can use them in expressions.
2023-10-09 18:21:49 +02:00
AlexandreS
1bc27bdd8b Fix shape painter drawing stars crashing games 2023-10-09 10:55:04 +02:00
Florian Rival
4d8b39df95 Add more tests for new variable syntax
Don't show in changelog
2023-10-08 19:02:55 +02:00
Florian Rival
e6970319b9 Fix usage of string properties and parameters using the new simplified syntax (#5737)
* Properties or parameters holding a string where wrongly interpreted as a number using the new syntax.
2023-10-08 17:41:34 +02:00
Florian Rival
cdf21ebd4c Allow to use the new variable syntax for groups (#5730)
* You can write `MyObjectGroup.SomeVariable` in the expressions as long as all the objects of the groups have this variable declared in the objects variable editor.
2023-10-08 11:59:35 +02:00
Clément Pasteau
dc014e2ab0 Fix missing ID in simple text fields (#5728)
Do not show in changelog
2023-10-06 17:17:36 +02:00
Clément Pasteau
25509e1c7b Bump to 5.2.175 (#5731) 2023-10-06 17:11:16 +02:00
440 changed files with 38984 additions and 6181 deletions

View File

@@ -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_,

View File

@@ -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,

View File

@@ -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);

View File

@@ -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(

View File

@@ -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

View File

@@ -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"))

View File

@@ -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

View File

@@ -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

View File

@@ -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");
@@ -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",
@@ -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",
@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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(

View File

@@ -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)) {

View File

@@ -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;

View File

@@ -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(

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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.
}

View File

@@ -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;

View File

@@ -110,7 +110,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 +154,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.

View File

@@ -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

View 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

View 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

View File

@@ -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);
}

View File

@@ -17,7 +17,7 @@ EventsBasedObject::EventsBasedObject()
}
EventsBasedObject::~EventsBasedObject() {}
EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObject)
: AbstractEventsBasedEntity(_eventBasedObject) {
// TODO Add a copy constructor in ObjectsContainer.
@@ -30,14 +30,19 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
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");
AbstractEventsBasedEntity::UnserializeFrom(project, element);
UnserializeObjectsFrom(project, element.GetChild("objects"));
if (element.HasChild("objectsFolderStructure")) {
UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
}
AddMissingObjectsInRootFolder();
}
} // namespace gd

View File

@@ -19,7 +19,7 @@ Layer::Layer()
isLocked(false),
isLightingLayer(false),
followBaseLayerCamera(false),
camera3DNearPlaneDistance(0.1),
camera3DNearPlaneDistance(3),
camera3DFarPlaneDistance(10000),
camera3DFieldOfView(45),
ambientLightColorR(200),

View File

@@ -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) {

View File

@@ -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"));

View File

@@ -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,

View 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

View 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

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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);
};

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 &parameter) {
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);

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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. */

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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() {};

View File

@@ -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(

View File

@@ -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

View File

@@ -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") {
{

View File

@@ -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];

View File

@@ -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;

View File

@@ -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',

View File

@@ -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] */

View File

@@ -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'))

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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;

View File

@@ -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
) {}
})()
);
}

View File

@@ -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;
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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;
}

View 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;
}
}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View 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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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
) {}
})()
);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,63 @@
declare namespace PIXI.filters {
export interface HslAdjustmentFilterOptions {
hue: number;
saturation: number;
lightness: number;
colorize: boolean;
alpha: number;
}
/**
* @class
* @extends PIXI.Filter
* @see {@link https://www.npmjs.com/package/@pixi/filter-hsl-adjustment|@pixi/filter-hsl-adjustment}
* @see {@link https://www.npmjs.com/package/pixi-filters|pixi-filters}
*/
export class HslAdjustmentFilter extends PIXI.Filter {
private _hue;
/** Default values for options. */
static readonly defaults: HslAdjustmentFilterOptions;
/**
* @param options - The optional parameters of the filter.
* @param {number} [options.hue=0] - The amount of hue in degrees (-180 to 180)
* @param {number} [options.saturation=0] - The amount of color saturation (-1 to 1)
* @param {number} [options.lightness=0] - The amount of lightness (-1 to 1)
* @param {boolean} [options.colorize=false] - Whether to colorize the image
* @param {number} [options.alpha=1] - The amount of alpha (0 to 1)
*/
constructor(options?: Partial<HslAdjustmentFilterOptions>);
/**
* Hue (-180 to 180)
* @default 0
*/
get hue(): number;
set hue(value: number);
/**
* Alpha (0-1)
* @default 1
*/
get alpha(): number;
set alpha(value: number);
/**
* Colorize (render as a single color)
* @default false
*/
get colorize(): boolean;
set colorize(value: boolean);
/**
* Lightness (-1 to 1)
* @default 0
*/
get lightness(): number;
set lightness(value: number);
/**
* Saturation (-1 to 1)
* @default 0
*/
get saturation(): number;
set saturation(value: number);
}
}
declare module '@pixi/filter-hsl-adjustment' {
export import HslAdjustmentFilter = PIXI.filters.HslAdjustmentFilter;
export import HslAdjustmentFilterOptions = PIXI.filters.HslAdjustmentFilterOptions;
}

View File

@@ -1,20 +1,84 @@
declare namespace PIXI.filters {
export type PointLike = PIXI.Point | number[];
interface ShockwaveFilterOptions {
amplitude: number;
wavelength: number;
speed: number;
brightness: number;
radius: number;
}
/**
* The ShockwaveFilter class lets you apply a shockwave effect.<br>
*
* @class
* @extends PIXI.Filter
* @see {@link https://www.npmjs.com/package/@pixi/filter-shockwave|@pixi/filter-shockwave}
* @see {@link https://www.npmjs.com/package/pixi-filters|pixi-filters}
*/
export class ShockwaveFilter extends PIXI.Filter {
/** Default constructor options. */
static readonly defaults: ShockwaveFilterOptions;
/**
* Sets the elapsed time of the shockwave.
* It could control the current size of shockwave.
*/
time: number;
/**
* @param {PIXI.Point|number[]} [center=[0.5, 0.5]] - See `center` property.
* @param {object} [options] - The optional parameters of shockwave filter.
* @param {number} [options.amplitude=0.5] - See `amplitude`` property.
* @param {number} [options.wavelength=1.0] - See `wavelength` property.
* @param {number} [options.speed=500.0] - See `speed` property.
* @param {number} [options.brightness=8] - See `brightness` property.
* @param {number} [options.radius=4] - See `radius` property.
* @param {number} [time=0] - See `time` property.
*/
constructor(
center?: PIXI.Point | number[],
options?: ShockwaveFilterOptions,
center?: PointLike,
options?: Partial<ShockwaveFilterOptions>,
time?: number
);
center: PIXI.Point | number[];
options: ShockwaveFilterOptions;
time: number;
}
export interface ShockwaveFilterOptions {
amplitude?: number;
wavelength?: number;
brightness?: number;
speed?: number;
radius?: number;
apply(
filterManager: PIXI.FilterSystem,
input: PIXI.RenderTexture,
output: PIXI.RenderTexture,
clear: PIXI.CLEAR_MODES
): void;
/**
* Sets the center of the shockwave in normalized screen coords. That is
* (0,0) is the top-left and (1,1) is the bottom right.
*
* @member {PIXI.Point|number[]}
*/
get center(): PointLike;
set center(value: PointLike);
/**
* The amplitude of the shockwave.
*/
get amplitude(): number;
set amplitude(value: number);
/**
* The wavelength of the shockwave.
*/
get wavelength(): number;
set wavelength(value: number);
/**
* The brightness of the shockwave.
*/
get brightness(): number;
set brightness(value: number);
/**
* The speed about the shockwave ripples out.
* The unit is `pixel/second`
*/
get speed(): number;
set speed(value: number);
/**
* The maximum radius of shockwave.
* `< 0.0` means it's infinity.
*/
get radius(): number;
set radius(value: number);
}
}

View File

@@ -2,11 +2,11 @@ namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'RadialBlur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
makePIXIFilter(target: EffectsTarget, effectData) {
const radialBlurFilter = new PIXI.filters.RadialBlurFilter();
return radialBlurFilter;
}
updatePreRender(filter, target) {
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter;
radialBlurFilter.center[0] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
@@ -17,7 +17,11 @@ namespace gdjs {
radialBlurFilter._centerY * target.getHeight()
);
}
updateDoubleParameter(filter, parameterName, value) {
updateDoubleParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter;
if (parameterName === 'radius') {
radialBlurFilter.radius = value < 0 ? -1 : value;
@@ -39,8 +43,16 @@ namespace gdjs {
radialBlurFilter.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
) {}
})()
);
}

View File

@@ -25,13 +25,17 @@ namespace gdjs {
);
return reflectionFilter;
}
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 === 'boundary') {
filter.boundary = value;
}
@@ -57,8 +61,16 @@ namespace gdjs {
filter.animationSpeed = 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 === 'mirror') {
filter.mirror = value;
}

View File

@@ -2,12 +2,16 @@ namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'RGBSplit',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
makePIXIFilter(target: EffectsTarget, effectData) {
const rgbSplitFilter = new PIXI.filters.RGBSplitFilter();
return rgbSplitFilter;
}
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
updateDoubleParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
) {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
if (parameterName === 'redX') {
rgbSplitFilter.red.x = value;
@@ -23,8 +27,16 @@ namespace gdjs {
rgbSplitFilter.blue.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
) {}
})()
);
}

View File

@@ -2,21 +2,33 @@ namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Sepia',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
makePIXIFilter(target: EffectsTarget, effectData) {
const colorMatrixFilter = new PIXI.ColorMatrixFilter();
colorMatrixFilter.sepia(false);
return colorMatrixFilter;
}
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
updateDoubleParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
) {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
}
colorMatrixFilter.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
) {}
})()
);
}

View File

@@ -0,0 +1,61 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Shockwave',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target: EffectsTarget, effectData) {
const shockwaveFilter = new PIXI.filters.ShockwaveFilter([0.5, 0.5]);
return shockwaveFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const shockwaveFilter = (filter as unknown) as PIXI.filters.ShockwaveFilter;
if (shockwaveFilter.speed !== 0) {
shockwaveFilter.time += target.getElapsedTime() / 1000;
}
shockwaveFilter.center[0] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
shockwaveFilter._centerX * target.getWidth()
);
shockwaveFilter.center[1] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
shockwaveFilter._centerY * target.getHeight()
);
}
updateDoubleParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
) {
const shockwaveFilter = filter as PIXI.filters.ShockwaveFilter;
if (parameterName === 'centerX') {
// @ts-ignore - extra properties are stored on the filter.
shockwaveFilter._centerX = value;
} else if (parameterName === 'centerY') {
// @ts-ignore - extra properties are stored on the filter.
shockwaveFilter._centerY = value;
} else if (parameterName === 'time') {
shockwaveFilter.time = value;
} else if (parameterName === 'speed') {
shockwaveFilter.speed = value;
} else if (parameterName === 'amplitude') {
shockwaveFilter.amplitude = value;
} else if (parameterName === 'wavelength') {
shockwaveFilter.wavelength = value;
} else if (parameterName === 'brightness') {
shockwaveFilter.brightness = value;
} else if (parameterName === 'radius') {
shockwaveFilter.radius = value;
}
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,
value: boolean
) {}
})()
);
}

View File

@@ -2,12 +2,16 @@ namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'TiltShift',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
makePIXIFilter(target: EffectsTarget, effectData) {
const tiltShiftFilter = new PIXI.filters.TiltShiftFilter();
return tiltShiftFilter;
}
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {}
updateDoubleParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
) {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
if (parameterName === 'blur') {
tiltShiftFilter.blur = value;
@@ -15,8 +19,16 @@ namespace gdjs {
tiltShiftFilter.gradientBlur = 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
) {}
})()
);
}

Some files were not shown because too many files have changed in this diff Show More