Compare commits

..

97 Commits

Author SHA1 Message Date
AlexandreS
0a6b0dc785 Use cloud storage provider internal name (#6108)
Don't show in changelog
2023-12-20 12:15:04 +01:00
github-actions[bot]
02e99726dc Update translations [skip ci] (#6103)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-20 11:55:13 +01:00
AlexandreS
843055d8df Add error message when versions fail to load (#6105)
Don't show in changelog
2023-12-20 11:40:07 +01:00
AlexandreS
f7fda5cb5e Fix placeholder thumbnail in objects lists for prefabs (#6107) 2023-12-20 11:37:28 +01:00
AlexandreS
e529642aec Fix program opening count logic when user has never acknowledged the feature (#6106)
Don't show in changelog
2023-12-20 10:01:35 +01:00
AlexandreS
c8e10d7043 Bump newIDE version (#6104) 2023-12-19 12:25:29 +01:00
AlexandreS
5f0de0e9a7 Adapt extract changelog script to currently used format (#6102)
Only show in developer changelog
2023-12-19 12:18:25 +01:00
github-actions[bot]
e51638ce4b Update translations [skip ci] (#6101)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-19 12:17:54 +01:00
AlexandreS
0fbd6a606a Add possibility to restore a previous version of a cloud project (#6022)
- Display all the project versions (paginated)
- Allow to open any version and name it for better tracking
- Make it possible to restore a given version
2023-12-19 11:50:28 +01:00
github-actions[bot]
2fa543c3db Update translations [skip ci] (#6083)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-18 16:56:41 +01:00
AlexandreS
38e35c9695 Prevent crash on new object dialog (#6099) 2023-12-18 16:50:21 +01:00
Florian Rival
ad13a1a101 Fix changes done in an extension not applied when clicking on 'Share' immediately without a preview or navigation in the editor (#6098) 2023-12-18 16:02:27 +01:00
AlexandreS
cb9d98d027 Wait 10 program openings before displaying GamesDashboard info (#6097)
Don't show in changelog
2023-12-18 15:51:23 +01:00
D8H
064c3f1572 Fix warning in the object list (#6091)
- don't show in changelog
2023-12-15 18:46:40 +01:00
D8H
9e5320f9d4 Add an item menu to add sub-folders in object folders (#6090) 2023-12-15 17:56:19 +01:00
Clément Pasteau
a1826d355d Rework subscriptions display in Profile (#6089) 2023-12-15 17:27:48 +01:00
D8H
32a3a094d1 Add new objects under the selected one in the list (#6087) 2023-12-15 16:51:50 +01:00
D8H
ee7dc2654b Fix parameter value choices autocompletion (#6086) 2023-12-15 12:02:24 +01:00
Clément Pasteau
64ffad3c0a Bump to 5.3.185 (#6082) 2023-12-14 14:06:01 +01:00
github-actions[bot]
dcc62f078f Update translations [skip ci] (#6076)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-14 13:54:43 +01:00
D8H
d920f05dbc Fix animation scanning in the 3D model object editor (#6080) 2023-12-14 13:28:38 +01:00
AlexandreS
ac82be800b Fix: update file metadata when saving cloud project triggers new commit (#6074) 2023-12-13 16:50:50 +01:00
Aurélien Vivet
2c92ae4042 Fix typo (#6075) 2023-12-13 16:35:36 +01:00
Clément Pasteau
465e934605 Bump version to 5.3.184 (#6072) 2023-12-13 14:34:04 +01:00
github-actions[bot]
248ba7675e Update translations [skip ci] (#6070)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-13 14:19:15 +01:00
AlexandreS
fd2b59ba45 Revert "Use python3 to update bindings (#6068)" (#6071)
This reverts commit 6edf63e98f.

Only show in developer changelog
2023-12-13 12:01:14 +01:00
Clément Pasteau
be54236ece Fix using which from key event when not defined (#6069)
Do not show in changelog
2023-12-13 11:59:57 +01:00
AlexandreS
6edf63e98f Use python3 to update bindings (#6068)
Only show in developer changelog
2023-12-13 11:36:44 +01:00
github-actions[bot]
034f1ad9cc Update translations [skip ci] (#6057)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-12-12 15:42:47 +01:00
Florian Rival
94b8c31ac2 Allow to increment position of points of sprites by 0.5 in the editor 2023-12-12 15:11:21 +01:00
Florian Rival
57d1241e2d Allow to increment position of vertices of collision masks by 0.5 2023-12-12 15:08:50 +01:00
github-actions[bot]
eb4708ca87 Update translations [skip ci] (#6051)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-11 14:47:52 +01:00
D8H
fac710780b Fix a flash of 1 black frame the first time a scene is started (#6048) 2023-12-11 14:41:27 +01:00
github-actions[bot]
d28aac325a Update translations [skip ci] (#6033)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-11 14:18:26 +01:00
Clément Pasteau
306b341ee5 Education premium users now also have a dedicated Discord channel (#6047) 2023-12-11 14:17:09 +01:00
D8H
1d8e04cb78 Allow to set 3D models dimensions with a scaling factor (#6044)
- It allows to keep consistent dimensions for a set of 3D models
2023-12-11 12:13:17 +01:00
Clément Pasteau
3b2855de59 Fix a possible crash when closing an app window (#6046) 2023-12-11 10:29:10 +01:00
AlexandreS
d5c2982b2d Increase home page mobile menu icon sizes (#6034) 2023-12-08 10:58:05 +01:00
Clément Pasteau
315b7387b3 Bump version to 5.3.183 (#6032) 2023-12-07 16:21:12 +01:00
Clément Pasteau
f4f92566f4 Remove showing GDevelop templates first + increase visibility (#6031)
Do not show in changelog
2023-12-07 15:56:22 +01:00
github-actions[bot]
7b72d4e080 Update translations [skip ci] (#6030)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-07 15:56:06 +01:00
D8H
ae96ebf79c Fix heavy assets failing to download on slow connections (#6024) 2023-12-07 15:36:27 +01:00
D8H
be813a0271 Start games without any loading screen if assets are ready before half the fade-in (#6025)
- This only works if the watermark is enabled.
- It allows players to start a game a 2nd time almost instantly.
2023-12-07 15:21:30 +01:00
github-actions[bot]
b306d80915 Update translations [skip ci] (#6020)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-07 11:56:18 +01:00
AlexandreS
9baed02aa1 Improve project list refreshing UI/UX (#6026) 2023-12-07 11:43:29 +01:00
Clément Pasteau
1b4c5c1b1c Improve export copy & logic (#6023)
Do not show in changelog
2023-12-06 15:13:30 +01:00
Arthur Pacaud (arthuro555)
4f04190614 Add automatic generation of TypeScript types for Core (in addition to existing Flow types) (#5429)
Only show in developer changelog
2023-12-05 18:47:20 +01:00
AlexandreS
d3134ecde9 Bump newIDE version (#6019) 2023-12-05 17:13:32 +01:00
github-actions[bot]
61ed7ffa16 Update translations [skip ci] (#6015)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-12-05 16:43:36 +01:00
Clément Pasteau
8be1961d3f Throw if wrong response from project api (#6014)
Do not show in changelog
2023-12-05 09:59:00 +01:00
github-actions[bot]
112c306610 Update translations [skip ci] (#6001)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-05 09:44:27 +01:00
D8H
423f15b513 Add actions to tween effect properties (#5993) 2023-12-04 15:25:40 +01:00
D8H
a9c89b14c3 Use a drop-down list for object effect parameters (#6005) 2023-12-04 14:39:12 +01:00
TRP
bd898463f5 Add an action to draw a torus with the shape painter object (#5981)
- Thanks @trp02
2023-12-04 14:11:41 +01:00
D8H
ed4635664c Fix 3D light rotation angle calculus when Y is the top (#6004)
- In order to get back the same light result as before, 27° should be subtracted to the rotation angle.
2023-12-04 12:48:56 +01:00
Clément Pasteau
b6f25db40c Throw if badges or achievements not loading properly (#6002)
Do not show in changelog
2023-12-04 12:21:11 +01:00
Florian Rival
f78662be5f Reduce the number of tutorial progress analytics events (#5992)
Don't show in changelog
2023-12-04 11:24:37 +01:00
github-actions[bot]
7aae35a029 Update translations [skip ci] (#5980)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-04 11:18:46 +01:00
D8H
6c323614ef Fix object type declaration for 3D capabilities actions (#6000)
- Don't show in changelog
2023-12-04 11:03:35 +01:00
AlexandreS
eb4170df20 Fix suggestion to add missing object variable that is used in events (#5999) 2023-12-04 10:40:29 +01:00
D8H
8830bb93ae Force sounds to download even when "preload in cache" is unchecked (#5984)
- Don't show in changelog
2023-12-01 16:30:11 +01:00
Florian Rival
87fa0a39ac Fix warning 2023-12-01 15:29:19 +01:00
Florian Rival
a9cc911ca8 Use JSX for rendering in game debugger message (#5983)
Only show in developer changelog
2023-12-01 09:57:21 +01:00
AlexandreS
d3fe6cf532 Remove unused project version get api call (#5982)
Don't show in changelog
2023-12-01 08:16:43 +01:00
Florian Rival
e2c40ff205 Improve structure variables completion (#5978)
* When a structure variable name is entered in an expression, completions will also be provided for children variables.
2023-11-30 13:47:19 +01:00
github-actions[bot]
970d04b0df Update translations [skip ci] (#5972)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-11-30 09:26:31 +01:00
D8H
8c5076443c Fix property name case in action sentences (#5977) 2023-11-29 15:29:04 +01:00
D8H
3744e98065 Use the new syntax when generating expressions and actions for properties (#5976) 2023-11-29 15:20:38 +01:00
AlexandreS
b6a1332124 Add game dashboard on home page and project manager (#5963) 2023-11-29 11:12:29 +01:00
Clément Pasteau
0251997703 Prevent loading announcements in state if response is not an array (#5975)
Do not show in changelog
2023-11-29 10:34:50 +01:00
D8H
57faa9fb4a Move community tier extensions in their own index section (#5973)
- Don't show in changelog.
2023-11-28 20:34:06 +01:00
D8H
ba95f66ccd Fix LDtk tile map resources export with the fast loading (#5951)
- Don't show in changelog
2023-11-28 14:31:47 +01:00
github-actions[bot]
9465873dbd Update translations [skip ci] (#5943)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-11-28 14:17:13 +01:00
Clément Pasteau
b5a9fe4fe1 Update fling game with new trigger (#5971)
Do not show in changelog
2023-11-28 14:16:43 +01:00
D8H
6531e2e970 Use relative links in extensions reference pages (#5970)
Don't show in changelog
2023-11-28 12:59:39 +01:00
Florian Rival
4f900c9451 Enable auto-completion for structure and array variables when writing an expression (#5960) 2023-11-28 11:30:26 +01:00
Clément Pasteau
f97f267a96 Put the Fling Game tutorial back (#5967)
* It was unintentionally removed!
2023-11-28 11:19:07 +01:00
Clément Pasteau
6cd8f54869 Fix possible crash in the New Object Dialog (#5968) 2023-11-28 09:51:07 +01:00
Clément Pasteau
9718fb788e Better wording for Discord role perks (#5966)
Do not show in changelog
2023-11-27 18:40:32 +01:00
Clément Pasteau
38651edf3e Discord username can now be added to one's Profile, allowing to claim a role on GDevelop's server (#5962)
* If you have a Gold or Startup subscription, head down to your profile, to claim access to a premium channel on Discord, where you can discuss together and ask for support.
2023-11-27 16:54:27 +01:00
Florian Rival
d34f1a654f Improve navigation for extensions in the documentation (#5950) 2023-11-24 11:43:47 +01:00
D8H
5abc74b66b Remove only (#5946)
Don't show in changelog
2023-11-23 19:05:08 +01:00
Clément Pasteau
a848764318 Fix libGD.wasm not properly loaded on Electron local + build warnings (#5942)
Don't show in changelog
2023-11-23 15:55:31 +01:00
github-actions[bot]
c0c6fddcbb Update translations [skip ci] (#5937)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-11-23 09:49:51 +01:00
AlexandreS
95ac26f05d Add placeholder in menu when no recent project file (#5940) 2023-11-23 09:28:33 +01:00
D8H
1f852648ef Make games launch faster by loading resources in the background (#5572)
* Only the first scene and global objects resources (images, sounds, 3D models etc...) will be downloaded during launch of the game. This usually allows for a very fast loading time.
* Other scenes resources will continue to load in the background. It has no impact on the game performance as this is done on other threads by the browser or the engine running the game.
* Scenes are loaded in the order they are listed in the project manager.
* You can also use actions and expressions to prioritize a scene (if it's known that a level will be needed soon for example) or read the current loading progress. This allows to create lightweight scenes that can act as custom loading screens. Otherwise, the launch loading screen will be shown if a scene is still loading when launched.
* Read more about this on https://wiki.gdevelop.io/gdevelop5/all-features/resources-loading/.
2023-11-22 22:51:24 +01:00
Florian Rival
b7da4361c3 Fix some C++ warnings and improve GDevelop.js README
Don't show in changelog
2023-11-22 19:03:21 +01:00
Arthur Pacaud (arthuro555)
71b369d40e Update Emscripten version to 3.1.21 (#5636)
* Any developer working on the C++ codebase should follow again the [README](https://github.com/4ian/GDevelop/tree/master/GDevelop.js) to install latest Emscripten version and re-compile C++.

Only show in developer changelog
2023-11-22 17:19:13 +01:00
AlexandreS
4d8cf56922 Do not change homepage tab at opening if an item from the asset store is requested (#5936) 2023-11-22 16:29:44 +01:00
github-actions[bot]
1a6e0ba5a1 Update translations [skip ci] (#5918)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-11-22 14:04:18 +01:00
AlexandreS
ec1ebcbf5b Fix desktop app not opening on Mac (#5934)
Don't show in changelog
2023-11-22 11:48:07 +01:00
Arthur Pacaud (arthuro555)
4ee9ccd7a9 Allow using resources as behavior properties (#5256)
Only show in developer changelog
2023-11-22 11:17:35 +01:00
AlexandreS
1ac248bfa4 Replace parcel watcher with chokidar (#5932) 2023-11-22 09:35:29 +01:00
D8H
65b78d4db7 Fix duplication of the "create" action in the search results (#5930) 2023-11-21 18:14:35 +01:00
D8H
639d90d743 Move deprecated physics actions at the bottom of search results (#5925) 2023-11-21 18:13:38 +01:00
D8H
45d0a78656 Simplify the wording of some actions names (#5929) 2023-11-21 18:12:09 +01:00
Florian Rival
0a0811e355 Fix crash with 'Put the object around another' action when target object was not existing (#5931) 2023-11-21 17:11:37 +01:00
437 changed files with 27846 additions and 12755 deletions

View File

@@ -113,7 +113,8 @@
"memory_resource": "cpp",
"__bits": "cpp",
"__verbose_abort": "cpp",
"variant": "cpp"
"variant": "cpp",
"charconv": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,

View File

@@ -164,7 +164,7 @@ void LinkEvent::UnserializeFrom(gd::Project& project,
}
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
return BaseEvent::AcceptVisitor(eventVisitor) |
return BaseEvent::AcceptVisitor(eventVisitor) ||
eventVisitor.VisitLinkEvent(*this);
}

View File

@@ -277,8 +277,11 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
auto variable = gd::make_unique<VariableNode>(name);
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
}
variable->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
@@ -302,8 +305,12 @@ class GD_CORE_API ExpressionParser2 {
"bracket for each opening bracket."));
}
SkipIfChar(IsClosingSquareBracket);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
@@ -315,8 +322,15 @@ class GD_CORE_API ExpressionParser2 {
auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
auto child =
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
if (identifierAndLocation.name.empty()) {
child->diagnostic = RaiseSyntaxError(_("A name should be entered after the dot."));
}
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->nameLocation = identifierAndLocation.location;
child->dotLocation = dotLocation;
child->location =
@@ -325,7 +339,11 @@ class GD_CORE_API ExpressionParser2 {
return std::move(child);
}
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
// Should never happen, unless a node called this function without checking if the current character
// was a dot or an opening bracket - this means there is an error in the grammar.
auto unrecognisedNode = gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>();
unrecognisedNode->diagnostic = RaiseSyntaxError(_("A dot or bracket was expected here."));
return std::move(unrecognisedNode);
}
std::unique_ptr<FunctionCallNode> FreeFunction(
@@ -361,18 +379,24 @@ class GD_CORE_API ExpressionParser2 {
const auto &childIdentifierNameLocation =
childIdentifierAndLocation.location;
std::unique_ptr<gd::ExpressionParserError> emptyNameError = childIdentifierName.empty() ?
RaiseSyntaxError(_("A name should be entered after the dot.")) : nullptr;
SkipAllWhitespaces();
if (IsNamespaceSeparator()) {
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
return BehaviorFunction(parentIdentifier,
auto behaviorFunction = BehaviorFunction(parentIdentifier,
childIdentifierName,
parentIdentifierLocation,
parentIdentifierDotLocation,
childIdentifierNameLocation,
namespaceSeparatorLocation);
if (emptyNameError) behaviorFunction->diagnostic = std::move(emptyNameError);
return std::move(behaviorFunction);
} else if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
@@ -381,7 +405,7 @@ class GD_CORE_API ExpressionParser2 {
childIdentifierName);
auto parametersNode = Parameters(function.get(), parentIdentifier);
function->parameters = std::move(parametersNode.parameters),
function->diagnostic = std::move(parametersNode.diagnostic);
function->diagnostic = emptyNameError ? std::move(emptyNameError) : std::move(parametersNode.diagnostic);
function->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
@@ -394,6 +418,8 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
variable->diagnostic = std::move(emptyNameError);
auto child =
gd::make_unique<VariableAccessorNode>(childIdentifierName);
child->child = VariableAccessorOrVariableBracketAccessor();
@@ -419,6 +445,7 @@ class GD_CORE_API ExpressionParser2 {
node->identifierNameLocation = parentIdentifierLocation;
node->identifierNameDotLocation = parentIdentifierDotLocation;
node->childIdentifierNameLocation = childIdentifierNameLocation;
node->diagnostic = std::move(emptyNameError);
return std::move(node);
}
@@ -491,11 +518,6 @@ class GD_CORE_API ExpressionParser2 {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
gd::String lastObjectName = "";
// By convention, object is always the first parameter, and behavior the
// second one.
size_t parameterIndex =
WrittenParametersFirstIndex(objectName, behaviorName);
bool previousCharacterIsParameterSeparator = false;
while (!IsEndReached()) {
SkipAllWhitespaces();
@@ -514,7 +536,6 @@ class GD_CORE_API ExpressionParser2 {
SkipAllWhitespaces();
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
SkipIfChar(IsParameterSeparator);
parameterIndex++;
}
ExpressionParserLocation invalidClosingParenthesisLocation;

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
@@ -43,6 +44,11 @@ class GD_CORE_API ExpressionParser2NodePrinter
*/
const gd::String& GetOutput() { return output; };
static gd::String PrintStringLiteral(const gd::String& str) {
return "\"" +
str.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") + "\"";
}
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
output += "(";
@@ -69,10 +75,7 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitNumberNode(NumberNode& node) override { output += node.number; }
void OnVisitTextNode(TextNode& node) override {
output +=
"\"" +
node.text.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") +
"\"";
output += PrintStringLiteral(node.text);
}
void OnVisitVariableNode(VariableNode& node) override {
output += node.name;
@@ -97,8 +100,8 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
output +=
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
output += node.objectName + "." + node.objectFunctionOrBehaviorName +
"::" + node.behaviorFunctionName;
} else {
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
}

View File

@@ -548,7 +548,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"ChangeLayerTimeScale",
_("Change layer time scale"),
_("Layer time scale"),
_("Change the time scale applied to the objects of the layer."),
_("Set the time scale of layer _PARAM1_ to _PARAM2_"),
"",
@@ -594,7 +594,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"SetLayerAmbientLightColor",
_("Set the ambient light color"),
_("Ambient light color"),
_("Set the ambient light color of the lighting layer in format "
"\"R;G;B\" string."),
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),

View File

@@ -5,6 +5,7 @@
*/
#include "AllBuiltinExtensions.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
using namespace std;
namespace gd {
@@ -57,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
extension
.AddCondition("DoesSceneExist",
_("Does scene exist"),
_("Check if scene exists."),
_("Check if a scene exists."),
_("Scene _PARAM1_ exists"),
"",
"res/actions/texte.png",
@@ -163,6 +164,45 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
"res/actions/window.png")
.SetHelpPath("/interface/scene-editor/events")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddAction("PrioritizeLoadingOfScene",
_("Preload scene"),
_("Preload a scene resources as soon as possible in background."),
_("Preload scene _PARAM1_ in background"),
"",
"res/actions/hourglass_black.svg",
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Name of the new scene"))
.MarkAsAdvanced();
extension.AddExpressionAndCondition("number",
"SceneLoadingProgress",
_("Scene loading progress"),
_("The progress of resources loading in background for a scene (between 0 and 1)."),
_("_PARAM0_ loading progress"),
_(""),
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Scene name"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
extension
.AddCondition("AreSceneAssetsLoaded",
_("Scene preloaded"),
_("Check if scene resources have finished to load in background."),
_("Scene _PARAM1_ was preloaded in background"),
"",
"res/actions/hourglass_black.svg",
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Scene name"))
.MarkAsAdvanced();
}
} // namespace gd

View File

@@ -87,8 +87,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
extension
.AddAction(
"SetWindowSize",
_("Change the size of the game window"),
_("This action changes the size of the game window. Note that this "
_("Game window size"),
_("Changes the size of the game window. Note that this "
"will only work on platform supporting this operation: games "
"running in browsers or on mobile phones can not update their "
"window size. Game resolution can still be updated."),

View File

@@ -5,6 +5,7 @@
*/
#include "ExpressionMetadata.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/String.h"
namespace gd {
@@ -46,16 +47,14 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: supplementaryInformation); // Otherwise don't change anything
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& (supplementaryInformation.find(
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
? (supplementaryInformation.empty()
? ""
: extensionNamespace + supplementaryInformation)
: supplementaryInformation));
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.

View File

@@ -8,6 +8,7 @@
#include <algorithm>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
@@ -64,17 +65,14 @@ InstructionMetadata& InstructionMetadata::AddParameter(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
supplementaryInformation //... so prefix it with the
// extension
// namespace.
)
: supplementaryInformation); // Otherwise don't change anything
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& (supplementaryInformation.find(
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
? (supplementaryInformation.empty()
? ""
: extensionNamespace + supplementaryInformation)
: supplementaryInformation));
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -190,7 +188,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
gd::String templateSentence = _("<subject> of _PARAM0_ <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
@@ -200,7 +198,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
gd::String templateSentence = _("<subject> <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")

View File

@@ -191,6 +191,16 @@ class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata
return *this;
}
/**
* \see gd::InstructionMetadata::SetHelpPath
*/
MultipleInstructionMetadata &SetHelpPath(const gd::String &path) {
if (expression) expression->SetHelpPath(path);
if (condition) condition->SetHelpPath(path);
if (action) action->SetHelpPath(path);
return *this;
}
/**
* \see gd::InstructionMetadata::MarkAsSimple
*/

View File

@@ -264,8 +264,11 @@ class GD_CORE_API PlatformExtension {
*
* \param name The name of the behavior
* \param fullname The user friendly name of the behavior
* \param defaultName The default name of behavior instances
* \param description The user friendly description of the behavior
* \param group The behavior category label
* \param icon The icon of the behavior.
* \param className The name of the class implementing the behavior
* \param instance An instance of the behavior that
* will be used to create the behavior
* \param sharedDatasInstance Optional
@@ -288,6 +291,7 @@ class GD_CORE_API PlatformExtension {
* \param name The name of the behavior
* \param fullname The user friendly name of the behavior
* \param description The user friendly description of the behavior
* \param group The behavior category label
* \param icon The icon of the behavior.
*/
gd::BehaviorMetadata& AddEventsBasedBehavior(

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "DependenciesAnalyzer.h"
#include <algorithm>
#include "GDCore/Events/Builtin/LinkEvent.h"
@@ -29,9 +28,9 @@ DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
bool DependenciesAnalyzer::Analyze() {
if (layout)
return Analyze(layout->GetEvents(), true);
return Analyze(layout->GetEvents());
else if (externalEvents)
return Analyze(externalEvents->GetEvents(), true);
return Analyze(externalEvents->GetEvents());
std::cout << "ERROR: DependenciesAnalyzer called without any layout or "
"external events.";
@@ -40,63 +39,38 @@ bool DependenciesAnalyzer::Analyze() {
DependenciesAnalyzer::~DependenciesAnalyzer() {}
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLevel) {
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
for (unsigned int i = 0; i < events.size(); ++i) {
const gd::LinkEvent* linkEvent = dynamic_cast<const gd::LinkEvent*>(&events[i]);
if (linkEvent) {
DependenciesAnalyzer analyzer(*this);
gd::String linked = linkEvent->GetTarget();
if (project.HasExternalEventsNamed(linked)) {
if (std::find(parentExternalEvents.begin(),
parentExternalEvents.end(),
linked) != parentExternalEvents.end())
return false; // Circular dependency!
externalEventsDependencies.insert(
linked); // There is a direct dependency
if (!isOnTopLevel) notTopLevelExternalEventsDependencies.insert(linked);
analyzer.AddParentExternalEvents(linked);
if (!analyzer.Analyze(project.GetExternalEvents(linked).GetEvents(),
isOnTopLevel))
linked) != parentExternalEvents.end()) {
// Circular dependency!
return false;
}
bool wasDependencyJustAdded = externalEventsDependencies.insert(linked).second;
if (wasDependencyJustAdded) {
parentExternalEvents.push_back(linked);
if (!Analyze(project.GetExternalEvents(linked).GetEvents()))
return false;
parentExternalEvents.pop_back();
}
} else if (project.HasLayoutNamed(linked)) {
if (std::find(parentScenes.begin(), parentScenes.end(), linked) !=
parentScenes.end())
return false; // Circular dependency!
scenesDependencies.insert(linked); // There is a direct dependency
if (!isOnTopLevel) notTopLevelScenesDependencies.insert(linked);
analyzer.AddParentScene(linked);
if (!analyzer.Analyze(project.GetLayout(linked).GetEvents(),
isOnTopLevel))
parentScenes.end()) {
// Circular dependency!
return false;
}
// Update with indirect dependencies.
scenesDependencies.insert(analyzer.GetScenesDependencies().begin(),
analyzer.GetScenesDependencies().end());
externalEventsDependencies.insert(
analyzer.GetExternalEventsDependencies().begin(),
analyzer.GetExternalEventsDependencies().end());
sourceFilesDependencies.insert(
analyzer.GetSourceFilesDependencies().begin(),
analyzer.GetSourceFilesDependencies().end());
notTopLevelScenesDependencies.insert(
analyzer.GetNotTopLevelScenesDependencies().begin(),
analyzer.GetNotTopLevelScenesDependencies().end());
notTopLevelExternalEventsDependencies.insert(
analyzer.GetNotTopLevelExternalEventsDependencies().begin(),
analyzer.GetNotTopLevelExternalEventsDependencies().end());
if (!isOnTopLevel) {
notTopLevelScenesDependencies.insert(
analyzer.GetScenesDependencies().begin(),
analyzer.GetScenesDependencies().end());
notTopLevelExternalEventsDependencies.insert(
analyzer.GetExternalEventsDependencies().begin(),
analyzer.GetExternalEventsDependencies().end());
}
bool wasDependencyJustAdded = scenesDependencies.insert(linked).second;
if (wasDependencyJustAdded) {
parentScenes.push_back(linked);
if (!Analyze(project.GetLayout(linked).GetEvents()))
return false;
parentScenes.pop_back();
}
}
}
@@ -112,45 +86,9 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLev
// Analyze sub events dependencies
if (events[i].CanHaveSubEvents()) {
if (!Analyze(events[i].GetSubEvents(), false)) return false;
if (!Analyze(events[i].GetSubEvents())) return false;
}
}
return true;
}
gd::String DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene() {
if (!externalEvents) {
std::cout << "ERROR: ExternalEventsCanBeCompiledForAScene called without "
"external events set!"
<< std::endl;
return "";
}
gd::String sceneName;
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
// For each layout, compute the dependencies and the dependencies which are
// not coming from a top level event.
DependenciesAnalyzer analyzer(project, project.GetLayout(i));
if (!analyzer.Analyze()) continue; // Analyze failed -> Cyclic dependencies
const std::set<gd::String>& dependencies =
analyzer.GetExternalEventsDependencies();
const std::set<gd::String>& notTopLevelDependencies =
analyzer.GetNotTopLevelExternalEventsDependencies();
// Check if the external events is a dependency, and that is is only present
// as a link on the top level.
if (dependencies.find(externalEvents->GetName()) != dependencies.end() &&
notTopLevelDependencies.find(externalEvents->GetName()) ==
notTopLevelDependencies.end()) {
if (!sceneName.empty())
return ""; // External events can be compiled only if one scene is
// including them.
else
sceneName = project.GetLayout(i).GetName();
}
}
return sceneName; // External events can be compiled and used for the scene.
}
#endif

View File

@@ -39,11 +39,6 @@ class GD_CORE_API DependenciesAnalyzer {
/**
* \brief Constructor for analyzing the dependencies of external events.
*
* You can also call then
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene to check if the
* external events can be compiled separately and called by a scene. \see
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
*/
DependenciesAnalyzer(const gd::Project& project_,
const gd::ExternalEvents& externalEvents);
@@ -60,18 +55,6 @@ class GD_CORE_API DependenciesAnalyzer {
*/
bool Analyze();
/**
* Check if the external events (passed in the constructor) can be compiled
* and called by a single scene:<br> This is possible when the link calling
* the external events does not have any parent event and when this situation
* occurs only in a single scene and not in another.
*
* \return The name of the scene which is able to call the compiled external
* events. If empty, no scene is able to call them. (So external events have
* to be included directly by links).
*/
gd::String ExternalEventsCanBeCompiledForAScene();
/**
* \brief Return the scenes being dependencies of the scene or external events
* passed in the constructor.
@@ -96,25 +79,6 @@ class GD_CORE_API DependenciesAnalyzer {
return sourceFilesDependencies;
};
/**
* \brief Return the scenes being dependencies of the scene or external events
* passed in the constructor, but being not top level dependencies: The links
* including them are not a top level events (i.e: They have a parent event).
*/
const std::set<gd::String>& GetNotTopLevelScenesDependencies() const {
return notTopLevelScenesDependencies;
};
/**
* \brief Return the external events being dependencies of the scene or
* external events passed in the constructor, but being not top level
* dependencies: The links including them are not a top level events (i.e:
* They have a parent event).
*/
const std::set<gd::String>& GetNotTopLevelExternalEventsDependencies() const {
return notTopLevelExternalEventsDependencies;
};
private:
/**
* \brief Analyze the dependencies of the events.
@@ -124,32 +88,11 @@ class GD_CORE_API DependenciesAnalyzer {
* (they have no parents). \return false if a circular dependency exists, true
* otherwise.
*/
bool Analyze(const gd::EventsList& events, bool isOnTopLevel);
void AddParentScene(gd::String parentScene) {
parentScenes.push_back(parentScene);
};
void AddParentExternalEvents(gd::String parentExternalEvents_) {
parentExternalEvents.push_back(parentExternalEvents_);
};
/**
* Return true if all links pointing to external events called \a
* externalEventsName are only at the top level of \a events. The function
* return false as soon as it discover a link to external events which is not
* at the top level ( i.e: It has a parent event ).
*
* \warning The function assumes that there are not cyclic dependencies.
*/
bool CheckIfExternalEventsIsLinkedOnlyAtTopLevel(
const gd::String& externalEventsName,
std::vector<std::shared_ptr<gd::BaseEvent> >& events);
bool Analyze(const gd::EventsList& events);
std::set<gd::String> scenesDependencies;
std::set<gd::String> externalEventsDependencies;
std::set<gd::String> sourceFilesDependencies;
std::set<gd::String> notTopLevelScenesDependencies;
std::set<gd::String> notTopLevelExternalEventsDependencies;
std::vector<gd::String>
parentScenes; ///< Used to check for circular dependencies.
std::vector<gd::String>

View File

@@ -226,7 +226,7 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
eventWorker.Launch(layout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);

View File

@@ -136,7 +136,7 @@ class GD_CORE_API ExpressionVariableReplacer
auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
if (!objectNameToUseForVariableAccessor.empty()) {
if (objectsContainersList.HasVariablesContainer(
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
objectNameToUseForVariableAccessor, targetVariablesContainer)) {
// The node represents an object variable, and this object variables are
// the target. Do the replacement or removals:
@@ -177,7 +177,7 @@ class GD_CORE_API ExpressionVariableReplacer
GetPotentialNewName(node.identifierName),
[&]() {
// This represents an object.
if (objectsContainersList.HasVariablesContainer(
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
node.identifierName, targetVariablesContainer)) {
// The node represents an object variable, and this object variables
// are the target. Do the replacement or removals:

View File

@@ -258,7 +258,7 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
eventWorker.Launch(layout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);

View File

@@ -11,13 +11,16 @@
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Events/Parsers/GrammarTerminals.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableParentFinder.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
@@ -486,47 +489,56 @@ class GD_CORE_API ExpressionCompletionFinder
auto type = gd::ExpressionTypeFinder::GetType(
platform, projectScopedContainers, rootType, node);
// Only attempt to complete with the children of the variable
// if it's the last child (no more `.AnotherVariable` written after).
bool eagerlyCompleteIfExactMatch = node.child == nullptr;
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
if (type == "globalvar") {
if (type == "globalvar" || type == "scenevar") {
const auto* variablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
type == "globalvar"
? projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer()
: projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
if (variablesContainer) {
AddCompletionsForVariablesMatchingSearch(
*variablesContainer, node.name, node.nameLocation);
}
} else if (type == "scenevar") {
const auto* variablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
if (variablesContainer) {
AddCompletionsForVariablesMatchingSearch(
*variablesContainer, node.name, node.nameLocation);
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
node.name,
node.nameLocation,
eagerlyCompleteIfExactMatch);
}
} else if (type == "objectvar") {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform,
objectsContainersList,
// Variable fields doesn't use expression completion,
// so the object will be found inside the expression itself.
"",
node);
platform, objectsContainersList, rootObjectName, node);
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
objectsContainersList, objectName, node.name, node.nameLocation);
objectsContainersList,
objectName,
node.name,
node.nameLocation,
eagerlyCompleteIfExactMatch);
}
} else {
AddCompletionsForObjectsAndVariablesMatchingSearch(
node.name, type, node.nameLocation);
node.name, type, node.nameLocation, eagerlyCompleteIfExactMatch);
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
// No completions
VariableAndItsParent variableAndItsParent =
gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, node);
// If no child, we're at the end of a variable (like `GrandChild` in
// `Something.Child.GrandChild`) so we can complete eagerly children if we
// can.
gd::String eagerlyCompleteForVariableName =
node.child == nullptr ? node.name : "";
AddCompletionsForChildrenVariablesOf(variableAndItsParent,
node.nameLocation,
eagerlyCompleteForVariableName);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
// No completions
}
VariableBracketAccessorNode& node) override {}
void OnVisitIdentifierNode(IdentifierNode& node) override {
const auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
@@ -537,45 +549,81 @@ class GD_CORE_API ExpressionCompletionFinder
AddCompletionsForObjectMatchingSearch(
node.identifierName, type, node.location);
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
if (type == "globalvar") {
if (type == "globalvar" || type == "scenevar") {
const auto* variablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
type == "globalvar"
? projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer()
: projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
if (variablesContainer) {
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
node.identifierName,
node.identifierNameLocation);
}
} else if (type == "scenevar") {
const auto* variablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
if (variablesContainer) {
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
node.identifierName,
node.identifierNameLocation);
if (IsCaretOn(node.identifierNameDotLocation) ||
IsCaretOn(node.childIdentifierNameLocation)) {
// Complete a potential child variable:
if (variablesContainer->Has(node.identifierName)) {
AddCompletionsForChildrenVariablesOf(
&variablesContainer->Get(node.identifierName),
node.childIdentifierNameLocation,
node.childIdentifierName);
}
} else {
// Complete a root variable of the scene or project.
// Don't attempt to complete children variables if there is
// already a dot written (`MyVariable.`).
bool eagerlyCompleteIfPossible =
!node.identifierNameDotLocation.IsValid();
AddCompletionsForVariablesMatchingSearch(
*variablesContainer,
node.identifierName,
node.identifierNameLocation,
eagerlyCompleteIfPossible);
}
}
} else if (type == "objectvar") {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform,
objectsContainersList,
// Variable fields doesn't use expression completion,
// so the object will be found inside the expression itself.
"",
node);
platform, objectsContainersList, rootObjectName, node);
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
objectsContainersList,
objectName,
node.identifierName,
node.identifierNameLocation);
if (IsCaretOn(node.identifierNameDotLocation) ||
IsCaretOn(node.childIdentifierNameLocation)) {
// Complete a potential child variable:
const auto* variablesContainer =
objectsContainersList.GetObjectOrGroupVariablesContainer(
objectName);
if (variablesContainer &&
variablesContainer->Has(node.identifierName)) {
AddCompletionsForChildrenVariablesOf(
&variablesContainer->Get(node.identifierName),
node.childIdentifierNameLocation,
node.childIdentifierName);
}
} else {
// Complete a root variable of the object.
// Don't attempt to complete children variables if there is
// already a dot written (`MyVariable.`).
bool eagerlyCompleteIfPossible =
!node.identifierNameDotLocation.IsValid();
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
objectsContainersList,
objectName,
node.identifierName,
node.identifierNameLocation,
eagerlyCompleteIfPossible);
}
}
} else {
// Object function, behavior name, variable, object variable.
if (IsCaretOn(node.identifierNameLocation)) {
// Is this the proper position?
// Don't attempt to complete children variables if there is
// already a dot written (`MyVariable.`).
bool eagerlyCompleteIfPossible =
!node.identifierNameDotLocation.IsValid();
AddCompletionsForAllIdentifiersMatchingSearch(
node.identifierName, type, node.identifierNameLocation);
node.identifierName,
type,
node.identifierNameLocation,
eagerlyCompleteIfPossible);
if (!node.identifierNameDotLocation.IsValid()) {
completions.push_back(
ExpressionCompletionDescription::ForExpressionWithPrefix(
@@ -586,27 +634,57 @@ class GD_CORE_API ExpressionCompletionFinder
}
} else if (IsCaretOn(node.identifierNameDotLocation) ||
IsCaretOn(node.childIdentifierNameLocation)) {
const gd::String& objectName = node.identifierName;
// Might be:
// - An object variable, object behavior or object expression.
// - Or a variable with a child.
projectScopedContainers.MatchIdentifierWithName<void>(
node.identifierName,
[&]() {
// This is an object.
const gd::String& objectName = node.identifierName;
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
objectsContainersList,
objectName,
node.childIdentifierName,
node.childIdentifierNameLocation,
true);
// Might be an object variable, object behavior or object expression:
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
objectsContainersList,
objectName,
node.childIdentifierName,
node.childIdentifierNameLocation);
completions.push_back(
ExpressionCompletionDescription::ForBehaviorWithPrefix(
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
objectName));
completions.push_back(
ExpressionCompletionDescription::ForExpressionWithPrefix(
type,
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
objectName));
completions.push_back(
ExpressionCompletionDescription::ForBehaviorWithPrefix(
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
objectName));
completions.push_back(
ExpressionCompletionDescription::ForExpressionWithPrefix(
type,
node.childIdentifierName,
node.childIdentifierNameLocation.GetStartPosition(),
node.childIdentifierNameLocation.GetEndPosition(),
objectName));
},
[&]() {
// This is a variable.
VariableAndItsParent variableAndItsParent =
gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, node);
AddCompletionsForChildrenVariablesOf(
variableAndItsParent,
node.childIdentifierNameLocation,
node.childIdentifierName);
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
}
@@ -736,7 +814,8 @@ class GD_CORE_API ExpressionCompletionFinder
auto type = gd::ExpressionTypeFinder::GetType(
platform, projectScopedContainers, rootType, node);
AddCompletionsForAllIdentifiersMatchingSearch(node.text, type, node.location);
AddCompletionsForAllIdentifiersMatchingSearch(
node.text, type, node.location);
completions.push_back(
ExpressionCompletionDescription::ForExpressionWithPrefix(
type,
@@ -755,10 +834,96 @@ class GD_CORE_API ExpressionCompletionFinder
(inclusive && searchedPosition <= location.GetEndPosition())));
}
/**
* A slightly less strict check than `gd::Project::IsNameSafe` as child
* variables can be completed even if they start with a number.
*/
bool IsIdentifierSafe(const gd::String& name) {
if (name.empty()) return false;
for (auto character : name) {
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
return false;
}
}
return true;
}
void AddCompletionsForChildrenVariablesOf(
VariableAndItsParent variableAndItsParent,
const ExpressionParserLocation& location,
gd::String eagerlyCompleteForVariableName = "") {
if (variableAndItsParent.parentVariable) {
AddCompletionsForChildrenVariablesOf(variableAndItsParent.parentVariable,
location,
eagerlyCompleteForVariableName);
} else if (variableAndItsParent.parentVariablesContainer) {
AddCompletionsForVariablesMatchingSearch(
*variableAndItsParent.parentVariablesContainer, "", location);
}
}
void AddCompletionsForChildrenVariablesOf(
const gd::Variable* variable,
const ExpressionParserLocation& location,
gd::String eagerlyCompleteForVariableName = "") {
if (!variable) return;
if (variable->GetType() == gd::Variable::Structure) {
for (const auto& name : variable->GetAllChildrenNames()) {
if (!IsIdentifierSafe(name)) continue;
const auto& childVariable = variable->GetChild(name);
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Variable,
location.GetStartPosition(),
location.GetEndPosition());
description.SetCompletion(name);
description.SetVariableType(childVariable.GetType());
completions.push_back(description);
if (name == eagerlyCompleteForVariableName) {
AddEagerCompletionForVariableChildren(childVariable, name, location);
}
}
} else {
// TODO: we could do a "comment only completion" to indicate that nothing
// can/should be completed?
}
}
void AddEagerCompletionForVariableChildren(
const gd::Variable& variable,
const gd::String& variableName,
const ExpressionParserLocation& location) {
if (variable.GetType() == gd::Variable::Structure) {
gd::String prefix = variableName + ".";
for (const auto& name : variable.GetAllChildrenNames()) {
gd::String completion =
IsIdentifierSafe(name)
? (prefix + name)
: (variableName + "[" +
gd::ExpressionParser2NodePrinter::PrintStringLiteral(name) +
"]");
const auto& childVariable = variable.GetChild(name);
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Variable,
location.GetStartPosition(),
location.GetEndPosition());
description.SetCompletion(completion);
description.SetVariableType(childVariable.GetType());
completions.push_back(description);
}
}
}
void AddCompletionsForVariablesMatchingSearch(
const gd::VariablesContainer& variablesContainer,
const gd::String& search,
const ExpressionParserLocation& location) {
const ExpressionParserLocation& location,
bool eagerlyCompleteIfExactMatch = false) {
variablesContainer.ForEachVariableMatchingSearch(
search,
[&](const gd::String& variableName, const gd::Variable& variable) {
@@ -769,6 +934,11 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetCompletion(variableName);
description.SetVariableType(variable.GetType());
completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) {
AddEagerCompletionForVariableChildren(
variable, variableName, location);
}
});
}
@@ -776,7 +946,8 @@ class GD_CORE_API ExpressionCompletionFinder
const gd::ObjectsContainersList& objectsContainersList,
const gd::String& objectOrGroupName,
const gd::String& search,
const ExpressionParserLocation& location) {
const ExpressionParserLocation& location,
bool eagerlyCompleteIfExactMatch) {
objectsContainersList.ForEachObjectOrGroupVariableMatchingSearch(
objectOrGroupName,
search,
@@ -788,6 +959,11 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetCompletion(variableName);
description.SetVariableType(variable.GetType());
completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) {
AddEagerCompletionForVariableChildren(
variable, variableName, location);
}
});
}
@@ -795,25 +971,27 @@ class GD_CORE_API ExpressionCompletionFinder
const gd::String& search,
const gd::String& type,
const ExpressionParserLocation& location) {
projectScopedContainers.GetObjectsContainersList().ForEachNameMatchingSearch(
search,
[&](const gd::String& name,
const gd::ObjectConfiguration* objectConfiguration) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Object,
location.GetStartPosition(),
location.GetEndPosition());
description.SetObjectConfiguration(objectConfiguration);
description.SetCompletion(name);
description.SetType(type);
completions.push_back(description);
});
projectScopedContainers.GetObjectsContainersList()
.ForEachNameMatchingSearch(
search,
[&](const gd::String& name,
const gd::ObjectConfiguration* objectConfiguration) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Object,
location.GetStartPosition(),
location.GetEndPosition());
description.SetObjectConfiguration(objectConfiguration);
description.SetCompletion(name);
description.SetType(type);
completions.push_back(description);
});
}
void AddCompletionsForObjectsAndVariablesMatchingSearch(
const gd::String& search,
const gd::String& type,
const ExpressionParserLocation& location) {
const ExpressionParserLocation& location,
bool eagerlyCompleteIfExactMatch) {
projectScopedContainers.ForEachIdentifierMatchingSearch(
search,
[&](const gd::String& objectName,
@@ -835,6 +1013,11 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetCompletion(variableName);
description.SetVariableType(variable.GetType());
completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) {
AddEagerCompletionForVariableChildren(
variable, variableName, location);
}
},
[&](const gd::NamedPropertyDescriptor& property) {
// Ignore properties here.
@@ -845,7 +1028,7 @@ class GD_CORE_API ExpressionCompletionFinder
}
void AddCompletionsForAllIdentifiersMatchingSearch(const gd::String& search,
const gd::String& type) {
const gd::String& type) {
AddCompletionsForAllIdentifiersMatchingSearch(
search,
type,
@@ -855,7 +1038,8 @@ class GD_CORE_API ExpressionCompletionFinder
void AddCompletionsForAllIdentifiersMatchingSearch(
const gd::String& search,
const gd::String& type,
const ExpressionParserLocation& location) {
const ExpressionParserLocation& location,
bool eagerlyCompleteIfExactMatch = false) {
projectScopedContainers.ForEachIdentifierMatchingSearch(
search,
[&](const gd::String& objectName,
@@ -877,6 +1061,11 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetCompletion(variableName);
description.SetVariableType(variable.GetType());
completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) {
AddEagerCompletionForVariableChildren(
variable, variableName, location);
}
},
[&](const gd::NamedPropertyDescriptor& property) {
ExpressionCompletionDescription description(
@@ -904,11 +1093,14 @@ class GD_CORE_API ExpressionCompletionFinder
const gd::String& rootType_,
size_t searchedPosition_,
gd::ExpressionNode* maybeParentNodeAtLocation_)
: platform(platform_),
: searchedPosition(searchedPosition_),
maybeParentNodeAtLocation(maybeParentNodeAtLocation_),
platform(platform_),
projectScopedContainers(projectScopedContainers_),
rootType(rootType_),
searchedPosition(searchedPosition_),
maybeParentNodeAtLocation(maybeParentNodeAtLocation_){};
rootObjectName("") // Always empty, might be changed if variable fields
// in the editor are changed to use completion.
{};
std::vector<ExpressionCompletionDescription> completions;
size_t searchedPosition;
@@ -917,6 +1109,7 @@ class GD_CORE_API ExpressionCompletionFinder
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
const gd::String rootType;
const gd::String rootObjectName;
};
} // namespace gd

View File

@@ -263,11 +263,17 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
}
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
RaiseError("invalid_function_name",
if (function.functionName.empty()) {
RaiseError("invalid_function_name",
_("Enter the name of the function to call."),
function.location);
} else {
RaiseError("invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
function.location);
}
return returnType;
}

View File

@@ -0,0 +1,370 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Contains a variables container or a variable. Useful
* to refer to the parent of a variable (which can be a VariablesContainer
* or another Variable).
*/
struct VariableAndItsParent {
const gd::VariablesContainer* parentVariablesContainer;
const gd::Variable* parentVariable;
};
/**
* \brief Find the last parent (i.e: the variables container) of a node representing a variable.
*
* Useful for completions, to know which variables can be entered in a node.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableParentFinder
: public ExpressionParser2NodeWorker {
public:
static VariableAndItsParent GetLastParentOfNode(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node) {
gd::ExpressionVariableParentFinder typeFinder(
platform, projectScopedContainers);
node.Visit(typeFinder);
return typeFinder.variableAndItsParent;
}
virtual ~ExpressionVariableParentFinder(){};
protected:
ExpressionVariableParentFinder(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_)
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
variableNode(nullptr),
thisIsALegacyPrescopedVariable(false),
bailOutBecauseEmptyVariableName(false),
legacyPrescopedVariablesContainer(nullptr){};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The node represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
childVariableNames.insert(childVariableNames.begin(), node.name);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.name,
[&]() {
// This is an object.
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.name);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList().Has(
node.name)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.name),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.name.empty() && node.child) {
// A variable accessor should always have a name if it has a child (i.e: another accessor).
// While the parser may have generated an empty name,
// flag this so we avoid finding a wrong parent (and so, run the risk of giving
// wrong autocompletions).
bailOutBecauseEmptyVariableName = true;
}
childVariableNames.insert(childVariableNames.begin(), node.name);
if (node.parent) node.parent->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
// This node is not necessarily a variable node.
// It will be checked when visiting the FunctionCallNode, just after.
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The identifier represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
childVariableNames.insert(childVariableNames.begin(),
node.identifierName);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.identifierName,
[&]() {
// This is an object.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.identifierName);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
if (projectScopedContainers.GetVariablesContainersList().Has(
node.identifierName)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.identifierName),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
// Add a child with an empty name, which will be interpreted as
// "take the first child/item of the structure/array".
childVariableNames.insert(childVariableNames.begin(), "");
if (node.parent) node.parent->Visit(*this);
}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
if (variableNode == nullptr) {
return;
}
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == variableNode) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
const auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, parameterIndex);
if (parameterMetadata == nullptr) return; // Unexpected
// Support for legacy pre-scoped variables:
if (parameterMetadata->GetValueTypeMetadata().IsLegacyPreScopedVariable()) {
if (parameterMetadata->GetType() == "objectvar") {
// Legacy convention where a "objectvar"
// parameter represents a variable of the object represented by the
// previous "object" parameter. The object on which the function is
// called is returned if no previous parameters are objects.
gd::String objectName = functionCall.objectName;
for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, previousIndex);
if (previousParameterMetadata != nullptr &&
gd::ParameterMetadata::IsObject(
previousParameterMetadata->GetType())) {
auto previousParameterNode =
functionCall.parameters[previousIndex].get();
IdentifierNode* objectNode =
dynamic_cast<IdentifierNode*>(previousParameterNode);
objectName = objectNode->identifierName;
break;
}
}
legacyPrescopedVariablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "scenevar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "globalvar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
}
} else {
thisIsALegacyPrescopedVariable = false;
legacyPrescopedVariablesContainer = nullptr;
}
}
private:
VariableAndItsParent WalkUntilLastParent(
const gd::Variable& variable,
const std::vector<gd::String>& childVariableNames,
size_t startIndex = 0) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue when visiting nodes.
const gd::Variable* currentVariable = &variable;
// Walk until size - 1 as we want the last parent.
for (size_t index = startIndex; index + 1 < childVariableNames.size();
++index) {
const gd::String& childName = childVariableNames[index];
if (childName.empty()) {
if (currentVariable->GetChildrenCount() == 0) {
// The array or structure is empty, we can't walk through it - there
// is no "parent".
return {};
}
if (currentVariable->GetType() == gd::Variable::Array) {
currentVariable = &currentVariable->GetAtIndex(0);
} else {
currentVariable =
currentVariable->GetAllChildren().begin()->second.get();
}
} else {
if (!currentVariable->HasChild(childName)) {
// Non existing child - there is no "parent".
return {};
}
currentVariable = &currentVariable->GetChild(childName);
}
}
// Return the last parent of the chain of variables (so not the last variable
// but the one before it).
return {.parentVariable = currentVariable};
}
VariableAndItsParent WalkUntilLastParent(
const gd::VariablesContainer& variablesContainer,
const std::vector<gd::String>& childVariableNames) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue when visiting nodes.
if (childVariableNames.empty())
return {}; // There is no "parent" to the variables container itself.
const gd::String& firstChildName = *childVariableNames.begin();
const gd::Variable* variable = variablesContainer.Has(firstChildName) ?
&variablesContainer.Get(firstChildName) : nullptr;
if (childVariableNames.size() == 1 || !variable)
return {// Only one child: the parent is the variables container itself.
.parentVariablesContainer = &variablesContainer};
return WalkUntilLastParent(*variable, childVariableNames, 1);
}
gd::ExpressionNode* variableNode;
std::vector<gd::String> childVariableNames;
bool thisIsALegacyPrescopedVariable;
bool bailOutBecauseEmptyVariableName;
const gd::VariablesContainer* legacyPrescopedVariablesContainer;
VariableAndItsParent variableAndItsParent;
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
};
} // namespace gd

View File

@@ -43,7 +43,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
struct dirent *lecture;
DIR *rep;
rep = opendir(directory.c_str());
int l = 0;
if (rep == NULL) {
cout << "Unable to open Extensions (" << directory << ") directory."
@@ -63,8 +62,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
LoadExtension(directory + "/" + lec, platform, forgiving);
librariesLoaded.push_back(directory + "/" + lec);
l++;
}
}
@@ -103,7 +100,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
struct dirent *lecture;
DIR *rep;
rep = opendir(directory.c_str());
int l = 0;
if (rep == NULL) {
cout << "Unable to open Extensions (" << directory << ") directory."
@@ -118,7 +114,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
lec.find(".xgd" + suffix, lec.length() - 4 - suffix.length()) !=
string::npos) {
librariesLoaded.push_back(directory + "/" + lec);
l++;
}
}

View File

@@ -63,14 +63,10 @@ void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName){
};
void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(audioName) &&
resources->GetResource(audioName).GetKind() == "audio") {
// Nothing to do, the audio is a reference to a proper resource.
return;
}
if (resourcesManager->HasResource(audioName) &&
resourcesManager->GetResource(audioName).GetKind() == "audio") {
// Nothing to do, the audio is a reference to a proper resource.
return;
}
// For compatibility with older projects (where events were referring to files
@@ -80,14 +76,10 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
};
void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(fontName) &&
resources->GetResource(fontName).GetKind() == "font") {
// Nothing to do, the font is a reference to a proper resource.
return;
}
if (resourcesManager->HasResource(fontName) &&
resourcesManager->GetResource(fontName).GetKind() == "font") {
// Nothing to do, the font is a reference to a proper resource.
return;
}
// For compatibility with older projects (where events were referring to files
@@ -96,12 +88,7 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
ExposeFile(fontName);
};
void ArbitraryResourceWorker::ExposeResources(
gd::ResourcesManager* resourcesManager) {
if (!resourcesManager) return;
resourcesManagers.push_back(resourcesManager);
void ArbitraryResourceWorker::ExposeResources() {
std::vector<gd::String> resources = resourcesManager->GetAllResourceNames();
for (std::size_t i = 0; i < resources.size(); i++) {
if (resourcesManager->GetResource(resources[i]).UseFile())
@@ -110,9 +97,6 @@ void ArbitraryResourceWorker::ExposeResources(
}
void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
if (resourcesManagers.empty()) return;
gd::ResourcesManager* resourcesManager = resourcesManagers[0];
gd::Resource& resource = resourcesManager->GetResource(resourceName);
if (!resource.GetMetadata().empty()) {
@@ -176,6 +160,7 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
}
if (resourceType == "tilemap") {
ExposeTilemap(resourceName);
ExposeEmbeddeds(resourceName);
return;
}
if (resourceType == "tileset") {
@@ -184,6 +169,7 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
}
if (resourceType == "json") {
ExposeJson(resourceName);
ExposeEmbeddeds(resourceName);
return;
}
if (resourceType == "video") {
@@ -243,10 +229,12 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
} else if (parameterMetadata.GetType() == "jsonResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeJson(updatedParameterValue);
worker.ExposeEmbeddeds(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilemapResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeTilemap(updatedParameterValue);
worker.ExposeEmbeddeds(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilesetResource") {
gd::String updatedParameterValue = parameterValue;
@@ -262,13 +250,6 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
return false;
};
void LaunchResourceWorkerOnEvents(const gd::Project& project,
gd::EventsList& events,
gd::ArbitraryResourceWorker& worker) {
gd::ResourceWorkerInEventsWorker eventsWorker(project, worker);
eventsWorker.Launch(events);
}
gd::ResourceWorkerInEventsWorker
GetResourceWorkerOnEvents(const gd::Project &project,
gd::ArbitraryResourceWorker &worker) {

View File

@@ -37,13 +37,14 @@ namespace gd {
* \see ResourcesMergingHelper
* \see gd::ResourcesInUseHelper
*
* \see gd::LaunchResourceWorkerOnEvents
* \see gd::GetResourceWorkerOnEvents
*
* \ingroup IDE
*/
class GD_CORE_API ArbitraryResourceWorker {
public:
ArbitraryResourceWorker(){};
public:
ArbitraryResourceWorker(gd::ResourcesManager &resourcesManager_)
: resourcesManager(&resourcesManager_){};
virtual ~ArbitraryResourceWorker();
/**
@@ -52,7 +53,7 @@ class GD_CORE_API ArbitraryResourceWorker {
* first to ensure that resources are known so that images, shaders & audio
* can make reference to them.
*/
void ExposeResources(gd::ResourcesManager *resourcesManager);
void ExposeResources();
/**
* \brief Expose a resource from a given type.
@@ -122,11 +123,6 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeEmbeddeds(gd::String &resourceName);
protected:
const std::vector<gd::ResourcesManager *> &GetResources() {
return resourcesManagers;
};
private:
/**
* \brief Expose a resource: resources that have a file are
@@ -134,7 +130,7 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
void ExposeResource(gd::Resource &resource);
std::vector<gd::ResourcesManager *> resourcesManagers;
gd::ResourcesManager * resourcesManager;
};
/**

View File

@@ -6,7 +6,7 @@
namespace gd {
void ObjectsUsingResourceCollector::DoVisitObject(gd::Object& object) {
gd::ResourceNameMatcher resourceNameMatcher(resourceName);
gd::ResourceNameMatcher resourceNameMatcher(*resourcesManager, resourceName);
object.GetConfiguration().ExposeResources(resourceNameMatcher);
if (resourceNameMatcher.AnyResourceMatches()) {

View File

@@ -4,8 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#ifndef ProjectObjectsUsingResourceCollector_H
#define ProjectObjectsUsingResourceCollector_H
#pragma once
#include <vector>
@@ -21,9 +20,10 @@ namespace gd {
class GD_CORE_API ObjectsUsingResourceCollector
: public ArbitraryObjectsWorker {
public:
ObjectsUsingResourceCollector(const gd::String& resourceName_)
: resourceName(resourceName_){};
public:
ObjectsUsingResourceCollector(gd::ResourcesManager &resourcesManager_,
const gd::String &resourceName_)
: resourcesManager(&resourcesManager_), resourceName(resourceName_){};
virtual ~ObjectsUsingResourceCollector();
std::vector<gd::String>& GetObjectNames() { return objectNames; }
@@ -33,12 +33,16 @@ class GD_CORE_API ObjectsUsingResourceCollector
std::vector<gd::String> objectNames;
gd::String resourceName;
gd::ResourcesManager *resourcesManager;
};
class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
public:
ResourceNameMatcher(const gd::String& resourceName_)
: resourceName(resourceName_), matchesResourceName(false){};
public:
ResourceNameMatcher(gd::ResourcesManager &resourcesManager,
const gd::String &resourceName_)
: resourceName(resourceName_),
matchesResourceName(false), gd::ArbitraryResourceWorker(
resourcesManager){};
virtual ~ResourceNameMatcher(){};
bool AnyResourceMatches() { return matchesResourceName; }
@@ -85,5 +89,3 @@ class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
};
}; // namespace gd
#endif // ProjectObjectsUsingResourceCollector_H

View File

@@ -18,8 +18,9 @@ namespace gd {
std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
gd::Project& project, const gd::String& resourceType) {
std::vector<gd::String> unusedResources;
// Search for resources used in the project
gd::ResourcesInUseHelper resourcesInUse;
gd::ResourcesInUseHelper resourcesInUse(project.GetResourcesManager());
gd::ResourceExposer::ExposeWholeProjectResources(project, resourcesInUse);
std::set<gd::String>& usedResources = resourcesInUse.GetAll(resourceType);

View File

@@ -25,8 +25,29 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
bool updateOriginalProject,
bool preserveAbsoluteFilenames,
bool preserveDirectoryStructure) {
if (updateOriginalProject) {
gd::ProjectResourcesCopier::CopyAllResourcesTo(
originalProject, originalProject, fs, destinationDirectory,
preserveAbsoluteFilenames, preserveDirectoryStructure);
} else {
gd::Project clonedProject = originalProject;
gd::ProjectResourcesCopier::CopyAllResourcesTo(
originalProject, clonedProject, fs, destinationDirectory,
preserveAbsoluteFilenames, preserveDirectoryStructure);
}
return true;
}
bool ProjectResourcesCopier::CopyAllResourcesTo(
gd::Project& originalProject,
gd::Project& clonedProject,
AbstractFileSystem& fs,
gd::String destinationDirectory,
bool preserveAbsoluteFilenames,
bool preserveDirectoryStructure) {
// Check if there are some resources with absolute filenames
gd::ResourcesAbsolutePathChecker absolutePathChecker(fs);
gd::ResourcesAbsolutePathChecker absolutePathChecker(originalProject.GetResourcesManager(), fs);
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, absolutePathChecker);
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
@@ -34,24 +55,18 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
<< destinationDirectory << "..." << std::endl;
// Get the resources to be copied
gd::ResourcesMergingHelper resourcesMergingHelper(fs);
gd::ResourcesMergingHelper resourcesMergingHelper(
clonedProject.GetResourcesManager(), fs);
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
resourcesMergingHelper.PreserveDirectoriesStructure(
preserveDirectoryStructure);
resourcesMergingHelper.PreserveAbsoluteFilenames(
preserveAbsoluteFilenames);
if (updateOriginalProject) {
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, resourcesMergingHelper);
} else {
std::shared_ptr<gd::Project> project(new gd::Project(originalProject));
gd::ResourceExposer::ExposeWholeProjectResources(*project, resourcesMergingHelper);
}
resourcesMergingHelper.PreserveAbsoluteFilenames(preserveAbsoluteFilenames);
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
resourcesMergingHelper);
// Copy resources
map<gd::String, gd::String>& resourcesNewFilename =
resourcesMergingHelper.GetAllResourcesOldAndNewFilename();
unsigned int i = 0;
for (map<gd::String, gd::String>::const_iterator it =
resourcesNewFilename.begin();
it != resourcesNewFilename.end();
@@ -71,8 +86,6 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
destinationFile + _("\"."));
}
}
++i;
}
return true;

View File

@@ -47,6 +47,13 @@ class GD_CORE_API ProjectResourcesCopier {
bool updateOriginalProject,
bool preserveAbsoluteFilenames = true,
bool preserveDirectoryStructure = true);
private:
static bool CopyAllResourcesTo(gd::Project& originalProject,
gd::Project& clonedProject,
gd::AbstractFileSystem& fs,
gd::String destinationDirectory,
bool preserveAbsoluteFilenames = true,
bool preserveDirectoryStructure = true);
};
} // namespace gd

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef RESOURCESABSOLUTEPATHCHECKER_H
#define RESOURCESABSOLUTEPATHCHECKER_H
#pragma once
#include "GDCore/IDE/AbstractFileSystem.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
@@ -22,10 +21,10 @@ namespace gd {
*/
class GD_CORE_API ResourcesAbsolutePathChecker
: public ArbitraryResourceWorker {
public:
ResourcesAbsolutePathChecker(AbstractFileSystem& fileSystem)
: ArbitraryResourceWorker(),
hasAbsoluteFilenames(false),
public:
ResourcesAbsolutePathChecker(gd::ResourcesManager &resourcesManager,
AbstractFileSystem &fileSystem)
: ArbitraryResourceWorker(resourcesManager), hasAbsoluteFilenames(false),
fs(fileSystem){};
virtual ~ResourcesAbsolutePathChecker(){};
@@ -47,5 +46,3 @@ class GD_CORE_API ResourcesAbsolutePathChecker
};
} // namespace gd
#endif // RESOURCESABSOLUTEPATHCHECKER_H

View File

@@ -33,8 +33,9 @@ std::set<gd::String> & usedImages = resourcesInUse.GetAllImages();
* \ingroup IDE
*/
class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
public:
ResourcesInUseHelper() : gd::ArbitraryResourceWorker(){};
public:
ResourcesInUseHelper(gd::ResourcesManager &resourcesManager)
: gd::ArbitraryResourceWorker(resourcesManager){};
virtual ~ResourcesInUseHelper(){};
std::set<gd::String>& GetAllImages() { return GetAll("image"); };

View File

@@ -28,11 +28,11 @@ namespace gd {
* \ingroup IDE
*/
class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
public:
ResourcesMergingHelper(gd::AbstractFileSystem& fileSystem)
: ArbitraryResourceWorker(),
preserveDirectoriesStructure(false),
preserveAbsoluteFilenames(false),
public:
ResourcesMergingHelper(gd::ResourcesManager &resourcesManager,
gd::AbstractFileSystem &fileSystem)
: ArbitraryResourceWorker(resourcesManager),
preserveDirectoriesStructure(false), preserveAbsoluteFilenames(false),
fs(fileSystem){};
virtual ~ResourcesMergingHelper(){};

View File

@@ -22,13 +22,16 @@ namespace gd {
*/
class ResourcesRenamer : public gd::ArbitraryResourceWorker {
public:
/**
* @brief Constructor taking the map from old name to new name.
* @param oldToNewNames_ A map associating to a resource name the new name to
* use.
*/
ResourcesRenamer(const std::map<gd::String, gd::String>& oldToNewNames_)
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
/**
* @brief Constructor taking the map from old name to new name.
* @param oldToNewNames_ A map associating to a resource name the new name to
* use.
*/
ResourcesRenamer(gd::ResourcesManager &resourcesManager,
const std::map<gd::String, gd::String> &oldToNewNames_)
: gd::ArbitraryResourceWorker(resourcesManager),
oldToNewNames(oldToNewNames_){};
virtual ~ResourcesRenamer(){};
virtual void ExposeFile(gd::String& resourceFileName) override{

View File

@@ -0,0 +1,37 @@
/*
* GDevelop JS Platform
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "SceneResourcesFinder.h"
#include "GDCore/IDE/ResourceExposer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
std::set<gd::String> SceneResourcesFinder::FindProjectResources(gd::Project &project) {
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
gd::ResourceExposer::ExposeProjectResources(project, resourceWorker);
return resourceWorker.resourceNames;
}
std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &project,
gd::Layout &layout) {
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
gd::ResourceExposer::ExposeLayoutResources(project, layout, resourceWorker);
return resourceWorker.resourceNames;
}
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
if (resourceName.empty()) {
return;
}
resourceNames.insert(resourceName);
}
} // namespace gd

View File

@@ -0,0 +1,87 @@
/*
* GDevelop JS Platform
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/String.h"
#include <set>
namespace gd {
class Project;
class Layout;
class SerializerElement;
} // namespace gd
namespace gd {
/**
* \brief Find resource usages in several parts of the project.
*
* \ingroup IDE
*/
class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
public:
/**
* @brief Find resource usages in a given scenes.
*
* It doesn't include resources used globally.
*/
static std::set<gd::String> FindSceneResources(gd::Project &project,
gd::Layout &layout);
/**
* @brief Find resource that are used globally in the project.
*
* It doesn't include resources used in scenes.
*/
static std::set<gd::String> FindProjectResources(gd::Project &project);
virtual ~SceneResourcesFinder(){};
private:
SceneResourcesFinder(gd::ResourcesManager &resourcesManager)
: gd::ArbitraryResourceWorker(resourcesManager){};
void AddUsedResource(gd::String &resourceName);
void ExposeFile(gd::String &resourceFileName) override{
// Don't do anything: we're renaming resources, not the files they are
// pointing to.
};
void ExposeImage(gd::String &imageResourceName) override {
AddUsedResource(imageResourceName);
};
void ExposeAudio(gd::String &audioResourceName) override {
AddUsedResource(audioResourceName);
};
void ExposeFont(gd::String &fontResourceName) override {
AddUsedResource(fontResourceName);
};
void ExposeJson(gd::String &jsonResourceName) override {
AddUsedResource(jsonResourceName);
};
void ExposeTilemap(gd::String &tilemapResourceName) override {
AddUsedResource(tilemapResourceName);
};
void ExposeTileset(gd::String &tilesetResourceName) override {
AddUsedResource(tilesetResourceName);
};
void ExposeVideo(gd::String &videoResourceName) override {
AddUsedResource(videoResourceName);
};
void ExposeBitmapFont(gd::String &bitmapFontName) override {
AddUsedResource(bitmapFontName);
};
void ExposeModel3D(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
std::set<gd::String> resourceNames;
};
} // namespace gd

View File

@@ -20,6 +20,7 @@
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
namespace gd {
@@ -33,27 +34,8 @@ void ProjectBrowserHelper::ExposeProjectEvents(
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
// Add (free) events functions
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
// Add (behavior) events functions
for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
}
// Add (object) events functions
for (auto &&eventsBasedObject :
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
auto &objectEventsFunctions = eventsBasedObject->GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
}
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
}
}
@@ -69,7 +51,7 @@ void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
}
}
void ProjectBrowserHelper::ExposeLayoutEvents(
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorker &worker) {
@@ -85,7 +67,7 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
}
}
void ProjectBrowserHelper::ExposeLayoutEvents(
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorkerWithContext &worker) {
auto projectScopedContainers =
@@ -103,6 +85,32 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
}
}
void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorker &worker) {
// Add layouts events
worker.Launch(layout.GetEvents());
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
bool hasCircularDependencies = !dependenciesAnalyzer.Analyze();
if (hasCircularDependencies) {
// The analyzer stops when it finds circular dependencies so the dependencies are not complete.
// TODO Should the analyzer still continue to avoid side effect on thing that would not be code generation related?
// Maybe a boolean parameter should be added?
return;
}
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
worker.Launch(externalEvents.GetEvents());
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
worker.Launch(dependencyLayout.GetEvents());
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
@@ -130,8 +138,43 @@ void ProjectBrowserHelper::ExposeProjectEvents(
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
// Add (free) events functions
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
}
}
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorker &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunctionsExtension, *eventsFunction,
globalObjectsAndGroups, objectsAndGroups);
worker.Launch(eventsFunction->GetEvents());
}
// Add (behavior) events functions
for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) {
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
}
// Add (object) events functions
for (auto &&eventsBasedObject :
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
}
}
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorkerWithContext &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
@@ -157,7 +200,6 @@ void ProjectBrowserHelper::ExposeProjectEvents(
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
}
}
}
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
@@ -189,6 +231,21 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
}
}
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorker &worker) {
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups,
objectsAndGroups);
worker.Launch(eventsFunction->GetEvents());
}
}
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker) {
@@ -216,7 +273,7 @@ void ProjectBrowserHelper::ExposeProjectObjects(
// Layout objects
for (size_t i = 0; i < project.GetLayoutsCount(); i++) {
worker.Launch(project.GetLayout(i));
gd::ProjectBrowserHelper::ExposeLayoutObjects(project.GetLayout(i), worker);
}
// Event based objects children
@@ -232,6 +289,14 @@ void ProjectBrowserHelper::ExposeProjectObjects(
}
};
void ProjectBrowserHelper::ExposeLayoutObjects(gd::Layout &layout,
gd::ArbitraryObjectsWorker &worker) {
// In the future, layouts may have children object containers.
// Layout objects
worker.Launch(layout);
}
void ProjectBrowserHelper::ExposeProjectFunctions(
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) {

View File

@@ -60,18 +60,52 @@ public:
* \brief Call the specified worker on all events of a layout and
* its external events.
*/
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorker &worker);
/**
* \brief Call the specified worker on all events of a layout and
* its external events.
*/
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of a layout and
* its dependencies according to EventLink (external events or other layout
* events).
*/
static void
ExposeLayoutEventsAndDependencies(gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorker &worker);
/**
* \brief Call the specified worker on all events of the event-based
* behavior
* extension.
*
* This should be the preferred way to traverse all the events of an events
* based extension.
*/
static void ExposeEventsFunctionsExtensionEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorker &worker);
/**
* \brief Call the specified worker on all events of the event-based
* extension.
*
* This should be the preferred way to traverse all the events of an events
* based extension.
*/
static void ExposeEventsFunctionsExtensionEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* behavior.
*
* This should be the preferred way to traverse all the events of an events
* based behavior.
@@ -93,10 +127,22 @@ public:
/**
* \brief Call the specified worker on all events of the event-based
* behavior.
* object.
*
* This should be the preferred way to traverse all the events of an
* event-based behavior.
* event-based object.
*/
static void
ExposeEventsBasedObjectEvents(gd::Project &project,
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorker &worker);
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an
* event-based object.
*/
static void
ExposeEventsBasedObjectEvents(gd::Project &project,
@@ -112,6 +158,14 @@ public:
static void ExposeProjectObjects(gd::Project &project,
gd::ArbitraryObjectsWorker &worker);
/**
* \brief Call the specified worker on all ObjectContainers of the layout.
*
* This should be the preferred way to traverse all the objects of a layout.
*/
static void ExposeLayoutObjects(gd::Layout &layout,
gd::ArbitraryObjectsWorker &worker);
/**
* \brief Call the specified worker on all FunctionsContainers of the project
* (global, layouts...)

View File

@@ -128,11 +128,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
} else {
gd::Instruction action;
action.SetType("SetReturn" + numberOrString);
gd::String receiver = isBehavior ? "Object.Behavior::" : "Object.";
gd::String propertyPrefix =
(isSharedProperties ? "SharedProperty" : "Property");
action.AddParameter(receiver + propertyPrefix + property.GetName() +
"()");
action.AddParameter(property.GetName());
event.GetActions().Insert(action, 0);
}
}
@@ -233,15 +229,13 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
gd::String parameterGetterCall =
"GetArgumentAs" + numberOrString + "(\"Value\")";
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
action.AddParameter("Value");
} else {
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
action.AddParameter("Value");
}
event.GetActions().Insert(action, 0);
}

View File

@@ -24,6 +24,7 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
namespace gd {
@@ -32,10 +33,9 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
// traverse the whole project (this time for events) and ExposeProjectEffects
// (this time for effects).
gd::ResourcesManager* resourcesManager = &(project.GetResourcesManager());
// Expose any project resources as files.
worker.ExposeResources(resourcesManager);
worker.ExposeResources();
project.GetPlatformSpecificAssets().ExposeResources(worker);
// Expose event resources
@@ -73,6 +73,49 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
}
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
// Expose global objects configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
objectWorker.Launch(project);
}
void ResourceExposer::ExposeLayoutResources(
gd::Project &project, gd::Layout &layout,
gd::ArbitraryResourceWorker &worker) {
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
// Expose layer effect resources
for (std::size_t layerIndex = 0; layerIndex < layout.GetLayersCount();
layerIndex++) {
auto &layer = layout.GetLayer(layerIndex);
auto &effects = layer.GetEffects();
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
}
}
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
eventWorker);
// Exposed extension event resources
// Note that using resources in extensions is very unlikely and probably not
// worth the effort of something smart.
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
}
}
void ResourceExposer::ExposeEffectResources(
gd::Platform &platform, gd::Effect &effect,
gd::ArbitraryResourceWorker &worker) {
@@ -88,11 +131,13 @@ void ResourceExposer::ExposeEffectResources(
auto &resourceType = propertyDescriptor.GetExtraInfo()[0];
const gd::String &resourceName = effect.GetStringParameter(propertyName);
gd::String potentiallyUpdatedResourceName = resourceName;
worker.ExposeResourceWithType(resourceType,
potentiallyUpdatedResourceName);
if (potentiallyUpdatedResourceName != resourceName) {
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
if (!resourceName.empty()) {
gd::String potentiallyUpdatedResourceName = resourceName;
worker.ExposeResourceWithType(resourceType,
potentiallyUpdatedResourceName);
if (potentiallyUpdatedResourceName != resourceName) {
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
}
}
}
}

View File

@@ -10,6 +10,7 @@ class Platform;
class Project;
class ArbitraryResourceWorker;
class Effect;
class Layout;
} // namespace gd
namespace gd {
@@ -31,6 +32,25 @@ public:
static void ExposeWholeProjectResources(gd::Project &project,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose only the resources used globally on a project.
*
* It doesn't include resources used in layouts.
*/
static void ExposeProjectResources(gd::Project &project,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in a given layout.
*
* It doesn't include resources used globally.
*/
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in a given effect.
*/
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
gd::ArbitraryResourceWorker &worker);
};

View File

@@ -1538,7 +1538,7 @@ void WholeProjectRefactorer::RenameLayer(gd::Project &project,
return;
gd::ProjectElementRenamer projectElementRenamer(project.GetCurrentPlatform(),
"layer", oldName, newName);
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
}
@@ -1552,7 +1552,7 @@ void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "layerEffectName", oldName, newName);
projectElementRenamer.SetLayerConstraint(layer.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
}
@@ -1566,7 +1566,7 @@ void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectAnimationName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
}
@@ -1580,7 +1580,7 @@ void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectPointName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
}
@@ -1594,7 +1594,7 @@ void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectEffectName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
}

View File

@@ -5,6 +5,8 @@
*/
#include "CustomConfigurationHelper.h"
#include <map>
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Project.h"
@@ -13,8 +15,6 @@
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include <map>
using namespace gd;
void CustomConfigurationHelper::InitializeContent(
@@ -25,7 +25,8 @@ void CustomConfigurationHelper::InitializeContent(
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "resource") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
@@ -51,7 +52,8 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "resource") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
@@ -59,8 +61,9 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
configurationContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetBoolValue() ? "true"
: "false");
configurationContent.GetChild(propertyName).GetBoolValue()
? "true"
: "false");
}
} else {
// No value was serialized for this property. `newProperty`
@@ -85,7 +88,8 @@ bool CustomConfigurationHelper::UpdateProperty(
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "resource") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());

View File

@@ -17,7 +17,7 @@ LoadingScreen::LoadingScreen()
backgroundFadeInDuration(0.2),
minDuration(1.5),
logoAndProgressFadeInDuration(0.2),
logoAndProgressLogoFadeInDelay(0.2),
logoAndProgressLogoFadeInDelay(0),
showProgressBar(true),
progressBarMinWidth(40),
progressBarMaxWidth(200),

View File

@@ -113,7 +113,7 @@ bool ObjectsContainersList::HasObjectWithVariableNamed(
return false;
}
bool ObjectsContainersList::HasVariablesContainer(
bool ObjectsContainersList::HasObjectOrGroupVariablesContainer(
const gd::String& objectOrGroupName,
const gd::VariablesContainer& variablesContainer) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
@@ -123,8 +123,31 @@ bool ObjectsContainersList::HasVariablesContainer(
&(*it)->GetObject(objectOrGroupName).GetVariables();
}
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.
// For groups, we consider that the first object of the group defines the
// variables available for this group. Note that this is slightly
// different than other methods where a group is considered as the
// "intersection" of all of its objects.
const auto& objectNames =
(*it)->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
if (!objectNames.empty()) {
return HasObjectVariablesContainer(objectNames[0], variablesContainer);
}
return false;
}
}
return false;
}
bool ObjectsContainersList::HasObjectVariablesContainer(
const gd::String& objectName,
const gd::VariablesContainer& variablesContainer) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
++it) {
if ((*it)->HasObjectNamed(objectName)) {
return &variablesContainer ==
&(*it)->GetObject(objectName).GetVariables();
}
}
@@ -140,8 +163,30 @@ ObjectsContainersList::GetObjectOrGroupVariablesContainer(
return &(*it)->GetObject(objectOrGroupName).GetVariables();
}
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.
// For groups, we consider that the first object of the group defines the
// variables available for this group. Note that this is slightly
// different than other methods where a group is considered as the
// "intersection" of all of its objects.
const auto& objectNames =
(*it)->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
if (!objectNames.empty()) {
return GetObjectVariablesContainer(objectNames[0]);
}
return nullptr;
}
}
return nullptr;
}
const gd::VariablesContainer*
ObjectsContainersList::GetObjectVariablesContainer(
const gd::String& objectName) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
++it) {
if ((*it)->HasObjectNamed(objectName)) {
return &(*it)->GetObject(objectName).GetVariables();
}
}
@@ -150,7 +195,6 @@ ObjectsContainersList::GetObjectOrGroupVariablesContainer(
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)) {
@@ -182,13 +226,12 @@ gd::Variable::Type ObjectsContainersList::GetTypeOfObjectOrGroupVariable(
return Variable::Type::Number;
}
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const {
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();
const auto& variables = (*it)->GetObject(objectName).GetVariables();
return variables.Get(variableName).GetType();
}

View File

@@ -61,7 +61,7 @@ class GD_CORE_API ObjectsContainersList {
* \brief Check if the specified object or group has the specified variables
* container.
*/
bool HasVariablesContainer(
bool HasObjectOrGroupVariablesContainer(
const gd::String& objectOrGroupName,
const gd::VariablesContainer& variablesContainer) const;
@@ -165,6 +165,13 @@ class GD_CORE_API ObjectsContainersList {
bool HasObjectWithVariableNamed(const gd::String& objectName,
const gd::String& variableName) const;
bool HasObjectVariablesContainer(
const gd::String& objectName,
const gd::VariablesContainer& variablesContainer) const;
const gd::VariablesContainer* GetObjectVariablesContainer(
const gd::String& objectName) const;
gd::Variable::Type GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const;
void ForEachObjectVariableMatchingSearch(

View File

@@ -534,19 +534,7 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
if (resources[i] == std::shared_ptr<Resource>()) break;
SerializerElement& resourceElement = resourcesElement.AddChild("resource");
resourceElement.SetAttribute("kind", resources[i]->GetKind());
resourceElement.SetAttribute("name", resources[i]->GetName());
resourceElement.SetAttribute("metadata", resources[i]->GetMetadata());
const gd::String& originName = resources[i]->GetOriginName();
const gd::String& originIdentifier = resources[i]->GetOriginIdentifier();
if (!originName.empty() || !originIdentifier.empty()) {
resourceElement.AddChild("origin")
.SetAttribute("name", originName)
.SetAttribute("identifier", originIdentifier);
}
resources[i]->SerializeTo(resourceElement);
gd::ResourcesManager::SerializeResourceTo(*resources[i], resourceElement);
}
SerializerElement& resourcesFoldersElement =
@@ -556,6 +544,22 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
folders[i].SerializeTo(resourcesFoldersElement.AddChild("folder"));
}
void ResourcesManager::SerializeResourceTo(gd::Resource &resource,
SerializerElement &resourceElement) {
resourceElement.SetAttribute("kind", resource.GetKind());
resourceElement.SetAttribute("name", resource.GetName());
resourceElement.SetAttribute("metadata", resource.GetMetadata());
const gd::String &originName = resource.GetOriginName();
const gd::String &originIdentifier = resource.GetOriginIdentifier();
if (!originName.empty() || !originIdentifier.empty()) {
resourceElement.AddChild("origin")
.SetAttribute("name", originName)
.SetAttribute("identifier", originIdentifier);
}
resource.SerializeTo(resourceElement);
}
void ImageResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}

View File

@@ -662,6 +662,11 @@ class GD_CORE_API ResourcesManager {
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Serialize one resource.
*/
static void SerializeResourceTo(gd::Resource& resource, SerializerElement& resourceElement);
/**
* \brief Unserialize the object.
*/

View File

@@ -403,6 +403,11 @@ String String::LowerCase() const
return lowerCasedStr;
}
String String::CapitalizeFirstLetter() const
{
return size() < 1 ? *this : substr(0, 1).UpperCase() + substr(1);
}
String String::FindAndReplace(String search, String replacement, bool all) const
{
gd::String result(*this);

View File

@@ -522,6 +522,11 @@ public:
*/
String LowerCase() const;
/**
* \brief Returns the string with the first letter in upper case.
*/
String CapitalizeFirstLetter() const;
/**
* \brief Searches a string for a specified substring and returns a new string where all occurrences of this substring is replaced.
* \param search The string that will be replaced by the new string.

File diff suppressed because it is too large Load Diff

View File

@@ -1252,6 +1252,49 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"MySpriteObject.getObjectStringWith2ObjectParam(fakeObjectListOf_"
"Object1, fakeObjectListOf_Object2) ?? \"\"");
}
SECTION("Edge cases (variables with object name in objectvar parameter)") {
SECTION("Simple case") {
gd::String output = gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"objectvar", // We suppose we generate an "objectvar" parameter.
"MyOtherSpriteObject", // This "variable name" is the same as an object name (but this is valid).
"MySpriteObject" // The object owning the variable: MySpriteObject.
);
// This seems "obvious", but we had cases where MyOtherSpriteObject could have been interpreted as an object
// when the code generation is not properly recognizing "objectvar".
REQUIRE(output == "getVariableForObject(MySpriteObject, MyOtherSpriteObject)");
}
SECTION("With child variable") {
gd::String output = gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"objectvar", // We suppose we generate an "objectvar" parameter.
"MyOtherSpriteObject.Child", // This "variable name" is the same as an object name (but this is valid).
"MySpriteObject" // The object owning the variable: MySpriteObject.
);
// This seems "obvious", but we had cases where MyOtherSpriteObject could have been interpreted as an object
// when the code generation is not properly recognizing "objectvar".
REQUIRE(output == "getVariableForObject(MySpriteObject, MyOtherSpriteObject).getChild(\"Child\")");
}
SECTION("With child and grandchild variable") {
gd::String output = gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"objectvar", // We suppose we generate an "objectvar" parameter.
"MyOtherSpriteObject.Child.Grandchild", // This "variable name" is the same as an object name (but this is valid).
"MySpriteObject" // The object owning the variable: MySpriteObject.
);
// This seems "obvious", but we had cases where MyOtherSpriteObject could have been interpreted as an object
// when the code generation is not properly recognizing "objectvar".
REQUIRE(output == "getVariableForObject(MySpriteObject, MyOtherSpriteObject).getChild(\"Child\").getChild(\"Grandchild\")");
}
}
SECTION("Mixed test (1)") {
{
auto node = parser.ParseExpression("-+-MyExtension::MouseX(,)");

View File

@@ -11,6 +11,7 @@
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableParentFinder.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
@@ -1606,6 +1607,21 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid object variables (empty variable name, extra dot)") {
{
auto node =
parser.ParseExpression("MySpriteObjects..");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"A name should be entered after the dot.");
}
}
SECTION("Invalid object variables (object group, partially existing variable)") {
{
auto node =
@@ -1690,6 +1706,42 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
"An object variable or expression should be entered.");
}
}
SECTION("Invalid object variables (extra dot)") {
{
auto node =
parser.ParseExpression("MySpriteObject.MyVariable.");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
}
}
SECTION("Invalid object variables (extra dot after brackets)") {
{
auto node =
parser.ParseExpression("MySpriteObject.MyVariable[\"MyChild\"].");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
}
}
SECTION("Invalid object variables (extra dot before brackets)") {
{
auto node =
parser.ParseExpression("MySpriteObject.MyVariable.[\"MyChild\"]");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
}
}
SECTION("Valid property") {
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
@@ -2156,6 +2208,14 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
dynamic_cast<gd::IdentifierNode &>(*node);
REQUIRE(identifierNode.identifierName == "MyObject");
REQUIRE(identifierNode.childIdentifierName == "");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"An object variable or expression should be entered.");
}
SECTION("Unfinished object function name of type string with parentheses") {
@@ -2167,6 +2227,14 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(objectFunctionCall.objectName == "MyObject");
REQUIRE(objectFunctionCall.functionName == "");
REQUIRE(type == "string");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"Enter the name of the function to call.");
}
SECTION("Unfinished object function name of type number with parentheses") {
@@ -2193,6 +2261,19 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(type == "number|string");
}
SECTION("Unfinished object function/variable name with multiple dots") {
auto node = parser.ParseExpression("MyObject..");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"A name should be entered after the dot.");
}
SECTION("Unfinished object behavior name") {
auto node = parser.ParseExpression("MyObject.MyBehavior::");
REQUIRE(node != nullptr);
@@ -2201,6 +2282,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
REQUIRE(objectFunctionName.behaviorFunctionName == "");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"An opening parenthesis was expected here to call a function.");
}
SECTION("Unfinished object behavior name of type string with parentheses") {
@@ -2213,6 +2300,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
REQUIRE(objectFunctionName.functionName == "");
REQUIRE(type == "string");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Enter the name of the function to call.");
}
SECTION("Unfinished object behavior name of type number with parentheses") {
@@ -2498,10 +2591,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
// TODO: The error message could be improved
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Cannot find an expression with this name: \nDouble "
"check that you've not made any typo in the name.");
"Enter the name of the function to call.");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 0);
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 25);
}
@@ -2809,8 +2900,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
// as ExpressionVariableOwnerFinder depends on this parameter type
// information.
auto node = parser.ParseExpression(
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObject1, "
"MyVar1, MyObject2, MyVar2)");
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MySpriteObject, "
"MyVariable, MySpriteObject2, MyVariable2)");
REQUIRE(node != nullptr);
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
auto &identifierObject1Node =
@@ -2822,17 +2913,25 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto &variable2Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[3]);
REQUIRE(identifierObject1Node.identifierName == "MyObject1");
REQUIRE(identifierObject2Node.identifierName == "MyObject2");
REQUIRE(variable1Node.identifierName == "MyVar1");
REQUIRE(variable2Node.identifierName == "MyVar2");
REQUIRE(identifierObject1Node.identifierName == "MySpriteObject");
REQUIRE(identifierObject2Node.identifierName == "MySpriteObject2");
REQUIRE(variable1Node.identifierName == "MyVariable");
REQUIRE(variable2Node.identifierName == "MyVariable2");
auto variable1ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "", variable1Node);
REQUIRE(variable1ObjectName == "MyObject1");
REQUIRE(variable1ObjectName == "MySpriteObject");
auto variable2ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "", variable2Node);
REQUIRE(variable2ObjectName == "MyObject2");
REQUIRE(variable2ObjectName == "MySpriteObject2");
// Also check the ability to find the last parent of the variables:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
auto lastParentOfVariable2Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable2Node);
REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject2.GetVariables());
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
@@ -2843,8 +2942,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
SECTION("Valid function call with 2 object variable from the same object") {
{
auto node = parser.ParseExpression(
"MyExtension::GetStringWith1ObjectParamAnd2ObjectVarParam(MyObject1, "
"MyVar1, MyVar2)");
"MyExtension::GetStringWith1ObjectParamAnd2ObjectVarParam(MySpriteObject, "
"MyVariable, MyVariable2)");
REQUIRE(node != nullptr);
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
auto &identifierObject1Node =
@@ -2854,16 +2953,24 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto &variable2Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
REQUIRE(identifierObject1Node.identifierName == "MyObject1");
REQUIRE(variable1Node.identifierName == "MyVar1");
REQUIRE(variable2Node.identifierName == "MyVar2");
REQUIRE(identifierObject1Node.identifierName == "MySpriteObject");
REQUIRE(variable1Node.identifierName == "MyVariable");
REQUIRE(variable2Node.identifierName == "MyVariable2");
auto variable1ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "", variable1Node);
REQUIRE(variable1ObjectName == "MyObject1");
REQUIRE(variable1ObjectName == "MySpriteObject");
auto variable2ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "", variable2Node);
REQUIRE(variable2ObjectName == "MyObject1");
REQUIRE(variable2ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variables:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
auto lastParentOfVariable2Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable2Node);
REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject.GetVariables());
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
@@ -2874,18 +2981,23 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
SECTION("Valid object function call with 1 object variable from the object of the function") {
{
auto node = parser.ParseExpression(
"MySpriteObject.GetObjectVariableAsNumber(MyVar1)");
"MySpriteObject.GetObjectVariableAsNumber(MyVariable)");
REQUIRE(node != nullptr);
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
auto &variable1Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[0]);
REQUIRE(variable1Node.identifierName == "MyVar1");
REQUIRE(variable1Node.identifierName == "MyVariable");
auto variable1ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "MySpriteObject", variable1Node);
REQUIRE(variable1ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variable:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
@@ -2895,19 +3007,24 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
SECTION("Valid object function call with 1 object variable from the object of the function with a child") {
{
auto node = parser.ParseExpression(
"MySpriteObject.GetObjectVariableAsNumber(MyVar1.MyChild)");
"MySpriteObject.GetObjectVariableAsNumber(MyVariable.MyChild)");
REQUIRE(node != nullptr);
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
auto &variable1Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[0]);
REQUIRE(variable1Node.identifierName == "MyVar1");
REQUIRE(variable1Node.identifierName == "MyVariable");
REQUIRE(variable1Node.childIdentifierName == "MyChild");
auto variable1ObjectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform, objectsContainersList, "MySpriteObject", variable1Node);
REQUIRE(variable1ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variable:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariable == &mySpriteObject.GetVariables().Get("MyVariable"));
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);

View File

@@ -91,8 +91,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::PropertyMovementAngle()");
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "MovementAngle");
}
{
auto &setter =
@@ -124,8 +123,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(3).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
REQUIRE(setterAction.GetParameter(3).GetPlainString() == "Value");
}
}
@@ -343,8 +341,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.PropertyMovementAngle()");
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "MovementAngle");
}
{
auto &setter =
@@ -375,8 +372,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "Value");
}
}
@@ -578,8 +574,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::SharedPropertyMovementAngle()");
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "MovementAngle");
}
{
auto &setter =

View File

@@ -65,11 +65,11 @@ class MockFileSystem : public gd::AbstractFileSystem {
TEST_CASE("ResourcesMergingHelper", "[common]") {
SECTION("Basics") {
gd::Project project;
MockFileSystem fs;
gd::ResourcesMergingHelper resourcesMerger(fs);
gd::ResourcesMergingHelper resourcesMerger(project.GetResourcesManager(), fs);
resourcesMerger.SetBaseDirectory("/game/base/folder/");
gd::Project project;
project.GetResourcesManager().AddResource("Image1", "/image1.png", "image");
project.GetResourcesManager().AddResource("Image2", "image2.png", "image");
project.GetResourcesManager().AddResource("Audio1", "audio1.png", "audio");
@@ -90,12 +90,12 @@ TEST_CASE("ResourcesMergingHelper", "[common]") {
"FileNameFrom(MakeAbsolute(subfolder/image3.png))");
}
SECTION("Can preserve directories structure") {
gd::Project project;
MockFileSystem fs;
gd::ResourcesMergingHelper resourcesMerger(fs);
gd::ResourcesMergingHelper resourcesMerger(project.GetResourcesManager(), fs);
resourcesMerger.SetBaseDirectory("/game/base/folder/");
resourcesMerger.PreserveDirectoriesStructure(true);
gd::Project project;
project.GetResourcesManager().AddResource("Image1", "/image1.png", "image");
project.GetResourcesManager().AddResource("Image2", "image2.png", "image");
project.GetResourcesManager().AddResource("Audio1", "audio1.png", "audio");

View File

@@ -15,11 +15,10 @@
TEST_CASE("ResourcesRenamer", "[common]") {
SECTION("It renames resources that are exposed") {
gd::Project project;
std::map<gd::String, gd::String> renamings = {
{"Resource1", "RenamedResource1"}};
gd::ResourcesRenamer resourcesRenamer(renamings);
gd::Project project;
gd::ResourcesRenamer resourcesRenamer(project.GetResourcesManager(), renamings);
// Add "classic", plain resources.
gd::ImageResource resource1;
@@ -45,11 +44,10 @@ TEST_CASE("ResourcesRenamer", "[common]") {
}
SECTION("It renames embedded resources") {
gd::Project project;
std::map<gd::String, gd::String> renamings = {
{"Resource1", "RenamedResource1"}};
gd::ResourcesRenamer resourcesRenamer(renamings);
gd::Project project;
gd::ResourcesRenamer resourcesRenamer(project.GetResourcesManager(), renamings);
// Add "classic", plain resources.
gd::ImageResource resource1;

View File

@@ -201,39 +201,34 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
"MyRenamedGlobalStructureVariable");
project.GetVariables().Rename("SharedVariableName",
"RenamedGlobalVariableFromASharedName");
auto changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedProjectVariables,
project.GetVariables());
auto changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedProjectVariables,
project.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
project.GetVariables(),
changeset);
project, project.GetVariables(), changeset);
layout1.GetVariables().Rename("MySceneVariable", "MyRenamedSceneVariable");
layout1.GetVariables().Rename("MySceneStructureVariable",
"MyRenamedSceneStructureVariable");
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedLayoutVariables,
layout1.GetVariables());
changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project, originalSerializedLayoutVariables, layout1.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
layout1.GetVariables(),
changeset);
project, layout1.GetVariables(), changeset);
object1.GetVariables().Rename("MyObjectVariable",
"MyRenamedObjectVariable");
object1.GetVariables().Rename("MyObjectStructureVariable",
"MyRenamedObjectStructureVariable");
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedObject1Variables,
object1.GetVariables());
changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedObject1Variables,
object1.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
object1.GetVariables(),
changeset);
project, object1.GetVariables(), changeset);
// Check the first layout is updated.
// clang-format off
@@ -329,6 +324,182 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
gd::Serializer::ToJSON(originalSerializedLayout2));
}
}
SECTION("Variable renamed (object group)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
gd::StandardEvent &event =
dynamic_cast<gd::StandardEvent &>(layout1.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard"));
gd::RepeatEvent &repeatEvent =
dynamic_cast<gd::RepeatEvent &>(layout1.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Repeat"));
// Declare variables in objects.
auto &object1 =
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
object1.GetVariables().InsertNew("MyObjectVariable");
object1.GetVariables()
.InsertNew("MyObjectStructureVariable")
.GetChild("MyChild")
.SetValue(123);
auto &object2 =
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
object2.GetVariables().InsertNew("MyObjectVariable");
object2.GetVariables()
.InsertNew("MyObjectStructureVariable")
.GetChild("MyChild")
.SetValue(123);
auto& group = layout1.GetObjectGroups().InsertNew("MyObjectGroup");
group.AddObject("Object1");
group.AddObject("Object2");
// Create an event using the variables.
// clang-format off
{
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
"1 + "
"Object1.MyObjectVariable + "
"Object2.MyObjectVariable + "
"MyObjectGroup.MyObjectVariable + "
"Object1.MyObjectStructureVariable.MyChild + "
"Object2.MyObjectStructureVariable.MyChild + "
"MyObjectGroup.MyObjectStructureVariable.MyChild"));
event.GetActions().Insert(action);
}
// Expressions with "old" "scenevar", "globalvar", "objectvar":
{
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
// "objectvar" (in a free expression):
"1 + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectVariable, Object2, MyObjectVariable) + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObjectGroup, MyObjectVariable, MyObjectGroup, MyObjectVariable) + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild) + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObjectGroup, MyObjectStructureVariable.MyChild, MyObjectGroup, MyObjectStructureVariable.MyChild) + "
// "objectvar" (using the name of the object being called):
"Object1.GetObjectVariableAsNumber(MyObjectVariable) + "
"Object2.GetObjectVariableAsNumber(MyObjectVariable) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyObjectVariable) + "
"Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
"Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild)"));
event.GetActions().Insert(action);
}
{
gd::Instruction action;
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
action.SetParametersCount(4);
action.SetParameter(0, gd::Expression("MySceneVariable"));
action.SetParameter(1, gd::Expression("MyGlobalVariable"));
action.SetParameter(2, gd::Expression("MyObjectGroup"));
action.SetParameter(3, gd::Expression("MyObjectVariable"));
event.GetActions().Insert(action);
}
repeatEvent.SetRepeatExpression("1 + Object1.MyObjectVariable + Object2.MyObjectVariable + MyObjectGroup.MyObjectVariable");
// clang-format on
// Do a copy of layout1 to ensure other scene is unchanged after the
// refactoring.
gd::Layout layout2 = layout1;
layout2.SetName("Layout2");
project.InsertLayout(layout2, 1);
gd::SerializerElement originalSerializedLayout2;
layout2.SerializeTo(originalSerializedLayout2);
// Do the changes and launch the refactoring.
project.GetVariables().ResetPersistentUuid();
layout1.GetVariables().ResetPersistentUuid();
object1.ResetPersistentUuid();
gd::SerializerElement originalSerializedObject1Variables;
object1.GetVariables().SerializeTo(originalSerializedObject1Variables);
object1.GetVariables().Rename("MyObjectVariable",
"MyRenamedObjectVariable");
object1.GetVariables().Rename("MyObjectStructureVariable",
"MyRenamedObjectStructureVariable");
auto changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedObject1Variables,
object1.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project, object1.GetVariables(), changeset);
// Check the first layout is updated.
// clang-format off
{
// Updated direct access to variables:
REQUIRE(event.GetActions()[0].GetParameter(0).GetPlainString() ==
"1 + "
"Object1.MyRenamedObjectVariable + "
"Object2.MyObjectVariable + "
"MyObjectGroup.MyRenamedObjectVariable + "
"Object1.MyRenamedObjectStructureVariable.MyChild + "
"Object2.MyObjectStructureVariable.MyChild + "
"MyObjectGroup.MyRenamedObjectStructureVariable.MyChild"
);
// Updated access to variables using the legacy "pre-scoped" "scenevar",
// "globalvar" and "objectvar" parameters in expressions:
REQUIRE(event.GetActions()[1].GetParameter(0).GetPlainString() ==
"1 + "
// Multiple "objectvar" parameters in a free function:
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyRenamedObjectVariable, Object2, MyObjectVariable) + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObjectGroup, MyRenamedObjectVariable, MyObjectGroup, MyRenamedObjectVariable) + "
// Multiple "objectvar" parameters in a free function, with child
// variable:
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyRenamedObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild) + "
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObjectGroup, MyRenamedObjectStructureVariable.MyChild, MyObjectGroup, MyRenamedObjectStructureVariable.MyChild) + "
// Single "objectvar" from the object being accessed:
"Object1.GetObjectVariableAsNumber(MyRenamedObjectVariable) + "
"Object2.GetObjectVariableAsNumber(MyObjectVariable) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyRenamedObjectVariable) + "
// Single "objectvar" from the object being accessed, with child
// variales:
"Object1.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild) + "
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild) + "
"Object1.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild.GrandChild) + "
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
"MyObjectGroup.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild.GrandChild)");
// Updated "objectvar" parameters of an
// instruction:
REQUIRE(event.GetActions()[2].GetParameter(2).GetPlainString() ==
"MyObjectGroup");
REQUIRE(event.GetActions()[2].GetParameter(3).GetPlainString() ==
"MyRenamedObjectVariable");
}
REQUIRE(repeatEvent.GetRepeatExpression() == "1 + Object1.MyRenamedObjectVariable + Object2.MyObjectVariable + MyObjectGroup.MyRenamedObjectVariable");
// clang-format on
// Check the other layout is untouched.
{
gd::SerializerElement serializedLayout2;
layout2.SerializeTo(serializedLayout2);
REQUIRE(gd::Serializer::ToJSON(serializedLayout2) ==
gd::Serializer::ToJSON(originalSerializedLayout2));
}
}
SECTION("Variable removed (project, layout, object)") {
gd::Project project;
gd::Platform platform;
@@ -495,36 +666,31 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
project.GetVariables().Remove("MyGlobalVariable");
project.GetVariables().Remove("MyGlobalStructureVariable");
project.GetVariables().Remove("SharedVariableName");
auto changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedProjectVariables,
project.GetVariables());
auto changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedProjectVariables,
project.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
project.GetVariables(),
changeset);
project, project.GetVariables(), changeset);
layout1.GetVariables().Remove("MySceneVariable");
layout1.GetVariables().Remove("MySceneStructureVariable");
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedLayoutVariables,
layout1.GetVariables());
changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project, originalSerializedLayoutVariables, layout1.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
layout1.GetVariables(),
changeset);
project, layout1.GetVariables(), changeset);
object1.GetVariables().Remove("MyObjectVariable");
object1.GetVariables().Remove("MyObjectStructureVariable");
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedObject1Variables,
object1.GetVariables());
changeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
project,
originalSerializedObject1Variables,
object1.GetVariables());
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
project,
object1.GetVariables(),
changeset);
project, object1.GetVariables(), changeset);
// Check the first layout is updated.
{

View File

@@ -5398,12 +5398,10 @@ class Runner {
getRegistryHub().getTestCaseRegistry().getFilteredTests(
testSpec, *m_config, testCases);
int testsRunForGroup = 0;
for (std::vector<TestCase>::const_iterator it = testCases.begin(),
itEnd = testCases.end();
it != itEnd;
++it) {
testsRunForGroup++;
if (m_testsAlreadyRun.find(*it) == m_testsAlreadyRun.end()) {
if (context.aborting()) break;

View File

@@ -61,13 +61,30 @@ namespace gdjs {
this.light.intensity = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'intensity') {
return this.light.intensity;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color = new THREE.Color(
this.light.color.setHex(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
}
updateColorParameter(parameterName: string, value: number): void {
if (parameterName === 'color') {
this.light.color.setHex(value);
}
}
getColorParameter(parameterName: string): number {
if (parameterName === 'color') {
return this.light.color.getHex();
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}

View File

@@ -74,6 +74,16 @@ namespace gdjs {
this.updateRotation();
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'intensity') {
return this.light.intensity;
} else if (parameterName === 'elevation') {
return this.elevation;
} else if (parameterName === 'rotation') {
return this.rotation;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color = new THREE.Color(
@@ -85,6 +95,17 @@ namespace gdjs {
this.updateRotation();
}
}
updateColorParameter(parameterName: string, value: number): void {
if (parameterName === 'color') {
this.light.color.setHex(value);
}
}
getColorParameter(parameterName: string): number {
if (parameterName === 'color') {
return this.light.color.getHex();
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
updateRotation() {
if (this.top === 'Z+') {
@@ -93,7 +114,7 @@ namespace gdjs {
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
} else {
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation) - 90;
this.rotationObject.rotation.y = gdjs.toRad(this.rotation - 90);
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
}
}

View File

@@ -58,6 +58,12 @@ namespace gdjs {
this.fog.density = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'density') {
return this.fog.density;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
@@ -65,6 +71,17 @@ namespace gdjs {
);
}
}
updateColorParameter(parameterName: string, value: number): void {
if (parameterName === 'color') {
this.fog.color.setHex(value);
}
}
getColorParameter(parameterName: string): number {
if (parameterName === 'color') {
return this.fog.color.getHex();
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}

View File

@@ -74,6 +74,16 @@ namespace gdjs {
this.updateRotation();
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'intensity') {
return this.light.intensity;
} else if (parameterName === 'elevation') {
return this.elevation;
} else if (parameterName === 'rotation') {
return this.rotation;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'skyColor') {
this.light.color = new THREE.Color(
@@ -90,6 +100,23 @@ namespace gdjs {
this.updateRotation();
}
}
updateColorParameter(parameterName: string, value: number): void {
if (parameterName === 'skyColor') {
this.light.color.setHex(value);
}
if (parameterName === 'groundColor') {
this.light.groundColor.setHex(value);
}
}
getColorParameter(parameterName: string): number {
if (parameterName === 'skyColor') {
return this.light.color.getHex();
}
if (parameterName === 'groundColor') {
return this.light.groundColor.getHex();
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
updateRotation() {
if (this.top === 'Z+') {
@@ -98,7 +125,7 @@ namespace gdjs {
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
} else {
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation) - 90;
this.rotationObject.rotation.y = gdjs.toRad(this.rotation - 90);
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
}
}

View File

@@ -64,7 +64,7 @@ module.exports = {
_('Position'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setZ')
@@ -80,7 +80,7 @@ module.exports = {
_('Position/Center'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setCenterZInScene')
@@ -96,7 +96,7 @@ module.exports = {
_('Size'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setDepth')
@@ -112,7 +112,7 @@ module.exports = {
_('Size'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters(
'number',
@@ -134,7 +134,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('yesorno', _('Activate flipping'))
.markAsSimple()
@@ -150,7 +150,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.setFunctionName('isFlippedZ');
@@ -164,7 +164,7 @@ module.exports = {
_('Angle'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationX')
@@ -180,7 +180,7 @@ module.exports = {
_('Angle'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationY')
@@ -198,7 +198,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
@@ -216,7 +216,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
@@ -234,7 +234,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'))
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
@@ -501,7 +501,7 @@ module.exports = {
'res/actions/flipX24.png',
'res/actions/flipX.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.setHidden()
.markAsSimple()
@@ -518,7 +518,7 @@ module.exports = {
'res/actions/flipY24.png',
'res/actions/flipY.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.setHidden()
.markAsSimple()
@@ -535,7 +535,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.markAsSimple()
.setHidden()
@@ -552,7 +552,7 @@ module.exports = {
'res/actions/flipX24.png',
'res/actions/flipX.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.setHidden()
.setFunctionName('isFlippedX');
@@ -567,7 +567,7 @@ module.exports = {
'res/actions/flipY24.png',
'res/actions/flipY.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.setHidden()
.setFunctionName('isFlippedY');
@@ -582,7 +582,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.setHidden()
.setFunctionName('isFlippedZ');
@@ -690,7 +690,7 @@ module.exports = {
_('Animations and images'),
'res/actions/animation24.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.markAsSimple()
.setHidden()
@@ -708,7 +708,7 @@ module.exports = {
_('Animations and images'),
'res/actions/animation24.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters(
'objectAnimationName',
gd.ParameterOptions.makeNewOptions().setDescription(
@@ -731,7 +731,7 @@ module.exports = {
'res/actions/animation24.png',
'res/actions/animation.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.markAsSimple()
.setHidden()
.setFunctionName('pauseAnimation');
@@ -747,7 +747,7 @@ module.exports = {
'res/actions/animation24.png',
'res/actions/animation.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.markAsSimple()
.setHidden()
.setFunctionName('resumeAnimation');
@@ -765,7 +765,7 @@ module.exports = {
_('Animations and images'),
'res/actions/animation24.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(_('Speed scale'))
@@ -786,7 +786,7 @@ module.exports = {
'res/conditions/animation24.png',
'res/conditions/animation.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.markAsSimple()
.setHidden()
.setFunctionName('isAnimationPaused');
@@ -804,7 +804,7 @@ module.exports = {
'res/conditions/animation24.png',
'res/conditions/animation.png'
)
.addParameter('object', _('3D model'), 'Model3DObject')
.addParameter('object', _('3D model'), 'Model3DObject', false)
.markAsSimple()
.setHidden()
.setFunctionName('hasAnimationEnded');
@@ -1364,7 +1364,7 @@ module.exports = {
'res/actions/flipX24.png',
'res/actions/flipX.png'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.markAsSimple()
.setHidden()
@@ -1381,7 +1381,7 @@ module.exports = {
'res/actions/flipY24.png',
'res/actions/flipY.png'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.markAsSimple()
.setHidden()
@@ -1398,7 +1398,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.addParameter('yesorno', _('Activate flipping'))
.markAsSimple()
.setHidden()
@@ -1415,7 +1415,7 @@ module.exports = {
'res/actions/flipX24.png',
'res/actions/flipX.png'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.setHidden()
.setFunctionName('isFlippedX');
@@ -1430,7 +1430,7 @@ module.exports = {
'res/actions/flipY24.png',
'res/actions/flipY.png'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.setHidden()
.setFunctionName('isFlippedY');
@@ -1445,7 +1445,7 @@ module.exports = {
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject')
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.setHidden()
.setFunctionName('isFlippedZ');
@@ -2736,6 +2736,9 @@ module.exports = {
originalDepth,
keepAspectRatio
) {
// These formulas are also used in:
// - gdjs.Model3DRuntimeObject3DRenderer._updateDefaultTransformation
// - Model3DEditor.modelSize
threeObject.rotation.set(
(rotationX * Math.PI) / 180,
(rotationY * Math.PI) / 180,

View File

@@ -60,6 +60,14 @@ namespace gdjs {
this.fog.far = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'near') {
return this.fog.near;
} else if (parameterName === 'far') {
return this.fog.far;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
@@ -67,6 +75,17 @@ namespace gdjs {
);
}
}
updateColorParameter(parameterName: string, value: number): void {
if (parameterName === 'color') {
this.fog.color.setHex(value);
}
}
getColorParameter(parameterName: string): number {
if (parameterName === 'color') {
return this.fog.color.getHex();
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}

View File

@@ -147,6 +147,9 @@ namespace gdjs {
originalDepth: float,
keepAspectRatio: boolean
) {
// These formulas are also used in:
// - Model3DEditor.modelSize
// - Model3DRendered2DInstance
threeObject.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),

View File

@@ -86,11 +86,13 @@ namespace gdjs {
export const turnCameraTowardObject = (
runtimeScene: RuntimeScene,
object: gdjs.RuntimeObject,
object: gdjs.RuntimeObject | null,
layerName: string,
cameraIndex: integer,
isStandingOnY: boolean
) => {
if (!object) return;
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();

View File

@@ -36,7 +36,7 @@ module.exports = {
extension
.addAction(
'Focus',
_('Change focus of the window'),
_('Window focus'),
_('Make the window gain or lose focus.'),
_('Focus the window: _PARAM0_'),
_('Windows, Linux, macOS'),
@@ -72,7 +72,7 @@ module.exports = {
extension
.addAction(
'Show',
_('Change visibility of the window'),
_('Window visibility'),
_('Make the window visible or invisible.'),
_('Window visible: _PARAM0_'),
_('Windows, Linux, macOS'),
@@ -624,7 +624,7 @@ module.exports = {
extension
.addAction(
'SetOpacity',
_('Set window opacity'),
_('Window opacity'),
_('Changes the window opacity.'),
_('Set the window opacity to _PARAM0_'),
_('Windows, Linux, macOS'),
@@ -645,7 +645,7 @@ module.exports = {
extension
.addAction(
'SetWindowPosition',
_('Set window position'),
_('Window position'),
_('Changes the window position.'),
_('Set the window position to _PARAM0_;_PARAM1_'),
_('Windows, Linux, macOS'),

View File

@@ -30,6 +30,7 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
behaviorsSharedData: [],
objects: [],
instances: [],
usedResources: [],
});
function createObject(behaviorProperties) {

View File

@@ -27,6 +27,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
behaviorsSharedData: [],
objects: [],
instances: [],
usedResources: [],
});
var object = new gdjs.TestRuntimeObject(runtimeScene, {

View File

@@ -31,11 +31,47 @@ namespace gdjs {
adjustmentFilter.alpha = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
return adjustmentFilter.gamma;
}
if (parameterName === 'saturation') {
return adjustmentFilter.saturation;
}
if (parameterName === 'contrast') {
return adjustmentFilter.contrast;
}
if (parameterName === 'brightness') {
return adjustmentFilter.brightness;
}
if (parameterName === 'red') {
return adjustmentFilter.red;
}
if (parameterName === 'green') {
return adjustmentFilter.green;
}
if (parameterName === 'blue') {
return adjustmentFilter.blue;
}
if (parameterName === 'alpha') {
return adjustmentFilter.alpha;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -27,11 +27,41 @@ namespace gdjs {
advancedBloomFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
return advancedBloomFilter.threshold;
}
if (parameterName === 'bloomScale') {
return advancedBloomFilter.bloomScale;
}
if (parameterName === 'brightness') {
return advancedBloomFilter.brightness;
}
if (parameterName === 'blur') {
return advancedBloomFilter.blur;
}
if (parameterName === 'quality') {
return advancedBloomFilter.quality;
}
if (parameterName === 'padding') {
return advancedBloomFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -17,11 +17,26 @@ namespace gdjs {
asciiFilter.size = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
return asciiFilter.size;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,8 @@
namespace gdjs {
interface BevelFilterExtra {
/** It's defined for the configuration but not for the filter. */
distance: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Bevel',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -12,13 +16,13 @@ namespace gdjs {
parameterName: string,
value: number
) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
} else if (parameterName === 'thickness') {
bevelFilter.thickness = value;
} else if (parameterName === 'distance') {
// @ts-ignore
bevelFilter.distance = value;
} else if (parameterName === 'lightAlpha') {
bevelFilter.lightAlpha = value;
@@ -26,12 +30,33 @@ namespace gdjs {
bevelFilter.shadowAlpha = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'rotation') {
return bevelFilter.rotation;
}
if (parameterName === 'thickness') {
return bevelFilter.thickness;
}
if (parameterName === 'distance') {
return bevelFilter.distance;
}
if (parameterName === 'lightAlpha') {
return bevelFilter.lightAlpha;
}
if (parameterName === 'shadowAlpha') {
return bevelFilter.shadowAlpha;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
@@ -43,6 +68,30 @@ namespace gdjs {
);
}
}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = value;
}
if (parameterName === 'shadowColor') {
bevelFilter.shadowColor = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
return bevelFilter.lightColor;
}
if (parameterName === 'shadowColor') {
return bevelFilter.shadowColor;
}
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -19,11 +19,26 @@ namespace gdjs {
}
colorMatrix.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
if (parameterName === 'opacity') {
return colorMatrix.alpha;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -19,11 +19,29 @@ namespace gdjs {
blendingModeFilter.blendMode = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
if (parameterName === 'alpha') {
return blendingModeFilter.alpha;
}
if (parameterName === 'blendmode') {
return blendingModeFilter.blendMode;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -25,11 +25,22 @@ namespace gdjs {
}
filter[parameterName] = value;
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
return filter[parameterName] || 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,8 @@
namespace gdjs {
interface BrightnessFilterExtra {
/** It allows to get back the value as the filter uses a matrix. */
__brightness: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Brightness',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -13,20 +17,36 @@ namespace gdjs {
parameterName: string,
value: number
) {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
if (parameterName !== 'brightness') {
return;
}
brightnessFilter.brightness(
gdjs.PixiFiltersTools.clampValue(value, 0, 1),
false
);
const brightness = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
brightnessFilter.__brightness = brightness;
brightnessFilter.brightness(brightness, false);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
if (parameterName === 'brightness') {
return brightnessFilter.__brightness;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -27,11 +27,35 @@ namespace gdjs {
);
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
return bulgePinchFilter.center[0];
}
if (parameterName === 'centerY') {
return bulgePinchFilter.center[1];
}
if (parameterName === 'radius') {
return bulgePinchFilter.radius;
}
if (parameterName === 'strength') {
return bulgePinchFilter.strength;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -34,11 +34,26 @@ namespace gdjs {
);
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
return colorMapFilter.mix;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,10 @@
namespace gdjs {
interface ColorReplaceFilterExtra {
/** It's only set to a number. */
originalColor: number;
/** It's only set to a number. */
newColor: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'ColorReplace',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -12,17 +18,27 @@ namespace gdjs {
parameterName: string,
value: number
) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'epsilon') {
colorReplaceFilter.epsilon = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'epsilon') {
return colorReplaceFilter.epsilon;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
@@ -33,6 +49,29 @@ namespace gdjs {
);
}
}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = value;
} else if (parameterName === 'newColor') {
colorReplaceFilter.newColor = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
return colorReplaceFilter.originalColor;
} else if (parameterName === 'newColor') {
return colorReplaceFilter.newColor;
}
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,25 +1,32 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
interface CRTFilterExtra {
_animationTimer: number;
animationSpeed: number;
animationFrequency: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'CRT',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const crtFilter = new PIXI.filters.CRTFilter();
const filter = new PIXI.filters.CRTFilter();
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
CRTFilterExtra;
crtFilter._animationTimer = 0;
return crtFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
if (filter.animationSpeed !== 0) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (crtFilter.animationSpeed !== 0) {
// Multiply by 10 so that the default value is a sensible speed
filter.time +=
(target.getElapsedTime() / 1000) * 10 * filter.animationSpeed;
crtFilter.time +=
(target.getElapsedTime() / 1000) * 10 * crtFilter.animationSpeed;
}
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
if (crtFilter.animationFrequency !== 0) {
crtFilter._animationTimer += target.getElapsedTime() / 1000;
if (crtFilter._animationTimer >= 1 / crtFilter.animationFrequency) {
crtFilter.seed = Math.random();
crtFilter._animationTimer = 0;
}
}
}
@@ -28,42 +35,91 @@ namespace gdjs {
parameterName: string,
value: number
) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (parameterName === 'lineWidth') {
filter.lineWidth = value;
crtFilter.lineWidth = value;
} else if (parameterName === 'lineContrast') {
filter.lineContrast = value;
crtFilter.lineContrast = value;
} else if (parameterName === 'noise') {
filter.noise = value;
crtFilter.noise = value;
} else if (parameterName === 'curvature') {
filter.curvature = value;
crtFilter.curvature = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
crtFilter.noiseSize = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
crtFilter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
crtFilter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
crtFilter.vignettingBlur = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
crtFilter.animationSpeed = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
crtFilter.animationFrequency = value;
} else if (parameterName === 'padding') {
filter.padding = value;
crtFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (parameterName === 'lineWidth') {
return crtFilter.lineWidth;
}
if (parameterName === 'lineContrast') {
return crtFilter.lineContrast;
}
if (parameterName === 'noise') {
return crtFilter.noise;
}
if (parameterName === 'curvature') {
return crtFilter.curvature;
}
if (parameterName === 'noiseSize') {
return crtFilter.noiseSize;
}
if (parameterName === 'vignetting') {
return crtFilter.vignetting;
}
if (parameterName === 'vignettingAlpha') {
return crtFilter.vignettingAlpha;
}
if (parameterName === 'vignettingBlur') {
return crtFilter.vignettingBlur;
}
if (parameterName === 'animationSpeed') {
return crtFilter.animationSpeed;
}
if (parameterName === 'animationFrequency') {
return crtFilter.animationFrequency;
}
if (parameterName === 'padding') {
return crtFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,
value: boolean
) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter;
if (parameterName === 'verticalLine') {
filter.verticalLine = value;
crtFilter.verticalLine = value;
}
}
})()

View File

@@ -29,11 +29,29 @@ namespace gdjs {
displacementFilter.scale.y = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
if (parameterName === 'scaleX') {
return displacementFilter.scale.x;
}
if (parameterName === 'scaleY') {
return displacementFilter.scale.y;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -19,11 +19,29 @@ namespace gdjs {
dotFilter.angle = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
return dotFilter.scale;
}
if (parameterName === 'angle') {
return dotFilter.angle;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -27,6 +27,28 @@ namespace gdjs {
dropShadowFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
return dropShadowFilter.blur;
}
if (parameterName === 'quality') {
return dropShadowFilter.quality;
}
if (parameterName === 'alpha') {
return dropShadowFilter.alpha;
}
if (parameterName === 'distance') {
return dropShadowFilter.distance;
}
if (parameterName === 'rotation') {
return dropShadowFilter.rotation;
}
if (parameterName === 'padding') {
return dropShadowFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
@@ -39,6 +61,23 @@ namespace gdjs {
);
}
}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
return dropShadowFilter.color;
}
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,20 +1,29 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
interface GlitchFilterExtra {
_animationTimer: number;
animationFrequency: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Glitch',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const glitchFilter = new PIXI.filters.GlitchFilter();
const filter = new PIXI.filters.GlitchFilter();
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
glitchFilter._animationTimer = 0;
return glitchFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (glitchFilter.animationFrequency !== 0) {
glitchFilter._animationTimer += target.getElapsedTime() / 1000;
if (
glitchFilter._animationTimer >=
1 / glitchFilter.animationFrequency
) {
glitchFilter.seed = Math.random();
glitchFilter._animationTimer = 0;
}
}
}
@@ -23,46 +32,102 @@ namespace gdjs {
parameterName: string,
value: number
) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'slices') {
filter.slices = value;
glitchFilter.slices = value;
} else if (parameterName === 'offset') {
filter.offset = value;
glitchFilter.offset = value;
} else if (parameterName === 'direction') {
filter.direction = value;
glitchFilter.direction = value;
} else if (parameterName === 'fillMode') {
filter.fillMode = value;
glitchFilter.fillMode = value;
} else if (parameterName === 'minSize') {
filter.minSize = value;
glitchFilter.minSize = value;
} else if (parameterName === 'sampleSize') {
filter.sampleSize = value;
glitchFilter.sampleSize = value;
} else if (parameterName === 'redX') {
filter.red.x = value;
glitchFilter.red.x = value;
} else if (parameterName === 'redY') {
filter.red.y = value;
glitchFilter.red.y = value;
} else if (parameterName === 'greenX') {
filter.green.x = value;
glitchFilter.green.x = value;
} else if (parameterName === 'greenY') {
filter.green.y = value;
glitchFilter.green.y = value;
} else if (parameterName === 'blueX') {
filter.blue.x = value;
glitchFilter.blue.x = value;
} else if (parameterName === 'blueY') {
filter.blue.y = value;
glitchFilter.blue.y = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
glitchFilter.animationFrequency = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'slices') {
return glitchFilter.slices;
}
if (parameterName === 'offset') {
return glitchFilter.offset;
}
if (parameterName === 'direction') {
return glitchFilter.direction;
}
if (parameterName === 'fillMode') {
return glitchFilter.fillMode;
}
if (parameterName === 'minSize') {
return glitchFilter.minSize;
}
if (parameterName === 'sampleSize') {
return glitchFilter.sampleSize;
}
if (parameterName === 'redX') {
return glitchFilter.red.x;
}
if (parameterName === 'redY') {
return glitchFilter.red.y;
}
if (parameterName === 'greenX') {
return glitchFilter.green.x;
}
if (parameterName === 'greenY') {
return glitchFilter.green.y;
}
if (parameterName === 'blueX') {
return glitchFilter.blue.x;
}
if (parameterName === 'blueY') {
return glitchFilter.blue.y;
}
if (parameterName === 'animationFrequency') {
return glitchFilter.animationFrequency;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,
value: boolean
) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'average') {
filter.average = value;
glitchFilter.average = value;
}
}
})()

View File

@@ -1,4 +1,7 @@
namespace gdjs {
interface GlowFilterExtra {
distance: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Glow',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -12,26 +15,60 @@ namespace gdjs {
parameterName: string,
value: number
) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'innerStrength') {
glowFilter.innerStrength = value;
} else if (parameterName === 'outerStrength') {
glowFilter.outerStrength = value;
} else if (parameterName === 'distance') {
// @ts-ignore
glowFilter.distance = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'innerStrength') {
return glowFilter.innerStrength;
}
if (parameterName === 'outerStrength') {
return glowFilter.outerStrength;
}
if (parameterName === 'distance') {
return glowFilter.distance;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
}
}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
glowFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
return glowFilter.color;
}
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,6 +1,10 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
interface GodrayFilterExtra {
animationSpeed: number;
light: number;
x: number;
y: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Godray',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -9,9 +13,11 @@ namespace gdjs {
return godrayFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
if (filter.animationSpeed !== 0) {
filter.time +=
(target.getElapsedTime() / 1000) * filter.animationSpeed;
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (godrayFilter.animationSpeed !== 0) {
godrayFilter.time +=
(target.getElapsedTime() / 1000) * godrayFilter.animationSpeed;
}
}
updateDoubleParameter(
@@ -19,36 +25,77 @@ namespace gdjs {
parameterName: string,
value: number
) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'lacunarity') {
filter.lacunarity = value;
godrayFilter.lacunarity = value;
} else if (parameterName === 'angle') {
filter.angle = value;
godrayFilter.angle = value;
} else if (parameterName === 'gain') {
filter.gain = value;
godrayFilter.gain = value;
} else if (parameterName === 'light') {
filter.light = value;
godrayFilter.light = value;
} else if (parameterName === 'x') {
filter.x = value;
godrayFilter.x = value;
} else if (parameterName === 'y') {
filter.y = value;
godrayFilter.y = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
godrayFilter.animationSpeed = value;
} else if (parameterName === 'padding') {
filter.padding = value;
godrayFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'lacunarity') {
return godrayFilter.lacunarity;
}
if (parameterName === 'angle') {
return godrayFilter.angle;
}
if (parameterName === 'gain') {
return godrayFilter.gain;
}
if (parameterName === 'light') {
return godrayFilter.light;
}
if (parameterName === 'x') {
return godrayFilter.x;
}
if (parameterName === 'y') {
return godrayFilter.y;
}
if (parameterName === 'animationSpeed') {
return godrayFilter.animationSpeed;
}
if (parameterName === 'padding') {
return godrayFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,
value: boolean
) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'parallel') {
filter.parallel = value;
godrayFilter.parallel = value;
}
}
})()

View File

@@ -23,11 +23,35 @@ namespace gdjs {
hslAdjustmentFilter.alpha = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const hslAdjustmentFilter = filter as PIXI.filters.HslAdjustmentFilter;
if (parameterName === 'hue') {
return hslAdjustmentFilter.hue;
}
if (parameterName === 'saturation') {
return hslAdjustmentFilter.saturation;
}
if (parameterName === 'lightness') {
return hslAdjustmentFilter.lightness;
}
if (parameterName === 'alpha') {
return hslAdjustmentFilter.alpha;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -23,11 +23,35 @@ namespace gdjs {
kawaseBlurFilter.quality = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
return kawaseBlurFilter.pixelSize[0];
}
if (parameterName === 'pixelizeY') {
return kawaseBlurFilter.pixelSize[1];
}
if (parameterName === 'blur') {
return kawaseBlurFilter.blur;
}
if (parameterName === 'quality') {
return kawaseBlurFilter.quality;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -34,16 +34,33 @@ namespace gdjs {
parameterName: string,
value: number
) {
if (parameterName !== 'opacity') {
return;
if (parameterName === 'opacity') {
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(
value,
0,
1
);
}
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
if (parameterName === 'opacity') {
return filter.uniforms.opacity;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,8 @@
namespace gdjs {
interface MotionBlurFilterExtra {
/**Use the private member avoids to instantiate Arrays.*/
_velocity: PIXI.Point;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'MotionBlur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -12,12 +16,11 @@ namespace gdjs {
parameterName: string,
value: number
) {
const motionBlurFilter = filter as PIXI.filters.MotionBlurFilter;
const motionBlurFilter = filter as PIXI.filters.MotionBlurFilter &
MotionBlurFilterExtra;
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;
@@ -25,11 +28,36 @@ namespace gdjs {
motionBlurFilter.offset = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const motionBlurFilter = filter as PIXI.filters.MotionBlurFilter &
MotionBlurFilterExtra;
if (parameterName === 'velocityX') {
return motionBlurFilter._velocity.x;
}
if (parameterName === 'velocityY') {
return motionBlurFilter._velocity.y;
}
if (parameterName === 'kernelSize') {
return motionBlurFilter.kernelSize;
}
if (parameterName === 'offset') {
return motionBlurFilter.offset;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -47,11 +47,22 @@ namespace gdjs {
1
);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
return filter.uniforms[parameterName] || 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -13,16 +13,30 @@ namespace gdjs {
value: number
) {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
if (parameterName !== 'noise') {
return;
if (parameterName === 'noise') {
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
if (parameterName === 'noise') {
return noiseFilter.noise;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,20 +1,29 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
interface OldFilmFilterExtra {
_animationTimer: number;
animationFrequency: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'OldFilm',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const oldFilmFilter = new PIXI.filters.OldFilmFilter();
const filter = new PIXI.filters.OldFilmFilter();
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
oldFilmFilter._animationTimer = 0;
return oldFilmFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (oldFilmFilter.animationFrequency !== 0) {
oldFilmFilter._animationTimer += target.getElapsedTime() / 1000;
if (
oldFilmFilter._animationTimer >=
1 / oldFilmFilter.animationFrequency
) {
oldFilmFilter.seed = Math.random();
oldFilmFilter._animationTimer = 0;
}
}
}
@@ -23,33 +32,78 @@ namespace gdjs {
parameterName: string,
value: number
) {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (parameterName === 'sepia') {
filter.sepia = value;
oldFilmFilter.sepia = value;
} else if (parameterName === 'noise') {
filter.noise = value;
oldFilmFilter.noise = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
oldFilmFilter.noiseSize = value;
} else if (parameterName === 'scratch') {
filter.scratch = value;
oldFilmFilter.scratch = value;
} else if (parameterName === 'scratchDensity') {
filter.scratchDensity = value;
oldFilmFilter.scratchDensity = value;
} else if (parameterName === 'scratchWidth') {
filter.scratchWidth = value;
oldFilmFilter.scratchWidth = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
oldFilmFilter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
oldFilmFilter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
oldFilmFilter.vignettingBlur = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
oldFilmFilter.animationFrequency = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (parameterName === 'sepia') {
return oldFilmFilter.sepia;
}
if (parameterName === 'noise') {
return oldFilmFilter.noise;
}
if (parameterName === 'noiseSize') {
return oldFilmFilter.noiseSize;
}
if (parameterName === 'scratch') {
return oldFilmFilter.scratch;
}
if (parameterName === 'scratchDensity') {
return oldFilmFilter.scratchDensity;
}
if (parameterName === 'scratchWidth') {
return oldFilmFilter.scratchWidth;
}
if (parameterName === 'vignetting') {
return oldFilmFilter.vignetting;
}
if (parameterName === 'vignettingAlpha') {
return oldFilmFilter.vignettingAlpha;
}
if (parameterName === 'vignettingBlur') {
return oldFilmFilter.vignettingBlur;
}
if (parameterName === 'animationFrequency') {
return oldFilmFilter.animationFrequency;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -19,6 +19,16 @@ namespace gdjs {
outlineFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'thickness') {
return outlineFilter.thickness;
}
if (parameterName === 'padding') {
return outlineFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
@@ -31,6 +41,23 @@ namespace gdjs {
);
}
}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
return outlineFilter.color;
}
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,8 @@
namespace gdjs {
interface PixelateFilterExtra {
/** It's only set to a number. */
size: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Pixelate',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -14,16 +18,33 @@ namespace gdjs {
parameterName: string,
value: number
) {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter;
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
if (parameterName === 'size') {
pixelateFilter.size = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
if (parameterName === 'size') {
return pixelateFilter.size;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -4,7 +4,7 @@ declare namespace PIXI.filters {
curvature: number;
lineWidth: number;
lineContrast: number;
verticalLine: number;
verticalLine: boolean;
noise: number;
noiseSize: number;
seed: number;
@@ -17,7 +17,7 @@ declare namespace PIXI.filters {
curvature?: number;
lineWidth?: number;
lineContrast?: number;
verticalLine?: number;
verticalLine?: boolean;
noise?: number;
noiseSize?: number;
seed?: number;

View File

@@ -6,6 +6,9 @@ declare namespace PIXI.filters {
direction: number;
fillMode: number;
seed: number;
average: boolean;
minSize: number;
sampleSize: number;
red: PIXI.Point;
green: PIXI.Point;
blue: PIXI.Point;
@@ -21,13 +24,13 @@ declare namespace PIXI.filters {
offset: number;
direction: number;
fillMode: number;
average: boolean;
seed: number;
average: boolean;
minSize: number;
sampleSize: number;
red: PIXI.Point;
green: PIXI.Point;
blue: PIXI.Point;
minSize: number;
sampleSize: number;
}
}

View File

@@ -1,4 +1,9 @@
namespace gdjs {
interface RadialBlurFilterExtra {
// extra properties are stored on the filter.
_centerX: number;
_centerY: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'RadialBlur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -7,13 +12,12 @@ namespace gdjs {
return radialBlurFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter;
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
radialBlurFilter.center[0] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
radialBlurFilter._centerX * target.getWidth()
);
radialBlurFilter.center[1] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
radialBlurFilter._centerY * target.getHeight()
);
}
@@ -22,7 +26,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter;
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
if (parameterName === 'radius') {
radialBlurFilter.radius = value < 0 ? -1 : value;
} else if (parameterName === 'angle') {
@@ -34,20 +39,49 @@ namespace gdjs {
25
);
} else if (parameterName === 'centerX') {
// @ts-ignore - extra properties are stored on the filter.
radialBlurFilter._centerX = value;
} else if (parameterName === 'centerY') {
// @ts-ignore - extra properties are stored on the filter.
radialBlurFilter._centerY = value;
} else if (parameterName === 'padding') {
radialBlurFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
if (parameterName === 'radius') {
radialBlurFilter.radius;
}
if (parameterName === 'angle') {
radialBlurFilter.angle;
}
if (parameterName === 'kernelSize') {
radialBlurFilter.kernelSize;
}
if (parameterName === 'centerX') {
radialBlurFilter._centerX;
}
if (parameterName === 'centerY') {
radialBlurFilter._centerY;
}
if (parameterName === 'padding') {
radialBlurFilter.padding;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,34 +1,39 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
interface ReflectionFilterExtra {
_animationTimer: number;
animationSpeed: number;
animationFrequency: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Reflection',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
let time = 0;
const reflectionFilter = new PIXI.filters.ReflectionFilter(
effectData.booleanParameters.mirror,
effectData.doubleParameters.boundary,
[
const reflectionFilter = new PIXI.filters.ReflectionFilter({
mirror: effectData.booleanParameters.mirror,
boundary: effectData.doubleParameters.boundary,
amplitude: [
effectData.doubleParameters.amplitudeStart,
effectData.doubleParameters.amplitudeEnding,
],
[
waveLength: [
effectData.doubleParameters.waveLengthStart,
effectData.doubleParameters.waveLengthEnding,
],
[
alpha: [
effectData.doubleParameters.alphaStart,
effectData.doubleParameters.alphaEnding,
],
time
);
time,
});
return reflectionFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
if (filter.animationSpeed !== 0) {
filter.time +=
(target.getElapsedTime() / 1000) * filter.animationSpeed;
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (reflectionFilter.animationSpeed !== 0) {
reflectionFilter.time +=
(target.getElapsedTime() / 1000) * reflectionFilter.animationSpeed;
}
}
updateDoubleParameter(
@@ -36,43 +41,84 @@ namespace gdjs {
parameterName: string,
value: number
) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'boundary') {
filter.boundary = value;
reflectionFilter.boundary = value;
}
if (parameterName === 'amplitudeStart') {
filter.amplitude[0] = value;
reflectionFilter.amplitude[0] = value;
}
if (parameterName === 'amplitudeEnding') {
filter.amplitude[1] = value;
reflectionFilter.amplitude[1] = value;
}
if (parameterName === 'waveLengthStart') {
filter.waveLength[0] = value;
reflectionFilter.waveLength[0] = value;
}
if (parameterName === 'waveLengthEnding') {
filter.waveLength[1] = value;
reflectionFilter.waveLength[1] = value;
}
if (parameterName === 'alphaStart') {
filter.alpha[0] = value;
reflectionFilter.alpha[0] = value;
}
if (parameterName === 'alphaEnding') {
filter.alpha[1] = value;
reflectionFilter.alpha[1] = value;
}
if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
reflectionFilter.animationSpeed = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'boundary') {
return reflectionFilter.boundary;
}
if (parameterName === 'amplitudeStart') {
return reflectionFilter.amplitude[0];
}
if (parameterName === 'amplitudeEnding') {
return reflectionFilter.amplitude[1];
}
if (parameterName === 'waveLengthStart') {
return reflectionFilter.waveLength[0];
}
if (parameterName === 'waveLengthEnding') {
return reflectionFilter.waveLength[1];
}
if (parameterName === 'alphaStart') {
return reflectionFilter.alpha[0];
}
if (parameterName === 'alphaEnding') {
return reflectionFilter.alpha[1];
}
if (parameterName === 'animationSpeed') {
return reflectionFilter.animationSpeed;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,
value: boolean
) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'mirror') {
filter.mirror = value;
reflectionFilter.mirror = value;
}
}
})()

View File

@@ -27,11 +27,41 @@ namespace gdjs {
rgbSplitFilter.blue.y = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
if (parameterName === 'redX') {
return rgbSplitFilter.red.x;
}
if (parameterName === 'redY') {
return rgbSplitFilter.red.y;
}
if (parameterName === 'greenX') {
return rgbSplitFilter.green.x;
}
if (parameterName === 'greenY') {
return rgbSplitFilter.green.y;
}
if (parameterName === 'blueX') {
return rgbSplitFilter.blue.x;
}
if (parameterName === 'blueY') {
return rgbSplitFilter.blue.y;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -14,16 +14,34 @@ namespace gdjs {
value: number
) {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
if (parameterName === 'opacity') {
colorMatrixFilter.alpha = gdjs.PixiFiltersTools.clampValue(
value,
0,
1
);
}
colorMatrixFilter.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
if (parameterName === 'opacity') {
return colorMatrixFilter.alpha;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

View File

@@ -1,4 +1,9 @@
namespace gdjs {
interface ShockwaveFilterExtra {
// extra properties are stored on the filter.
_centerX: number;
_centerY: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Shockwave',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -7,16 +12,15 @@ namespace gdjs {
return shockwaveFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const shockwaveFilter = (filter as unknown) as PIXI.filters.ShockwaveFilter;
const shockwaveFilter = (filter as unknown) as PIXI.filters.ShockwaveFilter &
ShockwaveFilterExtra;
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()
);
}
@@ -25,12 +29,11 @@ namespace gdjs {
parameterName: string,
value: number
) {
const shockwaveFilter = filter as PIXI.filters.ShockwaveFilter;
const shockwaveFilter = filter as PIXI.filters.ShockwaveFilter &
ShockwaveFilterExtra;
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;
@@ -46,11 +49,48 @@ namespace gdjs {
shockwaveFilter.radius = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const shockwaveFilter = filter as PIXI.filters.ShockwaveFilter &
ShockwaveFilterExtra;
if (parameterName === 'centerX') {
return shockwaveFilter._centerX;
}
if (parameterName === 'centerY') {
return shockwaveFilter._centerY;
}
if (parameterName === 'time') {
return shockwaveFilter.time;
}
if (parameterName === 'speed') {
return shockwaveFilter.speed;
}
if (parameterName === 'amplitude') {
return shockwaveFilter.amplitude;
}
if (parameterName === 'wavelength') {
return shockwaveFilter.wavelength;
}
if (parameterName === 'brightness') {
return shockwaveFilter.brightness;
}
if (parameterName === 'radius') {
return shockwaveFilter.radius;
}
return 0;
}
updateStringParameter(
filter: PIXI.Filter,
parameterName: string,
value: string
) {}
updateColorParameter(
filter: PIXI.Filter,
parameterName: string,
value: number
): void {}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
return 0;
}
updateBooleanParameter(
filter: PIXI.Filter,
parameterName: string,

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