Compare commits

...

64 Commits

Author SHA1 Message Date
Florian Rival
0ffaefaf1d Merge branch 'master' into pr/5429 2023-12-05 17:10:25 +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
Arthur Pacaud (arthuro555)
034e6ac839 Merge branch 'experimental-build/emscripten-upgrade' into gdcore-ts 2023-11-30 22:31:03 +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
Florian Rival
afc2d284c1 Fix missing distutils 2023-11-21 18:27:45 +01:00
Florian Rival
c2ed4e9910 Fix test not finishing on CircleCI Linux machines 2023-11-21 16:19:05 +01:00
Florian Rival
8d2341bd21 Try forceExit 2023-11-21 15:36:56 +01:00
Florian Rival
b187d9ec98 Merge branch 'master' into emsdk-update 2023-11-21 14:45:46 +01:00
Florian Rival
d2da102830 Add --detectOpenHandles 2023-11-21 14:45:06 +01:00
Florian Rival
a6c4f742c2 Comment debugging options to see if the test in the CI pass 2023-09-08 14:40:43 +02:00
Florian Rival
e6935206c0 Try without wasm-exceptions flag 2023-09-08 14:18:35 +02:00
Florian Rival
00190de683 Revert to Node 16 on Travis for now 2023-09-07 10:52:10 +02:00
Florian Rival
7a8c359379 Upgrade CircleCI/AppVeyor to Emscripten 3.1.21 2023-09-07 09:24:01 +02:00
Florian Rival
29aa2479f6 Upgrade Travis to Emscripten 3.1.21 and Node.js 18 2023-09-07 09:20:46 +02:00
Arthur Pacaud
7010b6880d Update Emscripten version 2023-09-07 04:31:09 +02:00
Arthur Pacaud
2a9d826ab5 Try a package-lock v1 2023-07-21 00:40:00 +02:00
Florian Rival
5b255e46c6 Regenerate again package-lock.json 2023-07-20 22:04:47 +02:00
Arthur Pacaud
5043baa367 Regenerate package-lock.json 2023-07-18 09:31:32 +02:00
Arthur Pacaud
66ff2d5b55 Add missing enum type generation 2023-07-15 02:04:40 +02:00
Arthur Pacaud
3a015f6696 Fix constructors generation regression 2023-07-15 01:51:14 +02:00
Arthur Pacaud
647f3e1cfa Fix method type generation 2023-07-15 01:33:06 +02:00
Arthur Pacaud
52c3241f44 Apply review instructions 2023-07-15 01:23:13 +02:00
Arthur Pacaud
0ae40e0911 Merge remote-tracking branch 'origin/master' into gdcore-ts 2023-07-15 01:17:28 +02:00
Arthur Pacaud
0f788b4913 Autogenerate TS typedefs for Core 2023-06-21 15:12:29 +02:00
348 changed files with 21235 additions and 11112 deletions

View File

@@ -30,7 +30,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
@@ -107,7 +107,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
- run:
name: Install system dependencies for Electron builder
@@ -127,7 +127,8 @@ jobs:
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
- run:
@@ -184,7 +185,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
@@ -200,7 +201,8 @@ jobs:
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
- save_cache:
paths:

View File

@@ -14,7 +14,7 @@ tasks:
init: |
sudo apt-get update
sudo apt install cmake python-is-python3 python3-distutils -y
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
cd GDevelop.js
npm install
source ../emsdk/emsdk_env.sh && npm run build -- --dev

View File

@@ -39,7 +39,7 @@ install:
- cd ..
# Install Emscripten (for GDevelop.js)
- git clone https://github.com/juj/emsdk.git
- cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
- cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# Install GDevelop.js dependencies
- cd GDevelop.js && npm install && cd ..
# Build GDevelop.js

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

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

@@ -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
: (supplementaryInformation.empty()
? ""
: extensionNamespace + 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
: (supplementaryInformation.empty()
? ""
: extensionNamespace + 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

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

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

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

View File

@@ -19,11 +19,29 @@ namespace gdjs {
tiltShiftFilter.gradientBlur = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
if (parameterName === 'blur') {
return tiltShiftFilter.blur;
}
if (parameterName === 'gradientBlur') {
return tiltShiftFilter.gradientBlur;
}
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 TwistFilterExtra {
// extra properties are stored on the filter.
_offsetX: number;
_offsetY: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Twist',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -8,13 +13,12 @@ namespace gdjs {
return twistFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter;
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
TwistFilterExtra;
twistFilter.offset.x = Math.round(
// @ts-ignore - extra properties are stored on the filter.
twistFilter._offsetX * target.getWidth()
);
twistFilter.offset.y = Math.round(
// @ts-ignore - extra properties are stored on the filter.
twistFilter._offsetY * target.getHeight()
);
}
@@ -23,7 +27,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter;
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
TwistFilterExtra;
if (parameterName === 'radius') {
twistFilter.radius = value;
} else if (parameterName === 'angle') {
@@ -31,18 +36,44 @@ namespace gdjs {
} else if (parameterName === 'padding') {
twistFilter.padding = value;
} else if (parameterName === 'offsetX') {
// @ts-ignore - extra properties are stored on the filter.
twistFilter._offsetX = value;
} else if (parameterName === 'offsetY') {
// @ts-ignore - extra properties are stored on the filter.
twistFilter._offsetY = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
TwistFilterExtra;
if (parameterName === 'radius') {
return twistFilter.radius;
}
if (parameterName === 'angle') {
return twistFilter.angle;
}
if (parameterName === 'padding') {
return twistFilter.padding;
}
if (parameterName === 'offsetX') {
return twistFilter._offsetX;
}
if (parameterName === 'offsetY') {
return twistFilter._offsetY;
}
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 ZoomBlurFilterExtra {
// extra properties are stored on the filter.
_centerX: number;
_centerY: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'ZoomBlur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -7,13 +12,12 @@ namespace gdjs {
return zoomBlurFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter;
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
zoomBlurFilter.center[0] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
zoomBlurFilter._centerX * target.getWidth()
);
zoomBlurFilter.center[1] = Math.round(
// @ts-ignore - extra properties are stored on the filter.
zoomBlurFilter._centerY * target.getHeight()
);
}
@@ -22,12 +26,11 @@ namespace gdjs {
parameterName: string,
value: number
) {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter;
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
if (parameterName === 'centerX') {
// @ts-ignore - extra properties are stored on the filter.
zoomBlurFilter._centerX = value;
} else if (parameterName === 'centerY') {
// @ts-ignore - extra properties are stored on the filter.
zoomBlurFilter._centerY = value;
} else if (parameterName === 'innerRadius') {
zoomBlurFilter.innerRadius = value;
@@ -41,11 +44,39 @@ namespace gdjs {
zoomBlurFilter.padding = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
if (parameterName === 'centerX') {
return zoomBlurFilter._centerX;
}
if (parameterName === 'centerY') {
return zoomBlurFilter._centerY;
}
if (parameterName === 'innerRadius') {
return zoomBlurFilter.innerRadius;
}
if (parameterName === 'strength') {
return zoomBlurFilter.strength;
}
if (parameterName === 'padding') {
return zoomBlurFilter.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,

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