diff --git a/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp b/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp index b16895cd7759..313a02b74187 100644 --- a/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp +++ b/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp @@ -3,14 +3,17 @@ * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * reserved. This project is released under the MIT License. */ -#include #include "EffectsCodeGenerator.h" + +#include + #include "GDCore/Extensions/Metadata/EffectMetadata.h" #include "GDCore/Extensions/Metadata/MetadataProvider.h" #include "GDCore/Project/Effect.h" -#include "GDCore/Project/Layer.h" #include "GDCore/Project/EffectsContainer.h" +#include "GDCore/Project/Layer.h" #include "GDCore/Project/Layout.h" +#include "GDCore/Project/Object.h" #include "GDCore/Project/Project.h" namespace gd { @@ -33,6 +36,24 @@ void ExposeProjectEffects( worker(effect); } } + + for (std::size_t i; i < layout.GetObjectsCount(); i++) { + auto& object = layout.GetObject(i); + auto& effects = object.GetEffects(); + for (std::size_t e = 0; e < effects.GetEffectsCount(); e++) { + auto& effect = effects.GetEffect(e); + worker(effect); + } + } + } + + // Add global object effects + for (std::size_t s = 0; s < project.GetObjectsCount(); s++) { + auto& effects = project.GetObject(s).GetEffects(); + for (std::size_t e = 0; e < effects.GetEffectsCount(); e++) { + auto& effect = effects.GetEffect(e); + worker(effect); + } } } diff --git a/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp b/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp index b90696b1d52f..781930049a40 100644 --- a/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp +++ b/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp @@ -1072,6 +1072,74 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension( .AddParameter("expression", _("Target X position")) .AddParameter("expression", _("Target Y position")); + obj.AddAction("EnableEffect", + _("Enable an object effect"), + _("Enable an effect on the object"), + _("Enable effect _PARAM1_ on _PARAM0_: _PARAM2_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("yesorno", _("Enable?")) + .MarkAsSimple(); + + obj.AddAction("SetEffectDoubleParameter", + _("Effect parameter (number)"), + _("Change the value of a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter name")) + .AddParameter("expression", _("New value")) + .MarkAsSimple(); + + obj.AddAction("SetEffectStringParameter", + _("Effect parameter (string)"), + _("Change the value (string) of a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter name")) + .AddParameter("string", _("New value")) + .MarkAsSimple(); + + obj.AddAction("SetEffectBooleanParameter", + _("Effect parameter (enable or disable)"), + _("Enable or disable a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter Name")) + .AddParameter("yesorno", _("Enable?")) + .MarkAsSimple(); + + obj.AddCondition("IsEffectEnabled", + _("Effect is enabled"), + _("Check if the effect on an object is enabled."), + _("Effect _PARAM1_ of _PARAM0_ is enabled"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .MarkAsSimple(); + extension .AddAction("Create", _("Create an object"), diff --git a/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp b/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp index 432eba5c7ce2..0d5cad66ce8c 100644 --- a/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp +++ b/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp @@ -7,7 +7,8 @@ namespace gd { -EffectMetadata::EffectMetadata(const gd::String& type_) : type(type_) {} +EffectMetadata::EffectMetadata(const gd::String& type_) + : type(type_), isMarkedAsNotWorkingForObjects(false) {} EffectMetadata& EffectMetadata::SetIncludeFile(const gd::String& includeFile) { includeFiles.clear(); @@ -22,4 +23,9 @@ EffectMetadata& EffectMetadata::AddIncludeFile(const gd::String& includeFile) { return *this; } +EffectMetadata& EffectMetadata::MarkAsNotWorkingForObjects() { + isMarkedAsNotWorkingForObjects = true; + return *this; +} + } // namespace gd diff --git a/Core/GDCore/Extensions/Metadata/EffectMetadata.h b/Core/GDCore/Extensions/Metadata/EffectMetadata.h index 90d859bff0a8..7e18898afb0c 100644 --- a/Core/GDCore/Extensions/Metadata/EffectMetadata.h +++ b/Core/GDCore/Extensions/Metadata/EffectMetadata.h @@ -8,6 +8,7 @@ #include #include #include + #include "GDCore/Project/PropertyDescriptor.h" #include "GDCore/String.h" @@ -49,7 +50,8 @@ class GD_CORE_API EffectMetadata { }; /** - * Set the help path of the effect, relative to the GDevelop documentation root. + * Set the help path of the effect, relative to the GDevelop documentation + * root. */ EffectMetadata& SetHelpPath(const gd::String& path) { helpPath = path; @@ -66,6 +68,11 @@ class GD_CORE_API EffectMetadata { */ EffectMetadata& AddIncludeFile(const gd::String& includeFile); + /** + * \brief Mark the effect as not working as an object effect. + */ + EffectMetadata& MarkAsNotWorkingForObjects(); + /** * \brief Return a reference to the properties of this effect. */ @@ -81,12 +88,14 @@ class GD_CORE_API EffectMetadata { } /** - * \brief Get the help path of the effect, relative to the GDevelop documentation root. + * \brief Get the help path of the effect, relative to the GDevelop + * documentation root. */ const gd::String& GetHelpPath() const { return helpPath; } /** - * \brief Get the type of the effect (its internal name, like "BlackAndWhite"). + * \brief Get the type of the effect (its internal name, like + * "BlackAndWhite"). */ const gd::String& GetType() const { return type; } @@ -107,6 +116,11 @@ class GD_CORE_API EffectMetadata { return includeFiles; } + /** + * \brief Check if the effect is marked as not working as an object effect. + */ + bool IsMarkedAsNotWorkingForObjects() const { return isMarkedAsNotWorkingForObjects; }; + private: gd::String extensionNamespace; gd::String type; @@ -114,6 +128,7 @@ class GD_CORE_API EffectMetadata { gd::String fullname; gd::String description; std::vector includeFiles; + bool isMarkedAsNotWorkingForObjects; std::map properties; }; diff --git a/Extensions/Effects/JsExtension.js b/Extensions/Effects/JsExtension.js index c881ea356d70..424e1170b6fe 100644 --- a/Extensions/Effects/JsExtension.js +++ b/Extensions/Effects/JsExtension.js @@ -897,7 +897,8 @@ module.exports = { .setFullName(_('Radial Blur')) .setDescription(_('Applies a Motion blur to an object.')) .addIncludeFile('Extensions/Effects/pixi-filters/filter-radial-blur.js') - .addIncludeFile('Extensions/Effects/radial-blur-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/radial-blur-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const radialBlurProperties = radialBlurEffect.getProperties(); radialBlurProperties .getOrCreate('radius') @@ -1084,7 +1085,8 @@ module.exports = { ) ) .addIncludeFile('Extensions/Effects/pixi-filters/filter-twist.js') - .addIncludeFile('Extensions/Effects/twist-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/twist-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const twistProperties = twistEffect.getProperties(); twistProperties .getOrCreate('radius') @@ -1120,7 +1122,8 @@ module.exports = { .setFullName(_('Zoom blur')) .setDescription(_('Applies a Zoom blur.')) .addIncludeFile('Extensions/Effects/pixi-filters/filter-zoom-blur.js') - .addIncludeFile('Extensions/Effects/zoom-blur-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/zoom-blur-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const zoomBlurProperties = zoomBlurEffect.getProperties(); zoomBlurProperties .getOrCreate('centerX') diff --git a/Extensions/Effects/adjustment-pixi-filter.ts b/Extensions/Effects/adjustment-pixi-filter.ts index 2488f74792c4..9fa37f6a4585 100644 --- a/Extensions/Effects/adjustment-pixi-filter.ts +++ b/Extensions/Effects/adjustment-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Adjustment', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const adjustmentFilter = new PIXI.filters.AdjustmentFilter(); return adjustmentFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter; if (parameterName === 'gamma') { diff --git a/Extensions/Effects/advanced-bloom-pixi-filter.ts b/Extensions/Effects/advanced-bloom-pixi-filter.ts index 29153075f7e0..33d249773117 100644 --- a/Extensions/Effects/advanced-bloom-pixi-filter.ts +++ b/Extensions/Effects/advanced-bloom-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('AdvancedBloom', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const advancedBloomFilter = new PIXI.filters.AdvancedBloomFilter(); return advancedBloomFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter; if (parameterName === 'threshold') { diff --git a/Extensions/Effects/ascii-pixi-filter.ts b/Extensions/Effects/ascii-pixi-filter.ts index 53faf4dbab13..ff19558be52c 100644 --- a/Extensions/Effects/ascii-pixi-filter.ts +++ b/Extensions/Effects/ascii-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Ascii', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const asciiFilter = new PIXI.filters.AsciiFilter(); return asciiFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter; if (parameterName === 'size') { diff --git a/Extensions/Effects/bevel-pixi-filter.ts b/Extensions/Effects/bevel-pixi-filter.ts index 40a60f935f9b..ae213896f0c1 100644 --- a/Extensions/Effects/bevel-pixi-filter.ts +++ b/Extensions/Effects/bevel-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Bevel', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const bevelFilter = new PIXI.filters.BevelFilter(); return bevelFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter; if (parameterName === 'rotation') { diff --git a/Extensions/Effects/black-and-white-pixi-filter.ts b/Extensions/Effects/black-and-white-pixi-filter.ts index 58199661eed5..6d8460aaa4fc 100644 --- a/Extensions/Effects/black-and-white-pixi-filter.ts +++ b/Extensions/Effects/black-and-white-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('BlackAndWhite', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorMatrix = new PIXI.filters.ColorMatrixFilter(); colorMatrix.blackAndWhite(false); return colorMatrix; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/blending-mode-pixi-filter.ts b/Extensions/Effects/blending-mode-pixi-filter.ts index ad9d8debe197..ef01f4182590 100644 --- a/Extensions/Effects/blending-mode-pixi-filter.ts +++ b/Extensions/Effects/blending-mode-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('BlendingMode', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const blendingModeFilter = new PIXI.filters.AlphaFilter(); return blendingModeFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter; diff --git a/Extensions/Effects/blur-pixi-filter.ts b/Extensions/Effects/blur-pixi-filter.ts index bc41e6540833..20ae38b50cc4 100644 --- a/Extensions/Effects/blur-pixi-filter.ts +++ b/Extensions/Effects/blur-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Blur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const blur = new PIXI.filters.BlurFilter(); return blur; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if ( parameterName !== 'blur' && diff --git a/Extensions/Effects/brightness-pixi-filter.ts b/Extensions/Effects/brightness-pixi-filter.ts index ae59ee41e520..0e177539502c 100644 --- a/Extensions/Effects/brightness-pixi-filter.ts +++ b/Extensions/Effects/brightness-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Brightness', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const brightness = new PIXI.filters.ColorMatrixFilter(); brightness.brightness(1, false); return brightness; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/bulge-pinch-pixi-filter.ts b/Extensions/Effects/bulge-pinch-pixi-filter.ts index 0aa94658f40e..a2dd4adb1edc 100644 --- a/Extensions/Effects/bulge-pinch-pixi-filter.ts +++ b/Extensions/Effects/bulge-pinch-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('BulgePinch', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const bulgePinchFilter = new PIXI.filters.BulgePinchFilter(); return bulgePinchFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter; if (parameterName === 'centerX') { diff --git a/Extensions/Effects/color-map-pixi-filter.ts b/Extensions/Effects/color-map-pixi-filter.ts index f4e443e924af..deb43d402c72 100644 --- a/Extensions/Effects/color-map-pixi-filter.ts +++ b/Extensions/Effects/color-map-pixi-filter.ts @@ -1,7 +1,7 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ColorMap', { - makePIXIFilter: function (layer, effectData) { - const colorMapTexture = layer + makePIXIFilter: function (target, effectData) { + const colorMapTexture = target .getRuntimeScene() .getGame() .getImageManager() @@ -17,7 +17,7 @@ namespace gdjs { ); return colorMapFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter; if (parameterName === 'mix') { diff --git a/Extensions/Effects/color-replace-pixi-filter.ts b/Extensions/Effects/color-replace-pixi-filter.ts index ee4475465c92..d1ecca500c17 100644 --- a/Extensions/Effects/color-replace-pixi-filter.ts +++ b/Extensions/Effects/color-replace-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ColorReplace', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorReplaceFilter = new PIXI.filters.ColorReplaceFilter(); return colorReplaceFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter; if (parameterName === 'epsilon') { diff --git a/Extensions/Effects/crt-pixi-filter.ts b/Extensions/Effects/crt-pixi-filter.ts index 525cd621ccb3..fe8574b71558 100644 --- a/Extensions/Effects/crt-pixi-filter.ts +++ b/Extensions/Effects/crt-pixi-filter.ts @@ -7,14 +7,14 @@ namespace gdjs { crtFilter._animationTimer = 0; return crtFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { // Multiply by 10 so that the default value is a sensible speed filter.time += - (layer.getElapsedTime() / 1000) * 10 * filter.animationSpeed; + (target.getElapsedTime() / 1000) * 10 * filter.animationSpeed; } if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/displacement-pixi-filter.ts b/Extensions/Effects/displacement-pixi-filter.ts index e9cf78d49362..06585538eaf0 100644 --- a/Extensions/Effects/displacement-pixi-filter.ts +++ b/Extensions/Effects/displacement-pixi-filter.ts @@ -1,8 +1,8 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Displacement', { - makePIXIFilter: function (layer, effectData) { - const displacementMapTexture = layer + makePIXIFilter: function (target, effectData) { + const displacementMapTexture = target .getRuntimeScene() .getGame() .getImageManager() @@ -14,7 +14,7 @@ namespace gdjs { ); return displacementFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const displacementFilter = (filter as unknown) as PIXI.filters.DisplacementFilter; diff --git a/Extensions/Effects/dot-pixi-filter.ts b/Extensions/Effects/dot-pixi-filter.ts index 3be1227158ec..1486497f82fe 100644 --- a/Extensions/Effects/dot-pixi-filter.ts +++ b/Extensions/Effects/dot-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Dot', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const dotFilter = new PIXI.filters.DotFilter(); return dotFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const dotFilter = (filter as unknown) as PIXI.filters.DotFilter; if (parameterName === 'scale') { diff --git a/Extensions/Effects/drop-shadow-pixi-filter.ts b/Extensions/Effects/drop-shadow-pixi-filter.ts index 539b96ef2d01..9ef270975c67 100644 --- a/Extensions/Effects/drop-shadow-pixi-filter.ts +++ b/Extensions/Effects/drop-shadow-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('DropShadow', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const dropShadowFilter = new PIXI.filters.DropShadowFilter(); return dropShadowFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter; if (parameterName === 'blur') { diff --git a/Extensions/Effects/glitch-pixi-filter.ts b/Extensions/Effects/glitch-pixi-filter.ts index ead4950c3bcf..62b54b5d3460 100644 --- a/Extensions/Effects/glitch-pixi-filter.ts +++ b/Extensions/Effects/glitch-pixi-filter.ts @@ -7,9 +7,9 @@ namespace gdjs { glitchFilter._animationTimer = 0; return glitchFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/glow-pixi-filter.ts b/Extensions/Effects/glow-pixi-filter.ts index aee043e23b4e..7a8ba75e222e 100644 --- a/Extensions/Effects/glow-pixi-filter.ts +++ b/Extensions/Effects/glow-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Glow', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const glowFilter = new PIXI.filters.GlowFilter(); return glowFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter; if (parameterName === 'innerStrength') { diff --git a/Extensions/Effects/godray-pixi-filter.ts b/Extensions/Effects/godray-pixi-filter.ts index 954fb3e7aafc..a7cc1215fb4e 100644 --- a/Extensions/Effects/godray-pixi-filter.ts +++ b/Extensions/Effects/godray-pixi-filter.ts @@ -6,9 +6,9 @@ namespace gdjs { const godrayFilter = new PIXI.filters.GodrayFilter(); return godrayFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { - filter.time += (layer.getElapsedTime() / 1000) * filter.animationSpeed; + filter.time += (target.getElapsedTime() / 1000) * filter.animationSpeed; } }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/kawase-blur-pixi-filter.ts b/Extensions/Effects/kawase-blur-pixi-filter.ts index 8909d7b98578..3d0f50c1dd08 100644 --- a/Extensions/Effects/kawase-blur-pixi-filter.ts +++ b/Extensions/Effects/kawase-blur-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('KawaseBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const kawaseBlurFilter = new PIXI.filters.KawaseBlurFilter(); return kawaseBlurFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter; if (parameterName === 'pixelizeX') { diff --git a/Extensions/Effects/light-night-pixi-filter.ts b/Extensions/Effects/light-night-pixi-filter.ts index e038c6757b04..5aec36c65f80 100644 --- a/Extensions/Effects/light-night-pixi-filter.ts +++ b/Extensions/Effects/light-night-pixi-filter.ts @@ -23,11 +23,11 @@ namespace gdjs { } LightNightPixiFilter.prototype.constructor = gdjs.LightNightPixiFilter; gdjs.PixiFiltersTools.registerFilterCreator('LightNight', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const filter = new gdjs.LightNightPixiFilter(); return filter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if (parameterName !== 'opacity') { return; diff --git a/Extensions/Effects/night-pixi-filter.ts b/Extensions/Effects/night-pixi-filter.ts index 3dd38b82e532..ee258d017aa6 100644 --- a/Extensions/Effects/night-pixi-filter.ts +++ b/Extensions/Effects/night-pixi-filter.ts @@ -27,11 +27,11 @@ namespace gdjs { } NightPixiFilter.prototype.constructor = gdjs.NightPixiFilter; gdjs.PixiFiltersTools.registerFilterCreator('Night', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const filter = new gdjs.NightPixiFilter(); return filter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if (parameterName !== 'intensity' && parameterName !== 'opacity') { return; diff --git a/Extensions/Effects/noise-pixi-filter.ts b/Extensions/Effects/noise-pixi-filter.ts index b4dacbce1e7c..ff6bbed76938 100644 --- a/Extensions/Effects/noise-pixi-filter.ts +++ b/Extensions/Effects/noise-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Noise', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const noiseFilter = new PIXI.filters.NoiseFilter(); return noiseFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const noiseFilter = (filter as unknown) as PIXI.filters.NoiseFilter; diff --git a/Extensions/Effects/old-film-pixi-filter.ts b/Extensions/Effects/old-film-pixi-filter.ts index 3812be0f0c1d..6353382188f7 100644 --- a/Extensions/Effects/old-film-pixi-filter.ts +++ b/Extensions/Effects/old-film-pixi-filter.ts @@ -7,9 +7,9 @@ namespace gdjs { oldFilmFilter._animationTimer = 0; return oldFilmFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/outline-pixi-filter.ts b/Extensions/Effects/outline-pixi-filter.ts index 2c498cc336fe..d129b9d5c514 100644 --- a/Extensions/Effects/outline-pixi-filter.ts +++ b/Extensions/Effects/outline-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Outline', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const outlineFilter = new PIXI.filters.OutlineFilter(); return outlineFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter; if (parameterName === 'thickness') { diff --git a/Extensions/Effects/pixelate-pixi-filter.ts b/Extensions/Effects/pixelate-pixi-filter.ts index 7aeab2a788b6..0c464b22fe80 100644 --- a/Extensions/Effects/pixelate-pixi-filter.ts +++ b/Extensions/Effects/pixelate-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Pixelate', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const pixelateFilter = new PIXI.filters.PixelateFilter( effectData.doubleParameters.size ); return pixelateFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter; if (parameterName === 'size') { diff --git a/Extensions/Effects/radial-blur-pixi-filter.ts b/Extensions/Effects/radial-blur-pixi-filter.ts index 20607873c4a0..681310b5547e 100644 --- a/Extensions/Effects/radial-blur-pixi-filter.ts +++ b/Extensions/Effects/radial-blur-pixi-filter.ts @@ -1,18 +1,18 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('RadialBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const radialBlurFilter = new PIXI.filters.RadialBlurFilter(); return radialBlurFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter; radialBlurFilter.center[0] = Math.round( // @ts-ignore - extra properties are stored on the filter. - radialBlurFilter._centerX * layer.getWidth() + radialBlurFilter._centerX * target.getWidth() ); radialBlurFilter.center[1] = Math.round( // @ts-ignore - extra properties are stored on the filter. - radialBlurFilter._centerY * layer.getHeight() + radialBlurFilter._centerY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/reflection-pixi-filter.ts b/Extensions/Effects/reflection-pixi-filter.ts index 2433f0eca13e..1330f9d9cabc 100644 --- a/Extensions/Effects/reflection-pixi-filter.ts +++ b/Extensions/Effects/reflection-pixi-filter.ts @@ -23,9 +23,9 @@ namespace gdjs { ); return reflectionFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { - filter.time += (layer.getElapsedTime() / 1000) * filter.animationSpeed; + filter.time += (target.getElapsedTime() / 1000) * filter.animationSpeed; } }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/rgb-split-pixi-filter.ts b/Extensions/Effects/rgb-split-pixi-filter.ts index ad0ef0cd7c8d..f176d7d6c34a 100644 --- a/Extensions/Effects/rgb-split-pixi-filter.ts +++ b/Extensions/Effects/rgb-split-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('RGBSplit', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const rgbSplitFilter = new PIXI.filters.RGBSplitFilter(); return rgbSplitFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter; if (parameterName === 'redX') { diff --git a/Extensions/Effects/sepia-pixi-filter.ts b/Extensions/Effects/sepia-pixi-filter.ts index f30b7d6f7069..bba31e1823e9 100644 --- a/Extensions/Effects/sepia-pixi-filter.ts +++ b/Extensions/Effects/sepia-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Sepia', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorMatrixFilter = new PIXI.filters.ColorMatrixFilter(); colorMatrixFilter.sepia(false); return colorMatrixFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const colorMatrixFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/tilt-shift-pixi-filter.ts b/Extensions/Effects/tilt-shift-pixi-filter.ts index 19804a7015b0..83f3c45aaf2d 100644 --- a/Extensions/Effects/tilt-shift-pixi-filter.ts +++ b/Extensions/Effects/tilt-shift-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('TiltShift', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const tiltShiftFilter = new PIXI.filters.TiltShiftFilter(); return tiltShiftFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter; if (parameterName === 'blur') { diff --git a/Extensions/Effects/twist-pixi-filter.ts b/Extensions/Effects/twist-pixi-filter.ts index fc67ed12fef1..8569ec9c0d44 100644 --- a/Extensions/Effects/twist-pixi-filter.ts +++ b/Extensions/Effects/twist-pixi-filter.ts @@ -5,20 +5,20 @@ namespace gdjs { import PIXI_ = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Twist', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const twistFilter = new PIXI.filters.TwistFilter(); twistFilter.offset = new PIXI_.Point(0, 0); return twistFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter; twistFilter.offset.x = Math.round( // @ts-ignore - extra properties are stored on the filter. - twistFilter._offsetX * layer.getWidth() + twistFilter._offsetX * target.getWidth() ); twistFilter.offset.y = Math.round( // @ts-ignore - extra properties are stored on the filter. - twistFilter._offsetY * layer.getHeight() + twistFilter._offsetY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/zoom-blur-pixi-filter.ts b/Extensions/Effects/zoom-blur-pixi-filter.ts index 2e671d733ea0..ef9ae39591f5 100644 --- a/Extensions/Effects/zoom-blur-pixi-filter.ts +++ b/Extensions/Effects/zoom-blur-pixi-filter.ts @@ -1,18 +1,18 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ZoomBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const zoomBlurFilter = new PIXI.filters.ZoomBlurFilter(); return zoomBlurFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter; zoomBlurFilter.center[0] = Math.round( // @ts-ignore - extra properties are stored on the filter. - zoomBlurFilter._centerX * layer.getWidth() + zoomBlurFilter._centerX * target.getWidth() ); zoomBlurFilter.center[1] = Math.round( // @ts-ignore - extra properties are stored on the filter. - zoomBlurFilter._centerY * layer.getHeight() + zoomBlurFilter._centerY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/ExampleJsExtension/dummyeffect.ts b/Extensions/ExampleJsExtension/dummyeffect.ts index 403a7b7f92c2..acde7f0ba6bc 100644 --- a/Extensions/ExampleJsExtension/dummyeffect.ts +++ b/Extensions/ExampleJsExtension/dummyeffect.ts @@ -57,11 +57,12 @@ namespace gdjs { ); return filter; }, - // Function called at every frame rendered - update: function (filter, layer) {}, - // If your filter depends on the time, you can get the elapsed time - // with `layer.getElapsedTime()`. - // You can update the uniforms or other state of the filter. + // Function called at every frame, after events and before the frame is rendered. + updatePreRender: function (filter, layer) { + // If your filter depends on the time, you can get the elapsed time + // with `layer.getElapsedTime()`. + // You can update the uniforms or other state of the filter. + }, // Function that will be called to update a (number) parameter of the PIXI filter with a new value updateDoubleParameter: function (filter, parameterName, value) { if (parameterName === 'opacity') { diff --git a/Extensions/Lighting/tests/lightruntimeobject.spec.js b/Extensions/Lighting/tests/lightruntimeobject.spec.js index 9b12da04f9ff..fc51de7a08cc 100644 --- a/Extensions/Lighting/tests/lightruntimeobject.spec.js +++ b/Extensions/Lighting/tests/lightruntimeobject.spec.js @@ -14,6 +14,7 @@ const addLightObject = (runtimeScene, radius) => { type: 'Lighting::LightObject', variables: [], behaviors: [], + effects: [], content: { radius: radius, color: '#b4b4b4', @@ -41,6 +42,7 @@ const addLightObstacle = (runtimeScene, width, height) => { type: 'Lighting::LightObstacleBehavior', }, ], + effects: [], }); obstacle.getWidth = function () { return width; diff --git a/Extensions/LinkedObjects/tests/linkedobjects.spec.js b/Extensions/LinkedObjects/tests/linkedobjects.spec.js index e92c23f105ec..4fb00c5030b4 100644 --- a/Extensions/LinkedObjects/tests/linkedobjects.spec.js +++ b/Extensions/LinkedObjects/tests/linkedobjects.spec.js @@ -19,32 +19,38 @@ describe('gdjs.LinksManager', function () { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object1B = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object1C = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2A = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2B = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2C = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); runtimeScene.addObject(object1A); diff --git a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts index 3fe2a187dec9..1874455f5d10 100644 --- a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts +++ b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts @@ -121,7 +121,7 @@ namespace gdjs { } } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { this._renderer.ensureUpToDate(); } diff --git a/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js b/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js index 783118cb0694..c2de695912de 100644 --- a/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js +++ b/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js @@ -66,6 +66,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { collisionMethod: collisionMethod, }, ], + effects: [], }); player.getWidth = function () { return 90; @@ -89,6 +90,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { cost: 2, }, ], + effects: [], }); obstacle.getWidth = function () { return 100; diff --git a/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js b/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js index 2b983ac6e6a4..318878b02282 100644 --- a/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js +++ b/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js @@ -77,6 +77,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { ], }, ], + effects: [], behaviors: [ { type: 'PathfindingBehavior::PathfindingBehavior', @@ -131,6 +132,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { ], }, ], + effects: [], behaviors: [ { type: 'PathfindingBehavior::PathfindingObstacleBehavior', diff --git a/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js b/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js index e6e4a8365344..ffd087af074a 100644 --- a/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js +++ b/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js @@ -34,6 +34,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { canBeGrabbed: true, }, ], + effects: [], }); platform.setCustomWidthAndHeight(60, 32); runtimeScene.addObject(platform); @@ -53,6 +54,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { canBeGrabbed: false, }, ], + effects: [], }); platform.setCustomWidthAndHeight(60, 32); runtimeScene.addObject(platform); @@ -72,6 +74,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Ladder', }, ], + effects: [], }); ladder.setCustomWidthAndHeight(20, 60); runtimeScene.addObject(ladder); @@ -106,6 +109,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -302,6 +306,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -469,6 +474,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -915,6 +921,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -937,6 +944,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Jumpthru', }, ], + effects: [], }); jumpthru.setCustomWidthAndHeight(60, 5); runtimeScene.addObject(jumpthru); @@ -1051,6 +1059,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1312,6 +1321,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1441,6 +1451,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1476,6 +1487,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Platform', }, ], + effects: [], }); object2.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object2); @@ -1599,6 +1611,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1990,6 +2003,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); diff --git a/Extensions/TextEntryObject/textentryruntimeobject.ts b/Extensions/TextEntryObject/textentryruntimeobject.ts index 0741ddd770a3..ffe3e32df5fc 100644 --- a/Extensions/TextEntryObject/textentryruntimeobject.ts +++ b/Extensions/TextEntryObject/textentryruntimeobject.ts @@ -38,7 +38,7 @@ namespace gdjs { } } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { if ((this._renderer as any).getString) { this._str = (this._renderer as any).getString(); } diff --git a/Extensions/TextObject/textruntimeobject.ts b/Extensions/TextObject/textruntimeobject.ts index 5f0366f134c5..c96039a757b5 100644 --- a/Extensions/TextObject/textruntimeobject.ts +++ b/Extensions/TextObject/textruntimeobject.ts @@ -131,7 +131,7 @@ namespace gdjs { return this._renderer.getRendererObject(); } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { this._renderer.ensureUpToDate(); } diff --git a/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json b/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json index 259cba20a08b..17d198c251ef 100644 --- a/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json +++ b/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json @@ -246,6 +246,7 @@ "type": "TileMap::TileMap", "variables": [], "behaviors": [], + "effects": [], "content": { "tilemapJsonFile": "example.json", "tilesetJsonFile": "tileset.json", @@ -266,6 +267,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipXY", "font": "", "characterSize": 10, @@ -285,6 +287,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipX", "font": "", "characterSize": 10, @@ -304,6 +307,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipY", "font": "", "characterSize": 10, @@ -323,6 +327,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Normal", "font": "", "characterSize": 10, @@ -365,4 +370,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js b/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js index f06f54d90fef..26826dfc39a2 100644 --- a/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js +++ b/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js @@ -47,6 +47,7 @@ describe('gdjs.TopDownMovementRuntimeBehavior', function () { const player = new gdjs.TestRuntimeObject(runtimeScene, { name: 'player', type: '', + effects: [], behaviors: [ { type: 'TopDownMovementBehavior::TopDownMovementBehavior', diff --git a/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp b/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp index 36495e6c0e88..c53620a1b45c 100644 --- a/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp +++ b/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp @@ -85,6 +85,9 @@ BaseObjectExtension::BaseObjectExtension() { objectConditions["Invisible"] .SetFunctionName("isHidden") .SetIncludeFile("runtimeobject.js"); + objectConditions["IsEffectEnabled"] + .SetFunctionName("isEffectEnabled") + .SetIncludeFile("runtimeobject.js"); objectActions["Delete"].SetFunctionName("deleteFromScene"); objectActions["MettreAutourPos"].SetFunctionName("putAround"); objectActions["MettreAutour"] @@ -173,6 +176,18 @@ BaseObjectExtension::BaseObjectExtension() { objectActions["RemoveObjectTimer"] .SetFunctionName("removeTimer") .SetIncludeFile("runtimeobject.js"); + objectActions["EnableEffect"] + .SetFunctionName("enableEffect") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectDoubleParameter"] + .SetFunctionName("setEffectDoubleParameter") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectStringParameter"] + .SetFunctionName("setEffectStringParameter") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectBooleanParameter"] + .SetFunctionName("setEffectBooleanParameter") + .SetIncludeFile("runtimeobject.js"); objectExpressions["X"].SetFunctionName("getX"); objectExpressions["Y"].SetFunctionName("getY"); diff --git a/GDJS/GDJS/IDE/ExporterHelper.cpp b/GDJS/GDJS/IDE/ExporterHelper.cpp index 8e3f92bb82b6..5feac88a4528 100644 --- a/GDJS/GDJS/IDE/ExporterHelper.cpp +++ b/GDJS/GDJS/IDE/ExporterHelper.cpp @@ -672,6 +672,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers, "pixi-renderers/spriteruntimeobject-pixi-renderer.js"); InsertUnique(includesFiles, "pixi-renderers/loadingscreen-pixi-renderer.js"); + InsertUnique(includesFiles, "pixi-renderers/pixi-effects-manager.js"); InsertUnique(includesFiles, "howler-sound-manager/howler.min.js"); InsertUnique(includesFiles, "howler-sound-manager/howler-sound-manager.js"); InsertUnique(includesFiles, diff --git a/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts b/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts index 6c5175551443..af8130c98fbf 100644 --- a/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts +++ b/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts @@ -140,7 +140,7 @@ namespace gdjs { this._cocosLayer.setVisible(visible); } - update(): void {} + updatePreRender(): void {} // Unimplemented updateClearColor(): void {} diff --git a/GDJS/Runtime/layer.ts b/GDJS/Runtime/layer.ts index 8c7b6f90a368..d4b706f5ade9 100644 --- a/GDJS/Runtime/layer.ts +++ b/GDJS/Runtime/layer.ts @@ -9,7 +9,7 @@ namespace gdjs { * * Viewports and multiple cameras are not supported. */ - export class Layer { + export class Layer implements EffectsTarget { _name: string; _cameraRotation: float = 0; _zoomFactor: float = 1; @@ -21,13 +21,16 @@ namespace gdjs { _cameraY: float; _cachedGameResolutionWidth: integer; _cachedGameResolutionHeight: integer; + _runtimeScene: gdjs.RuntimeScene; + _effectsManager: gdjs.EffectsManager; // Lighting layer properties. _isLightingLayer: boolean; _followBaseLayerCamera: boolean; _clearColor: Array; + _rendererEffects: Record = {}; _renderer: LayerRenderer; /** @@ -47,6 +50,7 @@ namespace gdjs { .getGame() .getGameResolutionHeight(); this._runtimeScene = runtimeScene; + this._effectsManager = runtimeScene.getGame().getEffectsManager(); this._isLightingLayer = layerData.isLightingLayer; this._followBaseLayerCamera = layerData.followBaseLayerCamera; this._clearColor = [ @@ -120,13 +124,13 @@ namespace gdjs { /** * Called at each frame, after events are run and before rendering. - * @param [runtimeScene] The scene the layer belongs to. */ - update(runtimeScene?: gdjs.RuntimeScene): void { + updatePreRender(runtimeScene?: gdjs.RuntimeScene): void { if (this._followBaseLayerCamera) { this.followBaseLayer(); } - return this._renderer.update(); + this._renderer.updatePreRender(); + this._effectsManager.updatePreRender(this._rendererEffects, this); } /** @@ -318,6 +322,7 @@ namespace gdjs { /** * Return the initial effects data for the layer. Only to * be used by renderers. + * @deprecated */ getInitialEffectsData(): EffectData[] { return this._initialEffectsData; @@ -328,28 +333,12 @@ namespace gdjs { * @param effectData The data of the effect to add. */ addEffect(effectData: EffectData): void { - this._renderer.addEffect(effectData); - for (let name in effectData.doubleParameters) { - this.setEffectDoubleParameter( - effectData.name, - name, - effectData.doubleParameters[name] - ); - } - for (let name in effectData.stringParameters) { - this.setEffectStringParameter( - effectData.name, - name, - effectData.stringParameters[name] - ); - } - for (let name in effectData.booleanParameters) { - this.setEffectBooleanParameter( - effectData.name, - name, - effectData.booleanParameters[name] - ); - } + this._effectsManager.addEffect( + effectData, + this._rendererEffects, + this._renderer.getRendererObject(), + this + ); } /** @@ -357,7 +346,11 @@ namespace gdjs { * @param effectName The name of the effect. */ removeEffect(effectName: string): void { - this._renderer.removeEffect(effectName); + this._effectsManager.removeEffect( + this._rendererEffects, + this._renderer.getRendererObject(), + effectName + ); } /** @@ -371,7 +364,8 @@ namespace gdjs { parameterName: string, value: float ): void { - return this._renderer.setEffectDoubleParameter( + this._effectsManager.setEffectDoubleParameter( + this._rendererEffects, name, parameterName, value @@ -389,7 +383,8 @@ namespace gdjs { parameterName: string, value: string ): void { - return this._renderer.setEffectStringParameter( + this._effectsManager.setEffectStringParameter( + this._rendererEffects, name, parameterName, value @@ -407,7 +402,8 @@ namespace gdjs { parameterName: string, value: boolean ): void { - return this._renderer.setEffectBooleanParameter( + this._effectsManager.setEffectBooleanParameter( + this._rendererEffects, name, parameterName, value @@ -420,7 +416,7 @@ namespace gdjs { * @param enable true to enable, false to disable */ enableEffect(name: string, enable: boolean): void { - this._renderer.enableEffect(name, enable); + this._effectsManager.enableEffect(this._rendererEffects, name, enable); } /** @@ -429,7 +425,7 @@ namespace gdjs { * @return true if the effect is enabled, false otherwise. */ isEffectEnabled(name: string): boolean { - return this._renderer.isEffectEnabled(name); + return this._effectsManager.isEffectEnabled(this._rendererEffects, name); } /** @@ -438,7 +434,7 @@ namespace gdjs { * @return true if the effect exists, false otherwise. */ hasEffect(name: string): boolean { - return this._renderer.hasEffect(name); + return this._effectsManager.hasEffect(this._rendererEffects, name); } /** @@ -463,10 +459,9 @@ namespace gdjs { * Return the time elapsed since the last frame, * in milliseconds, for objects on the layer. */ - getElapsedTime(): float { - return ( - this._runtimeScene.getTimeManager().getElapsedTime() * this._timeScale - ); + getElapsedTime(runtimeScene?: RuntimeScene): float { + runtimeScene = runtimeScene || this._runtimeScene; + return runtimeScene.getTimeManager().getElapsedTime() * this._timeScale; } /** diff --git a/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts b/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts index 4566ddf215c4..d0291e3a657f 100644 --- a/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts +++ b/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts @@ -13,7 +13,6 @@ namespace gdjs { export class LayerPixiRenderer { _pixiContainer: PIXI.Container; - _filters: Record = {}; _layer: any; _renderTexture: PIXI.RenderTexture | null = null; _lightingSprite: PIXI.Sprite | null = null; @@ -83,66 +82,10 @@ namespace gdjs { this._pixiContainer.visible = !!visible; } - update(): void { + updatePreRender(): void { if (this._renderTexture) { this._updateRenderTexture(); } - for (const filterName in this._filters) { - const filter = this._filters[filterName]; - filter.update(filter.pixiFilter, this._layer); - } - } - - /** - * Add a new effect, or replace the one with the same name. - * @param effectData The data of the effect to add. - */ - addEffect(effectData: EffectData): void { - const filterCreator = gdjs.PixiFiltersTools.getFilterCreator( - effectData.effectType - ); - if (!filterCreator) { - console.log( - 'Filter "' + - effectData.name + - '" has an unknown effect type: "' + - effectData.effectType + - '". Was it registered properly? Is the effect type correct?' - ); - return; - } - - const filter: gdjs.PixiFiltersTools.Filter = { - pixiFilter: filterCreator.makePIXIFilter(this._layer, effectData), - updateDoubleParameter: filterCreator.updateDoubleParameter, - updateStringParameter: filterCreator.updateStringParameter, - updateBooleanParameter: filterCreator.updateBooleanParameter, - update: filterCreator.update, - }; - if (this._isLightingLayer) { - filter.pixiFilter.blendMode = PIXI.BLEND_MODES.ADD; - } - this._pixiContainer.filters = (this._pixiContainer.filters || []).concat( - filter.pixiFilter - ); - this._filters[effectData.name] = filter; - } - - /** - * Remove the effect with the specified name - * @param effectName The name of the effect. - */ - removeEffect(effectName: string): void { - const filter = this._filters[effectName]; - if (!filter) { - return; - } - this._pixiContainer.filters = (this._pixiContainer.filters || []).filter( - function (pixiFilter) { - return pixiFilter !== filter.pixiFilter; - } - ); - delete this._filters[effectName]; } /** @@ -188,95 +131,6 @@ namespace gdjs { this._pixiContainer.removeChild(child); } - /** - * Update the parameter of an effect (with a number). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectDoubleParameter( - name: string, - parameterName: string, - value: float - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateDoubleParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Update the parameter of an effect (with a string). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectStringParameter( - name: string, - parameterName: string, - value: string - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateStringParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Enable or disable the parameter of an effect (boolean). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectBooleanParameter( - name: string, - parameterName: string, - value: boolean - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateBooleanParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Check if an effect exists. - * @param name The effect name - * @returns True if the effect exists, false otherwise - */ - hasEffect(name: string): boolean { - return !!this._filters[name]; - } - - /** - * Enable an effect. - * @param name The effect name - * @param value Set to true to enable, false to disable - */ - enableEffect(name: string, value: boolean): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - gdjs.PixiFiltersTools.enableEffect(filter, value); - } - - /** - * Check if an effect is enabled. - * @param name The effect name - * @return true if the filter is enabled - */ - isEffectEnabled(name: string): boolean { - const filter = this._filters[name]; - if (!filter) { - return false; - } - return gdjs.PixiFiltersTools.isEffectEnabled(filter); - } - updateClearColor(): void { this._clearColor = this._layer.getClearColor(); this._updateRenderTexture(); diff --git a/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts b/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts new file mode 100644 index 000000000000..0459f2197ce2 --- /dev/null +++ b/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts @@ -0,0 +1,286 @@ +/* + * GDevelop JS Platform + * Copyright 2013-2021 Florian Rival (Florian.Rival@gmail.com). All rights reserved. + * This project is released under the MIT License. + */ +namespace gdjs { + import PIXI = GlobalPIXIModule.PIXI; + + type RendererEffects = Record; + + export interface EffectsTarget { + getRuntimeScene: () => RuntimeScene; + getElapsedTime: (runtimeScene?: RuntimeScene) => number; + getHeight: () => number; + getWidth: () => number; + isLightingLayer?: () => boolean; + } + + /** + * Handle effects (aka PixiJS "filters") on PixiJS objects. + */ + class PixiEffectsManager { + /** + * Initialize the renderer effect (PixiJS filter) from the effect data. + * Call the applyEffect method afterwards, to correctly apply the + * initialized effects on the object. + * @param effectData The effect data + * @param rendererEffects The collection of PixiJS filters + * @param target The effects target + */ + initializeEffect( + effectData: EffectData, + rendererEffects: RendererEffects, + target: EffectsTarget + ): boolean { + const filterCreator = gdjs.PixiFiltersTools.getFilterCreator( + effectData.effectType + ); + if (!filterCreator) { + console.log( + 'Effect "' + + effectData.name + + '" has an unknown effect type: "' + + effectData.effectType + + '". Was it registered properly? Is the effect type correct?' + ); + return false; + } + + const filter: PixiFiltersTools.Filter = { + pixiFilter: filterCreator.makePIXIFilter(target, effectData), + updateDoubleParameter: filterCreator.updateDoubleParameter, + updateStringParameter: filterCreator.updateStringParameter, + updateBooleanParameter: filterCreator.updateBooleanParameter, + updatePreRender: filterCreator.updatePreRender, + }; + + if (target.isLightingLayer && target.isLightingLayer()) { + filter.pixiFilter.blendMode = PIXI.BLEND_MODES.ADD; + } + + rendererEffects[effectData.name] = filter; + return true; + } + + /** + * Apply the effect on the PixiJS DisplayObject. + * Called after the effect is initialized. + * @param rendererObject The renderer object + * @param effect The effect to be applied. + */ + applyEffect( + rendererObject: PIXI.DisplayObject, + effect: PixiFiltersTools.Filter + ): boolean { + rendererObject.filters = (rendererObject.filters || []).concat( + effect.pixiFilter + ); + return true; + } + + /** + * Update the filters applied on a PixiJS DisplayObject. + * This must be called after the events and before the rendering. + * + * This allows effects to be sure that they are up to date and ready + * to render. This is not called on objects that are not rendered on screen + * ("culling"). This is always called on layers. + */ + updatePreRender(rendererEffects: RendererEffects, target: EffectsTarget) { + for (const filterName in rendererEffects) { + const filter = rendererEffects[filterName]; + filter.updatePreRender(filter.pixiFilter, target); + } + } + + /** + * Add a new effect on a PixiJS DisplayObject, or replace the one + * with the same name. + * @param effectData The effect data + * @param rendererEffects The renderer effects + * @param rendererObject The renderer object + * @param target The effects target + */ + addEffect( + effectData: EffectData, + rendererEffects: RendererEffects, + rendererObject: PIXI.DisplayObject, + target: EffectsTarget + ): boolean { + let effectAdded = true; + effectAdded = + this.initializeEffect(effectData, rendererEffects, target) && + effectAdded; + effectAdded = + this.updateAllEffectParameters(rendererEffects, effectData) && + effectAdded; + effectAdded = + this.applyEffect(rendererObject, rendererEffects[effectData.name]) && + effectAdded; + return effectAdded; + } + + /** + * Remove the effect with the specified name from a PixiJS DisplayObject. + * @param rendererEffects The collection of PixiJS filters. + * @param rendererObject The renderer object. + * @param effectName The name of the effect. + */ + removeEffect( + rendererEffects: RendererEffects, + rendererObject: PIXI.DisplayObject, + effectName: string + ): boolean { + const filter = rendererEffects[effectName]; + if (!filter) return false; + rendererObject.filters = (rendererObject.filters || []).filter( + (pixiFilter) => pixiFilter !== filter.pixiFilter + ); + delete rendererEffects[effectName]; + return true; + } + + /** + * Update the parameter of an effect (with a number). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectDoubleParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: float + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateDoubleParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Update the parameter of an effect (with a string). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectStringParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: string + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateStringParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Enable or disable the parameter of an effect (boolean). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectBooleanParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: boolean + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateBooleanParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Updates all the effect parameters. + * @param rendererEffects + * @param effectData + */ + updateAllEffectParameters( + rendererEffects: RendererEffects, + effectData: EffectData + ): boolean { + let updatedDoubles = true; + let updatedStrings = true; + let updatedBooleans = true; + for (let name in effectData.doubleParameters) { + updatedDoubles = + this.setEffectDoubleParameter( + rendererEffects, + effectData.name, + name, + effectData.doubleParameters[name] + ) && updatedDoubles; + } + for (let name in effectData.stringParameters) { + updatedStrings = + this.setEffectStringParameter( + rendererEffects, + effectData.name, + name, + effectData.stringParameters[name] + ) && updatedStrings; + } + for (let name in effectData.booleanParameters) { + updatedBooleans = + this.setEffectBooleanParameter( + rendererEffects, + effectData.name, + name, + effectData.booleanParameters[name] + ) && updatedBooleans; + } + + return updatedDoubles && updatedStrings && updatedBooleans; + } + + /** + * Check if an effect exists. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @returns True if the effect exists, false otherwise + */ + hasEffect(rendererEffects: RendererEffects, name: string): boolean { + return !!rendererEffects[name]; + } + + /** + * Enable an effect. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param value Set to true to enable, false to disable + */ + enableEffect( + rendererEffects: RendererEffects, + name: string, + value: boolean + ): void { + const filter = rendererEffects[name]; + if (!filter) return; + gdjs.PixiFiltersTools.enableEffect(filter, value); + } + + /** + * Check if an effect is enabled. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @return true if the filter is enabled + */ + isEffectEnabled(rendererEffects: RendererEffects, name: string): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + return gdjs.PixiFiltersTools.isEffectEnabled(filter); + } + } + + // Expose the effect manager to the game engine. + export const EffectsManager = PixiEffectsManager; + export type EffectsManager = PixiEffectsManager; +} diff --git a/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts b/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts index 4a3dc4738c61..492c0587f71f 100644 --- a/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts +++ b/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts @@ -89,9 +89,9 @@ namespace gdjs { /** A wrapper allowing to create a PIXI filter and update it using a common interface */ export type FilterCreator = { /** Function to call to create the filter */ - makePIXIFilter: (layer: gdjs.Layer, effectData: EffectData) => any; - /** The function to be called to update the filter at every frame */ - update: (filter: PIXI.Filter, layer: gdjs.Layer) => any; + makePIXIFilter: (target: EffectsTarget, effectData: EffectData) => any; + /** The function to be called to update the filter at every frame before the rendering. */ + updatePreRender: (filter: PIXI.Filter, target: gdjs.EffectsTarget) => any; /** The function to be called to update a parameter (with a number) */ updateDoubleParameter: ( filter: PIXI.Filter, @@ -116,8 +116,8 @@ namespace gdjs { export type Filter = { /** The PIXI filter */ pixiFilter: PIXI.Filter; - /** The function to be called to update the filter at every frame */ - update: (filter: PIXI.Filter, layer: gdjs.Layer) => any; + /** The function to be called to update the filter at every frame before the rendering. */ + updatePreRender: (filter: PIXI.Filter, target: gdjs.EffectsTarget) => any; /** The function to be called to update a parameter (with a number) */ updateDoubleParameter: ( filter: PIXI.Filter, diff --git a/GDJS/Runtime/runtimegame.ts b/GDJS/Runtime/runtimegame.ts index e43e63dc569a..ca7e26b4ee94 100644 --- a/GDJS/Runtime/runtimegame.ts +++ b/GDJS/Runtime/runtimegame.ts @@ -40,6 +40,7 @@ namespace gdjs { _soundManager: SoundManager; _fontManager: FontManager; _jsonManager: JsonManager; + _effectsManager: EffectsManager; _bitmapFontManager: BitmapFontManager; _maxFPS: integer; _minFPS: integer; @@ -94,6 +95,7 @@ namespace gdjs { this._data.resources.resources, this._imageManager ); + this._effectsManager = new gdjs.EffectsManager(); this._maxFPS = this._data ? this._data.properties.maxFPS : 60; this._minFPS = this._data ? this._data.properties.minFPS : 15; this._gameResolutionWidth = this._data.properties.windowWidth; @@ -201,6 +203,15 @@ namespace gdjs { return this._jsonManager; } + /** + * Get the effects manager of the game, which allows to manage + * effects on runtime objects or runtime layers. + * @return The effects manager for the game + */ + getEffectsManager(): gdjs.EffectsManager { + return this._effectsManager; + } + /** * Get the object containing the game data * @return The object associated to the game. diff --git a/GDJS/Runtime/runtimeobject.ts b/GDJS/Runtime/runtimeobject.ts index a9fcba20c236..18588a295967 100644 --- a/GDJS/Runtime/runtimeobject.ts +++ b/GDJS/Runtime/runtimeobject.ts @@ -33,7 +33,7 @@ namespace gdjs { * A `gdjs.RuntimeObject` should not be instantiated directly, always a child class * (because gdjs.RuntimeObject don't call onCreated at the end of its constructor). */ - export class RuntimeObject { + export class RuntimeObject implements EffectsTarget { name: string; type: string; x: float = 0; @@ -70,6 +70,9 @@ namespace gdjs { //Variables: protected _variables: gdjs.VariablesContainer; + //Effects: + protected _rendererEffects: Record = {}; + //Forces: protected _forces: gdjs.Force[] = []; _averageForce: gdjs.Force; @@ -98,6 +101,13 @@ namespace gdjs { ); this._averageForce = new gdjs.Force(0, 0, 0); this._behaviorsTable = new Hashtable(); + for (let i = 0; i < objectData.effects.length; ++i) { + this._runtimeScene + .getGame() + .getEffectsManager() + .initializeEffect(objectData.effects[i], this._rendererEffects, this); + this.updateAllEffectParameters(objectData.effects[i]); + } //Also contains the behaviors: Used when a behavior is accessed by its name ( see getBehavior ). for (let i = 0, len = objectData.behaviors.length; i < len; ++i) { @@ -120,6 +130,16 @@ namespace gdjs { * (`RuntimeObject.prototype.onCreated.call(this);`). */ onCreated(): void { + for (const effectName in this._rendererEffects) { + this._runtimeScene + .getGame() + .getEffectsManager() + .applyEffect( + this.getRendererObject(), + this._rendererEffects[effectName] + ); + } + for (let i = 0; i < this._behaviors.length; ++i) { this._behaviors[i].onCreated(); } @@ -188,14 +208,20 @@ namespace gdjs { * * Objects can have different elapsed time if they are on layers with different time scales. * - * @param runtimeScene The RuntimeScene the object belongs to. + * @param runtimeScene The RuntimeScene the object belongs to (deprecated - can be omitted). */ - getElapsedTime(runtimeScene: gdjs.RuntimeScene): float { - //TODO: Memoize? - const theLayer = runtimeScene.getLayer(this.layer); + getElapsedTime(runtimeScene?: gdjs.RuntimeScene): float { + const theLayer = this._runtimeScene.getLayer(this.layer); return theLayer.getElapsedTime(); } + /** + * The gdjs.RuntimeScene the object belongs to. + */ + getRuntimeScene(): RuntimeScene { + return this._runtimeScene; + } + /** * Called once during the game loop, before events and rendering. * @param runtimeScene The gdjs.RuntimeScene the object belongs to. @@ -727,6 +753,159 @@ namespace gdjs { return this._variables.has(name); } + /** + * Returns the collection of effects to be rendered by the + * underlying renderer. + * @returns The renderer effects. + */ + getRendererEffects() { + return this._rendererEffects; + } + + /** + * Add a new effect, or replace the one with the same name. + * @param effectData The data describing the effect to add. + */ + addEffect(effectData: EffectData): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .addEffect( + effectData, + this._rendererEffects, + this.getRendererObject(), + this + ); + } + + /** + * Remove the effect with the specified name + * @param effectName The name of the effect. + */ + removeEffect(effectName: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .removeEffect( + this._rendererEffects, + this.getRendererObject(), + effectName + ); + } + + /** + * Change an effect parameter value (for parameters that are numbers). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (number). + */ + setEffectDoubleParameter( + name: string, + parameterName: string, + value: float + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectDoubleParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Change an effect parameter value (for parameters that are strings). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (string). + */ + setEffectStringParameter( + name: string, + parameterName: string, + value: string + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectStringParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Change an effect parameter value (for parameters that are booleans). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (boolean). + */ + setEffectBooleanParameter( + name: string, + parameterName: string, + value: boolean + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectBooleanParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Updates all the parameters of an effect. + * @param effectData The data describing the effect + */ + updateAllEffectParameters(effectData: EffectData): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .updateAllEffectParameters(this._rendererEffects, effectData); + } + + /** + * Enable or disable an effect. + * @param name The name of the effect to enable or disable. + * @param enable true to enable, false to disable + */ + enableEffect(name: string, enable: boolean): void { + this._runtimeScene + .getGame() + .getEffectsManager() + .enableEffect(this._rendererEffects, name, enable); + } + + /** + * Check if an effect is enabled + * @param name The name of the effect + * @return true if the effect is enabled, false otherwise. + */ + isEffectEnabled(name: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .isEffectEnabled(this._rendererEffects, name); + } + + /** + * Check if an effect exists on this object + * @param name The name of the effect + * @return true if the effect exists, false otherwise. + */ + hasEffect(name: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .hasEffect(this._rendererEffects, name); + } + /** * Hide (or show) the object. * @param enable Set it to true to hide the object, false to show it. diff --git a/GDJS/Runtime/runtimescene.ts b/GDJS/Runtime/runtimescene.ts index 184dd58e5220..5ba914632fd4 100644 --- a/GDJS/Runtime/runtimescene.ts +++ b/GDJS/Runtime/runtimescene.ts @@ -509,16 +509,16 @@ namespace gdjs { this._profiler.end('callbacks and extensions (post-events)'); } if (this._profiler) { - this._profiler.begin('objects (pre-render)'); + this._profiler.begin('objects (pre-render, effects update)'); } this._updateObjectsPreRender(); if (this._profiler) { - this._profiler.end('objects (pre-render)'); + this._profiler.end('objects (pre-render, effects update)'); } if (this._profiler) { this._profiler.begin('layers (effects update)'); } - this._updateLayers(); + this._updateLayersPreRender(); if (this._profiler) { this._profiler.end('layers (effects update)'); } @@ -577,19 +577,22 @@ namespace gdjs { } } - _updateLayers() { + /** + * Called to update effects of layers before rendering. + */ + _updateLayersPreRender() { for (const name in this._layers.items) { if (this._layers.items.hasOwnProperty(name)) { - /** @type gdjs.Layer */ - const theLayer: gdjs.Layer = this._layers.items[name]; - theLayer.update(this); + const layer = this._layers.items[name]; + layer.updatePreRender(this); } } } /** * Called to update visibility of the renderers of objects - * rendered on the scene and give a last chance for objects to update before rendering. + * rendered on the scene ("culling"), update effects (of visible objects) + * and give a last chance for objects to update before rendering. * * Visibility is set to false if object is hidden, or if * object is too far from the camera of its layer ("culling"). @@ -601,8 +604,16 @@ namespace gdjs { const object = this._allInstancesList[i]; const rendererObject = object.getRendererObject(); if (rendererObject) { - object.getRendererObject().visible = !object.isHidden(); + rendererObject.visible = !object.isHidden(); + + // Update effects, only for visible objects. + if (rendererObject.visible) { + this._runtimeGame + .getEffectsManager() + .updatePreRender(object.getRendererEffects(), object); + } } + // Perform pre-render update. object.updatePreRender(this); } @@ -613,9 +624,11 @@ namespace gdjs { // TODO: For compatibility, pass a scale of `2`, // meaning that size of cameras will be multiplied by 2 and so objects // will be hidden if they are outside of this *larger* camera area. - // Useful for objects not properly reporting their visibility AABB, + // This is useful for: + // - objects not properly reporting their visibility AABB, // (so we have a "safety margin") but these objects should be fixed // instead. + // - objects having effects rendering outside of their visibility AABB. this._updateLayersCameraCoordinates(2); this._constructListOfAllInstances(); for (let i = 0, len = this._allInstancesList.length; i < len; ++i) { @@ -642,6 +655,14 @@ namespace gdjs { rendererObject.visible = true; } } + + // Update effects, only for visible objects. + if (rendererObject.visible) { + this._runtimeGame + .getEffectsManager() + .updatePreRender(object.getRendererEffects(), object); + } + // Perform pre-render update. object.updatePreRender(this); } diff --git a/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts b/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts index 0bf75540a863..0dbf6e3d5f78 100644 --- a/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts +++ b/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts @@ -791,7 +791,7 @@ namespace gdjs { hotReloadSucceeded; }); - // Don't update the variables and behaviors for each runtime object to avoid + // Don't update the variables, behaviors and effects for each runtime object to avoid // doing the check for differences for every single object. // Update variables @@ -809,6 +809,13 @@ namespace gdjs { newObjectData.behaviors, runtimeObjects ); + + // Update effects + this._hotReloadRuntimeObjectsEffects( + oldObjectData.effects, + newObjectData.effects, + runtimeObjects + ); } if (!hotReloadSucceeded) { this._logs.push({ @@ -898,6 +905,80 @@ namespace gdjs { }); } + _hotReloadRuntimeObjectsEffects( + oldEffects: EffectData[], + newEffects: EffectData[], + runtimeObjects: RuntimeObject[] + ): void { + oldEffects.forEach((oldEffectData) => { + const name = oldEffectData.name; + const newEffectData = newEffects.filter( + (effectData) => effectData.name === name + )[0]; + if (!newEffectData) { + // Effect was removed. + runtimeObjects.forEach((runtimeObject) => { + if (runtimeObject.hasEffect(name)) { + if (!runtimeObject.removeEffect(name)) { + this._logs.push({ + kind: 'error', + message: + 'Effect ' + + name + + ' could not be removed from object' + + runtimeObject.getName(), + }); + } + } + }); + } else { + if (!HotReloader.deepEqual(oldEffectData, newEffectData)) { + let hotReloadSucceeded = true; + runtimeObjects.forEach((runtimeObject) => { + if (oldEffectData.effectType === newEffectData.effectType) { + hotReloadSucceeded = + runtimeObject.updateAllEffectParameters(newEffectData) && + hotReloadSucceeded; + } else { + // Another effect type was applied + runtimeObject.removeEffect(oldEffectData.name); + runtimeObject.addEffect(newEffectData); + } + }); + if (!hotReloadSucceeded) { + this._logs.push({ + kind: 'error', + message: + newEffectData.name + ' effect could not be hot-reloaded.', + }); + } + } + } + }); + newEffects.forEach((newEffectData) => { + const name = newEffectData.name; + const oldEffectData = oldEffects.filter( + (oldEffectData) => oldEffectData.name === name + )[0]; + if (!oldEffectData) { + // Effect was added + let hotReloadSucceeded = true; + runtimeObjects.forEach((runtimeObject) => { + hotReloadSucceeded = + runtimeObject.addEffect(newEffectData) && hotReloadSucceeded; + }); + if (!hotReloadSucceeded) { + this._logs.push({ + kind: 'error', + message: + newEffectData.name + + ' effect could not be added during hot-reload.', + }); + } + } + }); + } + /** * @returns true if hot-reload succeeded, false otherwise. */ diff --git a/GDJS/tests/benchmarks/runtimeobject.js b/GDJS/tests/benchmarks/runtimeobject.js index a551884cb5f8..32b20db99f51 100644 --- a/GDJS/tests/benchmarks/runtimeobject.js +++ b/GDJS/tests/benchmarks/runtimeobject.js @@ -3,7 +3,7 @@ describe('gdjs.RuntimeObject', function() { it('benchmark getAABB of rotated vs non rotated objects', function(){ this.timeout(20000); - var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); object.getWidth = function() { return 10; }; object.getHeight = function() { return 20; }; object.setPosition(15, 20); @@ -27,7 +27,7 @@ describe('gdjs.RuntimeObject', function() { it('benchmark getAABB of rotated vs non rotated objects, with non default center', function(){ this.timeout(20000); - var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); object.getWidth = function() { return 10; }; object.getHeight = function() { return 20; }; object.getCenterX = function() { return 0 }; @@ -52,4 +52,4 @@ describe('gdjs.RuntimeObject', function() { console.log(benchmarkSuite.run()); }); -}); \ No newline at end of file +}); diff --git a/GDJS/tests/benchmarks/spriteruntimeobject.js b/GDJS/tests/benchmarks/spriteruntimeobject.js index 6c79d9bb7825..f28b04f02f17 100644 --- a/GDJS/tests/benchmarks/spriteruntimeobject.js +++ b/GDJS/tests/benchmarks/spriteruntimeobject.js @@ -16,6 +16,7 @@ describe('gdjs.SpriteRuntimeObject', function () { updateIfNotVisible: false, variables: [], behaviors: [], + effects: [], animations: [ { name: 'NewObject2', diff --git a/GDJS/tests/games/Sprite update test/game.json b/GDJS/tests/games/Sprite update test/game.json index 524a08303cfb..c57bbec10a2e 100644 --- a/GDJS/tests/games/Sprite update test/game.json +++ b/GDJS/tests/games/Sprite update test/game.json @@ -356,6 +356,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "sprite1", @@ -398,6 +399,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Press any key to reload the scene\nGreen objects loaded from external layout are displaced at first and flicker\nRed objects belongs to this scene are fine", "font": "", "characterSize": 20, @@ -414,6 +416,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "sprite2", @@ -456,6 +459,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text from external Layout seems fine, no flicker", "font": "", "characterSize": 20, @@ -650,6 +654,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Hurt", @@ -881,6 +886,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "The idle animation should be shown immediately. \nThe first \"hurt\" animation should not be visible even when the scene is just changed.", "font": "", "characterSize": 20, @@ -1206,4 +1212,4 @@ } ], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/TweenBenchmark/game.json b/GDJS/tests/games/TweenBenchmark/game.json index d01f3593c9b4..2a4b9fa2ad68 100644 --- a/GDJS/tests/games/TweenBenchmark/game.json +++ b/GDJS/tests/games/TweenBenchmark/game.json @@ -4250,6 +4250,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -4275,6 +4276,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Load Sprite scene", "font": "", "characterSize": 40, @@ -7351,6 +7353,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -7399,6 +7402,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Load Text scene", "font": "", "characterSize": 40, @@ -7627,4 +7631,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/TweenTest/game.json b/GDJS/tests/games/TweenTest/game.json index 8052ff40d3c9..6c770839d469 100644 --- a/GDJS/tests/games/TweenTest/game.json +++ b/GDJS/tests/games/TweenTest/game.json @@ -789,6 +789,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Variable tweens", "font": "", "characterSize": 20, @@ -811,6 +812,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -839,6 +841,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position tweens", "font": "", "characterSize": 20, @@ -858,6 +861,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -909,6 +913,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position X tweens", "font": "", "characterSize": 20, @@ -927,6 +932,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "0", "font": "", "characterSize": 20, @@ -956,6 +962,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -997,6 +1004,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position Y\n tweens", "font": "", "characterSize": 20, @@ -1016,6 +1024,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1067,6 +1076,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Angle \ntweens", "font": "", "characterSize": 20, @@ -1096,6 +1106,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1137,6 +1148,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale tweens", "font": "", "characterSize": 20, @@ -1166,6 +1178,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1207,6 +1220,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale-X tweens", "font": "", "characterSize": 20, @@ -1236,6 +1250,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1277,6 +1292,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale-Y\ntweens", "font": "", "characterSize": 20, @@ -1306,6 +1322,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1347,6 +1364,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Opacity tweens", "font": "", "characterSize": 20, @@ -1366,6 +1384,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1417,6 +1436,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Pause/resume tweens", "font": "", "characterSize": 20, @@ -1430,6 +1450,7 @@ "name": "PauseResumeTweenSprite", "type": "Sprite", "updateIfNotVisible": false, + "effects": [], "variables": [ { "name": "var", @@ -1487,6 +1508,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Colour tweens", "font": "", "characterSize": 20, @@ -1505,6 +1527,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "0", "font": "", "characterSize": 20, @@ -1524,6 +1547,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1575,6 +1599,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Character size", "font": "", "characterSize": 20, @@ -1599,6 +1624,7 @@ "content": {} } ], + "effects": [], "string": "Hello world", "font": "", "characterSize": 20, @@ -1614,6 +1640,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Background", @@ -1652,6 +1679,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Background_Wip", @@ -4680,4 +4708,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json b/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json index 1cbd8c89f9f7..49b4182621af 100755 --- a/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json +++ b/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json @@ -170,6 +170,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scene1 - Audio Status", "font": "", "characterSize": 40, @@ -432,6 +433,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scene2 - Audio status...", "font": "", "characterSize": 40, @@ -538,4 +540,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/effects/all effects demo.json b/GDJS/tests/games/effects/all effects demo.json index 44ed8199924e..7e8a4235c888 100644 --- a/GDJS/tests/games/effects/all effects demo.json +++ b/GDJS/tests/games/effects/all effects demo.json @@ -3467,6 +3467,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "t1", @@ -4725,6 +4726,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -5487,4 +5489,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json b/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json index 7c2eafd1db24..ca1b621b5a34 100644 --- a/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json +++ b/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json @@ -312,6 +312,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -338,6 +339,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -362,6 +364,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -390,6 +393,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -430,6 +434,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should be changed by MyBehavior, using ThirdBehavior of NewObject6. (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -454,6 +459,7 @@ "type": "NewEventsFunctionsExtension::ThirdBehavior" } ], + "effects": [], "string": "Unmodified text, just having ThirdBehavior (success 6)", "font": "", "characterSize": 20, @@ -485,6 +491,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should be changed by MyBehavior, using \"BehaviorUsingMyBehaviorFromOtherExtension\" of the same object. (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -509,6 +516,7 @@ "type": "NewEventsFunctionsExtension::ThirdBehavior" } ], + "effects": [], "string": "Unmodified text, just having ThirdBehavior (success 8)", "font": "", "characterSize": 20, @@ -536,6 +544,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "SHould be Modified by a function (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -563,6 +572,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "SHould be Modified by NewObject 8 (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -590,6 +600,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should rotate (if not FAIL, otherwise success 11)", "font": "", "characterSize": 20, @@ -1737,4 +1748,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json b/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json index 2b245417990c..1f43fca39064 100644 --- a/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json +++ b/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json @@ -1860,6 +1860,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2156,6 +2157,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2269,6 +2271,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2336,6 +2339,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2353,6 +2357,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2368,6 +2373,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2434,6 +2440,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2491,6 +2498,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2549,6 +2557,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2564,6 +2573,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "acceleration": 1500, @@ -2642,6 +2652,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "acceleration": 5000, @@ -2799,6 +2810,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "acceleration": 500, @@ -2956,6 +2968,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3109,6 +3122,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3265,6 +3279,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [], "string": "x 0", "font": "Bimbo_JVE.ttf", @@ -3280,6 +3295,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3337,6 +3353,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3394,6 +3411,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3451,6 +3469,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3508,6 +3527,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "bottomEdgeAnchor": 0, @@ -3575,6 +3595,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -5430,4 +5451,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-functions-nested-create/game.json b/GDJS/tests/games/events-functions-nested-create/game.json index 2ba9ffc6666a..2eab7bc45d3f 100644 --- a/GDJS/tests/games/events-functions-nested-create/game.json +++ b/GDJS/tests/games/events-functions-nested-create/game.json @@ -149,6 +149,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -357,4 +358,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json b/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json index 15a48c9d63d7..57bfa963198a 100644 --- a/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json +++ b/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json @@ -202,6 +202,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -241,6 +242,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -280,6 +282,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject3", @@ -322,6 +325,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text", "font": "", "characterSize": 20, @@ -557,4 +561,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/objects-benchmarks/rotating objects.json b/GDJS/tests/games/objects-benchmarks/rotating objects.json index a4ba2ee9b570..580ab7e1cd0a 100644 --- a/GDJS/tests/games/objects-benchmarks/rotating objects.json +++ b/GDJS/tests/games/objects-benchmarks/rotating objects.json @@ -163,6 +163,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -202,6 +203,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -403,4 +405,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json b/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json index d8d637b05c60..1d45e31c5f1e 100644 --- a/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json +++ b/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json @@ -272,6 +272,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Add child objects in the editor and position, scale and rotate them as you like.\nIn preview, rotate the parent using A and S keys and drag the parent with the mouse.\nAll child object should keep it relative rotation and position to the parent object.", "font": "", "characterSize": 20, @@ -286,6 +287,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Draggable", @@ -349,6 +351,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -685,4 +688,4 @@ "externalEvents": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json b/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json index 36ce78df9db6..ba098a60c94e 100644 --- a/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json +++ b/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json @@ -342,6 +342,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Add child objects in the editor and position, scale and rotate them as you like.\nAdd a variable \"parent\" to each child object and a variable \"id\" to its parent.\nThe value of this variable must be the same, in order to have the child choose to which it belongs.\nIn preview, move the pointer over a parent, then you can rotate the parent using A and S keys.\nYou can also drag the parent with the mouse.\nAll child objects should keep its relative rotation and position to the parent object they belong to.", "font": "", "characterSize": 18, @@ -356,6 +357,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Draggable", @@ -419,6 +421,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1077,4 +1080,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/platformer sandbox/platformer sandbox.json b/GDJS/tests/games/platformer sandbox/platformer sandbox.json index 40c6e1183506..e18d2809211d 100644 --- a/GDJS/tests/games/platformer sandbox/platformer sandbox.json +++ b/GDJS/tests/games/platformer sandbox/platformer sandbox.json @@ -983,6 +983,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1280,6 +1281,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1403,6 +1405,7 @@ "yGrabOffset": 0 } ], + "effects": [], "animations": [ { "name": "", @@ -1462,6 +1465,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1480,6 +1484,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1496,6 +1501,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1572,6 +1578,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1649,6 +1656,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1707,6 +1715,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1766,6 +1775,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1782,6 +1792,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -1862,6 +1873,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -2021,6 +2033,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -2181,6 +2194,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2335,6 +2349,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2492,6 +2507,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "x 0", "font": "Bimbo_JVE.ttf", "characterSize": 40, @@ -2508,6 +2524,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2566,6 +2583,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2624,6 +2642,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2682,6 +2701,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2739,6 +2759,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Anchor", @@ -2808,6 +2829,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -4712,4 +4734,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json index 673e06b2020f..132946415a10 100644 --- a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json +++ b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json @@ -290,6 +290,7 @@ "value": "3" } ], + "effects": [], "behaviors": [ { "name": "TopDownMovement", @@ -384,6 +385,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -481,6 +483,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -760,4 +763,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json index 8ac61618c03c..5991d403f4ed 100644 --- a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json +++ b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json @@ -223,6 +223,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -372,6 +373,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "DestroyOutside", @@ -699,4 +701,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json b/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json index 26e9d30b0f54..bc8bbfdd214f 100644 --- a/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json +++ b/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json @@ -232,6 +232,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Ship", @@ -290,6 +291,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -578,4 +580,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/rotated-objects-hitboxes/game.json b/GDJS/tests/games/rotated-objects-hitboxes/game.json index 72a3958da1e9..dc8e36bc13b6 100644 --- a/GDJS/tests/games/rotated-objects-hitboxes/game.json +++ b/GDJS/tests/games/rotated-objects-hitboxes/game.json @@ -502,6 +502,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -540,6 +541,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -578,6 +580,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -619,6 +622,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text blabl balbalbal", "font": "", "characterSize": 20, @@ -634,6 +638,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -691,6 +696,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -730,7 +736,8 @@ "type": "TiledSpriteObject::TiledSprite", "width": 32, "variables": [], - "behaviors": [] + "behaviors": [], + "effects": [] } ], "events": [ @@ -776,4 +783,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json b/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json index 6ca633deaaf5..e18df0c2fc43 100644 --- a/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json +++ b/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json @@ -187,6 +187,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Square", @@ -488,4 +489,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json b/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json index 247cfe2cdc33..c58910571585 100644 --- a/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json +++ b/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json @@ -199,6 +199,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -632,4 +633,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/karma.conf.js b/GDJS/tests/karma.conf.js index e2305e4ebff4..9d6fd45ba626 100644 --- a/GDJS/tests/karma.conf.js +++ b/GDJS/tests/karma.conf.js @@ -76,6 +76,8 @@ module.exports = function (config) { '../../newIDE/app/resources/GDJS/Runtime/Extensions/TopDownMovementBehavior/topdownobstacleruntimebehavior.js', '../../newIDE/app/resources/GDJS/Runtime/Extensions/Firebase/A_firebasejs/*.js', '../../newIDE/app/resources/GDJS/Runtime/Extensions/Firebase/B_firebasetools/*.js', + '../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/kawase-blur-pixi-filter.js', + '../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/pixi-filters/filter-kawase-blur.js', // Test extensions: './tests/Extensions/**.js', diff --git a/GDJS/tests/tests/common.js b/GDJS/tests/tests/common.js index fbf3497e65d7..e3a2fdda7d93 100644 --- a/GDJS/tests/tests/common.js +++ b/GDJS/tests/tests/common.js @@ -14,12 +14,12 @@ describe('gdjs.evtTools.object.twoListsTest', function() { var map2 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2C = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2C = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var list1 = [obj1A, obj1B, obj1C]; var list2 = [obj2A, obj2B, obj2C]; @@ -49,9 +49,9 @@ describe('gdjs.evtTools.object.pickObjectsIf', function() { var map1 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); var list1 = [obj1A, obj1B, obj1C]; map1.put("obj1", list1); @@ -73,11 +73,11 @@ describe('gdjs.evtTools.object.pickObjectsIf', function() { describe('gdjs.evtTools.object.pickRandomObject', function() { it('should pick only one object', function(){ var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var map1 = new Hashtable(); var list1 = [obj1A, obj1B, obj1C]; @@ -103,11 +103,11 @@ describe('gdjs.evtTools.object.pickRandomObject', function() { describe('gdjs.evtTools.object.pickOnly', function() { it('picks only the object passed as parameter', function(){ var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var map1 = new Hashtable(); map1.put("obj1", [obj1A, obj1B, obj1C]); @@ -139,9 +139,9 @@ describe('gdjs.evtTools.object.pickNearestObject', function() { var map1 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); obj1A.setPosition(50, 50); obj1B.setPosition(160, 160); obj1C.setPosition(100, 300); diff --git a/GDJS/tests/tests/effects.js b/GDJS/tests/tests/effects.js new file mode 100644 index 000000000000..4deaa10bdeb0 --- /dev/null +++ b/GDJS/tests/tests/effects.js @@ -0,0 +1,163 @@ +// @ts-check + +describe('gdjs.EffectsManager', () => { + const runtimeGame = new gdjs.RuntimeGame({ + variables: [], + // @ts-ignore TODO: make a function to create an empty game and use it across tests. + properties: { windowWidth: 800, windowHeight: 600 }, + resources: { resources: [] }, + }); + + it('can add effects on a runtime object', () => { + const runtimeScene = new gdjs.RuntimeScene(runtimeGame); + const object = new gdjs.TestRuntimeObject(runtimeScene, { + name: 'obj1', + type: '', + variables: [], + behaviors: [], + effects: [ + { + name: 'InitialKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }, + ], + }); + expect(object.hasEffect('NonExistingEffect')).to.be(false); + expect(object.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('NonExistingEffect')).to.be(false); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + object.enableEffect('InitialKawaseBlurEffect', false); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(false); + object.enableEffect('InitialKawaseBlurEffect', true); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + object.setEffectDoubleParameter('InitialKawaseBlurEffect', 'pixelizeX', 10); + object.setEffectStringParameter( + 'InitialKawaseBlurEffect', + 'useless', + 'will-be-ignored' + ); + object.setEffectBooleanParameter( + 'InitialKawaseBlurEffect', + 'useless', + true + ); + + object.addEffect({ + name: 'AddedKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }); + expect(object.hasEffect('AddedKawaseBlurEffect')).to.be(true); + expect(object.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('AddedKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + }); + + it('can add effects on a runtime layer', () => { + const runtimeScene = new gdjs.RuntimeScene(runtimeGame); + runtimeScene.loadFromScene({ + layers: [ + { + name: '', + visibility: true, + effects: [ + { + name: 'InitialKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }, + ], + cameras: [], + + ambientLightColorR: 0, + ambientLightColorG: 0, + ambientLightColorB: 0, + isLightingLayer: false, + followBaseLayerCamera: true, + }, + ], + variables: [], + r: 0, + v: 0, + b: 0, + mangledName: 'Scene1', + name: 'Scene1', + stopSoundsOnStartup: false, + title: '', + behaviorsSharedData: [], + objects: [], + instances: [], + }); + + const runtimeLayer = runtimeScene.getLayer(''); + + expect(runtimeLayer.hasEffect('NonExistingEffect')).to.be(false); + expect(runtimeLayer.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('NonExistingEffect')).to.be(false); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + runtimeLayer.enableEffect('InitialKawaseBlurEffect', false); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be( + false + ); + runtimeLayer.enableEffect('InitialKawaseBlurEffect', true); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + runtimeLayer.setEffectDoubleParameter( + 'InitialKawaseBlurEffect', + 'pixelizeX', + 10 + ); + runtimeLayer.setEffectStringParameter( + 'InitialKawaseBlurEffect', + 'useless', + 'will-be-ignored' + ); + runtimeLayer.setEffectBooleanParameter( + 'InitialKawaseBlurEffect', + 'useless', + true + ); + + runtimeLayer.addEffect({ + name: 'AddedKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }); + expect(runtimeLayer.hasEffect('AddedKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('AddedKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + }); +}); diff --git a/GDJS/tests/tests/inputmanager.js b/GDJS/tests/tests/inputmanager.js index 0536c4317e41..71a2b175882a 100644 --- a/GDJS/tests/tests/inputmanager.js +++ b/GDJS/tests/tests/inputmanager.js @@ -155,6 +155,7 @@ describe('gdjs.RuntimeObject.cursorOnObject', function() { name: 'obj1', type: '', behaviors: [], + effects: [], }); object.setPosition(450, 500); diff --git a/GDJS/tests/tests/runtimescene.js b/GDJS/tests/tests/runtimescene.js index 4ce52610037d..010dc03b9357 100644 --- a/GDJS/tests/tests/runtimescene.js +++ b/GDJS/tests/tests/runtimescene.js @@ -129,6 +129,7 @@ describe('gdjs.RuntimeScene integration tests', function () { type: 'Sprite', name: 'MyObject', behaviors: [], + effects: [], // @ts-expect-error ts-migrate(2322) FIXME: Type '{ type: string; name: string; behaviors: nev... Remove this comment to see the full error message animations: [], updateIfNotVisible: false, diff --git a/GDJS/tests/tests/spriteruntimeobject.js b/GDJS/tests/tests/spriteruntimeobject.js index 2ec9a5590144..6d414b57647e 100644 --- a/GDJS/tests/tests/spriteruntimeobject.js +++ b/GDJS/tests/tests/spriteruntimeobject.js @@ -20,6 +20,7 @@ describe('gdjs.SpriteRuntimeObject', function() { variables: [], behaviors: [], animations: [], + effects: [], }); expect(object.getScaleX()).to.be(1); @@ -40,6 +41,7 @@ describe('gdjs.SpriteRuntimeObject', function() { updateIfNotVisible: false, behaviors: [], variables: [], + effects: [], animations: [ { name: 'firstAnimation', diff --git a/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js b/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js index 61f68b447db0..d4d876146b0f 100644 --- a/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js +++ b/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js @@ -25,6 +25,7 @@ describe('gdjs.SpriteRuntimeObject (using a PIXI RuntimeGame with assets)', func updateIfNotVisible: false, variables: [], behaviors: [], + effects: [], animations: [ { name: 'NewObject2', diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 29d74ed75046..d7c1b8696739 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -1426,11 +1426,13 @@ interface EffectMetadata { [Ref] EffectMetadata SetHelpPath([Const] DOMString helpPath); [Ref] EffectMetadata SetIncludeFile([Const] DOMString includeFile); [Ref] EffectMetadata AddIncludeFile([Const] DOMString includeFile); + [Ref] EffectMetadata MarkAsNotWorkingForObjects(); [Const, Ref] DOMString GetType(); [Const, Ref] DOMString GetFullName(); [Const, Ref] DOMString GetDescription(); [Const, Ref] DOMString GetHelpPath(); + boolean IsMarkedAsNotWorkingForObjects(); [Ref] MapStringPropertyDescriptor GetProperties(); }; diff --git a/GDevelop.js/types/gdeffectmetadata.js b/GDevelop.js/types/gdeffectmetadata.js index 3c9a70789649..4633044817a6 100644 --- a/GDevelop.js/types/gdeffectmetadata.js +++ b/GDevelop.js/types/gdeffectmetadata.js @@ -5,10 +5,12 @@ declare class gdEffectMetadata { setHelpPath(helpPath: string): gdEffectMetadata; setIncludeFile(includeFile: string): gdEffectMetadata; addIncludeFile(includeFile: string): gdEffectMetadata; + markAsNotWorkingForObjects(): gdEffectMetadata; getType(): string; getFullName(): string; getDescription(): string; getHelpPath(): string; + isMarkedAsNotWorkingForObjects(): boolean; getProperties(): gdMapStringPropertyDescriptor; delete(): void; ptr: number; diff --git a/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js b/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js index 91aa357267ab..67b75da9b9f2 100644 --- a/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js +++ b/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js @@ -1,43 +1,29 @@ // @flow import { Trans } from '@lingui/macro'; - import * as React from 'react'; import HelpButton from '../UI/HelpButton'; import Text from '../UI/Text'; -import Paper from '@material-ui/core/Paper'; -import { Line, Column } from '../UI/Grid'; +import { EmptyPlaceholder } from '../UI/EmptyPlaceholder'; const EmptyBehaviorsPlaceholder = () => ( - - - - - There are no behaviors here. - - - - Behaviors are predefined actions that are assigned to objects. - Behaviors can have no or multiple parameters. - - - - - Add your first behavior using the button "Add a behavior to the - object". - - - - - - - - + } + > + + There are no behaviors here. + + + + Behaviors are predefined actions that are assigned to objects. Behaviors + can have no or multiple parameters. + + + + + Add your first behavior using the button "Add a behavior to the object". + + + ); export default EmptyBehaviorsPlaceholder; diff --git a/newIDE/app/src/BehaviorsEditor/index.js b/newIDE/app/src/BehaviorsEditor/index.js index 965b422bd507..28978dd697fd 100644 --- a/newIDE/app/src/BehaviorsEditor/index.js +++ b/newIDE/app/src/BehaviorsEditor/index.js @@ -109,29 +109,78 @@ const BehaviorsEditor = (props: Props) => { return ( - - {allBehaviorNames.length === 0 && ( -
- - - -
- )} - {allBehaviorNames.map((behaviorName, index) => { - const behaviorContent = object.getBehavior(behaviorName); - const behaviorTypeName = behaviorContent.getTypeName(); + {allBehaviorNames.length === 0 ? ( + + + + ) : ( + + {allBehaviorNames.map((behaviorName, index) => { + const behaviorContent = object.getBehavior(behaviorName); + const behaviorTypeName = behaviorContent.getTypeName(); + + const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata( + gd.JsPlatform.get(), + behaviorTypeName + ); + if (gd.MetadataProvider.isBadBehaviorMetadata(behaviorMetadata)) { + return ( + + { + ev.stopPropagation(); + onRemoveBehavior(behaviorName); + }} + > + + , + ]} + > + + Unknown behavior{' '} + + + + + + + + + This behavior is unknown. It might be a behavior that + was defined in an extension and that was later removed. + You should delete it. + + + + + ); + } + + const behavior = behaviorMetadata.get(); + const BehaviorComponent = BehaviorsEditorService.getEditor( + behaviorTypeName + ); + const tutorialHints = getBehaviorTutorialHints(behaviorTypeName); + const enabledTutorialHints = tutorialHints.filter( + hint => !values.hiddenTutorialHints[hint.identifier] + ); + const iconUrl = behaviorMetadata.getIconFilename(); - const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata( - gd.JsPlatform.get(), - behaviorTypeName - ); - if (gd.MetadataProvider.isBadBehaviorMetadata(behaviorMetadata)) { return ( , { ev.stopPropagation(); onRemoveBehavior(behaviorName); @@ -141,112 +190,62 @@ const BehaviorsEditor = (props: Props) => { , ]} > - - Unknown behavior{' '} - - - + {iconUrl ? ( + + ) : null} + + + onChangeBehaviorName(behaviorContent, text) + } + /> - - - This behavior is unknown. It might be a behavior that was - defined in an extension and that was later removed. You - should delete it. - - - - - ); - } - - const behavior = behaviorMetadata.get(); - const BehaviorComponent = BehaviorsEditorService.getEditor( - behaviorTypeName - ); - const tutorialHints = getBehaviorTutorialHints(behaviorTypeName); - const enabledTutorialHints = tutorialHints.filter( - hint => !values.hiddenTutorialHints[hint.identifier] - ); - const iconUrl = behaviorMetadata.getIconFilename(); - - return ( - - , - { - ev.stopPropagation(); - onRemoveBehavior(behaviorName); - }} + - - , - ]} - > - {iconUrl ? ( - - ) : null} - - - onChangeBehaviorName(behaviorContent, text) - } - /> - - - - - {enabledTutorialHints.length ? ( + {enabledTutorialHints.length ? ( + + + {tutorialHints.map(tutorialHint => ( + + ))} + + + ) : null} - - {tutorialHints.map(tutorialHint => ( - - ))} - + - ) : null} - - - - - - - ); - })} - +
+ + + ); + })} + + )} ( + ( + + )} + > + + + Effects can change how layers or objects are rendered on screen. + + + + + After adding an effect, set up its parameters. Launch a preview to see + the result. Using the events and the name of the effect, you can change + the parameters during the game. + + + +); diff --git a/newIDE/app/src/EffectsList/EnumerateEffects.js b/newIDE/app/src/EffectsList/EnumerateEffects.js index e2daa3c3309c..395eb61509eb 100644 --- a/newIDE/app/src/EffectsList/EnumerateEffects.js +++ b/newIDE/app/src/EffectsList/EnumerateEffects.js @@ -11,6 +11,7 @@ export type EnumeratedEffectMetadata = {| fullName: string, description: string, parametersSchema: Schema, + isMarkedAsNotWorkingForObjects: boolean, |}; /** @@ -115,6 +116,7 @@ export const enumerateEffectsMetadata = ( effectMetadata, fullName: effectMetadata.getFullName(), description: effectMetadata.getDescription(), + isMarkedAsNotWorkingForObjects: effectMetadata.isMarkedAsNotWorkingForObjects(), parametersSchema, }; }); diff --git a/newIDE/app/src/EffectsList/EnumerateEffects.spec.js b/newIDE/app/src/EffectsList/EnumerateEffects.spec.js index 6b76b8ae7389..dc584ebdff2c 100644 --- a/newIDE/app/src/EffectsList/EnumerateEffects.spec.js +++ b/newIDE/app/src/EffectsList/EnumerateEffects.spec.js @@ -5,7 +5,7 @@ import { makeTestProject } from '../fixtures/TestProject'; const gd: libGDevelop = global.gd; describe('EnumerateEffects', () => { - it('can enumerate effects', () => { + it('can enumerate effects for layers', () => { makeTestExtensions(gd); const { project } = makeTestProject(gd); const enumeratedEffectsMetadata = enumerateEffectsMetadata(project); @@ -14,12 +14,21 @@ describe('EnumerateEffects', () => { expect.objectContaining({ type: 'FakeSepia', fullName: 'Fake Sepia Effect', + isMarkedAsNotWorkingForObjects: false, + }) + ); + expect(enumeratedEffectsMetadata).toContainEqual( + expect.objectContaining({ + type: 'FakeSepiaThatWouldWorkOnlyForLayers', + fullName: 'Fake Sepia Effect only for layers', + isMarkedAsNotWorkingForObjects: true, }) ); expect(enumeratedEffectsMetadata).toContainEqual( expect.objectContaining({ type: 'FakeNight', fullName: 'Fake Night Effect', + isMarkedAsNotWorkingForObjects: false, }) ); }); diff --git a/newIDE/app/src/EffectsList/index.js b/newIDE/app/src/EffectsList/index.js index d6ab2e176325..594bbe69770e 100644 --- a/newIDE/app/src/EffectsList/index.js +++ b/newIDE/app/src/EffectsList/index.js @@ -10,7 +10,6 @@ import SelectOption from '../UI/SelectOption'; import { mapFor } from '../Utils/MapFor'; import RaisedButton from '../UI/RaisedButton'; import IconButton from '../UI/IconButton'; -import EmptyMessage from '../UI/EmptyMessage'; import ElementWithMenu from '../UI/Menu/ElementWithMenu'; import MoreVert from '@material-ui/icons/MoreVert'; import SemiControlledTextField from '../UI/SemiControlledTextField'; @@ -33,6 +32,8 @@ import { type ChooseResourceFunction, } from '../ResourcesList/ResourceSource.flow'; import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow'; +import ScrollView from '../UI/ScrollView'; +import { EmptyEffectsPlaceholder } from './EmptyEffectsPlaceholder'; type Props = {| project: gdProject, @@ -41,6 +42,7 @@ type Props = {| resourceExternalEditors: Array, effectsContainer: gdEffectsContainer, onEffectsUpdated: () => void, + target: 'object' | 'layer', |}; const getEnumeratedEffectMetadata = ( @@ -107,165 +109,158 @@ export default function EffectsList(props: Props) { return ( - {({ i18n }) => { - return ( - - - - - - Effects can change how the layer is rendered on screen. - After adding an effect, set up its parameters. Launch a - preview to see the result. Using the events and the name of - the effect, you can change the parameters during the game. - - - - - {effectsContainer.getEffectsCount() > 3 && ( - - - - - Using a lot of effects can have a severe negative impact - on the rendering performance, especially on low-end or - mobile devices. Consider using less effects if possible. - You can also disable and re-enable effects as needed using - events. - - - - - )} - {mapFor(0, effectsContainer.getEffectsCount(), (i: number) => { - const effect: gdEffect = effectsContainer.getEffectAt(i); - const effectType = effect.getEffectType(); - const effectMetadata = getEnumeratedEffectMetadata( - allEffectMetadata, - effectType - ); + {({ i18n }) => ( + + {effectsContainer.getEffectsCount() !== 0 ? ( + + {effectsContainer.getEffectsCount() > 3 && ( + + + + + Using a lot of effects can have a severe negative impact + on the rendering performance, especially on low-end or + mobile devices. Consider using less effects if possible. + You can also disable and re-enable effects as needed + using events. + + + + + )} + {mapFor(0, effectsContainer.getEffectsCount(), (i: number) => { + const effect: gdEffect = effectsContainer.getEffectAt(i); + const effectType = effect.getEffectType(); + const effectMetadata = getEnumeratedEffectMetadata( + allEffectMetadata, + effectType + ); - return ( - - - - Effect name: - - { - if (newName === effect.getName()) return; + return ( + + + + Effect name: + + { + if (newName === effect.getName()) return; - effect.setName(newName); - forceUpdate(); - onEffectsUpdated(); - }} - fullWidth - /> - - Type: - - - chooseEffectType(effect, newEffectType) - } - fullWidth - hintText={t`Choose the effect to apply`} - > - {allEffectMetadata.map(effectMetadata => ( - - ))} - - - - - } - buildMenuTemplate={(i18n: I18nType) => [ - { - label: i18n._(t`Delete`), - click: () => removeEffect(effect.getName()), - }, - { type: 'separator' }, - { - type: 'checkbox', - label: i18n._(t`Show Parameter Names`), - checked: showEffectParameterNames, - click: () => - setShowEffectParameterNames( - !showEffectParameterNames - ), - }, - ]} - /> - - - - {!!effectType && effectMetadata ? ( - - - - - - - - i18n._( - t`Parameter name in events: \`${parameterName}\` ` - ) - : undefined + effect.setName(newName); + forceUpdate(); + onEffectsUpdated(); + }} + fullWidth + /> + + Type: + + + chooseEffectType(effect, newEffectType) + } + fullWidth + hintText={t`Choose the effect to apply`} + > + {allEffectMetadata.map(effectMetadata => ( + - - ) : null} - - - - ); - })} - {effectsContainer.getEffectsCount() === 0 ? ( - - No effects applied. - - ) : null} - - - Add an effect} - onClick={addEffect} - icon={} - /> - + ))} + + + + + } + buildMenuTemplate={(i18n: I18nType) => [ + { + label: i18n._(t`Delete`), + click: () => removeEffect(effect.getName()), + }, + { type: 'separator' }, + { + type: 'checkbox', + label: i18n._(t`Show Parameter Names`), + checked: showEffectParameterNames, + click: () => + setShowEffectParameterNames( + !showEffectParameterNames + ), + }, + ]} + /> + + + + {!!effectType && effectMetadata ? ( + + + + + + + + i18n._( + t`Parameter name in events: \`${parameterName}\` ` + ) + : undefined + } + /> + + ) : null} + + + + ); + })} + + ) : ( + + + )} + + + Add an effect} + onClick={addEffect} + icon={} + /> + - ); - }} + + )} ); } diff --git a/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js b/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js index fe65c208172b..90685e444b87 100644 --- a/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js +++ b/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js @@ -4,40 +4,23 @@ import { Trans } from '@lingui/macro'; import * as React from 'react'; import HelpButton from '../../UI/HelpButton'; import Text from '../../UI/Text'; -import Paper from '@material-ui/core/Paper'; -import { Line, Column } from '../../UI/Grid'; +import { EmptyPlaceholder } from '../../UI/EmptyPlaceholder'; const EmptyEventsPlaceholder = () => ( - - - - - There are no events here. - - - - Events are composed of conditions (on the left of an event) and - actions (on the right). When conditions are fulfilled, the actions - are executed. - - - - - Add your first event using the button "Add a new event". - - - - - - - - + }> + + There are no events here. + + + + Events are composed of conditions (on the left of an event) and actions + (on the right). When conditions are fulfilled, the actions are executed. + + + + Add your first event using the button "Add a new event". + + ); export default EmptyEventsPlaceholder; diff --git a/newIDE/app/src/LayersList/LayerEditorDialog.js b/newIDE/app/src/LayersList/LayerEditorDialog.js index e43ffa50ab49..639e7799ee29 100644 --- a/newIDE/app/src/LayersList/LayerEditorDialog.js +++ b/newIDE/app/src/LayersList/LayerEditorDialog.js @@ -22,7 +22,7 @@ import HotReloadPreviewButton, { import HelpButton from '../UI/HelpButton'; import { Tab, Tabs } from '../UI/Tabs'; import EffectsList from '../EffectsList'; -import { Spacer } from '../UI/Grid'; +import { Column, Spacer } from '../UI/Grid'; const gd: libGDevelop = global.gd; type Props = {| @@ -100,99 +100,104 @@ const LayerEditorDialog = (props: Props) => { />, ]} onRequestClose={onCancelChanges} + fullHeight + flexBody > - - Properties} value="properties" /> - Effects} value="effects" /> - - {currentTab === 'properties' && ( - - {layer.isLightingLayer() ? ( - - - The lighting layer renders an ambient light on the scene. All - lights should be placed on this layer so that shadows are - properly rendered. By default, the layer follows the base layer - camera. Uncheck this if you want to manually move the camera - using events. - - - ) : null} - - There are {instancesCount} instances of objects on this layer. - - {!props.project.getUseDeprecatedZeroAsDefaultZOrder() && ( + + + Properties} value="properties" /> + Effects} value="effects" /> + + {currentTab === 'properties' && ( + + {layer.isLightingLayer() ? ( + + + The lighting layer renders an ambient light on the scene. All + lights should be placed on this layer so that shadows are + properly rendered. By default, the layer follows the base + layer camera. Uncheck this if you want to manually move the + camera using events. + + + ) : null} - Objects created using events on this layer will be given a "Z - order" of {highestZOrder + 1}, so that they appear in front of all - objects of this layer. You can change this using the action to - change an object Z order, after using an action to create it. + There are {instancesCount} instances of objects on this layer. - )} - Layer visible} - checked={layer.getVisibility()} - onCheck={(e, checked) => { - layer.setVisibility(checked); - forceUpdate(); - }} - /> - {layer.isLightingLayer() ? ( - - - Lighting settings + {!props.project.getUseDeprecatedZeroAsDefaultZOrder() && ( + + Objects created using events on this layer will be given a "Z + order" of {highestZOrder + 1}, so that they appear in front of + all objects of this layer. You can change this using the action + to change an object Z order, after using an action to create it. - Automatically follow the base layer.} - checked={layer.isFollowingBaseLayerCamera()} - onCheck={(e, checked) => { - layer.setFollowBaseLayerCamera(checked); - forceUpdate(); - }} - /> - Ambient light color} - disableAlpha - color={{ - r: layer.getAmbientLightColorRed(), - g: layer.getAmbientLightColorGreen(), - b: layer.getAmbientLightColorBlue(), - }} - onChange={(color: ColorResult) => { - layer.setAmbientLightColor( - color.rgb.r, - color.rgb.g, - color.rgb.b - ); - forceUpdate(); - }} - /> - - ) : ( - // Add some space to avoid a dialog to short that would show scrollbars - - - - - )} - - )} - {currentTab === 'effects' && ( - - )} + )} + Layer visible} + checked={layer.getVisibility()} + onCheck={(e, checked) => { + layer.setVisibility(checked); + forceUpdate(); + }} + /> + {layer.isLightingLayer() ? ( + + + Lighting settings + + Automatically follow the base layer.} + checked={layer.isFollowingBaseLayerCamera()} + onCheck={(e, checked) => { + layer.setFollowBaseLayerCamera(checked); + forceUpdate(); + }} + /> + Ambient light color} + disableAlpha + color={{ + r: layer.getAmbientLightColorRed(), + g: layer.getAmbientLightColorGreen(), + b: layer.getAmbientLightColorBlue(), + }} + onChange={(color: ColorResult) => { + layer.setAmbientLightColor( + color.rgb.r, + color.rgb.g, + color.rgb.b + ); + forceUpdate(); + }} + /> + + ) : ( + // Add some space to avoid a dialog to short that would show scrollbars + + + + + )} + + )} + {currentTab === 'effects' && ( + + )} + ); }; diff --git a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js index b2589e22a5c7..d0d0dd3352fc 100644 --- a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js +++ b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js @@ -22,6 +22,7 @@ import useForceUpdate from '../Utils/UseForceUpdate'; import HotReloadPreviewButton, { type HotReloadPreviewButtonProps, } from '../HotReload/HotReloadPreviewButton'; +import EffectsList from '../EffectsList'; type Props = {| open: boolean, @@ -41,6 +42,7 @@ type Props = {| resourceExternalEditors: Array, unsavedChanges?: UnsavedChanges, onUpdateBehaviorsSharedData: () => void, + initialTab: ?string, // Preview: hotReloadPreviewButtonProps: HotReloadPreviewButtonProps, @@ -55,7 +57,9 @@ type InnerDialogProps = {| |}; const InnerDialog = (props: InnerDialogProps) => { - const [currentTab, setCurrentTab] = React.useState('properties'); + const [currentTab, setCurrentTab] = React.useState( + props.initialTab || 'properties' + ); const [newObjectName, setNewObjectName] = React.useState(props.objectName); const forceUpdate = useForceUpdate(); const onCancelChanges = useSerializableObjectCancelableEditor({ @@ -119,6 +123,11 @@ const InnerDialog = (props: InnerDialogProps) => { value={'behaviors'} key={'behaviors'} /> + Effects} + value={'effects'} + key={'effects'} + /> } @@ -179,6 +188,19 @@ const InnerDialog = (props: InnerDialogProps) => { onUpdateBehaviorsSharedData={props.onUpdateBehaviorsSharedData} /> )} + {currentTab === 'effects' && ( + + )} ); }; @@ -233,7 +255,7 @@ export default class ObjectEditorDialog extends Component { } render() { - const { object } = this.props; + const { object, initialTab } = this.props; const { editorComponent, castToObjectType, helpPagePath } = this.state; if (!object || !castToObjectType) return null; @@ -246,6 +268,7 @@ export default class ObjectEditorDialog extends Component { helpPagePath={helpPagePath} object={castToObjectType(object)} objectName={this.state.objectName} + initialTab={initialTab} /> ); } diff --git a/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js b/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js index b9e14978dead..7857532aefa0 100644 --- a/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js +++ b/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js @@ -11,9 +11,9 @@ describe('EnumerateObjects', () => { allObjectsList, } = enumerateObjects(project, testLayout); - expect(containerObjectsList).toHaveLength(12); + expect(containerObjectsList).toHaveLength(13); expect(projectObjectsList).toHaveLength(2); - expect(allObjectsList).toHaveLength(14); + expect(allObjectsList).toHaveLength(15); }); it('can do a case-insensitive search in the lists of objects', () => { diff --git a/newIDE/app/src/ObjectsList/UseObjectsListCommands.js b/newIDE/app/src/ObjectsList/UseObjectsListCommands.js index 6b6b2532693a..928dd14fe50f 100644 --- a/newIDE/app/src/ObjectsList/UseObjectsListCommands.js +++ b/newIDE/app/src/ObjectsList/UseObjectsListCommands.js @@ -8,11 +8,12 @@ import ObjectsRenderingService from '../ObjectsRendering/ObjectsRenderingService const generateLayoutObjectsOptions = ( project: gdProject, layout: gdLayout, - onChoose: (object: gdObject) => void + onChoose: (object: gdObject, arg: ?string) => void, + onChooseArg: ?string ): Array => { return enumerateObjects(project, layout).containerObjectsList.map(item => ({ text: item.object.getName(), - handler: () => onChoose(item.object), + handler: () => onChoose(item.object, onChooseArg), iconSrc: ObjectsRenderingService.getThumbnail.bind(ObjectsRenderingService)( project, item.object @@ -23,7 +24,7 @@ const generateLayoutObjectsOptions = ( type Props = {| project: gdProject, layout: gdLayout, - onEditObject: (object: gdObject) => void, + onEditObject: (object: gdObject, initialTab: ?string) => void, onEditObjectVariables: (object: gdObject) => void, |}; @@ -36,6 +37,27 @@ const useObjectsListCommands = (props: Props) => { ), }); + useCommandWithOptions('EDIT_OBJECT_BEHAVIORS', true, { + generateOptions: React.useCallback( + () => + generateLayoutObjectsOptions( + project, + layout, + onEditObject, + 'behaviors' + ), + [project, layout, onEditObject] + ), + }); + + useCommandWithOptions('EDIT_OBJECT_EFFECTS', true, { + generateOptions: React.useCallback( + () => + generateLayoutObjectsOptions(project, layout, onEditObject, 'effects'), + [project, layout, onEditObject] + ), + }); + useCommandWithOptions('EDIT_OBJECT_VARIABLES', true, { generateOptions: React.useCallback( () => diff --git a/newIDE/app/src/ObjectsList/index.js b/newIDE/app/src/ObjectsList/index.js index bd7f2a3e5f2a..cc42e182641f 100644 --- a/newIDE/app/src/ObjectsList/index.js +++ b/newIDE/app/src/ObjectsList/index.js @@ -106,7 +106,7 @@ type Props = {| getAllObjectTags: () => Tags, onChangeSelectedObjectTags: SelectedTags => void, - onEditObject: gdObject => void, + onEditObject: (object: gdObject, initialTab: ?string) => void, onObjectCreated: gdObject => void, onObjectSelected: string => void, onObjectPasted?: gdObject => void, @@ -466,6 +466,14 @@ export default class ObjectsList extends React.Component { label: i18n._(t`Edit object variables`), click: () => this._editVariables(object), }, + { + label: i18n._(t`Edit behaviors`), + click: () => this.props.onEditObject(object, 'behaviors'), + }, + { + label: i18n._(t`Edit effects`), + click: () => this.props.onEditObject(object, 'effects'), + }, { type: 'separator' }, { label: i18n._(t`Tags`), diff --git a/newIDE/app/src/SceneEditor/index.js b/newIDE/app/src/SceneEditor/index.js index 1706fc43b655..4668f6c72c35 100644 --- a/newIDE/app/src/SceneEditor/index.js +++ b/newIDE/app/src/SceneEditor/index.js @@ -134,6 +134,7 @@ type State = {| editedLayer: ?gdLayer, editedLayerInitialTab: 'properties' | 'effects', editedObjectWithContext: ?ObjectWithContext, + editedObjectInitialTab: ?string, variablesEditedInstance: ?gdInitialInstance, variablesEditedObject: ?gdObject, selectedObjectNames: Array, @@ -185,6 +186,7 @@ export default class SceneEditor extends React.Component { editedLayer: null, editedLayerInitialTab: 'properties', editedObjectWithContext: null, + editedObjectInitialTab: 'properties', variablesEditedInstance: null, variablesEditedObject: null, selectedObjectNames: [], @@ -364,7 +366,7 @@ export default class SceneEditor extends React.Component { this.setState({ layoutVariablesDialogOpen: open }); }; - editObject = (editedObject: ?gdObject) => { + editObject = (editedObject: ?gdObject, initialTab: ?string) => { const { project } = this.props; if (editedObject) { this.setState({ @@ -372,10 +374,12 @@ export default class SceneEditor extends React.Component { object: editedObject, global: project.hasObjectNamed(editedObject.getName()), }, + editedObjectInitialTab: initialTab || 'properties', }); } else { this.setState({ editedObjectWithContext: null, + editedObjectInitialTab: 'properties', }); } }; @@ -1170,6 +1174,7 @@ export default class SceneEditor extends React.Component { ( {muiTheme => ( diff --git a/newIDE/app/src/UI/EmptyPlaceholder.js b/newIDE/app/src/UI/EmptyPlaceholder.js new file mode 100644 index 000000000000..c551789c8d47 --- /dev/null +++ b/newIDE/app/src/UI/EmptyPlaceholder.js @@ -0,0 +1,32 @@ +// @flow +import * as React from 'react'; +import Paper from '@material-ui/core/Paper'; +import { Line, Column } from './Grid'; + +type Props = {| + children: React.Node, + renderButtons: () => React.Node, +|}; + +/** + * A placeholder for when there is no content to display. + * Also take a look at EmptyMessage for a less visible message. + */ +export const EmptyPlaceholder = (props: Props) => ( + + + + {props.children} + + {props.renderButtons()} + + + + +); diff --git a/newIDE/app/src/fixtures/TestExtensions.js b/newIDE/app/src/fixtures/TestExtensions.js index faee2c9df519..deef0798f0cd 100644 --- a/newIDE/app/src/fixtures/TestExtensions.js +++ b/newIDE/app/src/fixtures/TestExtensions.js @@ -35,6 +35,19 @@ export const makeTestExtensions = (gd: libGDevelop) => { .setLabel('Opacity (between 0 and 1)') .setType('number'); + const layerOnlySepiaEffect = extension + .addEffect('FakeSepiaThatWouldWorkOnlyForLayers') + .setFullName('Fake Sepia Effect only for layers') + .setDescription('A fake sepia effect only for layers') + .addIncludeFile('Extensions/Effects/fake-sepia-only-for-layers.js') + .markAsNotWorkingForObjects(); + const layerOnlySepiaProperties = layerOnlySepiaEffect.getProperties(); + layerOnlySepiaProperties + .getOrCreate('opacity') + .setValue('1') + .setLabel('Opacity (between 0 and 1)') + .setType('number'); + const nightEffect = extension .addEffect('FakeNight') .setFullName('Fake Night Effect') diff --git a/newIDE/app/src/fixtures/TestProject.js b/newIDE/app/src/fixtures/TestProject.js index 4d0a3ebb2bbd..00d30dc8c4ee 100644 --- a/newIDE/app/src/fixtures/TestProject.js +++ b/newIDE/app/src/fixtures/TestProject.js @@ -9,6 +9,7 @@ export type TestProject = {| panelSpriteObject: any, spriteObject: gdSpriteObject, spriteObjectWithBehaviors: gdSpriteObject, + spriteObjectWithoutBehaviors: gdSpriteObject, testLayout: gdLayout, group1: gdObjectGroup, group2: gdObjectGroup, @@ -29,6 +30,8 @@ export type TestProject = {| layerWithEffects: gdLayer, layerWithEffectWithoutEffectType: gdLayer, layerWithoutEffects: gdLayer, + spriteObjectWithEffects: gdSpriteObject, + spriteObjectWithoutEffects: gdSpriteObject, |}; */ @@ -73,6 +76,9 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { const spriteObjectWithBehaviors = new gd.SpriteObject( 'MySpriteObjectWithBehaviors' ); + const spriteObjectWithoutBehaviors = new gd.SpriteObject( + 'MySpriteObjectWithoutBehaviors' + ); { const animation = new gd.Animation(); @@ -148,6 +154,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { testLayout.insertObject(panelSpriteObject, 0); testLayout.insertObject(spriteObject, 0); testLayout.insertObject(spriteObjectWithBehaviors, 0); + testLayout.insertObject(spriteObjectWithoutBehaviors, 0); const group1 = new gd.ObjectGroup(); group1.setName('GroupOfSprites'); @@ -503,6 +510,33 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { const layerWithoutEffects = new gd.Layer(); + const spriteObjectWithoutEffects = new gd.SpriteObject( + 'MySpriteObjectWithoutEffects' + ); + const spriteObjectWithEffects = new gd.SpriteObject( + 'MySpriteObjectWithEffects' + ); + { + const effect1 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect1', 0); + const effect2 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect2', 1); + const effect3 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect3', 1); + + effect1.setEffectType('FakeSepia'); + effect1.setDoubleParameter('opacity', 0.6); + effect2.setEffectType('FakeNight'); + effect2.setDoubleParameter('intensity', 0.1); + effect2.setDoubleParameter('opacity', 0.2); + effect3.setEffectType('FakeEffectWithVariousParameters'); + effect3.setDoubleParameter('intensity', 0.1); + effect3.setStringParameter('image', 'my-image'); + } + return { project, shapePainterObject, @@ -511,6 +545,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { panelSpriteObject, spriteObject, spriteObjectWithBehaviors, + spriteObjectWithoutBehaviors, testLayout, group1, group2, @@ -531,5 +566,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { layerWithEffects, layerWithEffectWithoutEffectType, layerWithoutEffects, + spriteObjectWithEffects, + spriteObjectWithoutEffects, }; }; diff --git a/newIDE/app/src/stories/index.js b/newIDE/app/src/stories/index.js index 3b7ea9c8bae3..639c4d01505d 100644 --- a/newIDE/app/src/stories/index.js +++ b/newIDE/app/src/stories/index.js @@ -3639,6 +3639,18 @@ storiesOf('BehaviorsEditor', module) onUpdateBehaviorsSharedData={() => {}} /> + )) + .add('without any behaviors', () => ( + + Promise.reject('Unimplemented')} + resourceExternalEditors={fakeResourceExternalEditors} + onUpdateBehaviorsSharedData={() => {}} + /> + )); storiesOf('VariablesList', module) @@ -4520,8 +4532,9 @@ storiesOf('LayersList', module) storiesOf('EffectsList', module) .addDecorator(paperDecorator) .addDecorator(muiDecorator) - .add('with some effects', () => ( + .add('with some effects (for a layer)', () => ( { @@ -4533,8 +4546,23 @@ storiesOf('EffectsList', module) onEffectsUpdated={action('effects updated')} /> )) - .add('with an effect without effect type', () => ( + .add('with some effects (for an object)', () => ( { + action('onChooseResource'); + return Promise.reject(); + }} + resourceSources={[]} + effectsContainer={testProject.spriteObjectWithEffects.getEffects()} + onEffectsUpdated={action('effects updated')} + /> + )) + .add('with an effect without effect type (for a layer)', () => ( + { @@ -4546,8 +4574,9 @@ storiesOf('EffectsList', module) onEffectsUpdated={action('effects updated')} /> )) - .add('without effects', () => ( + .add('without effects (for a layer)', () => ( { @@ -4558,6 +4587,20 @@ storiesOf('EffectsList', module) effectsContainer={testProject.layerWithoutEffects.getEffects()} onEffectsUpdated={action('effects updated')} /> + )) + .add('without effects (for an object)', () => ( + { + action('onChooseResource'); + return Promise.reject(); + }} + resourceSources={[]} + effectsContainer={testProject.spriteObjectWithoutEffects.getEffects()} + onEffectsUpdated={action('effects updated')} + /> )); storiesOf('NewObjectDialog', module)