Compare commits

..

97 Commits

Author SHA1 Message Date
Clément Pasteau
bce459b2d2 Add missing event data (#7449)
Do not show in changelog
2025-03-06 15:31:26 +01:00
D8H
95dddc48b6 Deprecate old variable actions and conditions in extensions (#7406)
- The extension variables must be used instead
- Old extensions can be maintained by copy-pasting existing old actions and conditions from the extension events
2025-03-06 13:13:14 +01:00
Clément Pasteau
d39f210630 Bump 5.5.225 (#7447) 2025-03-06 12:08:31 +01:00
github-actions[bot]
090649f125 Update translations [skip ci] (#7440)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-03-06 12:01:45 +01:00
Clément Pasteau
7c8c02911c Improve Guided Lessons by preventing clicking outside of the next action (#7444) 2025-03-06 11:54:37 +01:00
D8H
0c6a28c0a1 Fix onCreated functions not being called on behaviors added at hot-reload (#7443) 2025-03-04 14:26:21 +01:00
Florian Rival
b9d5974356 Fix expression documentation not displaying parameters 2025-03-04 00:13:22 +01:00
D8H
74a1e11cfc Improve the abs expression documentation (#7442) 2025-03-03 21:53:52 +01:00
D8H
c712d0bb9e Forbid to install assets or behaviors from extensions incompatible with the editor (#7441) 2025-03-03 20:43:48 +01:00
Clément Pasteau
828e6e031a Update AdMob extension to handle Consent & Tracking Authorization on iOS (#7431)
* Admob will now initialize 2 seconds after the app loads, giving more control to when it starts. This is particularily helpful for App Store validations, with Tracking Authorization message.
* New conditions are available to know AdMob initialization status: "AdMob initializing" and "AdMob Initialized".
* New action to stop the auto-initialization: "Prevent AdMob auto initialization" and new action to trigger the initialization: "Initialize AdMob manually".
  * Typically, you'd prevent the initialization at the beginning of the scene, and trigger it manually when a user interacts with a button or something, so you can control when they'll see the consent messages.
2025-03-03 10:53:59 +01:00
D8H
727f991c01 Allow anchored objects to be moved from their initial position (#7417)
* If an anchored object is moved using events (or another behavior), it will stay on its new position.
  This allows to anchor objects on the left, right, top or bottom of the screen (or inside the bounds of a custom object, for example in a dialog) and still allow them to move.
2025-03-03 10:52:15 +01:00
D8H
5d4523b925 Fix object parameter not shown when editing an action/condition when the object doesn't exist (#7373) 2025-03-03 10:40:06 +01:00
github-actions[bot]
3f7cbc731a Update translations [skip ci] (#7434)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-03-03 10:38:44 +01:00
D8H
c1dfa34f0d Deprecate internal actions and conditions to set and compare property values in extension events (#7405)
- The unified variable action and condition must be used instead.
2025-03-03 10:31:05 +01:00
D8H
cf39de49d6 Allow custom objects to declare multi-line text properties (#7436) 2025-02-28 17:23:51 +01:00
D8H
5b325dd8ec Fix access to objects in variable expressions of "for each child variable" loops (#7435) 2025-02-28 11:06:51 +01:00
Clément Pasteau
1f42331cbf Allow selecting a Pixel Art version of 2D starting points (#7425)
* Available for Platformer, Top-Down & Physics templates
2025-02-27 18:06:15 +01:00
Florian Rival
224f17b90a Add missing await 2025-02-27 17:27:58 +01:00
D8H
3541d356e0 Fix text alignment at runtime (#7432) 2025-02-27 10:58:18 +01:00
github-actions[bot]
63e79d678a Update translations [skip ci] (#7416)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-02-27 09:32:51 +01:00
Florian Rival
812153ffb3 Allow the embedded games platform to ask for opening external urls
Don't show in changelog
2025-02-26 15:26:26 +01:00
Clément Pasteau
c62ea9e10a Show error if games frame does not load (#7430) 2025-02-26 14:13:54 +01:00
Clément Pasteau
04c63b2c74 Fix using non-existing vars in calc() (#7427)
Do not show in changelog
2025-02-26 11:15:18 +01:00
Florian Rival
dcc3bae17e Fix warning
Don't show in changelog
2025-02-25 10:31:21 +01:00
Florian Rival
441834cad5 Prepare custom token to speed up embedded game frame login (#7423)
Don't show in changelog
2025-02-25 10:14:15 +01:00
Florian Rival
d4352ba5d9 Adapt embedded game frame position and allow sharing
Don't show in changelog
2025-02-24 19:35:29 +01:00
Clément Pasteau
163239d08d Fix toggle "update game page" sometimes not being visible when publishing. (#7422)
* This could happen when the last build was too old, and the interface considered the game as new, so published it automatically to the game page.
2025-02-24 15:23:39 +01:00
Florian Rival
e3e9b41672 Display first letter of tabs when at minimum size 2025-02-24 12:24:17 +01:00
inspace
7ba7c220e9 Ensure cursor position is correct when a mouse down event is detected (#7412)
Only show in developer changelog
2025-02-24 11:30:36 +01:00
AlexandreS
617bf2c5b9 Add missing keys in keyboard condition/action (#7421)
- Added Left and Right bracket and the Menu keys
2025-02-24 10:38:14 +01:00
Clément Pasteau
d647d1e397 Fix top bar to be draggable on desktop, except tabs (#7420) 2025-02-24 10:32:44 +01:00
Florian Rival
c5bf9730aa Refactor toolbar and titlebar display and ensure game iframe is hidden when not on the Play tab (#7419)
Don't show in changelog
2025-02-23 15:28:32 +01:00
Florian Rival
c5022596b8 Make the games/page in Play section to take the full width
Don't shown in changelog
2025-02-22 14:37:36 +01:00
D8H
6e61c48898 Fix a regression that hid all 3D models in the editor (#7418)
Don't show in changelog
2025-02-22 14:00:56 +01:00
Clément Pasteau
75fc55bac4 Rework the Play section to have games played inside the app (#7372) 2025-02-20 18:46:00 +01:00
github-actions[bot]
8a4916fd93 Update translations [skip ci] (#7414) 2025-02-20 10:08:45 +01:00
AlexandreS
b008863281 Do not show the reminder if the editor was just idle for too long (#7413) 2025-02-19 15:56:25 +01:00
github-actions[bot]
f0377771a8 Update translations [skip ci] (#7399)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-02-19 09:33:26 +01:00
D8H
299ef90f93 [3D character] Fix the "is moving" condition when moving sideways (#7411) 2025-02-17 14:01:37 +01:00
D8H
800c3333d4 Improve typing of the 3D object editor renderers (#7410)
- Don't show in changelog
2025-02-17 12:57:27 +01:00
AlexandreS
303ebfaf1b Refresh in app tutorial message in preview when hot reloading preview (#7407)
Don't show in changelog
2025-02-17 12:06:44 +01:00
AlexandreS
288e4f4b75 Fix: Remove crash when opening 3D Racing game example external layouts
- Use default values when updating Cube 3D three object in editor (#7408)
2025-02-17 11:30:54 +01:00
Clément Pasteau
15db4be4a1 Fix typo (#7402)
Do not show in changelog
2025-02-14 10:08:01 +01:00
D8H
925ffa74ec Allow to tween 3D boxes tint color (#7401)
- Don't show in changelog
2025-02-13 12:35:52 +01:00
D8H
752d047464 Upgrade GDJS to Prettier 3.4.2 (#7398)
- Don't show in changelog
2025-02-12 18:50:02 +01:00
D8H
7a21f9533e Upgrade to TypeScript 5.4.5 (#7394)
- Don't show in changelog
2025-02-12 17:51:36 +01:00
Ansel Games
9820770fb3 Update "Create objects from an external layout" action to show all parameters in the events sheet (#7383) 2025-02-12 17:49:23 +01:00
D8H
a92784295a Fix GDJS test resource paths (#7397) 2025-02-12 17:41:19 +01:00
Neyl
81f6c62bb1 Update Animation Crossfade default value to a more realistic one (100ms) (#7395) 2025-02-12 17:13:36 +01:00
github-actions[bot]
a1bbb1c14c Update translations [skip ci] (#7388)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2025-02-12 16:56:29 +01:00
Clément Pasteau
9154144228 Fix a project opened from a file being always detected as a new game in the dashboard (#7392) 2025-02-12 16:55:59 +01:00
AlexandreS
c0176dfb2b Add lightweight markdown interpreter for in-app tutorial in-game messages (#7391)
Don't show in changelog
2025-02-12 14:35:32 +01:00
AlexandreS
23cd527d94 Fix spatial sound action not applied if sound is not playing yet (#7393) 2025-02-12 14:33:46 +01:00
Neyl
fa2cdb1624 fix crossfade duration in 3D model editor 2025-02-12 12:02:08 +01:00
Clément Pasteau
03eca4de4b Upgrade pixi to 7.4.2 (#7190) 2025-02-11 14:20:59 +01:00
Clément Pasteau
841484e5b7 Disable lazy loading on authorized images, fixing the image stuck with a loader (#7389) 2025-02-11 10:58:47 +01:00
github-actions[bot]
b88c5b08f5 Update translations [skip ci] (#7370)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-02-11 09:36:17 +01:00
Aurélien Vivet
3f2043f248 Add a clearer warning before the deletion of a game and a project. (#7368)
* This is mainly to prevent accidental deletion of games
2025-02-11 09:35:33 +01:00
Florian Rival
6941e64418 Adapt tabs width according to screen width, to keep them visible on screen as much as possible (#7387) 2025-02-10 18:24:48 +01:00
Neyl
fb7bb9181d add support for crossfading animations for 3D models
the new option allows to smoothly transition between animations. The fading duration can be customized with an action.
2025-02-10 17:56:18 +01:00
AlexandreS
7ee50a2568 Fix: Prevent accidentally saving a project in the root folder of the editor 2025-02-10 13:04:23 +01:00
AlexandreS
90cdc875a9 Add possibility to display message in the preview when an in-app tutorial is running (#7379) 2025-02-10 10:59:35 +01:00
Florian Rival
47d0fba205 Fix a crash when searching instructions after extension are re-generated (#7377)
* Also improve the display of actions/conditions/expressions when searched with a clearer visual separator for their folders
2025-02-07 16:03:09 +01:00
Aurélien Vivet
db745369dc Re-order in-app tutorials (#7378) 2025-02-07 15:30:19 +01:00
D8H
7c49346bed Add a warning on array children with inconsistent type (#7376) 2025-02-06 18:29:38 +01:00
AlexandreS
b9a7786ab0 Add an action to join a lobby in one-click (#7352) 2025-02-06 18:04:41 +01:00
AlexandreS
76c09908c8 Fix border radius added by browser on text input (#7375) 2025-02-06 14:45:54 +01:00
Neyl
b6e186e775 Tint color for 3D cube (#7354)
add a tint color property for 3D cube.
2025-02-06 11:10:39 +01:00
D8H
8c98239e12 Allow to swap assets of any object type (#7365) 2025-02-04 17:22:53 +01:00
AlexandreS
4d56333ae7 Display shorter title in Curriculum's table of content when available (#7371) 2025-02-04 16:22:13 +01:00
AlexandreS
146794d33a Bump newIDE version (#7369) 2025-02-04 15:32:07 +01:00
AlexandreS
3a685c62df Refine free conditions/actions search to give priority to results with the search word in the displayed name (#7360) 2025-02-04 11:28:43 +01:00
github-actions[bot]
8bcbf9bd47 Update translations [skip ci] (#7353)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-02-04 10:25:13 +01:00
Clément Pasteau
601a4e214e Fix games sometimes disappearing from the dashboard (#7366)
* This was caused by games wrongly being considered as not saved during the export, and they were hidden from the dashboard afterwards
2025-02-04 10:07:06 +01:00
D8H
3d0b264130 [Physics3D] Fix remaining hit-boxes of deleted character (#7359)
- Also fix 3D character behavior deactivation
2025-02-03 17:54:38 +01:00
D8H
97e989dee0 Ensure behaviors are compatible with the object before pasting them (#7343) 2025-02-03 13:02:41 +01:00
D8H
6440149fc8 Change documentation pages of some actions and conditions to point to more specific pages (#7364) 2025-02-03 12:58:28 +01:00
Neyl
232359f0a4 FIX the destroy of the form, "is submitted" condition icon, and readonly & disable feature (#7363)
Fix the destroy of the form, readonly and disable feature, and a typo for "is submitted" condition icon
2025-02-03 12:52:57 +01:00
Florian Rival
bf7ecd9ceb Add "Scroll" text to in-app tutorial highlights (#7356)
Don't show in changelog
2025-01-30 11:31:30 +01:00
D8H
2ba7075a7a Update the "trigger once" help page link (#7355) 2025-01-28 19:05:58 +01:00
Clément Pasteau
e45256cebb Fix a few issues with displaying projects correctly in the create section (#7349) 2025-01-27 18:51:25 +01:00
Florian Rival
1ee2d57d2f Bump newIDE version 2025-01-27 18:35:49 +01:00
github-actions[bot]
e890d8f211 Update translations [skip ci] (#7348)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-27 18:34:42 +01:00
Florian Rival
e70f8532f6 Improve course display 2025-01-27 16:54:42 +01:00
Florian Rival
446494156b Fix crash when adding/modifying functions in extensions (#7351) 2025-01-26 21:40:11 +01:00
D8H
15a1d392a1 Fix extension usage listing for example web pages (#7339) 2025-01-24 15:19:49 +01:00
AlexandreS
65b5aa6bac Use new compact UI in the Preferences dialog (#7345) 2025-01-24 14:02:48 +01:00
Florian Rival
cbaa785b95 Fix log and extra character
Don't show in changelog
2025-01-24 13:46:05 +01:00
github-actions[bot]
b6091de09a Update translations [skip ci] (#7341)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2025-01-24 13:29:17 +01:00
Clément Pasteau
b53dc29f43 Add preferences menu item back in the File menu (#7346) 2025-01-24 13:28:56 +01:00
Aurélien Vivet
0848a109d4 Bring consistency on arrow direction for UI items that can be opened/closed (#7322) 2025-01-24 10:50:51 +01:00
Florian Rival
4bf21e94eb Change in-app tutorials order 2025-01-24 10:43:05 +01:00
Clément Pasteau
2d6b2c1753 Improve homepage bottom bar icons on mobile (#7342) 2025-01-23 18:31:04 +01:00
AlexandreS
c4e230d9ba Split text input padding into 2 separate properties (#7338)
Don't show in changelog
2025-01-23 16:12:18 +01:00
D8H
56ab2fdd05 Revert "Fix used extension check false positive from usused object or behavior events (#7331)" (#7340)
This reverts commit cb24f191fd.

Don't show in changelog
2025-01-23 14:11:13 +01:00
github-actions[bot]
872f4032ff Update translations [skip ci] (#7337)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-23 11:56:54 +01:00
D8H
428b4c21a8 Disable panel sprite tilling by default (#7335)
* This ensures the rendering is more performant by default
2025-01-23 09:43:40 +01:00
477 changed files with 10991 additions and 5887 deletions

View File

@@ -301,7 +301,13 @@ workflows:
- build-gdevelop_js-wasm-only
gdevelop_js-wasm-extra-checks:
jobs:
- build-gdevelop_js-debug-sanitizers-and-extra-checks
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
# Extra checks are resource intensive so don't all run them.
filters:
branches:
only:
- master
- /experimental-build.*/
builds:
jobs:
- build-macos:

View File

@@ -141,7 +141,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"res/function32.png")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
.MarkAsAdvanced()
.SetHidden();
extension
.AddExpression(
@@ -177,7 +178,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.UseStandardRelationalOperatorParameters(
"number", gd::ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition(
@@ -191,7 +193,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.UseStandardRelationalOperatorParameters(
"string", gd::ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
}
} // namespace gd

View File

@@ -117,7 +117,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Change the position of the center of _PARAM0_: _PARAM1_ "
"_PARAM2_ (x "
"axis), _PARAM3_ _PARAM4_ (y axis)"),
_("Position/Center"),
_("Position Center"),
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
@@ -133,7 +133,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Center X position"),
_("the X position of the center of rotation"),
_("the X position of the center"),
_("Position/Center"),
_("Position Center"),
"res/actions/position24_black.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -144,7 +144,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Center Y position"),
_("the Y position of the center of rotation"),
_("the Y position of the center"),
_("Position/Center"),
_("Position Center"),
"res/actions/position24_black.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -155,7 +155,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating "
"the object) left position"),
_("the bounding box left position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-left_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -166,7 +166,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Bounding box top position"),
_("the bounding box (the area encapsulating the object) top position"),
_("the bounding box top position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-top_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -177,7 +177,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating "
"the object) right position"),
_("the bounding box right position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-right_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -188,7 +188,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating "
"the object) bottom position"),
_("the bounding box bottom position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-bottom_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -199,7 +199,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating "
"the object) center X position"),
_("the bounding box center X position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-center_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -210,7 +210,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating "
"the object) center Y position"),
_("the bounding box center Y position"),
_("Position/Bounding Box"),
_("Position Bounding Box"),
"res/conditions/bounding-box-center_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions());
@@ -255,7 +255,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Angle"),
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angular speed (in degrees per second)"))
.AddCodeOnlyParameter("currentScene", "")
@@ -269,7 +268,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Angle"),
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle to rotate towards (in degrees)"))
.AddParameter("expression", _("Angular speed (in degrees per second)"))
@@ -285,7 +283,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Angle"),
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
@@ -304,12 +301,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Speed on X axis (in pixels per second)"))
.AddParameter("expression", _("Speed on Y axis (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0");
.SetDefaultValue("0")
.SetHelpPath("/tutorials/how-to-move-objects/");
obj.AddAction("AddForceAL",
_("Add a force (angle)"),
@@ -321,12 +318,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddAction(
@@ -339,13 +336,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddAction(
@@ -360,13 +357,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/forceTourne24.png",
"res/actions/forceTourne.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", "X position of the center")
.AddParameter("expression", "Y position of the center")
.AddParameter("expression", "Speed (in Degrees per seconds)")
.AddParameter("expression", "Distance (in pixels)")
.AddParameter("forceMultiplier", "Force multiplier")
.SetHelpPath("/tutorials/how-to-move-objects/")
.SetHidden();
obj.AddAction("Arreter",
@@ -376,8 +373,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/arreter24.png",
"res/actions/arreter.png")
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddAction("Delete",
@@ -429,6 +426,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddAction("SetStringObjectVariable",
@@ -443,6 +441,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddAction("SetBooleanObjectVariable",
@@ -458,6 +457,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("yesorno", _("Value"))
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("NumberObjectVariable",
@@ -472,6 +472,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("StringObjectVariable",
@@ -486,6 +487,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("BooleanObjectVariable",
@@ -502,6 +504,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("yesorno", _("Value"))
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForLayoutEventsOnly();
obj.AddAction("ModVarObjet",
@@ -514,6 +517,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.SetHelpPath("/all-features/variables/object-variables/")
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
@@ -528,6 +532,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.SetHelpPath("/all-features/variables/object-variables/")
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
@@ -544,6 +549,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:"))
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddAction(
@@ -560,6 +566,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("ObjectVariableChildExists",
@@ -567,24 +574,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Check if the specified child of the object "
"structure variable exists."),
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Structure variable"))
.AddParameter("string", _("Name of the child"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddAction("ObjectVariableRemoveChild",
_("Remove a child"),
_("Remove a child from an object structure variable."),
_("Remove child _PARAM2_ from variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddAction("ObjectVariableClearChildren",
@@ -592,11 +601,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Remove all the children from the object array or structure "
"variable."),
_("Clear children from variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array or structure variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddAction("Cache",
@@ -691,8 +701,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/conditions/arret24.png",
"res/conditions/arret.png")
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddCondition("Vitesse",
@@ -702,10 +712,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
.AddParameter("object", _("Object"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
// Deprecated
@@ -722,6 +732,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Tolerance, in degrees"))
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddCondition("IsTotalForceAngleAround",
@@ -735,6 +746,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Tolerance, in degrees"))
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddCondition("VarObjet",
@@ -749,6 +761,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("VarObjetTxt",
@@ -763,6 +776,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("ObjectVariableAsBoolean",
@@ -777,6 +791,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("VarObjetDef",
@@ -796,12 +811,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add text variable"),
_("Adds a text (string) to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("string", _("Text to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
@@ -809,12 +825,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add variable array value"),
_("Adds a number to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
@@ -823,12 +840,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add boolean variable"),
_("Adds a boolean to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
@@ -838,7 +856,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add existing variable"),
_("Adds an existing variable to the end of an object array variable."),
_("Add variable _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
@@ -847,6 +865,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetParameterLongDescription(_("The content of the object variable will "
"*be copied* and added at the "
"end of the array."))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetHidden();
@@ -855,7 +874,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add existing variable"),
_("Adds an existing variable to the end of an object array variable."),
_("Add variable _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
@@ -864,6 +883,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetParameterLongDescription(_("The content of the object variable will "
"*be copied* and added at the "
"end of the array."))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddAction(
@@ -871,12 +891,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add text variable"),
_("Adds a text (string) to the end of an object array variable."),
_("Add text _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("string", _("Text to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
@@ -884,12 +905,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add number variable"),
_("Adds a number to the end of an object array variable."),
_("Add number _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
@@ -898,12 +920,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Add boolean variable"),
_("Adds a boolean to the end of an object array variable."),
_("Add boolean _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
@@ -914,12 +937,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"variable."),
_("Remove variable at index _PARAM2_ from array variable _PARAM1_ of "
"_PARAM0_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddCondition(
@@ -927,13 +951,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Number of children"),
_("Compare the number of children in an object array variable."),
_("The number of children in the array variable _PARAM1_"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
obj.AddStrExpression(
@@ -941,40 +966,44 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("First text child"),
_("Get the value of the first element of an object array variable, if "
"it is a text (string) variable."),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
.AddParameter("objectvar", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
obj.AddExpression(
"ArrayVariableFirstNumber",
_("First number child"),
_("Get the value of the first element of an object array variable, if "
"it is a number variable."),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
.AddParameter("objectvar", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
obj.AddStrExpression(
"ArrayVariableLastString",
_("Last text child"),
_("Get the value of the last element of an object array variable, if "
"it is a text (string) variable."),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
.AddParameter("objectvar", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
obj.AddExpression(
"ArrayVariableLastNumber",
_("Last number child"),
_("Get the value of the last element of an object array variable, if "
"it is a number variable."),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
.AddParameter("objectvar", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
obj.AddCondition("BehaviorActivated",
_("Behavior activated"),
@@ -1009,12 +1038,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/forceVers24.png",
"res/actions/forceVers.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target Object"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddAction(
@@ -1029,13 +1058,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/forceTourne24.png",
"res/actions/forceTourne.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Rotate around this object"))
.AddParameter("expression", _("Speed (in degrees per second)"))
.AddParameter("expression", _("Distance (in pixels)"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
obj.AddAction("MettreAutour",
@@ -1063,10 +1092,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/ecarter24.png",
"res/actions/ecarter.png")
.SetHidden()
.AddParameter("object", _("Object"))
.AddParameter("objectList", "Object 2 (won't move)");
.AddParameter("objectList", "Object 2 (won't move)")
.SetHelpPath("/tutorials/how-to-move-objects/");
// Deprecated action
obj.AddAction("Ecarter",
@@ -1090,7 +1119,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Position"),
"res/actions/ecarter24.png",
"res/actions/ecarter.png")
.AddParameter("object", _("Object"))
.AddParameter("objectList", _("Objects (won't move)"))
.AddParameter("yesorno",
@@ -1099,6 +1127,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"",
true)
.SetDefaultValue("no")
.SetHelpPath("/all-features/collisions/")
.MarkAsSimple();
obj.AddCondition("CollisionPoint",
@@ -1111,6 +1140,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position of the point"))
.AddParameter("expression", _("Y position of the point"))
.SetHelpPath("/all-features/collisions/")
.MarkAsSimple();
extension
@@ -1142,6 +1172,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("expression", _("Time in seconds"))
.SetHelpPath("/all-features/timers-and-time/")
.SetHidden();
obj.AddCondition(
@@ -1157,6 +1188,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("relationalOperator", _("Sign of the test"), "time")
.AddParameter("expression", _("Time in seconds"))
.SetHelpPath("/all-features/timers-and-time/")
.SetManipulatedType("number");
obj.AddCondition("ObjectTimerPaused",
@@ -1168,6 +1200,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timerPaused.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/")
.MarkAsAdvanced();
obj.AddAction(
@@ -1180,7 +1213,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer");
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/");
obj.AddAction("PauseObjectTimer",
_("Pause an object timer"),
@@ -1191,6 +1225,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/pauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/")
.MarkAsAdvanced();
obj.AddAction("UnPauseObjectTimer",
@@ -1202,6 +1237,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/unPauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/")
.MarkAsAdvanced();
obj.AddAction("RemoveObjectTimer",
@@ -1213,6 +1249,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/")
.MarkAsAdvanced();
obj.AddExpression("X",
@@ -1241,28 +1278,32 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("X coordinate of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/");
obj.AddExpression("ForceY",
_("Y coordinate of the sum of forces"),
_("Y coordinate of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/");
obj.AddExpression("ForceAngle",
_("Angle of the sum of forces"),
_("Angle of the sum of forces (in degrees)"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/");
obj.AddExpression("ForceLength",
_("Length of the sum of forces"),
_("Length of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/");
obj.AddExpression("Longueur",
_("Length of the sum of forces"),
@@ -1270,6 +1311,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"))
.SetHelpPath("/tutorials/how-to-move-objects/")
.SetHidden();
obj.AddExpression("Width",
@@ -1366,10 +1408,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"VariableChildCount",
_("Number of children"),
_("Number of children in an object array or structure variable"),
_("Variables/Arrays and structures"),
_("Variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array or structure variable"));
.AddParameter("objectvar", _("Array or structure variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
obj.AddStrExpression("VariableString",
_("Text variable"),
@@ -1378,6 +1421,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.SetHelpPath("/all-features/variables/object-variables/")
.SetRelevantForFunctionEventsOnly();
obj.AddExpression("ObjectTimerElapsedTime",
@@ -1386,7 +1430,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Timers"),
"res/actions/time.png")
.AddParameter("object", _("Object"))
.AddParameter("identifier", _("Timer's name"), "objectTimer");
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.SetHelpPath("/all-features/timers-and-time/");
obj.AddExpression("AngleToObject",
_("Angle between two objects"),
@@ -1606,6 +1651,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/doMove24.png",
"res/actions/doMove.png")
.AddCodeOnlyParameter("currentScene", "")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
extension
@@ -1621,6 +1667,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectList", _("Object 2"))
.AddParameter("expression", _("Tolerance, in degrees"))
.AddCodeOnlyParameter("conditionInverted", "")
.SetHelpPath("/tutorials/how-to-move-objects/")
.MarkAsAdvanced();
extension
@@ -1753,6 +1800,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"",
true)
.SetDefaultValue("no")
.SetHelpPath("/all-features/collisions/")
.MarkAsSimple();
extension
@@ -1815,6 +1863,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Scene variable where to store the Y position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddCodeOnlyParameter("conditionInverted", "")
.SetHelpPath("/all-features/collisions/")
.MarkAsAdvanced();
extension
@@ -1846,6 +1895,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Scene variable where to store the Y position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddCodeOnlyParameter("conditionInverted", "")
.SetHelpPath("/all-features/collisions/")
.MarkAsAdvanced();
extension

View File

@@ -92,14 +92,16 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
extension.AddCondition(
"Once",
_("Trigger once while true"),
_("Run actions only once, for each time the conditions have been met."),
_("Trigger once"),
"",
"res/conditions/once24.png",
"res/conditions/once.png");
extension
.AddCondition("Once",
_("Trigger once while true"),
_("Run actions only once, for each time the conditions "
"have been met."),
_("Trigger once"),
"",
"res/conditions/once24.png",
"res/conditions/once.png")
.SetHelpPath("/all-features/advanced-conditions/trigger-once");
extension
.AddCondition("CompareNumbers",

View File

@@ -28,7 +28,7 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
.AddAction("CreateObjectsFromExternalLayout",
_("Create objects from an external layout"),
_("Create objects from an external layout."),
_("Create objects from the external layout named _PARAM1_"),
_("Create objects from the external layout named _PARAM1_ at position _PARAM2_;_PARAM3_;_PARAM4_"),
"",
"res/ribbon_default/externallayout32.png",
"res/ribbon_default/externallayout32.png")

View File

@@ -151,7 +151,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("abs",
_("Absolute value"),
_("Absolute value"),
_("Return the non-negative value by removing the sign. "
"The absolute value of -8 is 8."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));

View File

@@ -116,6 +116,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddParameter("variable", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -129,6 +130,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("string", _("Name of the child"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -142,6 +144,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var.png")
.AddParameter("variable", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -154,6 +157,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Structure or array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -170,6 +174,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -183,6 +188,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("string", _("Text to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -195,6 +201,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -207,6 +214,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -221,6 +229,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced();
extension
@@ -231,7 +240,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"it is a text (string)."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"));
.AddParameter("variable", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
extension
.AddExpression(
@@ -241,7 +251,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"it is a number."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"));
.AddParameter("variable", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
extension
.AddStrExpression(
@@ -251,7 +262,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"it is a text (string)."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"));
.AddParameter("variable", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
extension
.AddExpression(
@@ -261,7 +273,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"it is a number."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"));
.AddParameter("variable", _("Array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/");
// Legacy instructions
@@ -270,26 +283,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number variable"),
_("Compare the number value of a scene variable."),
_("The number of scene variable _PARAM0_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition("VarSceneTxt",
_("Text variable"),
_("Compare the text (string) of a scene variable."),
_("The text of scene variable _PARAM0_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition(
@@ -297,13 +312,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Boolean variable"),
_("Compare the boolean value of a scene variable."),
_("The boolean value of scene variable _PARAM0_ is _PARAM1_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition("VariableChildExists",
@@ -311,12 +327,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Check if the specified child of the scene structure "
"variable exists."),
_("Child _PARAM1_ of scene variable _PARAM0_ exists"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("string", _("Name of the child"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -325,12 +343,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Check if the specified child of the global structure "
"variable exists."),
_("Child _PARAM1_ of global variable _PARAM0_ exists"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("string", _("Name of the child"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -338,7 +358,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"Variable defined",
"Test if the scene variable exists.",
"Scene variable _PARAM0_ is defined",
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -350,13 +370,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number variable"),
_("Compare the number value of a global variable."),
_("the global variable _PARAM0_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -364,13 +385,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Text variable"),
_("Compare the text (string) of a global variable."),
_("the text of the global variable _PARAM0_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -379,20 +401,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Boolean variable"),
_("Compare the boolean value of a global variable."),
_("The boolean value of global variable _PARAM0_ is _PARAM1_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition("VarGlobalDef",
"Variable defined",
"Test if a global variable exists.",
"Global variable _PARAM0_ is defined",
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -405,26 +428,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change number variable"),
_("Modify the number value of a scene variable."),
_("the scene variable _PARAM0_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction("ModVarSceneTxt",
_("Change text variable"),
_("Modify the text (string) of a scene variable."),
_("the text of scene variable _PARAM0_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction(
@@ -432,12 +457,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change boolean variable"),
_("Modify the boolean value of a scene variable."),
_("Set the boolean value of scene variable _PARAM0_ to _PARAM1_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction("ToggleSceneVariableAsBoolean",
@@ -446,24 +472,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("If it was true, it will become false, and if it was "
"false it will become true."),
_("Toggle the boolean value of scene variable _PARAM0_"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction("ModVarGlobal",
_("Change number variable"),
_("Modify the number value of a global variable."),
_("the global variable _PARAM0_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -471,13 +499,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change text variable"),
_("Modify the text (string) of a global variable."),
_("the text of global variable _PARAM0_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -486,12 +515,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change boolean variable"),
_("Modify the boolean value of a global variable."),
_("Set the boolean value of global variable _PARAM0_ to _PARAM1_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction("ToggleGlobalVariableAsBoolean",
@@ -500,11 +530,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("If it was true, it will become false, and if it was "
"false it will become true."),
_("Toggle the boolean value of global variable _PARAM0_"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction(
@@ -512,13 +543,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove a child"),
_("Remove a child from a scene structure variable."),
_("Remove child _PARAM1_ from scene structure variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction(
@@ -526,13 +559,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove a child"),
_("Remove a child from a global structure variable."),
_("Remove child _PARAM1_ from global structure variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction("VariableClearChildren",
@@ -540,11 +575,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove all the children from the scene structure or array "
"variable."),
_("Clear children from scene variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Structure or array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -553,11 +590,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove all the children from the global structure or array "
"variable."),
_("Clear children from global variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Structure or array variable"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -566,7 +605,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Adds an existing variable at the end of a scene array "
"variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
@@ -574,7 +613,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -583,12 +624,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add text variable"),
_("Adds a text (string) at the end of a scene array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("string", _("Text to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -596,12 +639,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add number variable"),
_("Adds a number at the end of a scene array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -609,12 +654,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add boolean variable"),
_("Adds a boolean at the end of a scene array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -624,12 +671,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"variable."),
_("Remove variable at index _PARAM1_ from scene array "
"variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -638,13 +687,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number of children"),
_("Compare the number of children in a scene array variable."),
_("The number of children in the array variable _PARAM0_"),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -653,10 +704,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First text child"),
_("Get the value of the first element of a scene array variable, if "
"it is a text (string)."),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression(
@@ -664,10 +717,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First number child"),
_("Get the value of the first element of a scene array variable, if "
"it is a number."),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddStrExpression(
@@ -675,10 +730,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last text child"),
_("Get the value of the last element of a scene array variable, if "
"it is a text (string)."),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression(
@@ -686,10 +743,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last number child"),
_("Get the value of the last element of a scene array variable, if "
"it is a number."),
_("External variables/Scene variables/Arrays and structures"),
_("External variables Scene variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddAction(
@@ -697,7 +756,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add existing variable"),
_("Adds an existing variable at the end of a global array variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
@@ -705,7 +764,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -715,12 +776,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"array variable."),
_("Remove variable at index _PARAM1_ from global array "
"variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -729,12 +792,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add text variable"),
_("Adds a text (string) at the end of a global array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("string", _("Text to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -742,12 +807,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add number variable"),
_("Adds a number at the end of a global array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -755,12 +822,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add boolean variable"),
_("Adds a boolean at the end of a global array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -769,13 +838,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number of children"),
_("Compare the number of children in a global array variable."),
_("The number of children of the array variable _PARAM0_"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden()
.MarkAsAdvanced();
extension
@@ -783,20 +854,24 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First text child"),
_("Value of the first element of a global array "
"variable, if it is a text (string) variable."),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression("GlobalVariableFirstNumber",
_("First number child"),
_("Value of the first element of a global array "
"variable, if it is a number variable"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddStrExpression(
@@ -804,10 +879,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last text child"),
_("Value of the last element of a global array variable, if "
"it is a text (string) variable."),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression(
@@ -815,20 +892,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last number child"),
_("Value of the last element of a global array variable, if "
"it is a number variable"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression("GlobalVariableChildCount",
_("Number of children"),
_("Number of children in a global array or "
"structure variable"),
_("External variables/Global variables/Arrays and structures"),
_("External variables Global variables Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array or structure variable"))
.SetRelevantForFunctionEventsOnly();
.SetHelpPath("/all-features/variables/structures-and-arrays/")
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression("VariableChildCount",
@@ -837,43 +917,48 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"structure variable"),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array or structure variable"), "AllowUndeclaredVariable");
.AddParameter("variable", _("Array or structure variable"), "AllowUndeclaredVariable")
.SetHelpPath("/all-features/variables/structures-and-arrays/");
extension
.AddExpression("Variable",
_("Number variable"),
_("Number value of a scene variable"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddStrExpression("VariableString",
_("Text variable"),
_("Text of a scene variable"),
_("External variables/Scene variables"),
_("External variables Scene variables"),
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddExpression("GlobalVariable",
_("Number variable"),
_("Number value of a global variable"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/actions/var.png")
.AddParameter("globalvar", _("Name of the global variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddStrExpression("GlobalVariableString",
_("Text variable"),
_("Text of a global variable"),
_("External variables/Global variables"),
_("External variables Global variables"),
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
}
} // namespace gd

View File

@@ -101,7 +101,7 @@ const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
} else if (propertyType == "KeyboardKey") {
return keyboardKeyValueType;
}
// For "String", "Resource" or default
// For "String", "Resource", "MultilineString" or default
return stringValueType;
};

View File

@@ -0,0 +1,235 @@
#include "ExampleExtensionUsagesFinder.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
namespace gd {
std::set<gd::String>
ExampleExtensionUsagesFinder::GetUsedExtensions(gd::Project &project) {
ExampleExtensionUsagesFinder worker(project);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
gd::ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(project,
worker);
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
worker.isStoreExtension =
eventsFunctionsExtension.GetOriginName() == "gdevelop-extension-store";
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
project, eventsFunctionsExtension, worker);
}
if (!worker.has3DObjects) {
worker.usedExtensions.erase("Scene3D");
}
return worker.usedExtensions;
};
void ExampleExtensionUsagesFinder::AddUsedExtension(
const gd::PlatformExtension &extension) {
usedExtensions.insert(extension.GetName());
}
void ExampleExtensionUsagesFinder::AddUsedBuiltinExtension(
const gd::String &extensionName) {
usedExtensions.insert(extensionName);
}
// Objects scanner
void ExampleExtensionUsagesFinder::DoVisitObject(gd::Object &object) {
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType());
if (metadata.GetMetadata().IsRenderedIn3D()) {
has3DObjects = true;
}
AddUsedExtension(metadata.GetExtension());
};
// Behaviors scanner
void ExampleExtensionUsagesFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
AddUsedExtension(metadata.GetExtension());
};
// Instructions scanner
bool ExampleExtensionUsagesFinder::DoVisitInstruction(
gd::Instruction &instruction, bool isCondition) {
auto metadata =
isCondition ? gd::MetadataProvider::GetExtensionAndConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
// Unused event-based objects or events-based behaviors may use object and
// behavior instructions that should not be detected as extension usage.
// The extension of actually used objects and behaviors will be detected on
// scene objects. This is why object or behavior instructions usually don't
// have any import.
// Built-in extensions that are included by default don't declare any include
// files on their instructions either. To still detect their usage, we
// consider that main events and dedicated extensions can't have dead code.
if (!isStoreExtension || !metadata.GetMetadata().GetIncludeFiles().empty()) {
AddUsedExtension(metadata.GetExtension());
}
gd::ParameterMetadataTools::IterateOverParameters(
instruction.GetParameters(), metadata.GetMetadata().GetParameters(),
[this](const gd::ParameterMetadata &parameterMetadata,
const gd::Expression &parameterValue,
const gd::String &lastObjectName) {
const gd::String &parameterType = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsExpression("string", parameterType)) {
rootType = "string";
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("number",
parameterType)) {
rootType = "number";
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable",
parameterType))
AddUsedBuiltinExtension("BuiltinVariables");
});
return false;
}
// Expressions scanner
// Ignore literals nodes
void ExampleExtensionUsagesFinder::OnVisitNumberNode(NumberNode &node){};
void ExampleExtensionUsagesFinder::OnVisitTextNode(TextNode &node){};
// Ignore nodes without valid extensions
void ExampleExtensionUsagesFinder::OnVisitEmptyNode(EmptyNode &node){};
void ExampleExtensionUsagesFinder::OnVisitObjectFunctionNameNode(
ObjectFunctionNameNode &node){};
// Visit sub-expressions
void ExampleExtensionUsagesFinder::OnVisitSubExpressionNode(
SubExpressionNode &node) {
node.expression->Visit(*this);
};
void ExampleExtensionUsagesFinder::OnVisitOperatorNode(OperatorNode &node) {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
};
void ExampleExtensionUsagesFinder::OnVisitUnaryOperatorNode(
UnaryOperatorNode &node) {
node.factor->Visit(*this);
};
// Add variable extension and visit sub-expressions on variable nodes
void ExampleExtensionUsagesFinder::OnVisitVariableNode(VariableNode &node) {
AddUsedBuiltinExtension("BuiltinVariables");
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(),
GetProjectScopedContainers(),
rootType, node);
if (gd::ParameterMetadata::IsExpression("variable", type)) {
// Nothing to do (this can't reference an object)
} else {
GetProjectScopedContainers().MatchIdentifierWithName<void>(
node.name,
[&]() {
// This represents an object.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.name);
AddUsedExtension(metadata.GetExtension());
},
[&]() {
// This is a variable.
},
[&]() {
// This is a property.
},
[&]() {
// This is a parameter.
},
[&]() {
// This is something else.
});
}
if (node.child)
node.child->Visit(*this);
};
void ExampleExtensionUsagesFinder::OnVisitVariableAccessorNode(
VariableAccessorNode &node) {
AddUsedBuiltinExtension("BuiltinVariables");
if (node.child)
node.child->Visit(*this);
};
void ExampleExtensionUsagesFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode &node) {
AddUsedBuiltinExtension("BuiltinVariables");
node.expression->Visit(*this);
if (node.child)
node.child->Visit(*this);
};
// Add extensions bound to Objects/Behaviors/Functions
void ExampleExtensionUsagesFinder::OnVisitIdentifierNode(IdentifierNode &node) {
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(),
GetProjectScopedContainers(),
rootType, node);
if (gd::ParameterMetadata::IsObject(type) ||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
// An object or object variable is used.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
AddUsedExtension(metadata.GetExtension());
}
};
void ExampleExtensionUsagesFinder::OnVisitFunctionCallNode(
FunctionCallNode &node) {
// Extensions of non-free functions are already found when scanning objects.
if (!(node.objectName.empty() && node.behaviorName.empty()))
return;
gd::ExtensionAndMetadata<gd::ExpressionMetadata> metadata;
// Try to find a free number expression
metadata = gd::MetadataProvider::GetExtensionAndExpressionMetadata(
project.GetCurrentPlatform(), node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata.GetMetadata())) {
// Try to find a free str expression
metadata = gd::MetadataProvider::GetExtensionAndStrExpressionMetadata(
project.GetCurrentPlatform(), node.functionName);
// No valid expression found, return.
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata.GetMetadata()))
return;
}
// Unused event-based objects or events-based behaviors may use object and
// behavior expressions that should not be detected as extension usage.
// The extension of actually used objects and behaviors will be detected on
// scene objects. This is why object or behavior expressions usually don't
// have any import.
// Built-in extensions that are included by default don't declare any include
// files on their instructions either. To still detect their usage, we
// consider that main events and dedicated extensions can't have dead code.
if (!isStoreExtension || !metadata.GetMetadata().GetIncludeFiles().empty()) {
AddUsedExtension(metadata.GetExtension());
}
};
} // namespace gd

View File

@@ -0,0 +1,77 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <set>
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/String.h"
namespace gd {
class Project;
class Object;
class Behavior;
} // namespace gd
namespace gd {
/**
* @brief List extension usages for generated example web-pages.
*
* Dependency transitivity is not ensured (see UsedExtensionsFinder).
*/
class GD_CORE_API ExampleExtensionUsagesFinder
: public ArbitraryObjectsWorker,
public ArbitraryEventsWorkerWithContext,
public ExpressionParser2NodeWorker {
public:
static std::set<gd::String> GetUsedExtensions(gd::Project &project);
private:
ExampleExtensionUsagesFinder(gd::Project &project_) : project(project_){};
gd::Project &project;
gd::String rootType;
bool isStoreExtension = false;
// Result
std::set<gd::String> usedExtensions;
bool has3DObjects = false;
void AddUsedExtension(const gd::PlatformExtension &extension);
void AddUsedBuiltinExtension(const gd::String &extensionName);
// Object Visitor
void DoVisitObject(gd::Object &object) override;
// Behavior Visitor
void DoVisitBehavior(gd::Behavior &behavior) override;
// Instructions Visitor
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
// Expression Visitor
void OnVisitSubExpressionNode(SubExpressionNode &node) override;
void OnVisitOperatorNode(OperatorNode &node) override;
void OnVisitUnaryOperatorNode(UnaryOperatorNode &node) override;
void OnVisitNumberNode(NumberNode &node) override;
void OnVisitTextNode(TextNode &node) override;
void OnVisitVariableNode(VariableNode &node) override;
void OnVisitVariableAccessorNode(VariableAccessorNode &node) override;
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode &node) override;
void OnVisitIdentifierNode(IdentifierNode &node) override;
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode &node) override;
void OnVisitFunctionCallNode(FunctionCallNode &node) override;
void OnVisitEmptyNode(EmptyNode &node) override;
};
}; // namespace gd

View File

@@ -69,16 +69,9 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
// Unused event-based objects or events-based behaviors may use object and
// behavior instructions that should not be detected as extension usage.
// The extension of actually used objects and behaviors will be detected on
// scene objects. This is why object or behavior instructions usually don't
// have any import.
if (!metadata.GetMetadata().GetIncludeFiles().empty()) {
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.AddUsedIncludeFiles(includeFile);
}
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.AddUsedIncludeFiles(includeFile);
}
gd::ParameterMetadataTools::IterateOverParameters(
@@ -206,16 +199,9 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
// Unused event-based objects or events-based behaviors may use object and
// behavior expressions that should not be detected as extension usage.
// The extension of actually used objects and behaviors will be detected on
// scene objects. This is why object or behavior expressions usually don't
// have any import.
if (!metadata.GetMetadata().GetIncludeFiles().empty()) {
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.AddUsedIncludeFiles(includeFile);
}
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.AddUsedIncludeFiles(includeFile);
}
};

View File

@@ -39,6 +39,22 @@ void ProjectBrowserHelper::ExposeProjectEvents(
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources)
// and ExposeProjectEffects (this time for effects).
ExposeProjectEventsWithoutExtensions(project, worker);
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
}
}
void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
gd::Project& project, gd::ArbitraryEventsWorker& worker) {
// Add layouts events
@@ -51,6 +67,28 @@ void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
}
}
void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
gd::Project& project, gd::ArbitraryEventsWorkerWithContext& worker) {
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
auto &layout = project.GetLayout(s);
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
worker.Launch(layout.GetEvents(), projectScopedContainers);
}
// Add external events events
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
const auto &externalEvents = project.GetExternalEvents(s);
const gd::String &associatedLayout = externalEvents.GetAssociatedLayout();
if (project.HasLayoutNamed(associatedLayout)) {
auto &layout = project.GetLayout(associatedLayout);
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
worker.Launch(project.GetExternalEvents(s).GetEvents(), projectScopedContainers);
}
}
}
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
gd::Project &project, gd::Layout &layout,
gd::ArbitraryEventsWorker &worker) {
@@ -111,38 +149,6 @@ void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources)
// and ExposeProjectEffects (this time for effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
auto &layout = project.GetLayout(s);
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
worker.Launch(layout.GetEvents(), projectScopedContainers);
}
// Add external events events
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
const auto &externalEvents = project.GetExternalEvents(s);
const gd::String &associatedLayout = externalEvents.GetAssociatedLayout();
if (project.HasLayoutNamed(associatedLayout)) {
auto &layout = project.GetLayout(associatedLayout);
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
worker.Launch(project.GetExternalEvents(s).GetEvents(), projectScopedContainers);
}
}
// Add events based extensions
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
}
}
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorker &worker) {

View File

@@ -57,6 +57,15 @@ public:
ExposeProjectEventsWithoutExtensions(gd::Project &project,
gd::ArbitraryEventsWorker &worker);
/**
* \brief Call the specified worker on all events of the project (layout and
* external events) but not events from extensions.
*
* Only use this for stats.
*/
static void ExposeProjectEventsWithoutExtensions(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of a layout and
* its external events.

View File

@@ -42,6 +42,9 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties) {
auto &propertyName = property.GetName();
const auto &primitiveType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
auto &functionsContainer = eventsBasedEntity.GetEventsFunctions();
gd::String capitalizedName = CapitalizeFirstLetter(property.GetName());
gd::String setterName = "Set" + capitalizedName;
@@ -59,9 +62,9 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
property.GetLabel().empty() ? property.GetName() : property.GetLabel();
gd::String descriptionSubject =
(property.GetType() == "Boolean" ? "if " : "the ") +
(primitiveType == "boolean" ? "if " : "the ") +
UnCapitalizeFirstLetter(propertyLabel) +
(isSharedProperties || property.GetType() == "Boolean"
(isSharedProperties || primitiveType == "boolean"
? "."
: " of the object.") +
(property.GetDescription().empty() ? ""
@@ -71,19 +74,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
"objects using the behavior."
: "");
gd::String propertyGetterName =
(isSharedProperties ? "SharedProperty" : "Property") + property.GetName();
gd::String getterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(), propertyGetterName);
gd::String setterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(),
"Set" + propertyGetterName);
gd::String getterName = capitalizedName;
gd::String numberOrString =
property.GetType() == "Number" ? "Number" : "String";
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
auto &getter = functionsContainer.InsertNewEventsFunction(
@@ -99,7 +90,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
.SetName(legacyExpressionType)
.SetExtraInfo(GetStringifiedExtraInfo(property));
getter.SetFullName(propertyLabel).SetGroup(functionGroupName);
if (property.GetType() == "Boolean") {
if (primitiveType == "boolean") {
getter.SetFunctionType(gd::EventsFunction::Condition)
.SetDescription("Check " + descriptionSubject)
.SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel));
@@ -112,13 +103,12 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
auto &event =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
if (property.GetType() == "Boolean") {
if (primitiveType == "boolean") {
gd::Instruction condition;
condition.SetType(getterType);
condition.AddParameter("Object");
if (isBehavior) {
condition.AddParameter("Behavior");
}
condition.SetType("BooleanVariable");
condition.AddParameter(propertyName);
condition.AddParameter("True");
condition.AddParameter("");
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
@@ -127,6 +117,8 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
event.GetActions().Insert(action, 0);
} else {
gd::Instruction action;
gd::String numberOrString =
primitiveType == "number" ? "Number" : "String";
action.SetType("SetReturn" + numberOrString);
action.AddParameter(property.GetName());
event.GetActions().Insert(action, 0);
@@ -136,7 +128,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
if (!functionsContainer.HasEventsFunctionNamed(setterName)) {
auto &setter = functionsContainer.InsertNewEventsFunction(
setterName, functionsContainer.GetEventsFunctionsCount());
if (property.GetType() == "Boolean") {
if (primitiveType == "boolean") {
setter.SetFunctionType(gd::EventsFunction::Action)
.SetFullName(propertyLabel)
.SetGroup(functionGroupName)
@@ -177,26 +169,24 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
setter.SetGetterName(getterName);
}
if (property.GetType() == "Boolean") {
if (primitiveType == "boolean") {
{
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
condition.SetType("BooleanVariable");
condition.AddParameter("Value");
condition.AddParameter("True");
condition.AddParameter("");
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("yes");
} else {
action.AddParameter("yes");
}
action.SetType("SetBooleanVariable");
action.AddParameter(propertyName);
action.AddParameter("True");
action.AddParameter("");
event.GetActions().Insert(action, 0);
}
{
@@ -205,20 +195,17 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
condition.SetInverted(true);
condition.SetType("BooleanVariable");
condition.AddParameter("Value");
condition.AddParameter("False");
condition.AddParameter("");
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("no");
} else {
action.AddParameter("no");
}
action.SetType("SetBooleanVariable");
action.AddParameter(propertyName);
action.AddParameter("False");
action.AddParameter("");
event.GetActions().Insert(action, 0);
}
} else {
@@ -227,16 +214,11 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("=");
action.AddParameter("Value");
} else {
action.AddParameter("=");
action.AddParameter("Value");
}
action.SetType(primitiveType == "number" ? "SetNumberVariable"
: "SetStringVariable");
action.AddParameter(propertyName);
action.AddParameter("=");
action.AddParameter("Value");
event.GetActions().Insert(action, 0);
}
}

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2022 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_PROPERTYFUNCTIONGENERATOR_H
#define GDCORE_PROPERTYFUNCTIONGENERATOR_H
#pragma once
namespace gd {
class String;
@@ -73,5 +72,3 @@ class GD_CORE_API PropertyFunctionGenerator {
};
} // namespace gd
#endif // GDCORE_PROPERTYFUNCTIONGENERATOR_H

View File

@@ -47,6 +47,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
previewIconUrl = other.previewIconUrl;
iconUrl = other.iconUrl;
helpPath = other.helpPath;
gdevelopVersion = other.gdevelopVersion;
eventsFunctionsContainer = other.eventsFunctionsContainer;
eventsBasedBehaviors = other.eventsBasedBehaviors;
eventsBasedObjects = other.eventsBasedObjects;
@@ -81,6 +82,7 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
element.SetAttribute("previewIconUrl", previewIconUrl);
element.SetAttribute("iconUrl", iconUrl);
element.SetAttribute("helpPath", helpPath);
element.SetAttribute("gdevelopVersion", gdevelopVersion);
auto& dependenciesElement = element.AddChild("dependencies");
dependenciesElement.ConsiderAsArray();
for (auto& dependency : dependencies)
@@ -126,6 +128,7 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
previewIconUrl = element.GetStringAttribute("previewIconUrl");
iconUrl = element.GetStringAttribute("iconUrl");
helpPath = element.GetStringAttribute("helpPath");
gdevelopVersion = element.GetStringAttribute("gdevelopVersion");
if (element.HasChild("origin")) {
gd::String originName =

View File

@@ -134,6 +134,19 @@ class GD_CORE_API EventsFunctionsExtension {
return *this;
}
/**
* \brief Get the GDevelop version required by this extension.
*/
const gd::String& GetGDevelopVersion() const { return gdevelopVersion; };
/**
* \brief Set the GDevelop version required by this extension.
*/
EventsFunctionsExtension& SetGDevelopVersion(const gd::String& gdevelopVersion_) {
gdevelopVersion = gdevelopVersion_;
return *this;
}
/**
* \brief Return a reference to the list of the events based behaviors.
*/
@@ -385,6 +398,7 @@ class GD_CORE_API EventsFunctionsExtension {
gd::String iconUrl;
gd::String helpPath; ///< The relative path to the help for this extension in
///< the documentation (or an absolute URL).
gd::String gdevelopVersion;
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
std::vector<gd::DependencyMetadata> dependencies;

View File

@@ -0,0 +1,57 @@
/*
* GDevelop Core
* Copyright 2008-2025 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ObjectTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
namespace gd {
bool ObjectTools::IsBehaviorCompatibleWithObject(
const gd::Platform &platform, const gd::String &objectType,
const gd::String &behaviorType,
std::unordered_set<gd::String> coveredBehaviorType) {
bool isBehaviorTypeAlreadyCovered =
!coveredBehaviorType.insert(behaviorType).second;
if (isBehaviorTypeAlreadyCovered) {
return true;
}
const gd::BehaviorMetadata &behaviorMetadata =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
// Should not happen because the behavior was added successfully (so its
// metadata are valid) - but double check anyway and bail out if the
// behavior metadata are invalid.
return false;
}
if (!behaviorMetadata.GetObjectType().empty() &&
behaviorMetadata.GetObjectType() != objectType) {
return false;
}
for (const gd::String &requiredBehaviorType :
behaviorMetadata.GetRequiredBehaviorTypes()) {
const gd::BehaviorMetadata &requiredBehaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(platform, requiredBehaviorType);
if (requiredBehaviorMetadata.IsHidden()) {
const gd::ObjectMetadata &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (objectMetadata.GetDefaultBehaviors().find(requiredBehaviorType) ==
objectMetadata.GetDefaultBehaviors().end()) {
// A capability is missing in the object.
return false;
}
}
if (!gd::ObjectTools::IsBehaviorCompatibleWithObject(
platform, objectType, requiredBehaviorType, coveredBehaviorType)) {
return false;
}
}
return true;
}
} // namespace gd

View File

@@ -0,0 +1,35 @@
/*
* GDevelop Core
* Copyright 2008-2025 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/String.h"
#include <unordered_set>
namespace gd {
class Platform;
class Object;
} // namespace gd
namespace gd {
class GD_CORE_API ObjectTools {
public:
static bool IsBehaviorCompatibleWithObject(const gd::Platform &platform,
const gd::String &objectType,
const gd::String &behaviorType) {
std::unordered_set<gd::String> coveredBehaviorType;
return IsBehaviorCompatibleWithObject(platform, objectType, behaviorType,
coveredBehaviorType);
}
private:
static bool IsBehaviorCompatibleWithObject(
const gd::Platform &platform, const gd::String &objectType,
const gd::String &behaviorType,
std::unordered_set<gd::String> coveredBehaviorType);
};
} // namespace gd

View File

@@ -116,14 +116,11 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyMovementAngle");
REQUIRE(setterAction.GetParametersCount() == 4);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(3).GetPlainString() == "Value");
REQUIRE(setterAction.GetType() == "SetNumberVariable");
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "MovementAngle");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "Value");
}
}
@@ -206,12 +203,12 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::PropertyRotate");
REQUIRE(getterCondition.GetType() == "BooleanVariable");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 2);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
REQUIRE(getterCondition.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(getterCondition.GetParametersCount() == 3);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(getterCondition.GetParameter(1).GetPlainString() == "True");
REQUIRE(getterCondition.GetParameter(2).GetPlainString() == "");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
@@ -257,19 +254,19 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
REQUIRE(setterNoCondition.GetType() == "BooleanVariable");
REQUIRE(!setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 3);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() == "Value");
REQUIRE(setterNoCondition.GetParameter(1).GetPlainString() == "False");
REQUIRE(setterNoCondition.GetParameter(2).GetPlainString() == "");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterNoAction.GetType() == "SetBooleanVariable");
REQUIRE(setterNoAction.GetParametersCount() == 3);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterNoAction.GetParameter(2).GetPlainString() == "no");
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "False");
REQUIRE(setterNoAction.GetParameter(2).GetPlainString() == "");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
@@ -277,19 +274,19 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterYesCondition.GetType() == "BooleanVariable");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
REQUIRE(setterYesCondition.GetParametersCount() == 3);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() == "Value");
REQUIRE(setterYesCondition.GetParameter(1).GetPlainString() == "True");
REQUIRE(setterYesCondition.GetParameter(2).GetPlainString() == "");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterYesAction.GetType() == "SetBooleanVariable");
REQUIRE(setterYesAction.GetParametersCount() == 3);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "yes");
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "True");
REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "");
}
}
SECTION("Can generate functions for a number property in an object") {
@@ -366,11 +363,9 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyMovementAngle");
REQUIRE(setterAction.GetType() == "SetNumberVariable");
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "MovementAngle");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "Value");
}
@@ -454,11 +449,12 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedObject::PropertyRotate");
REQUIRE(getterCondition.GetType() == "BooleanVariable");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 1);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
REQUIRE(getterCondition.GetParametersCount() == 3);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(getterCondition.GetParameter(1).GetPlainString() == "True");
REQUIRE(getterCondition.GetParameter(2).GetPlainString() == "");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
@@ -500,18 +496,19 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
REQUIRE(setterNoCondition.GetType() == "BooleanVariable");
REQUIRE(!setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 3);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() == "Value");
REQUIRE(setterNoCondition.GetParameter(1).GetPlainString() == "False");
REQUIRE(setterNoCondition.GetParameter(2).GetPlainString() == "");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterNoAction.GetParametersCount() == 2);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "no");
REQUIRE(setterNoAction.GetType() == "SetBooleanVariable");
REQUIRE(setterNoAction.GetParametersCount() == 3);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "False");
REQUIRE(setterNoAction.GetParameter(2).GetPlainString() == "");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
@@ -519,18 +516,19 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterYesCondition.GetType() == "BooleanVariable");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
REQUIRE(setterYesCondition.GetParametersCount() == 3);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() == "Value");
REQUIRE(setterYesCondition.GetParameter(1).GetPlainString() == "True");
REQUIRE(setterYesCondition.GetParameter(2).GetPlainString() == "");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterYesAction.GetParametersCount() == 2);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "yes");
REQUIRE(setterYesAction.GetType() == "SetBooleanVariable");
REQUIRE(setterYesAction.GetParametersCount() == 3);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Rotate");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "True");
REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "");
}
}
@@ -588,9 +586,11 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMovementAngle");
REQUIRE(setterAction.GetType() == "SetNumberVariable");
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "MovementAngle");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "Value");
}
}

View File

@@ -1,15 +1,4 @@
namespace gdjs {
export interface Object3DDataContent {
width: float;
height: float;
depth: float;
}
/** Base parameters for {@link gdjs.RuntimeObject3D} */
export interface Object3DData extends ObjectData {
/** The base parameters of the RuntimeObject3D */
content: Object3DDataContent;
}
const getValidDimensionValue = (value: float | undefined) =>
value === undefined ? 100 : value <= 0 ? 1 : value;
@@ -35,11 +24,8 @@ namespace gdjs {
*/
export abstract class RuntimeObject3D
extends gdjs.RuntimeObject
implements
gdjs.Resizable,
gdjs.Scalable,
gdjs.Flippable,
gdjs.Base3DHandler {
implements gdjs.Resizable, gdjs.Scalable, gdjs.Flippable, gdjs.Base3DHandler
{
/**
* Position on the Z axis.
*/
@@ -84,7 +70,7 @@ namespace gdjs {
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: Object3DData
objectData: gdjs.Object3DData
) {
super(instanceContainer, objectData);
// TODO Should 0 be replaced by 0.01 instead of using the default value?

View File

@@ -117,6 +117,17 @@ namespace gdjs {
getUnrotatedAABBMaxZ(): number;
}
export interface Object3DDataContent {
width: float;
height: float;
depth: float;
}
/** Base parameters for {@link gdjs.RuntimeObject3D} */
export interface Object3DData extends ObjectData {
/** The base parameters of the RuntimeObject3D */
content: Object3DDataContent;
}
export namespace Base3DHandler {
export const is3D = (
object: gdjs.RuntimeObject
@@ -131,7 +142,8 @@ namespace gdjs {
*/
export class Base3DBehavior
extends gdjs.RuntimeBehavior
implements Base3DHandler {
implements Base3DHandler
{
private object: gdjs.RuntimeObject & Base3DHandler;
constructor(

View File

@@ -3,31 +3,31 @@ namespace gdjs {
export interface Cube3DObjectData extends Object3DData {
/** The base parameters of the Cube3D object */
content: Object3DDataContent & {
enableTextureTransparency: boolean;
facesOrientation: 'Y' | 'Z';
enableTextureTransparency: boolean | undefined;
facesOrientation: 'Y' | 'Z' | undefined;
frontFaceResourceName: string;
backFaceResourceName: string;
backFaceUpThroughWhichAxisRotation: 'X' | 'Y';
backFaceUpThroughWhichAxisRotation: 'X' | 'Y' | undefined;
leftFaceResourceName: string;
rightFaceResourceName: string;
topFaceResourceName: string;
bottomFaceResourceName: string;
frontFaceResourceRepeat: boolean;
backFaceResourceRepeat: boolean;
leftFaceResourceRepeat: boolean;
rightFaceResourceRepeat: boolean;
topFaceResourceRepeat: boolean;
bottomFaceResourceRepeat: boolean;
frontFaceResourceRepeat: boolean | undefined;
backFaceResourceRepeat: boolean | undefined;
leftFaceResourceRepeat: boolean | undefined;
rightFaceResourceRepeat: boolean | undefined;
topFaceResourceRepeat: boolean | undefined;
bottomFaceResourceRepeat: boolean | undefined;
frontFaceVisible: boolean;
backFaceVisible: boolean;
leftFaceVisible: boolean;
rightFaceVisible: boolean;
topFaceVisible: boolean;
bottomFaceVisible: boolean;
tint: string | undefined;
materialType: 'Basic' | 'StandardWithoutMetalness';
};
}
type FaceName = 'front' | 'back' | 'left' | 'right' | 'top' | 'bottom';
const faceNameToBitmaskIndex = {
front: 0,
@@ -45,6 +45,7 @@ namespace gdjs {
trfb: integer;
frn: [string, string, string, string, string, string];
mt: number;
tint: string;
};
type Cube3DObjectNetworkSyncData = Object3DNetworkSyncData &
@@ -67,10 +68,11 @@ namespace gdjs {
string,
string,
string,
string
string,
];
_materialType: gdjs.Cube3DRuntimeObject.MaterialType =
gdjs.Cube3DRuntimeObject.MaterialType.Basic;
_tint: string;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -117,6 +119,9 @@ namespace gdjs {
objectData.content.topFaceResourceName,
objectData.content.bottomFaceResourceName,
];
this._tint = objectData.content.tint || '255;255;255';
this._materialType = this._convertMaterialType(
objectData.content.materialType
);
@@ -134,7 +139,7 @@ namespace gdjs {
* Sets the visibility of a face of the 3D box.
*
* @param faceName - The name of the face to set visibility for.
* @param value - The visibility value to set.
* @param enable - The visibility value to set.
*/
setFaceVisibility(faceName: FaceName, enable: boolean) {
const faceIndex = faceNameToBitmaskIndex[faceName];
@@ -157,7 +162,7 @@ namespace gdjs {
* Sets the texture repeat of a face of the 3D box.
*
* @param faceName - The name of the face to set visibility for.
* @param value - The visibility value to set.
* @param enable - The visibility value to set.
*/
setRepeatTextureOnFace(faceName: FaceName, enable: boolean) {
const faceIndex = faceNameToBitmaskIndex[faceName];
@@ -203,11 +208,22 @@ namespace gdjs {
if (this._faceResourceNames[faceIndex] === resourceName) {
return;
}
this._faceResourceNames[faceIndex] = resourceName;
this._renderer.updateFace(faceIndex);
}
setColor(tint: string): void {
if (this._tint === tint) {
return;
}
this._tint = tint;
this._renderer.updateTint();
}
getColor(): string {
return this._tint;
}
/** @internal */
getFaceAtIndexResourceName(faceIndex: integer): string {
return this._faceResourceNames[faceIndex];
@@ -291,6 +307,10 @@ namespace gdjs {
newObjectData.content.frontFaceResourceName
);
}
if (oldObjectData.content.tint !== newObjectData.content.tint) {
this.setColor(newObjectData.content.tint || '255;255;255');
}
if (
oldObjectData.content.backFaceResourceName !==
newObjectData.content.backFaceResourceName
@@ -342,7 +362,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'front',
newObjectData.content.frontFaceResourceRepeat
newObjectData.content.frontFaceResourceRepeat || false
);
}
if (
@@ -351,7 +371,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'back',
newObjectData.content.backFaceResourceRepeat
newObjectData.content.backFaceResourceRepeat || false
);
}
if (
@@ -360,7 +380,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'left',
newObjectData.content.leftFaceResourceRepeat
newObjectData.content.leftFaceResourceRepeat || false
);
}
if (
@@ -369,7 +389,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'right',
newObjectData.content.rightFaceResourceRepeat
newObjectData.content.rightFaceResourceRepeat || false
);
}
if (
@@ -378,7 +398,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'top',
newObjectData.content.topFaceResourceRepeat
newObjectData.content.topFaceResourceRepeat || false
);
}
if (
@@ -387,7 +407,7 @@ namespace gdjs {
) {
this.setRepeatTextureOnFace(
'bottom',
newObjectData.content.bottomFaceResourceRepeat
newObjectData.content.bottomFaceResourceRepeat || false
);
}
if (
@@ -395,14 +415,14 @@ namespace gdjs {
newObjectData.content.backFaceUpThroughWhichAxisRotation
) {
this.setBackFaceUpThroughWhichAxisRotation(
newObjectData.content.backFaceUpThroughWhichAxisRotation
newObjectData.content.backFaceUpThroughWhichAxisRotation || 'X'
);
}
if (
oldObjectData.content.facesOrientation !==
newObjectData.content.facesOrientation
) {
this.setFacesOrientation(newObjectData.content.facesOrientation);
this.setFacesOrientation(newObjectData.content.facesOrientation || 'Y');
}
if (
oldObjectData.content.materialType !==
@@ -423,6 +443,7 @@ namespace gdjs {
vfb: this._visibleFacesBitmask,
trfb: this._textureRepeatFacesBitmask,
frn: this._faceResourceNames,
tint: this._tint,
};
}
@@ -476,6 +497,12 @@ namespace gdjs {
}
}
}
if (networkSyncData.tint !== undefined) {
if (this._tint !== networkSyncData.tint) {
this._tint = networkSyncData.tint;
this._renderer.updateTint();
}
}
}
/**

View File

@@ -74,15 +74,13 @@ namespace gdjs {
instanceContainer: gdjs.RuntimeInstanceContainer
) {
const geometry = new THREE.BoxGeometry(1, 1, 1);
// TODO (3D) - feature: support color instead of texture?
const materials = [
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[0]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[1]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[2]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[3]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[4]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[5]),
];
const materials: THREE.Material[] = new Array(6)
.fill(0)
.map((_, index) =>
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[index])
);
const boxMesh = new THREE.Mesh(geometry, materials);
super(runtimeObject, instanceContainer, boxMesh);
@@ -92,6 +90,28 @@ namespace gdjs {
this.updateSize();
this.updatePosition();
this.updateRotation();
this.updateTint();
}
updateTint() {
const tints: number[] = [];
const normalizedTint = gdjs
.rgbOrHexToRGBColor(this._cube3DRuntimeObject.getColor())
.map((component) => component / 255);
for (
let i = 0;
i < this._boxMesh.geometry.attributes.position.count;
i++
) {
tints.push(...normalizedTint);
}
this._boxMesh.geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(tints), 3)
);
}
updateFace(faceIndex: integer) {
@@ -121,13 +141,11 @@ namespace gdjs {
*/
updateTextureUvMapping(faceIndex?: number) {
// @ts-ignore - position is stored as a Float32BufferAttribute
const pos: THREE.BufferAttribute = this._boxMesh.geometry.getAttribute(
'position'
);
const pos: THREE.BufferAttribute =
this._boxMesh.geometry.getAttribute('position');
// @ts-ignore - uv is stored as a Float32BufferAttribute
const uvMapping: THREE.BufferAttribute = this._boxMesh.geometry.getAttribute(
'uv'
);
const uvMapping: THREE.BufferAttribute =
this._boxMesh.geometry.getAttribute('uv');
const startIndex =
faceIndex === undefined ? 0 : faceIndexToMaterialIndex[faceIndex] * 4;
const endIndex =
@@ -149,9 +167,10 @@ namespace gdjs {
continue;
}
const shouldRepeatTexture = this._cube3DRuntimeObject.shouldRepeatTextureOnFaceAtIndex(
materialIndexToFaceIndex[materialIndex]
);
const shouldRepeatTexture =
this._cube3DRuntimeObject.shouldRepeatTextureOnFaceAtIndex(
materialIndexToFaceIndex[materialIndex]
);
const shouldOrientateFacesTowardsY =
this._cube3DRuntimeObject.getFacesOrientation() === 'Y';
@@ -180,12 +199,10 @@ namespace gdjs {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
}
}
break;
@@ -211,12 +228,10 @@ namespace gdjs {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
x = -x;
y = -y;
}

View File

@@ -1,21 +1,11 @@
namespace gdjs {
export interface Object3DDataContent {
width: float;
height: float;
depth: float;
}
/** Base parameters for {@link gdjs.RuntimeObject3D} */
export interface Object3DData extends ObjectData {
/** The base parameters of the RuntimeObject3D */
content: Object3DDataContent;
}
/**
* Base class for 3D custom objects.
*/
export class CustomRuntimeObject3D
extends gdjs.CustomRuntimeObject
implements gdjs.Base3DHandler {
implements gdjs.Base3DHandler
{
/**
* Position on the Z axis.
*/
@@ -41,13 +31,13 @@ namespace gdjs {
constructor(
parent: gdjs.RuntimeInstanceContainer,
objectData: Object3DData & CustomObjectConfiguration
objectData: gdjs.Object3DData & gdjs.CustomObjectConfiguration
) {
super(parent, objectData);
this._renderer.reinitialize(this, parent);
}
protected _createRender() {
protected override _createRender() {
const parent = this._runtimeScene;
return new gdjs.CustomRuntimeObject3DRenderer(
this,
@@ -56,21 +46,23 @@ namespace gdjs {
);
}
protected _reinitializeRenderer(): void {
protected override _reinitializeRenderer(): void {
this.getRenderer().reinitialize(this, this.getParent());
}
getRenderer(): gdjs.CustomRuntimeObject3DRenderer {
override getRenderer(): gdjs.CustomRuntimeObject3DRenderer {
return super.getRenderer() as gdjs.CustomRuntimeObject3DRenderer;
}
get3DRendererObject() {
override get3DRendererObject() {
// It can't be null because Three.js is always loaded
// when a custom 3D object is used.
return this.getRenderer().get3DRendererObject()!;
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
override extraInitializationFromInitialInstance(
initialInstanceData: InstanceData
) {
super.extraInitializationFromInitialInstance(initialInstanceData);
if (initialInstanceData.depth !== undefined) {
this.setDepth(initialInstanceData.depth);
@@ -292,7 +284,7 @@ namespace gdjs {
return this._maxZ - this._minZ;
}
_updateUntransformedHitBoxes(): void {
override _updateUntransformedHitBoxes(): void {
super._updateUntransformedHitBoxes();
let minZ = Number.MAX_VALUE;

View File

@@ -6,7 +6,8 @@ namespace gdjs {
* The renderer for a {@link gdjs.CustomRuntimeObject3D} using Three.js.
*/
export class CustomRuntimeObject3DRenderer
implements gdjs.RuntimeInstanceContainerRenderer {
implements gdjs.RuntimeInstanceContainerRenderer
{
_object: gdjs.CustomRuntimeObject3D;
_instanceContainer: gdjs.CustomRuntimeObjectInstanceContainer;
_isContainerDirty: boolean = true;
@@ -139,16 +140,16 @@ namespace gdjs {
imageManager: gdjs.PixiImageManager
): ThreeAnimationFrameTextureManager {
if (!imageManager._threeAnimationFrameTextureManager) {
imageManager._threeAnimationFrameTextureManager = new ThreeAnimationFrameTextureManager(
imageManager
);
imageManager._threeAnimationFrameTextureManager =
new ThreeAnimationFrameTextureManager(imageManager);
}
return imageManager._threeAnimationFrameTextureManager;
}
}
class ThreeAnimationFrameTextureManager
implements gdjs.AnimationFrameTextureManager<THREE.Material> {
implements gdjs.AnimationFrameTextureManager<THREE.Material>
{
private _imageManager: gdjs.PixiImageManager;
constructor(imageManager: gdjs.PixiImageManager) {
@@ -163,16 +164,16 @@ namespace gdjs {
}
getAnimationFrameWidth(material: THREE.Material) {
const map = (material as
| THREE.MeshBasicMaterial
| THREE.MeshStandardMaterial).map;
const map = (
material as THREE.MeshBasicMaterial | THREE.MeshStandardMaterial
).map;
return map ? map.image.width : 0;
}
getAnimationFrameHeight(material: THREE.Material) {
const map = (material as
| THREE.MeshBasicMaterial
| THREE.MeshStandardMaterial).map;
const map = (
material as THREE.MeshBasicMaterial | THREE.MeshStandardMaterial
).map;
return map ? map.image.height : 0;
}
}

View File

@@ -69,7 +69,7 @@ module.exports = {
_('Center Z position'),
_('the Z position of the center of rotation'),
_('the Z position of the center'),
_('Position/Center'),
_('Position Center'),
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D object'), '', false)
@@ -800,6 +800,20 @@ module.exports = {
.markAsSimple()
.setHidden()
.setFunctionName('hasAnimationEnded');
object
.addScopedAction(
'SetCrossfadeDuration',
_('Set crossfade duration'),
_('Set the crossfade duration when switching to a new animation.'),
_('Set crossfade duration of _PARAM0_ to _PARAM1_ seconds'),
_('Animations and images'),
'res/conditions/animation24.png',
'res/conditions/animation.png'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Crossfade duration (in seconds)'), '', false)
.setFunctionName('setCrossfadeDuration');
}
const Cube3DObject = new gd.ObjectJsImplementation();
@@ -822,7 +836,8 @@ module.exports = {
propertyName === 'bottomFaceResourceName' ||
propertyName === 'backFaceUpThroughWhichAxisRotation' ||
propertyName === 'facesOrientation' ||
propertyName === 'materialType'
propertyName === 'materialType' ||
propertyName === 'tint'
) {
objectContent[propertyName] = newValue;
return true;
@@ -902,6 +917,12 @@ module.exports = {
.setLabel(_('Depth'))
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setGroup(_('Default size'));
objectProperties
.getOrCreate('tint')
.setValue(objectContent.tint || '255;255;255')
.setType('Color')
.setLabel(_('Tint'))
.setGroup(_('Texture'));
objectProperties
.getOrCreate('frontFaceResourceName')
@@ -1092,6 +1113,7 @@ module.exports = {
topFaceResourceRepeat: false,
bottomFaceResourceRepeat: false,
materialType: 'Basic',
tint: '255;255;255',
};
Cube3DObject.updateInitialInstanceProperty = function (
@@ -1568,6 +1590,21 @@ module.exports = {
.addParameter('imageResource', _('Image'), '', false)
.setFunctionName('setFaceResourceName');
object
.addScopedAction(
'SetTint',
_('Tint color'),
_('Change the tint of the cube.'),
_('Change the tint of _PARAM0_ to _PARAM1_'),
_('Effects'),
'res/actions/color24.png',
'res/actions/color.png'
)
.addParameter('object', _('3D Cube'), 'Cube3DObject', false)
.addParameter('color', _('Tint'), '', false)
.getCodeExtraInformation()
.setFunctionName('setColor');
extension
.addExpressionAndConditionAndAction(
'number',
@@ -2062,6 +2099,10 @@ module.exports = {
3: [1, 0],
};
/**
* @param {*} objectConfiguration
* @returns {string | null}
*/
const getFirstVisibleFaceResourceName = (objectConfiguration) => {
const object = gd.castObject(
objectConfiguration,
@@ -2090,25 +2131,44 @@ module.exports = {
return null;
};
/** @type {THREE.MeshBasicMaterial | null} */
let transparentMaterial = null;
/**
* @returns {THREE.MeshBasicMaterial}
*/
const getTransparentMaterial = () => {
if (!transparentMaterial)
transparentMaterial = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
// Set the alpha test to to ensure the faces behind are rendered
// (no "back face culling" that would still be done if alphaTest is not set).
alphaTest: 1,
});
return transparentMaterial;
if (transparentMaterial) {
return transparentMaterial;
}
const newTransparentMaterial = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
// Set the alpha test to to ensure the faces behind are rendered
// (no "back face culling" that would still be done if alphaTest is not set).
alphaTest: 1,
});
transparentMaterial = newTransparentMaterial;
return newTransparentMaterial;
};
class RenderedCube3DObject2DInstance extends RenderedInstance {
/** @type {number} */
_defaultWidth;
/** @type {number} */
_defaultHeight;
/** @type {number} */
_defaultDepth;
/** @type {number} */
_centerX = 0;
/** @type {number} */
_centerY = 0;
/**
* The name of the resource that is rendered.
* If no face is visible, this will be null.
* @type {string | null | undefined}
*/
_renderedResourceName = undefined;
_renderFallbackObject = false;
constructor(
project,
@@ -2124,11 +2184,6 @@ module.exports = {
pixiContainer,
pixiResourcesLoader
);
// Name of the resource that is rendered.
// If no face is visible, this will be null.
this._renderedResourceName = undefined;
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
@@ -2145,7 +2200,6 @@ module.exports = {
this._pixiObject.addChild(this._pixiTexturedObject);
this._pixiObject.addChild(this._pixiFallbackObject);
this._pixiContainer.addChild(this._pixiObject);
this._renderFallbackObject = false;
this.updateTexture();
}
@@ -2160,9 +2214,10 @@ module.exports = {
}
static getThumbnail(project, resourcesLoader, objectConfiguration) {
const textureResourceName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
const textureResourceName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
if (textureResourceName) {
return resourcesLoader.getResourceFullUrl(
project,
@@ -2174,18 +2229,20 @@ module.exports = {
}
updateTextureIfNeeded() {
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (textureName === this._renderedResourceName) return;
this.updateTexture();
}
updateTexture() {
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (!textureName) {
this._renderFallbackObject = true;
@@ -2308,6 +2365,17 @@ module.exports = {
}
class RenderedCube3DObject3DInstance extends Rendered3DInstance {
_defaultWidth = 1;
_defaultHeight = 1;
_defaultDepth = 1;
_faceResourceNames = new Array(6).fill(null);
_faceVisibilities = new Array(6).fill(null);
_shouldRepeatTextureOnFace = new Array(6).fill(null);
_facesOrientation = 'Y';
_backFaceUpThroughWhichAxisRotation = 'X';
_shouldUseTransparentTexture = false;
_tint = '';
constructor(
project,
instance,
@@ -2324,21 +2392,9 @@ module.exports = {
threeGroup,
pixiResourcesLoader
);
this._defaultWidth = 1;
this._defaultHeight = 1;
this._defaultDepth = 1;
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
this._faceResourceNames = new Array(6).fill(null);
this._faceVisibilities = new Array(6).fill(null);
this._shouldRepeatTextureOnFace = new Array(6).fill(null);
this._facesOrientation = 'Y';
this._backFaceUpThroughWhichAxisRotation = 'X';
this._shouldUseTransparentTexture = false;
const geometry = new THREE.BoxGeometry(1, 1, 1);
const materials = [
getTransparentMaterial(),
@@ -2357,8 +2413,9 @@ module.exports = {
async _updateThreeObjectMaterials() {
const getFaceMaterial = async (project, faceIndex) => {
if (!this._faceVisibilities[faceIndex])
if (!this._faceVisibilities[faceIndex]) {
return getTransparentMaterial();
}
return await this._pixiResourcesLoader.getThreeMaterial(
project,
@@ -2389,11 +2446,35 @@ module.exports = {
this._updateTextureUvMapping();
}
_updateTint() {
const tints = [];
const normalizedTint = objectsRenderingService
.hexNumberToRGBArray(
objectsRenderingService.rgbOrHexToHexNumber(this._tint)
)
.map((component) => component / 255);
for (
let i = 0;
i < this._threeObject.geometry.attributes.position.count;
i++
) {
tints.push(...normalizedTint);
}
this._threeObject.geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(tints), 3)
);
}
static _getResourceNameToDisplay(objectConfiguration) {
return getFirstVisibleFaceResourceName(objectConfiguration);
}
updateThreeObject() {
/** @type {gdjs.Cube3DObjectData} */
//@ts-ignore This works because the properties are set to `content` in JavaScript.
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
@@ -2421,13 +2502,19 @@ module.exports = {
let materialsDirty = false;
let uvMappingDirty = false;
let tintDirty = false;
const shouldUseTransparentTexture =
object.content.enableTextureTransparency;
object.content.enableTextureTransparency || false;
if (this._shouldUseTransparentTexture !== shouldUseTransparentTexture) {
this._shouldUseTransparentTexture = shouldUseTransparentTexture;
materialsDirty = true;
}
const tint = object.content.tint || '255;255;255';
if (this._tint !== tint) {
this._tint = tint;
tintDirty = true;
}
const faceResourceNames = [
object.content.frontFaceResourceName,
@@ -2471,12 +2558,12 @@ module.exports = {
}
const shouldRepeatTextureOnFace = [
object.content.frontFaceResourceRepeat,
object.content.backFaceResourceRepeat,
object.content.leftFaceResourceRepeat,
object.content.rightFaceResourceRepeat,
object.content.topFaceResourceRepeat,
object.content.bottomFaceResourceRepeat,
object.content.frontFaceResourceRepeat || false,
object.content.backFaceResourceRepeat || false,
object.content.leftFaceResourceRepeat || false,
object.content.rightFaceResourceRepeat || false,
object.content.topFaceResourceRepeat || false,
object.content.bottomFaceResourceRepeat || false,
];
if (
this._shouldRepeatTextureOnFace[0] !== shouldRepeatTextureOnFace[0] ||
@@ -2491,16 +2578,17 @@ module.exports = {
}
const backFaceUpThroughWhichAxisRotation =
object.content.backFaceUpThroughWhichAxisRotation;
object.content.backFaceUpThroughWhichAxisRotation || 'X';
if (
backFaceUpThroughWhichAxisRotation !==
this._backFaceUpThroughWhichAxisRotation
) {
this._backFaceUpThroughWhichAxisRotation = backFaceUpThroughWhichAxisRotation;
this._backFaceUpThroughWhichAxisRotation =
backFaceUpThroughWhichAxisRotation;
uvMappingDirty = true;
}
const facesOrientation = object.content.facesOrientation;
const facesOrientation = object.content.facesOrientation || 'Y';
if (facesOrientation !== this._facesOrientation) {
this._facesOrientation = facesOrientation;
uvMappingDirty = true;
@@ -2520,6 +2608,7 @@ module.exports = {
if (materialsDirty) this._updateThreeObjectMaterials();
if (uvMappingDirty) this._updateTextureUvMapping();
if (tintDirty) this._updateTint();
}
/**
@@ -2529,11 +2618,11 @@ module.exports = {
* for the method to work.
*/
_updateTextureUvMapping() {
/** @type {THREE.BufferAttribute} */
// @ts-ignore - position is stored as a Float32BufferAttribute
/** @type {THREE.BufferAttribute} */
const pos = this._threeObject.geometry.getAttribute('position');
// @ts-ignore - uv is stored as a Float32BufferAttribute
/** @type {THREE.BufferAttribute} */
// @ts-ignore - uv is stored as a Float32BufferAttribute
const uvMapping = this._threeObject.geometry.getAttribute('uv');
const startIndex = 0;
const endIndex = 23;
@@ -2552,9 +2641,10 @@ module.exports = {
continue;
}
const shouldRepeatTexture = this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldRepeatTexture =
this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldOrientateFacesTowardsY = this._facesOrientation === 'Y';
@@ -2589,16 +2679,13 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
}
}
break;
@@ -2628,16 +2715,13 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
x = -x;
y = -y;
}
@@ -2786,6 +2870,19 @@ module.exports = {
const epsilon = 1 / (1 << 16);
class Model3DRendered2DInstance extends RenderedInstance {
/** @type {number} */
_defaultWidth;
/** @type {number} */
_defaultHeight;
/** @type {number} */
_defaultDepth;
/** @type {[number, number, number] | null} */
_originPoint;
/** @type {[number, number, number] | null} */
_centerPoint;
/** @type {[number, number, number]} */
_modelOriginPoint = [0, 0, 0];
constructor(
@@ -3028,10 +3125,15 @@ module.exports = {
}
}
/**
* @param {[number, number, number] | null} point1
* @param {[number, number, number] | null} point2
* @returns {boolean}
*/
const isSamePoint = (point1, point2) => {
if (!point1 && !point2) return true;
if (point1 && !point2) return false;
if (!point1 && point2) return false;
if (!!point1 !== !!point2) return false;
// At this point || or && doesn't matter and the type checking prefer ||.
if (!point1 || !point2) return true;
return (
point1[0] === point2[0] &&
point1[1] === point2[1] &&
@@ -3039,6 +3141,10 @@ module.exports = {
);
};
/**
* @param {string} location
* @returns {[number, number, number] | null}
*/
const getPointForLocation = (location) => {
switch (location) {
case 'ModelOrigin':
@@ -3057,8 +3163,27 @@ module.exports = {
};
class Model3DRendered3DInstance extends Rendered3DInstance {
_defaultWidth = 1;
_defaultHeight = 1;
_defaultDepth = 1;
_originalWidth = 1;
_originalHeight = 1;
_originalDepth = 1;
_rotationX = 0;
_rotationY = 0;
_rotationZ = 0;
_keepAspectRatio = false;
/** @type {[number, number, number] | null} */
_originPoint = null;
/** @type {[number, number, number] | null} */
_centerPoint = null;
/** @type {[number, number, number]} */
_modelOriginPoint = [0, 0, 0];
/** @type {THREE.Object3D | null} */
_clonedModel3D = null;
constructor(
project,
instance,
@@ -3076,29 +3201,12 @@ module.exports = {
pixiResourcesLoader
);
this._defaultWidth = 1;
this._defaultHeight = 1;
this._defaultDepth = 1;
this._originalWidth = 1;
this._originalHeight = 1;
this._originalDepth = 1;
this._rotationX = 0;
this._rotationY = 0;
this._rotationZ = 0;
this._keepAspectRatio = false;
this._originPoint = null;
this._centerPoint = null;
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
this._threeObject = new THREE.Group();
this._threeObject.rotation.order = 'ZYX';
this._threeGroup.add(this._threeObject);
this._threeModelGroup = null;
this._clonedModel3D = null;
}
getOriginX() {
@@ -3140,27 +3248,30 @@ module.exports = {
}
_updateDefaultTransformation() {
if (!this._clonedModel3D) return; // Model is not ready - nothing to do.
if (!this._clonedModel3D) {
// Model is not ready - nothing to do.
return;
}
if (this._threeModelGroup) {
// Remove any previous container as we will recreate it just below
this._threeObject.clear();
}
// This group hold the rotation defined by properties.
// Always restart from a new group to avoid miscomputing bounding boxes/sizes.
this._threeModelGroup = new THREE.Group();
this._threeModelGroup.rotation.order = 'ZYX';
this._threeModelGroup.add(this._clonedModel3D);
const threeModelGroup = new THREE.Group();
this._threeModelGroup = threeModelGroup;
threeModelGroup.rotation.order = 'ZYX';
threeModelGroup.add(this._clonedModel3D);
this._threeModelGroup.rotation.set(
threeModelGroup.rotation.set(
(this._rotationX * Math.PI) / 180,
(this._rotationY * Math.PI) / 180,
(this._rotationZ * Math.PI) / 180
);
this._threeModelGroup.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(
this._threeModelGroup
);
threeModelGroup.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeModelGroup);
const shouldKeepModelOrigin = !this._originPoint;
if (shouldKeepModelOrigin) {
@@ -3187,7 +3298,7 @@ module.exports = {
// Center the model.
const centerPoint = this._centerPoint;
if (centerPoint) {
this._threeModelGroup.position.set(
threeModelGroup.position.set(
-(boundingBox.min.x + modelWidth * centerPoint[0]),
// The model is flipped on Y axis.
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
@@ -3196,8 +3307,8 @@ module.exports = {
}
// Rotate the model.
this._threeModelGroup.scale.set(1, 1, 1);
this._threeModelGroup.rotation.set(
threeModelGroup.scale.set(1, 1, 1);
threeModelGroup.rotation.set(
(this._rotationX * Math.PI) / 180,
(this._rotationY * Math.PI) / 180,
(this._rotationZ * Math.PI) / 180
@@ -3212,8 +3323,8 @@ module.exports = {
// Flip on Y because the Y axis is on the opposite side of direct basis.
// It avoids models to be like a mirror refection.
scaleMatrix.makeScale(scaleX, -scaleY, scaleZ);
this._threeModelGroup.updateMatrix();
this._threeModelGroup.applyMatrix4(scaleMatrix);
threeModelGroup.updateMatrix();
threeModelGroup.applyMatrix4(scaleMatrix);
if (this._keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
@@ -3280,7 +3391,7 @@ module.exports = {
this._defaultDepth = this._originalDepth;
}
this._threeObject.add(this._threeModelGroup);
this._threeObject.add(threeModelGroup);
}
updateThreeObject() {

View File

@@ -23,7 +23,7 @@ Model3DObjectConfiguration::Model3DObjectConfiguration()
: width(100), height(100), depth(100), rotationX(0), rotationY(0),
rotationZ(0), modelResourceName(""), materialType("StandardWithoutMetalness"),
originLocation("ModelOrigin"), centerLocation("ModelOrigin"),
keepAspectRatio(true) {}
keepAspectRatio(true), crossfadeDuration(0.1f) {}
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
const gd::String &newValue) {
@@ -71,6 +71,10 @@ bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
keepAspectRatio = newValue == "1";
return true;
}
if(propertyName == "crossfadeDuration") {
crossfadeDuration = newValue.To<double>();
return true;
}
return false;
}
@@ -167,6 +171,13 @@ Model3DObjectConfiguration::GetProperties() const {
.SetGroup(_("Points"))
.SetAdvanced(true);
objectProperties["crossfadeDuration"]
.SetValue(gd::String::From(crossfadeDuration))
.SetType("number")
.SetLabel(_("Crossfade duration"))
.SetGroup(_("Animations"))
.SetMeasurementUnit(gd::MeasurementUnit::GetSecond());
return objectProperties;
}
@@ -198,6 +209,7 @@ void Model3DObjectConfiguration::DoUnserializeFrom(
originLocation = content.GetStringAttribute("originLocation");
centerLocation = content.GetStringAttribute("centerLocation");
keepAspectRatio = content.GetBoolAttribute("keepAspectRatio");
crossfadeDuration = content.GetDoubleAttribute("crossfadeDuration");
RemoveAllAnimations();
auto &animationsElement = content.GetChild("animations");
@@ -226,6 +238,7 @@ void Model3DObjectConfiguration::DoSerializeTo(
content.SetAttribute("originLocation", originLocation);
content.SetAttribute("centerLocation", centerLocation);
content.SetAttribute("keepAspectRatio", keepAspectRatio);
content.SetAttribute("crossfadeDuration", crossfadeDuration);
auto &animationsElement = content.AddChild("animations");
animationsElement.ConsiderAsArrayOf("animation");

View File

@@ -152,6 +152,7 @@ public:
double GetRotationX() const { return rotationX; };
double GetRotationY() const { return rotationY; };
double GetRotationZ() const { return rotationZ; };
double GetCrossfadeDuration() const { return crossfadeDuration; };
const gd::String& GetModelResourceName() const { return modelResourceName; };
const gd::String& GetMaterialType() const { return materialType; };
@@ -173,6 +174,7 @@ private:
double rotationX;
double rotationY;
double rotationZ;
double crossfadeDuration;
gd::String modelResourceName;
gd::String materialType;

View File

@@ -9,6 +9,7 @@ namespace gdjs {
ai: integer;
ass: float;
ap: boolean;
cfd: float;
};
type Model3DObjectNetworkSyncData = Object3DNetworkSyncData &
@@ -36,6 +37,7 @@ namespace gdjs {
| 'BottomCenterZ'
| 'BottomCenterY';
animations: Model3DAnimation[];
crossfadeDuration: float;
};
}
@@ -63,7 +65,8 @@ namespace gdjs {
*/
export class Model3DRuntimeObject
extends gdjs.RuntimeObject3D
implements gdjs.Animatable {
implements gdjs.Animatable
{
_renderer: gdjs.Model3DRuntimeObjectRenderer;
_modelResourceName: string;
@@ -97,6 +100,7 @@ namespace gdjs {
_currentAnimationIndex: integer = 0;
_animationSpeedScale: float = 1;
_animationPaused: boolean = false;
_crossfadeDuration: float = 0;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -121,6 +125,8 @@ namespace gdjs {
this.onModelChanged(objectData);
this._crossfadeDuration = objectData.content.crossfadeDuration || 0;
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
@@ -202,6 +208,7 @@ namespace gdjs {
ai: this._currentAnimationIndex,
ass: this._animationSpeedScale,
ap: this._animationPaused,
cfd: this._crossfadeDuration,
};
}
@@ -233,6 +240,9 @@ namespace gdjs {
networkSyncData.ap ? this.pauseAnimation() : this.resumeAnimation();
}
}
if (networkSyncData.cfd !== undefined) {
this._crossfadeDuration = networkSyncData.cfd;
}
}
_reloadModel(objectData: Model3DObjectData) {
@@ -348,6 +358,11 @@ namespace gdjs {
return this._renderer.hasAnimationEnded();
}
setCrossfadeDuration(duration: number): void {
if (this._crossfadeDuration === duration) return;
this._crossfadeDuration = duration;
}
isAnimationPaused() {
return this._animationPaused;
}

View File

@@ -359,7 +359,6 @@ namespace gdjs {
}
playAnimation(animationName: string, shouldLoop: boolean) {
this._animationMixer.stopAllAction();
const clip = THREE.AnimationClip.findByName(
this._originalModel.animations,
animationName
@@ -370,12 +369,22 @@ namespace gdjs {
);
return;
}
const previousAction = this._action;
this._action = this._animationMixer.clipAction(clip);
this._action.setLoop(
shouldLoop ? THREE.LoopRepeat : THREE.LoopOnce,
Number.POSITIVE_INFINITY
);
this._action.clampWhenFinished = true;
if (previousAction && previousAction !== this._action) {
this._action.enabled = true;
this._action.crossFadeFrom(
previousAction,
this._model3DRuntimeObject._crossfadeDuration,
false
);
}
this._action.play();
// Make sure the first frame is displayed.
this._animationMixer.update(0);

View File

@@ -38,7 +38,7 @@ module.exports = {
.setName('Consent Cordova plugin')
.setDependencyType('cordova')
.setExportName('cordova-plugin-consent')
.setVersion('3.0.0-alpha.8')
.setVersion('3.0.0-alpha.9')
.onlyIfOtherDependencyIsExported('AdMob Cordova plugin');
extension
@@ -93,6 +93,68 @@ module.exports = {
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.setTestMode');
extension
.addAction(
'PreventAdmobAutoInitialization',
_('Prevent AdMob auto initialization'),
_(
'Prevent AdMob from initializing automatically. You will need to call "Initialize AdMob" action manually.\n' +
'This is useful if you want to control when the consent dialog will be shown (for example, after the user has accepted your game terms).'
),
_('Prevent AdMob auto initialization'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.preventAdmobAutoInitialization');
extension
.addAction(
'InitializeAdmob',
_('Initialize AdMob manually'),
_(
'Initialize AdMob manually. This will trigger the consent dialog if needed, and then load the ads.\n' +
'Use this action if you have disabled the auto init and want to control when the consent dialog will be shown.'
),
_('Initialize AdMob'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.initializeAdmob');
extension
.addCondition(
'AdmobInitializing',
_('AdMob initializing'),
_('Check if AdMob is initializing.'),
_('AdMob is initializing'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAdmobInitializing');
extension
.addCondition(
'AdmobInitialized',
_('AdMob initialized'),
_('Check if AdMob has been initialized.'),
_('AdMob has been initialized'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAdmobInitialized');
// App Open
extension
.addCondition(

View File

@@ -1,6 +1,7 @@
namespace gdjs {
declare var admob: any;
declare var cordova: any;
declare var consent: any;
export namespace adMob {
const logger = new gdjs.Logger('AdMob');
@@ -108,26 +109,119 @@ namespace gdjs {
let rewardedVideoRewardReceived = false; // Becomes true when the video is closed and the reward is received.
let rewardedVideoErrored = false; // Becomes true when the video fails to load.
let npaValue = '0'; // TODO: expose an API to change this and also an automatic way using the consent SDK.
let npaValue = '0'; // 0 means that the user has consented to personalized ads, 1 means that the user has not consented to personalized ads.
let setupTimeoutId: NodeJS.Timeout | null = null;
const askForConsentAndInitializeAdmob = async () => {
if (admobStarted) {
logger.warn('AdMob is already started.');
return;
}
if (isStarting) {
logger.warn('AdMob is already starting.');
return;
}
try {
logger.info('Starting AdMob.');
isStarting = true;
if (cordova.platformId === 'ios') {
try {
/*
trackingStatus:
0 = notDetermined
1 = restricted
2 = denied
3 = authorized
*/
let trackingStatus = await consent.trackingAuthorizationStatus();
// If tracking is not determined, we ask the user for tracking authorization.
if (trackingStatus === 0) {
trackingStatus = await consent.requestTrackingAuthorization();
}
// If tracking is restricted or denied, we set npaValue to 1.
if (trackingStatus === 1 || trackingStatus === 2) {
npaValue = '1';
}
// otherwise, we set npaValue to 0.
npaValue = '0';
} catch (error) {
logger.error(
'Error while asking for tracking authorization, continuing:',
error
);
}
}
try {
// ConsentStatus:
// Unknown = 0,
// Required = 1,
// NotRequired = 2,
// Obtained = 3,
const consentStatus = await consent.getConsentStatus();
if (consentStatus === consent.ConsentStatus.Required) {
await consent.requestInfoUpdate();
}
await consent.loadAndShowIfRequired();
} catch (error) {
logger.error('Error while asking for consent, continuing:', error);
}
// We should be looking at canRequestAds to know if we can request ads or not.
// But as we want to be able to test ads in debug or if the consent didn't work,
// we ignore this value for now.
// const canRequestAds = await consent.canRequestAds();
if (true) {
await admob.start();
logger.info('AdMob successfully started.');
isStarting = false;
admobStarted = true;
}
} catch (error) {
logger.error('Error while starting AdMob:', error);
isStarting = false;
admobStarted = false;
}
};
// Admob initialization listener
document.addEventListener(
'deviceready',
async () => {
// Obtain user consent ?
logger.info('Starting AdMob.');
isStarting = true;
await admob.start();
logger.info('AdMob successfully started.');
isStarting = false;
admobStarted = true;
setupTimeoutId = setTimeout(async () => {
isStarting = false; // Reset to false, as it will be set to true in askForConsentAndInitializeAdmob.
await askForConsentAndInitializeAdmob();
// Wait a bit before starting admob, to avoid the consent appearing too soon.
}, 2000);
},
false
);
export const preventAdmobAutoInitialization = () => {
if (setupTimeoutId) {
isStarting = false;
clearTimeout(setupTimeoutId);
setupTimeoutId = null;
}
};
export const initializeAdmob = async () => {
preventAdmobAutoInitialization();
await askForConsentAndInitializeAdmob();
};
export const isAdmobInitialized = () => admobStarted;
export const isAdmobInitializing = () => isStarting;
/**
* Helper to know if we are on mobile and admob is correctly initialized.
*/
@@ -334,6 +428,7 @@ namespace gdjs {
position: atTop ? 'top' : 'bottom',
size: bannerRequestedAdSizeType,
offset: 0,
npa: npaValue,
});
banner.on('load', () => {

View File

@@ -1,10 +1,10 @@
namespace gdjs {
/**
* Tools to manipulate the game window positioning and
* interactions with the operating system.
* @author arthuro555
*/
export namespace evtTools {
/**
* Tools to manipulate the game window positioning and
* interactions with the operating system.
* @author arthuro555
*/
export namespace advancedWindow {
const getElectronBrowserWindow = (runtimeScene: gdjs.RuntimeScene) => {
try {

View File

@@ -20,25 +20,41 @@ namespace gdjs {
}
export class AnchorRuntimeBehavior extends gdjs.RuntimeBehavior {
_relativeToOriginalWindowSize: any;
// Configuration
_relativeToOriginalWindowSize: boolean;
_leftEdgeAnchor: HorizontalAnchor;
_rightEdgeAnchor: HorizontalAnchor;
_topEdgeAnchor: any;
_bottomEdgeAnchor: any;
_invalidDistances: boolean = true;
_leftEdgeDistance: number = 0;
_rightEdgeDistance: number = 0;
_topEdgeDistance: number = 0;
_bottomEdgeDistance: number = 0;
_topEdgeAnchor: VerticalAnchor;
_bottomEdgeAnchor: VerticalAnchor;
_useLegacyBottomAndRightAnchors: boolean = false;
// State
_hasJustBeenCreated: boolean = true;
_leftEdgeDistance: float = 0;
_rightEdgeDistance: float = 0;
_topEdgeDistance: float = 0;
_bottomEdgeDistance: float = 0;
_oldDrawableX: float = 0;
_oldDrawableY: float = 0;
_oldWidth: float = 0;
_oldHeight: float = 0;
_parentOldMinX: float = 0;
_parentOldMinY: float = 0;
_parentOldMaxX: float = 0;
_parentOldMaxY: float = 0;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
behaviorData,
owner: gdjs.RuntimeObject
) {
super(instanceContainer, behaviorData, owner);
this._relativeToOriginalWindowSize = !!behaviorData.relativeToOriginalWindowSize;
this._relativeToOriginalWindowSize =
!!behaviorData.relativeToOriginalWindowSize;
this._leftEdgeAnchor = behaviorData.leftEdgeAnchor;
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
this._topEdgeAnchor = behaviorData.topEdgeAnchor;
@@ -80,265 +96,306 @@ namespace gdjs {
return true;
}
onActivate() {
this._invalidDistances = true;
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
const objectHasMoved =
this._oldDrawableX !== this.owner.getDrawableX() ||
this._oldDrawableY !== this.owner.getDrawableY() ||
this._oldWidth !== this.owner.getWidth() ||
this._oldHeight !== this.owner.getHeight();
if (objectHasMoved) {
this._updateAnchorDistances(instanceContainer);
}
const parentHasResized =
this._parentOldMinX !== instanceContainer.getUnrotatedViewportMinX() ||
this._parentOldMinY !== instanceContainer.getUnrotatedViewportMinY() ||
this._parentOldMaxX !== instanceContainer.getUnrotatedViewportMaxX() ||
this._parentOldMaxY !== instanceContainer.getUnrotatedViewportMaxY();
if (parentHasResized) {
this._followAnchor(instanceContainer);
}
}
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
private _updateAnchorDistances(
instanceContainer: gdjs.RuntimeInstanceContainer
) {
const workingPoint: FloatPoint = gdjs.staticArray(
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
) as FloatPoint;
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
let parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
let parentCenterX = (parentMaxX + parentMinX) / 2;
let parentCenterY = (parentMaxY + parentMinY) / 2;
let parentWidth = parentMaxX - parentMinX;
let parentHeight = parentMaxY - parentMinY;
const layer = instanceContainer.getLayer(this.owner.getLayer());
if (this._invalidDistances) {
let parentMinX = this._parentOldMinX;
let parentMinY = this._parentOldMinY;
let parentMaxX = this._parentOldMaxX;
let parentMaxY = this._parentOldMaxY;
if (this._hasJustBeenCreated) {
if (this._relativeToOriginalWindowSize) {
parentMinX = instanceContainer.getInitialUnrotatedViewportMinX();
parentMinY = instanceContainer.getInitialUnrotatedViewportMinY();
parentMaxX = instanceContainer.getInitialUnrotatedViewportMaxX();
parentMaxY = instanceContainer.getInitialUnrotatedViewportMaxY();
parentCenterX = (parentMaxX + parentMinX) / 2;
parentCenterY = (parentMaxY + parentMinY) / 2;
parentWidth = parentMaxX - parentMinX;
parentHeight = parentMaxY - parentMinY;
} else {
parentMinX = instanceContainer.getUnrotatedViewportMinX();
parentMinY = instanceContainer.getUnrotatedViewportMinY();
parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
}
}
const parentCenterX = (parentMaxX + parentMinX) / 2;
const parentCenterY = (parentMaxY + parentMinY) / 2;
const parentWidth = parentMaxX - parentMinX;
const parentHeight = parentMaxY - parentMinY;
//Calculate the distances from the window's bounds.
const topLeftPixel = this._relativeToOriginalWindowSize
? [this.owner.getDrawableX(), this.owner.getDrawableY()]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX(),
this.owner.getDrawableY(),
workingPoint
);
//Calculate the distances from the window's bounds.
const topLeftPixel = this._relativeToOriginalWindowSize
? [this.owner.getDrawableX(), this.owner.getDrawableY()]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX(),
this.owner.getDrawableY(),
workingPoint
);
// Left edge
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
this._leftEdgeDistance = topLeftPixel[0] - parentMinX;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
this._leftEdgeDistance = topLeftPixel[0] - parentMaxX;
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
this._leftEdgeDistance = (topLeftPixel[0] - parentMinX) / parentWidth;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
this._leftEdgeDistance = topLeftPixel[0] - parentCenterX;
// Left edge
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
this._leftEdgeDistance = topLeftPixel[0] - parentMinX;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
this._leftEdgeDistance = topLeftPixel[0] - parentMaxX;
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
this._leftEdgeDistance = (topLeftPixel[0] - parentMinX) / parentWidth;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
this._leftEdgeDistance = topLeftPixel[0] - parentCenterX;
}
// Top edge
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
this._topEdgeDistance = topLeftPixel[1] - parentMinY;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
this._topEdgeDistance = topLeftPixel[1] - parentMaxY;
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
this._topEdgeDistance = (topLeftPixel[1] - parentMinY) / parentHeight;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
this._topEdgeDistance = topLeftPixel[1] - parentCenterY;
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const bottomRightPixel = this._relativeToOriginalWindowSize
? [
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
workingPoint
);
// Right edge
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
this._rightEdgeDistance = bottomRightPixel[0] - parentMinX;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
this._rightEdgeDistance = bottomRightPixel[0] - parentMaxX;
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
this._rightEdgeDistance =
(bottomRightPixel[0] - parentMinX) / parentWidth;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
this._rightEdgeDistance = bottomRightPixel[0] - parentCenterX;
}
// Bottom edge
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentMinY;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentMaxY;
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
this._bottomEdgeDistance =
(bottomRightPixel[1] - parentMinY) / parentHeight;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentCenterY;
}
this._hasJustBeenCreated = false;
}
private _followAnchor(instanceContainer: gdjs.RuntimeInstanceContainer) {
const workingPoint: FloatPoint = gdjs.staticArray(
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
) as FloatPoint;
const layer = instanceContainer.getLayer(this.owner.getLayer());
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
let parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
const parentCenterX = (parentMaxX + parentMinX) / 2;
const parentCenterY = (parentMaxY + parentMinY) / 2;
const parentWidth = parentMaxX - parentMinX;
const parentHeight = parentMaxY - parentMinY;
//Move and resize the object if needed
let leftPixel = 0;
let topPixel = 0;
let rightPixel = 0;
let bottomPixel = 0;
// Left edge
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
leftPixel = parentMinX + this._leftEdgeDistance;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
leftPixel = parentMaxX + this._leftEdgeDistance;
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
leftPixel = parentMinX + this._leftEdgeDistance * parentWidth;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
leftPixel = parentCenterX + this._leftEdgeDistance;
}
// Top edge
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
topPixel = parentMinY + this._topEdgeDistance;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
topPixel = parentMaxY + this._topEdgeDistance;
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
topPixel = parentMinY + this._topEdgeDistance * parentHeight;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
topPixel = parentCenterY + this._topEdgeDistance;
}
// Right edge
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
rightPixel = parentMinX + this._rightEdgeDistance;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
rightPixel = parentMaxX + this._rightEdgeDistance;
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
rightPixel = parentMinX + this._rightEdgeDistance * parentWidth;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
rightPixel = parentCenterX + this._rightEdgeDistance;
}
// Bottom edge
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
bottomPixel = parentMinY + this._bottomEdgeDistance;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
bottomPixel = parentMaxY + this._bottomEdgeDistance;
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
bottomPixel = parentMinY + this._bottomEdgeDistance * parentHeight;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
bottomPixel = parentCenterY + this._bottomEdgeDistance;
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const topLeftCoord = this._convertCoords(
instanceContainer,
layer,
leftPixel,
topPixel,
workingPoint
);
let left = topLeftCoord[0];
let top = topLeftCoord[1];
const bottomRightCoord = this._convertCoords(
instanceContainer,
layer,
rightPixel,
bottomPixel,
workingPoint
);
const right = bottomRightCoord[0];
const bottom = bottomRightCoord[1];
// Compatibility with GD <= 5.0.133
if (this._useLegacyBottomAndRightAnchors) {
//Move and resize the object according to the anchors
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setWidth(right - left);
}
// Top edge
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
this._topEdgeDistance = topLeftPixel[1] - parentMinY;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
this._topEdgeDistance = topLeftPixel[1] - parentMaxY;
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
this._topEdgeDistance = (topLeftPixel[1] - parentMinY) / parentHeight;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
this._topEdgeDistance = topLeftPixel[1] - parentCenterY;
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
this.owner.setHeight(bottom - top);
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const bottomRightPixel = this._relativeToOriginalWindowSize
? [
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
workingPoint
);
// Right edge
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
this._rightEdgeDistance = bottomRightPixel[0] - parentMinX;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
this._rightEdgeDistance = bottomRightPixel[0] - parentMaxX;
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
this._rightEdgeDistance =
(bottomRightPixel[0] - parentMinX) / parentWidth;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
this._rightEdgeDistance = bottomRightPixel[0] - parentCenterX;
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(left + this.owner.getX() - this.owner.getDrawableX());
}
// Bottom edge
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentMinY;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentMaxY;
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
this._bottomEdgeDistance =
(bottomRightPixel[1] - parentMinY) / parentHeight;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
this._bottomEdgeDistance = bottomRightPixel[1] - parentCenterY;
if (this._topEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(top + this.owner.getY() - this.owner.getDrawableY());
}
this._invalidDistances = false;
} else {
//Move and resize the object if needed
let leftPixel = 0;
let topPixel = 0;
let rightPixel = 0;
let bottomPixel = 0;
// Left edge
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
leftPixel = parentMinX + this._leftEdgeDistance;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowRight) {
leftPixel = parentMaxX + this._leftEdgeDistance;
} else if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
leftPixel = parentMinX + this._leftEdgeDistance * parentWidth;
} else if (this._leftEdgeAnchor === HorizontalAnchor.WindowCenter) {
leftPixel = parentCenterX + this._leftEdgeDistance;
}
// Top edge
if (this._topEdgeAnchor === VerticalAnchor.WindowTop) {
topPixel = parentMinY + this._topEdgeDistance;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowBottom) {
topPixel = parentMaxY + this._topEdgeDistance;
} else if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
topPixel = parentMinY + this._topEdgeDistance * parentHeight;
} else if (this._topEdgeAnchor === VerticalAnchor.WindowCenter) {
topPixel = parentCenterY + this._topEdgeDistance;
}
// Right edge
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
rightPixel = parentMinX + this._rightEdgeDistance;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowRight) {
rightPixel = parentMaxX + this._rightEdgeDistance;
} else if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
rightPixel = parentMinX + this._rightEdgeDistance * parentWidth;
} else if (this._rightEdgeAnchor === HorizontalAnchor.WindowCenter) {
rightPixel = parentCenterX + this._rightEdgeDistance;
}
// Bottom edge
if (this._bottomEdgeAnchor === VerticalAnchor.WindowTop) {
bottomPixel = parentMinY + this._bottomEdgeDistance;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowBottom) {
bottomPixel = parentMaxY + this._bottomEdgeDistance;
} else if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
bottomPixel = parentMinY + this._bottomEdgeDistance * parentHeight;
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
bottomPixel = parentCenterY + this._bottomEdgeDistance;
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const topLeftCoord = this._convertCoords(
instanceContainer,
layer,
leftPixel,
topPixel,
workingPoint
);
let left = topLeftCoord[0];
let top = topLeftCoord[1];
const bottomRightCoord = this._convertCoords(
instanceContainer,
layer,
rightPixel,
bottomPixel,
workingPoint
);
const right = bottomRightCoord[0];
const bottom = bottomRightCoord[1];
// Compatibility with GD <= 5.0.133
if (this._useLegacyBottomAndRightAnchors) {
//Move and resize the object according to the anchors
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setWidth(right - left);
}
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
this.owner.setHeight(bottom - top);
}
}
// End of compatibility code
else {
// Resize if right and left anchors are set
if (
this._rightEdgeAnchor !== HorizontalAnchor.None &&
this._leftEdgeAnchor !== HorizontalAnchor.None
) {
const width = right - left;
this.owner.setX(
this.owner.getX() === this.owner.getDrawableX()
? left
: // It uses the position of the origin relatively to the object
// size to apply it with the new size.
// This is the same as doing:
// lerp(left, right, (this.owner.getX() - this.owner.getDrawableX() / this.owner.getWidth())
// But, the division is done at the end to avoid rounding errors.
left +
((this.owner.getX() - this.owner.getDrawableX()) * width) /
this.owner.getWidth()
);
this.owner.setWidth(width);
} else {
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(
left + this.owner.getX() - this.owner.getDrawableX()
);
}
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(
right +
this.owner.getX() -
this.owner.getDrawableX() -
this.owner.getWidth()
);
}
}
// Resize if top and bottom anchors are set
if (
this._bottomEdgeAnchor !== VerticalAnchor.None &&
this._topEdgeAnchor !== VerticalAnchor.None
) {
const height = bottom - top;
this.owner.setY(
this.owner.getY() === this.owner.getDrawableY()
? top
: top +
((this.owner.getY() - this.owner.getDrawableY()) * height) /
this.owner.getHeight()
);
this.owner.setHeight(height);
} else {
if (this._topEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(
top + this.owner.getY() - this.owner.getDrawableY()
);
}
}
// End of compatibility code
else {
// Resize if right and left anchors are set
if (
this._rightEdgeAnchor !== HorizontalAnchor.None &&
this._leftEdgeAnchor !== HorizontalAnchor.None
) {
const width = right - left;
this.owner.setX(
this.owner.getX() === this.owner.getDrawableX()
? left
: // It uses the position of the origin relatively to the object
// size to apply it with the new size.
// This is the same as doing:
// lerp(left, right, (this.owner.getX() - this.owner.getDrawableX() / this.owner.getWidth())
// But, the division is done at the end to avoid rounding errors.
left +
((this.owner.getX() - this.owner.getDrawableX()) * width) /
this.owner.getWidth()
);
this.owner.setWidth(width);
} else {
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(
left + this.owner.getX() - this.owner.getDrawableX()
);
}
if (this._rightEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(
right +
this.owner.getX() -
this.owner.getDrawableX() -
this.owner.getWidth()
);
}
}
// Resize if top and bottom anchors are set
if (
this._bottomEdgeAnchor !== VerticalAnchor.None &&
this._topEdgeAnchor !== VerticalAnchor.None
) {
const height = bottom - top;
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(
this.owner.getY() === this.owner.getDrawableY()
? top
: top +
((this.owner.getY() - this.owner.getDrawableY()) * height) /
this.owner.getHeight()
bottom +
this.owner.getY() -
this.owner.getDrawableY() -
this.owner.getHeight()
);
this.owner.setHeight(height);
} else {
if (this._topEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(
top + this.owner.getY() - this.owner.getDrawableY()
);
}
if (this._bottomEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(
bottom +
this.owner.getY() -
this.owner.getDrawableY() -
this.owner.getHeight()
);
}
}
}
this._oldDrawableX = this.owner.getDrawableX();
this._oldDrawableY = this.owner.getDrawableY();
this._oldWidth = this.owner.getWidth();
this._oldHeight = this.owner.getHeight();
this._parentOldMinX = instanceContainer.getUnrotatedViewportMinX();
this._parentOldMinY = instanceContainer.getUnrotatedViewportMinY();
this._parentOldMaxX = instanceContainer.getUnrotatedViewportMaxX();
this._parentOldMaxY = instanceContainer.getUnrotatedViewportMaxY();
}
}

View File

@@ -54,7 +54,7 @@ module.exports = {
objectProperties
.getOrCreate('text')
.setValue(objectContent.text)
.setType('textarea')
.setType('multilinestring')
.setLabel(_('BBCode text'));
objectProperties
@@ -106,8 +106,7 @@ module.exports = {
return objectProperties;
};
objectBBText.content = {
text:
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
text: '[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
visible: true,
@@ -193,9 +192,10 @@ module.exports = {
parameterType === 'string' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)
@@ -245,9 +245,10 @@ module.exports = {
parameterType === 'number' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)

View File

@@ -43,7 +43,8 @@ namespace gdjs {
*/
export class BBTextRuntimeObject
extends gdjs.RuntimeObject
implements gdjs.OpacityHandler {
implements gdjs.OpacityHandler
{
_opacity: float;
_text: string;

View File

@@ -54,7 +54,7 @@ module.exports = {
objectProperties
.getOrCreate('text')
.setValue(objectContent.text)
.setType('textarea')
.setType('multilinestring')
.setLabel(_('Text'));
objectProperties
@@ -107,8 +107,7 @@ module.exports = {
return objectProperties;
};
bitmapTextObject.content = {
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
text: 'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
@@ -665,9 +664,8 @@ module.exports = {
this._pixiObject.align = align;
const color = object.content.tint;
this._pixiObject.tint = objectsRenderingService.rgbOrHexToHexNumber(
color
);
this._pixiObject.tint =
objectsRenderingService.rgbOrHexToHexNumber(color);
const scale = object.content.scale;
this._pixiObject.scale.set(scale);

View File

@@ -173,5 +173,6 @@ namespace gdjs {
return this._pixiObject.textHeight * this.getScale();
}
}
export const BitmapTextRuntimeObjectRenderer = BitmapTextRuntimeObjectPixiRenderer;
export const BitmapTextRuntimeObjectRenderer =
BitmapTextRuntimeObjectPixiRenderer;
}

View File

@@ -52,7 +52,8 @@ namespace gdjs {
*/
export class BitmapTextRuntimeObject
extends gdjs.RuntimeObject
implements gdjs.TextContainer, gdjs.OpacityHandler, gdjs.Scalable {
implements gdjs.TextContainer, gdjs.OpacityHandler, gdjs.Scalable
{
_opacity: float;
_text: string;
/** color in format [r, g, b], where each component is in the range [0, 255] */

View File

@@ -249,13 +249,16 @@ namespace gdjs {
(clipTextEnd === 0 || clipTextEnd !== dialogueText.length)
) {
pauseScrolling = true;
setTimeout(function () {
pauseScrolling = false;
commandCalls.splice(index, 1);
if (gdjs.dialogueTree.getVariable('debug')) {
logger.info('CMD:', call);
}
}, parseInt(call.params[1], 10));
setTimeout(
function () {
pauseScrolling = false;
commandCalls.splice(index, 1);
if (gdjs.dialogueTree.getVariable('debug')) {
logger.info('CMD:', call);
}
},
parseInt(call.params[1], 10)
);
}
if (call.cmd === command) {
commandParameters = call.params;

View File

@@ -157,9 +157,8 @@ namespace gdjs {
if (!instanceContainer.touchDraggableManagers[touchId]) {
//Create the shared manager if necessary.
// @ts-ignore
instanceContainer.touchDraggableManagers[
touchId
] = new DraggableManager(instanceContainer, touchId);
instanceContainer.touchDraggableManagers[touchId] =
new DraggableManager(instanceContainer, touchId);
}
// @ts-ignore
return instanceContainer.touchDraggableManagers[touchId];

View File

@@ -22,7 +22,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
const adjustmentFilter =
filter as unknown as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
adjustmentFilter.gamma = value;
} else if (parameterName === 'saturation') {
@@ -42,7 +43,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
const adjustmentFilter =
filter as unknown as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
return adjustmentFilter.gamma;
}
@@ -88,7 +90,8 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): AdjustmentFilterNetworkSyncData {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
const adjustmentFilter =
filter as unknown as PIXI.filters.AdjustmentFilter;
return {
ga: adjustmentFilter.gamma,
sa: adjustmentFilter.saturation,
@@ -104,7 +107,8 @@ namespace gdjs {
filter: PIXI.Filter,
data: AdjustmentFilterNetworkSyncData
): void {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
const adjustmentFilter =
filter as unknown as PIXI.filters.AdjustmentFilter;
adjustmentFilter.gamma = data.ga;
adjustmentFilter.saturation = data.sa;
adjustmentFilter.contrast = data.co;

View File

@@ -20,7 +20,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter =
filter as unknown as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
advancedBloomFilter.threshold = value;
} else if (parameterName === 'bloomScale') {
@@ -36,7 +37,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter =
filter as unknown as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
return advancedBloomFilter.threshold;
}
@@ -78,7 +80,8 @@ namespace gdjs {
getNetworkSyncData(
filter: PIXI.Filter
): AdvancedBloomFilterNetworkSyncData {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter =
filter as unknown as PIXI.filters.AdvancedBloomFilter;
return {
th: advancedBloomFilter.threshold,
bs: advancedBloomFilter.bloomScale,
@@ -92,7 +95,8 @@ namespace gdjs {
filter: PIXI.Filter,
syncData: AdvancedBloomFilterNetworkSyncData
) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter =
filter as unknown as PIXI.filters.AdvancedBloomFilter;
advancedBloomFilter.threshold = syncData.th;
advancedBloomFilter.bloomScale = syncData.bs;
advancedBloomFilter.brightness = syncData.bn;

View File

@@ -15,13 +15,13 @@ namespace gdjs {
parameterName: string,
value: number
) {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
const asciiFilter = filter as unknown as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
asciiFilter.size = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
const asciiFilter = filter as unknown as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
return asciiFilter.size;
}
@@ -46,14 +46,14 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): AsciiFilterNetworkSyncData {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
const asciiFilter = filter as unknown as PIXI.filters.AsciiFilter;
return { size: asciiFilter.size };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: AsciiFilterNetworkSyncData
) {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
const asciiFilter = filter as unknown as PIXI.filters.AsciiFilter;
asciiFilter.size = data.size;
}
})()

View File

@@ -25,7 +25,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
@@ -40,7 +40,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'rotation') {
return bevelFilter.rotation;
@@ -64,7 +64,7 @@ namespace gdjs {
parameterName: string,
value: string
) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.rgbOrHexStringToNumber(value);
@@ -78,7 +78,7 @@ namespace gdjs {
parameterName: string,
value: number
): void {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = value;
@@ -88,7 +88,7 @@ namespace gdjs {
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
return bevelFilter.lightColor;
}
@@ -103,7 +103,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): BevelFilterNetworkSyncData {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
return {
r: bevelFilter.rotation,
@@ -119,7 +119,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: BevelFilterNetworkSyncData
) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
const bevelFilter = filter as unknown as PIXI.filters.BevelFilter &
BevelFilterExtra;
bevelFilter.rotation = data.r;
bevelFilter.thickness = data.t;

View File

@@ -16,14 +16,14 @@ namespace gdjs {
parameterName: string,
value: number
) {
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrix = filter as unknown as PIXI.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
}
colorMatrix.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrix = filter as unknown as PIXI.ColorMatrixFilter;
if (parameterName === 'opacity') {
return colorMatrix.alpha;
}
@@ -50,14 +50,14 @@ namespace gdjs {
getNetworkSyncData(
filter: PIXI.Filter
): BlackAndWhiteFilterNetworkSyncData {
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrix = filter as unknown as PIXI.ColorMatrixFilter;
return { a: colorMatrix.alpha };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: BlackAndWhiteFilterNetworkSyncData
) {
const colorMatrix = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrix = filter as unknown as PIXI.ColorMatrixFilter;
colorMatrix.alpha = data.a;
}
})()

View File

@@ -16,7 +16,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
const blendingModeFilter = filter as unknown as PIXI.AlphaFilter;
if (parameterName === 'alpha') {
blendingModeFilter.alpha = value;
} else if (parameterName === 'blendmode') {
@@ -24,7 +24,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
const blendingModeFilter = filter as unknown as PIXI.AlphaFilter;
if (parameterName === 'alpha') {
return blendingModeFilter.alpha;
}
@@ -54,7 +54,7 @@ namespace gdjs {
getNetworkSyncData(
filter: PIXI.Filter
): BlendingModeFilterNetworkSyncData {
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
const blendingModeFilter = filter as unknown as PIXI.AlphaFilter;
return {
a: blendingModeFilter.alpha,
bm: blendingModeFilter.blendMode,
@@ -64,7 +64,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: BlendingModeFilterNetworkSyncData
) {
const blendingModeFilter = (filter as unknown) as PIXI.AlphaFilter;
const blendingModeFilter = filter as unknown as PIXI.AlphaFilter;
blendingModeFilter.alpha = data.a;
blendingModeFilter.blendMode = data.bm;
}

View File

@@ -20,7 +20,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
const brightnessFilter = filter as unknown as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
if (parameterName !== 'brightness') {
return;
@@ -30,7 +30,7 @@ namespace gdjs {
brightnessFilter.brightness(brightness, false);
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
const brightnessFilter = filter as unknown as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
if (parameterName === 'brightness') {
return brightnessFilter.__brightness;
@@ -56,7 +56,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): BrightnessFilterNetworkSyncData {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
const brightnessFilter = filter as unknown as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
return { b: brightnessFilter.__brightness };
}
@@ -64,7 +64,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: BrightnessFilterNetworkSyncData
) {
const brightnessFilter = (filter as unknown) as PIXI.ColorMatrixFilter &
const brightnessFilter = filter as unknown as PIXI.ColorMatrixFilter &
BrightnessFilterExtra;
brightnessFilter.__brightness = data.b;
brightnessFilter.brightness(data.b, false);

View File

@@ -18,7 +18,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter =
filter as unknown as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
bulgePinchFilter.center[0] = value;
} else if (parameterName === 'centerY') {
@@ -34,7 +35,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter =
filter as unknown as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
return bulgePinchFilter.center[0];
}
@@ -68,7 +70,8 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): BulgePinchFilterNetworkSyncData {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter =
filter as unknown as PIXI.filters.BulgePinchFilter;
return {
cx: bulgePinchFilter.center[0],
cy: bulgePinchFilter.center[1],
@@ -80,7 +83,8 @@ namespace gdjs {
filter: PIXI.Filter,
data: BulgePinchFilterNetworkSyncData
) {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter =
filter as unknown as PIXI.filters.BulgePinchFilter;
bulgePinchFilter.center[0] = data.cx;
bulgePinchFilter.center[1] = data.cy;
bulgePinchFilter.radius = data.r;

View File

@@ -29,7 +29,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
value / 100,
@@ -39,7 +39,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
return colorMapFilter.mix;
}
@@ -63,20 +63,20 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
if (parameterName === 'nearest') {
colorMapFilter.nearest = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): ColorMapFilterNetworkSyncData {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
return { mix: colorMapFilter.mix, near: colorMapFilter.nearest };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: ColorMapFilterNetworkSyncData
) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
const colorMapFilter = filter as unknown as PIXI.filters.ColorMapFilter;
colorMapFilter.mix = data.mix;
colorMapFilter.nearest = data.near;
}

View File

@@ -23,15 +23,17 @@ namespace gdjs {
parameterName: string,
value: number
) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'epsilon') {
colorReplaceFilter.epsilon = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'epsilon') {
return colorReplaceFilter.epsilon;
}
@@ -42,8 +44,9 @@ namespace gdjs {
parameterName: string,
value: string
) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.rgbOrHexStringToNumber(value);
} else if (parameterName === 'newColor') {
@@ -55,8 +58,9 @@ namespace gdjs {
parameterName: string,
value: number
): void {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = value;
} else if (parameterName === 'newColor') {
@@ -64,8 +68,9 @@ namespace gdjs {
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
return colorReplaceFilter.originalColor;
} else if (parameterName === 'newColor') {
@@ -81,8 +86,9 @@ namespace gdjs {
getNetworkSyncData(
filter: PIXI.Filter
): ColorReplaceFilterNetworkSyncData {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
return {
e: colorReplaceFilter.epsilon,
oc: colorReplaceFilter.originalColor,
@@ -93,8 +99,9 @@ namespace gdjs {
filter: PIXI.Filter,
data: ColorReplaceFilterNetworkSyncData
) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
const colorReplaceFilter =
filter as unknown as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
colorReplaceFilter.epsilon = data.e;
colorReplaceFilter.originalColor = data.oc;
colorReplaceFilter.newColor = data.nc;

View File

@@ -23,13 +23,13 @@ namespace gdjs {
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const filter = new PIXI.filters.CRTFilter();
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
crtFilter._animationTimer = 0;
return crtFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (crtFilter.animationSpeed !== 0) {
// Multiply by 10 so that the default value is a sensible speed
@@ -49,7 +49,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (parameterName === 'lineWidth') {
crtFilter.lineWidth = value;
@@ -76,7 +76,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
if (parameterName === 'lineWidth') {
return crtFilter.lineWidth;
@@ -131,13 +131,13 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter;
const crtFilter = filter as unknown as PIXI.filters.CRTFilter;
if (parameterName === 'verticalLine') {
crtFilter.verticalLine = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): CRTFilterNetworkSyncData {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
return {
lw: crtFilter.lineWidth,
@@ -158,7 +158,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: CRTFilterNetworkSyncData
) {
const crtFilter = (filter as unknown) as PIXI.filters.CRTFilter &
const crtFilter = filter as unknown as PIXI.filters.CRTFilter &
CRTFilterExtra;
crtFilter.lineWidth = data.lw;
crtFilter.lineContrast = data.lc;

View File

@@ -25,7 +25,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
const displacementFilter = filter as unknown as PIXI.DisplacementFilter;
if (parameterName === 'scaleX') {
displacementFilter.scale.x = value;
}
@@ -34,7 +34,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
const displacementFilter = filter as unknown as PIXI.DisplacementFilter;
if (parameterName === 'scaleX') {
return displacementFilter.scale.x;
}
@@ -64,7 +64,7 @@ namespace gdjs {
getNetworkSyncData(
filter: PIXI.Filter
): DisplacementFilterNetworkSyncData {
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
const displacementFilter = filter as unknown as PIXI.DisplacementFilter;
return {
sx: displacementFilter.scale.x,
sy: displacementFilter.scale.y,
@@ -74,7 +74,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: DisplacementFilterNetworkSyncData
) {
const displacementFilter = (filter as unknown) as PIXI.DisplacementFilter;
const displacementFilter = filter as unknown as PIXI.DisplacementFilter;
displacementFilter.scale.x = data.sx;
displacementFilter.scale.y = data.sy;
}

View File

@@ -16,7 +16,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
const dotFilter = filter as unknown as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
dotFilter.scale = value;
} else if (parameterName === 'angle') {
@@ -24,7 +24,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
const dotFilter = filter as unknown as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
return dotFilter.scale;
}
@@ -52,14 +52,14 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): DotFilterNetworkSyncData {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
const dotFilter = filter as unknown as PIXI.filters.DotFilter;
return { s: dotFilter.scale, a: dotFilter.angle };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: DotFilterNetworkSyncData
) {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
const dotFilter = filter as unknown as PIXI.filters.DotFilter;
dotFilter.scale = data.s;
dotFilter.angle = data.a;
}

View File

@@ -22,7 +22,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
dropShadowFilter.blur = value;
} else if (parameterName === 'quality') {
@@ -38,7 +39,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
return dropShadowFilter.blur;
}
@@ -64,7 +66,8 @@ namespace gdjs {
parameterName: string,
value: string
) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = gdjs.rgbOrHexStringToNumber(value);
}
@@ -74,13 +77,15 @@ namespace gdjs {
parameterName: string,
value: number
): void {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
return dropShadowFilter.color;
}
@@ -91,13 +96,15 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
if (parameterName === 'shadowOnly') {
dropShadowFilter.shadowOnly = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): DropShadowFilterNetworkSyncData {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
return {
b: dropShadowFilter.blur,
q: dropShadowFilter.quality,
@@ -113,7 +120,8 @@ namespace gdjs {
filter: PIXI.Filter,
data: DropShadowFilterNetworkSyncData
) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
const dropShadowFilter =
filter as unknown as PIXI.filters.DropShadowFilter;
dropShadowFilter.blur = data.b;
dropShadowFilter.quality = data.q;
dropShadowFilter.alpha = data.a;

View File

@@ -24,13 +24,13 @@ namespace gdjs {
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const filter = new PIXI.filters.GlitchFilter();
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
glitchFilter._animationTimer = 0;
return glitchFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (glitchFilter.animationFrequency !== 0) {
glitchFilter._animationTimer += target.getElapsedTime() / 1000;
@@ -48,7 +48,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'slices') {
glitchFilter.slices = value;
@@ -79,7 +79,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'slices') {
return glitchFilter.slices;
@@ -140,14 +140,14 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
if (parameterName === 'average') {
glitchFilter.average = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): GlitchFilterNetworkSyncData {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
return {
s: glitchFilter.slices,
@@ -170,7 +170,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: GlitchFilterNetworkSyncData
) {
const glitchFilter = (filter as unknown) as PIXI.filters.GlitchFilter &
const glitchFilter = filter as unknown as PIXI.filters.GlitchFilter &
GlitchFilterExtra;
glitchFilter.slices = data.s;
glitchFilter.offset = data.o;

View File

@@ -21,7 +21,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'innerStrength') {
glowFilter.innerStrength = value;
@@ -32,7 +32,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'innerStrength') {
return glowFilter.innerStrength;
@@ -50,7 +50,7 @@ namespace gdjs {
parameterName: string,
value: string
) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
glowFilter.color = gdjs.rgbOrHexStringToNumber(value);
@@ -61,14 +61,14 @@ namespace gdjs {
parameterName: string,
value: number
): void {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
glowFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
return glowFilter.color;
@@ -81,7 +81,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): GlowFilterNetworkSyncData {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
return {
is: glowFilter.innerStrength,
@@ -94,7 +94,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: GlowFilterNetworkSyncData
): void {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
const glowFilter = filter as unknown as PIXI.filters.GlowFilter &
GlowFilterExtra;
glowFilter.innerStrength = data.is;
glowFilter.outerStrength = data.os;

View File

@@ -24,7 +24,7 @@ namespace gdjs {
return godrayFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (godrayFilter.animationSpeed !== 0) {
godrayFilter.time +=
@@ -36,7 +36,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'lacunarity') {
godrayFilter.lacunarity = value;
@@ -57,7 +57,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'lacunarity') {
return godrayFilter.lacunarity;
@@ -103,14 +103,14 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
if (parameterName === 'parallel') {
godrayFilter.parallel = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): GodrayFilterNetworkSyncData {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
return {
la: godrayFilter.lacunarity,
@@ -128,7 +128,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: GodrayFilterNetworkSyncData
) {
const godrayFilter = (filter as unknown) as PIXI.filters.GodrayFilter &
const godrayFilter = filter as unknown as PIXI.filters.GodrayFilter &
GodrayFilterExtra;
godrayFilter.lacunarity = data.la;
godrayFilter.angle = data.a;

View File

@@ -18,7 +18,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
const kawaseBlurFilter =
filter as unknown as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
kawaseBlurFilter.pixelSize[0] = value;
} else if (parameterName === 'pixelizeY') {
@@ -30,7 +31,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
const kawaseBlurFilter =
filter as unknown as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
return kawaseBlurFilter.pixelSize[0];
}
@@ -64,7 +66,8 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): KawaseBlurFilterNetworkSyncData {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
const kawaseBlurFilter =
filter as unknown as PIXI.filters.KawaseBlurFilter;
return {
px: kawaseBlurFilter.pixelSize[0],
py: kawaseBlurFilter.pixelSize[1],
@@ -76,7 +79,8 @@ namespace gdjs {
filter: PIXI.Filter,
data: KawaseBlurFilterNetworkSyncData
) {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
const kawaseBlurFilter =
filter as unknown as PIXI.filters.KawaseBlurFilter;
kawaseBlurFilter.pixelSize[0] = data.px;
kawaseBlurFilter.pixelSize[1] = data.py;
kawaseBlurFilter.blur = data.b;

View File

@@ -15,13 +15,13 @@ namespace gdjs {
parameterName: string,
value: number
) {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
const noiseFilter = filter as unknown as PIXI.NoiseFilter;
if (parameterName === 'noise') {
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
const noiseFilter = filter as unknown as PIXI.NoiseFilter;
if (parameterName === 'noise') {
return noiseFilter.noise;
}
@@ -46,14 +46,14 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): NoiseFilterNetworkSyncData {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
const noiseFilter = filter as unknown as PIXI.NoiseFilter;
return { n: noiseFilter.noise };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: NoiseFilterNetworkSyncData
) {
const noiseFilter = (filter as unknown) as PIXI.NoiseFilter;
const noiseFilter = filter as unknown as PIXI.NoiseFilter;
noiseFilter.noise = data.n;
}
})()

View File

@@ -20,13 +20,13 @@ namespace gdjs {
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const filter = new PIXI.filters.OldFilmFilter();
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
oldFilmFilter._animationTimer = 0;
return oldFilmFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (oldFilmFilter.animationFrequency !== 0) {
oldFilmFilter._animationTimer += target.getElapsedTime() / 1000;
@@ -44,7 +44,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (parameterName === 'sepia') {
oldFilmFilter.sepia = value;
@@ -69,7 +69,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
if (parameterName === 'sepia') {
return oldFilmFilter.sepia;
@@ -122,7 +122,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): OldFilmFilterNetworkSyncData {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
return {
se: oldFilmFilter.sepia,
@@ -141,7 +141,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: OldFilmFilterNetworkSyncData
) {
const oldFilmFilter = (filter as unknown) as PIXI.filters.OldFilmFilter &
const oldFilmFilter = filter as unknown as PIXI.filters.OldFilmFilter &
OldFilmFilterExtra;
oldFilmFilter.sepia = data.se;
oldFilmFilter.noise = data.n;

View File

@@ -17,7 +17,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
if (parameterName === 'thickness') {
outlineFilter.thickness = value;
} else if (parameterName === 'padding') {
@@ -25,7 +25,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
if (parameterName === 'thickness') {
return outlineFilter.thickness;
}
@@ -39,7 +39,7 @@ namespace gdjs {
parameterName: string,
value: string
) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = gdjs.rgbOrHexStringToNumber(value);
}
@@ -49,13 +49,13 @@ namespace gdjs {
parameterName: string,
value: number
): void {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = value;
}
}
getColorParameter(filter: PIXI.Filter, parameterName: string): number {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
return outlineFilter.color;
}
@@ -67,7 +67,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): OutlineFilterNetworkSyncData {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
return {
t: outlineFilter.thickness,
p: outlineFilter.padding,
@@ -78,7 +78,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: OutlineFilterNetworkSyncData
) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
const outlineFilter = filter as unknown as PIXI.filters.OutlineFilter;
outlineFilter.thickness = data.t;
outlineFilter.padding = data.p;
outlineFilter.color = data.c;

View File

@@ -21,15 +21,17 @@ namespace gdjs {
parameterName: string,
value: number
) {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
const pixelateFilter =
filter as unknown as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
if (parameterName === 'size') {
pixelateFilter.size = value;
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
const pixelateFilter =
filter as unknown as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
if (parameterName === 'size') {
return pixelateFilter.size;
}
@@ -54,16 +56,18 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): PixelateFilterNetworkSyncData {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
const pixelateFilter =
filter as unknown as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
return { s: pixelateFilter.size };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: PixelateFilterNetworkSyncData
) {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
const pixelateFilter =
filter as unknown as PIXI.filters.PixelateFilter &
PixelateFilterExtra;
pixelateFilter.size = data.s;
}
})()

View File

@@ -1,26 +1,32 @@
declare namespace PIXI.filters {
export class DropShadowFilter extends PIXI.Filter {
constructor(options?: DropShadowFilterOptions);
/** @deprecated */
rotation: number;
/** @deprecated */
distance: number;
offset: PIXI.Point;
alpha: number;
blur: number;
color: number;
distance: number;
kernels: number[];
pixelSize: number | number[] | PIXI.Point;
quality: number;
rotation: number;
shadowOnly: boolean;
}
export interface DropShadowFilterOptions {
/** @deprecated */
rotation?: number;
/** @deprecated */
distance?: number;
offset: PIXI.Point;
alpha?: number;
blur?: number;
color?: number;
distance?: number;
kernels?: number[];
pixelSize?: number | number[] | PIXI.Point;
quality?: number;
resolution?: number;
rotation?: number;
shadowOnly?: boolean;
}
}

View File

@@ -20,8 +20,9 @@ namespace gdjs {
return radialBlurFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
const radialBlurFilter =
filter as unknown as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
radialBlurFilter.center[0] = Math.round(
radialBlurFilter._centerX * target.getWidth()
);
@@ -34,8 +35,9 @@ namespace gdjs {
parameterName: string,
value: number
) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
const radialBlurFilter =
filter as unknown as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
if (parameterName === 'radius') {
radialBlurFilter.radius = value < 0 ? -1 : value;
} else if (parameterName === 'angle') {
@@ -55,8 +57,9 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
const radialBlurFilter =
filter as unknown as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
if (parameterName === 'radius') {
radialBlurFilter.radius;
}
@@ -96,8 +99,9 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): RadialBlurFilterNetworkSyncData {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
const radialBlurFilter =
filter as unknown as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
return {
r: radialBlurFilter.radius,
a: radialBlurFilter.angle,
@@ -111,8 +115,9 @@ namespace gdjs {
filter: PIXI.Filter,
data: RadialBlurFilterNetworkSyncData
) {
const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
const radialBlurFilter =
filter as unknown as PIXI.filters.RadialBlurFilter &
RadialBlurFilterExtra;
radialBlurFilter.radius = data.r;
radialBlurFilter.angle = data.a;
radialBlurFilter.kernelSize = data.ks;

View File

@@ -40,8 +40,9 @@ namespace gdjs {
return reflectionFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (reflectionFilter.animationSpeed !== 0) {
reflectionFilter.time +=
(target.getElapsedTime() / 1000) * reflectionFilter.animationSpeed;
@@ -52,8 +53,9 @@ namespace gdjs {
parameterName: string,
value: number
) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'boundary') {
reflectionFilter.boundary = value;
}
@@ -80,8 +82,9 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'boundary') {
return reflectionFilter.boundary;
}
@@ -126,15 +129,17 @@ namespace gdjs {
parameterName: string,
value: boolean
) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
if (parameterName === 'mirror') {
reflectionFilter.mirror = value;
}
}
getNetworkSyncData(filter: PIXI.Filter): ReflectionFilterNetworkSyncData {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
return {
b: reflectionFilter.boundary,
ams: reflectionFilter.amplitude[0],
@@ -151,8 +156,9 @@ namespace gdjs {
filter: PIXI.Filter,
data: ReflectionFilterNetworkSyncData
) {
const reflectionFilter = (filter as unknown) as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
const reflectionFilter =
filter as unknown as PIXI.filters.ReflectionFilter &
ReflectionFilterExtra;
reflectionFilter.boundary = data.b;
reflectionFilter.amplitude[0] = data.ams;
reflectionFilter.amplitude[1] = data.ame;

View File

@@ -20,7 +20,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
const rgbSplitFilter = filter as unknown as PIXI.filters.RGBSplitFilter;
if (parameterName === 'redX') {
rgbSplitFilter.red.x = value;
} else if (parameterName === 'redY') {
@@ -36,7 +36,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
const rgbSplitFilter = filter as unknown as PIXI.filters.RGBSplitFilter;
if (parameterName === 'redX') {
return rgbSplitFilter.red.x;
}
@@ -76,7 +76,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): RGBSplitFilterNetworkSyncData {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
const rgbSplitFilter = filter as unknown as PIXI.filters.RGBSplitFilter;
return {
rX: rgbSplitFilter.red.x,
rY: rgbSplitFilter.red.y,
@@ -90,7 +90,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: RGBSplitFilterNetworkSyncData
) {
const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter;
const rgbSplitFilter = filter as unknown as PIXI.filters.RGBSplitFilter;
rgbSplitFilter.red.x = data.rX;
rgbSplitFilter.red.y = data.rY;
rgbSplitFilter.green.x = data.gX;

View File

@@ -16,7 +16,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrixFilter = filter as unknown as PIXI.ColorMatrixFilter;
if (parameterName === 'opacity') {
colorMatrixFilter.alpha = gdjs.PixiFiltersTools.clampValue(
value,
@@ -26,7 +26,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrixFilter = filter as unknown as PIXI.ColorMatrixFilter;
if (parameterName === 'opacity') {
return colorMatrixFilter.alpha;
}
@@ -51,14 +51,14 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): SepiaFilterNetworkSyncData {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrixFilter = filter as unknown as PIXI.ColorMatrixFilter;
return { a: colorMatrixFilter.alpha };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: SepiaFilterNetworkSyncData
) {
const colorMatrixFilter = (filter as unknown) as PIXI.ColorMatrixFilter;
const colorMatrixFilter = filter as unknown as PIXI.ColorMatrixFilter;
colorMatrixFilter.alpha = data.a;
}
})()

View File

@@ -22,8 +22,9 @@ namespace gdjs {
return shockwaveFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const shockwaveFilter = (filter as unknown) as PIXI.filters.ShockwaveFilter &
ShockwaveFilterExtra;
const shockwaveFilter =
filter as unknown as PIXI.filters.ShockwaveFilter &
ShockwaveFilterExtra;
if (shockwaveFilter.speed !== 0) {
shockwaveFilter.time += target.getElapsedTime() / 1000;
}

View File

@@ -16,7 +16,8 @@ namespace gdjs {
parameterName: string,
value: number
) {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
const tiltShiftFilter =
filter as unknown as PIXI.filters.TiltShiftFilter;
if (parameterName === 'blur') {
tiltShiftFilter.blur = value;
} else if (parameterName === 'gradientBlur') {
@@ -24,7 +25,8 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
const tiltShiftFilter =
filter as unknown as PIXI.filters.TiltShiftFilter;
if (parameterName === 'blur') {
return tiltShiftFilter.blur;
}
@@ -52,14 +54,16 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): TiltShiftFilterNetworkSyncData {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
const tiltShiftFilter =
filter as unknown as PIXI.filters.TiltShiftFilter;
return { b: tiltShiftFilter.blur, gb: tiltShiftFilter.gradientBlur };
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: TiltShiftFilterNetworkSyncData
) {
const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter;
const tiltShiftFilter =
filter as unknown as PIXI.filters.TiltShiftFilter;
tiltShiftFilter.blur = data.b;
tiltShiftFilter.gradientBlur = data.gb;
}

View File

@@ -20,7 +20,7 @@ namespace gdjs {
return twistFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
const twistFilter = filter as unknown as PIXI.filters.TwistFilter &
TwistFilterExtra;
twistFilter.offset.x = Math.round(
twistFilter._offsetX * target.getWidth()
@@ -34,7 +34,7 @@ namespace gdjs {
parameterName: string,
value: number
) {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
const twistFilter = filter as unknown as PIXI.filters.TwistFilter &
TwistFilterExtra;
if (parameterName === 'radius') {
twistFilter.radius = value;
@@ -49,7 +49,7 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
const twistFilter = filter as unknown as PIXI.filters.TwistFilter &
TwistFilterExtra;
if (parameterName === 'radius') {
return twistFilter.radius;
@@ -87,7 +87,7 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): TwistFilterNetworkSyncData {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
const twistFilter = filter as unknown as PIXI.filters.TwistFilter &
TwistFilterExtra;
return {
r: twistFilter.radius,
@@ -101,7 +101,7 @@ namespace gdjs {
filter: PIXI.Filter,
data: TwistFilterNetworkSyncData
) {
const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter &
const twistFilter = filter as unknown as PIXI.filters.TwistFilter &
TwistFilterExtra;
twistFilter.radius = data.r;
twistFilter.angle = data.a;

View File

@@ -19,8 +19,9 @@ namespace gdjs {
return zoomBlurFilter;
}
updatePreRender(filter: PIXI.Filter, target: EffectsTarget) {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
const zoomBlurFilter =
filter as unknown as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
zoomBlurFilter.center[0] = Math.round(
zoomBlurFilter._centerX * target.getWidth()
);
@@ -33,8 +34,9 @@ namespace gdjs {
parameterName: string,
value: number
) {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
const zoomBlurFilter =
filter as unknown as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
if (parameterName === 'centerX') {
zoomBlurFilter._centerX = value;
} else if (parameterName === 'centerY') {
@@ -52,8 +54,9 @@ namespace gdjs {
}
}
getDoubleParameter(filter: PIXI.Filter, parameterName: string): number {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
const zoomBlurFilter =
filter as unknown as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
if (parameterName === 'centerX') {
return zoomBlurFilter._centerX;
}
@@ -90,8 +93,9 @@ namespace gdjs {
value: boolean
) {}
getNetworkSyncData(filter: PIXI.Filter): ZoomBlurFilterNetworkSyncData {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
const zoomBlurFilter =
filter as unknown as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
return {
cx: zoomBlurFilter._centerX,
cy: zoomBlurFilter._centerY,
@@ -104,8 +108,9 @@ namespace gdjs {
filter: PIXI.Filter,
data: ZoomBlurFilterNetworkSyncData
) {
const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
const zoomBlurFilter =
filter as unknown as PIXI.filters.ZoomBlurFilter &
ZoomBlurFilterExtra;
zoomBlurFilter._centerX = data.cx;
zoomBlurFilter._centerY = data.cy;
zoomBlurFilter.innerRadius = data.ir;

View File

@@ -50,12 +50,12 @@ namespace gdjs {
// `effectData.booleanParameters.someBoolean`
logger.info(
'The PIXI texture found for the Dummy Effect (not actually used):',
(layer
.getRuntimeScene()
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
effectData.stringParameters.someImage
)
(
layer
.getRuntimeScene()
.getGame()
.getImageManager() as gdjs.PixiImageManager
).getPIXITexture(effectData.stringParameters.someImage)
);
return filter;
}

View File

@@ -148,17 +148,22 @@ namespace gdjs {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading = true;
FBInstant.getInterstitialAdAsync(adPlacementId)
.then(function (interstitial) {
gdjs.evtTools.facebookInstantGames._preloadedInterstitial = interstitial;
gdjs.evtTools.facebookInstantGames._preloadedInterstitial =
interstitial;
return interstitial.loadAsync();
})
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = true;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading =
false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded =
true;
logger.info('Facebook Instant Games interstitial preloaded.');
})
.catch(function (err) {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading =
false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded =
false;
logger.error('Interstitial failed to preload: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
});
@@ -181,7 +186,8 @@ namespace gdjs {
errorVariable.setString(err.message || 'Unknown error');
})
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded =
false;
});
};
@@ -199,20 +205,26 @@ namespace gdjs {
) {
return;
}
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading = true;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading =
true;
FBInstant.getRewardedVideoAsync(adPlacementId)
.then(function (rewardedVideo) {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideo = rewardedVideo;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideo =
rewardedVideo;
return rewardedVideo.loadAsync();
})
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = true;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading =
false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded =
true;
logger.info('Facebook Instant Games rewarded video preloaded.');
})
.catch(function (err) {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading =
false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded =
false;
logger.error('Rewarded video failed to preload: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
});
@@ -235,7 +247,8 @@ namespace gdjs {
errorVariable.setString(err.message || 'Unknown error');
})
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded =
false;
});
};

View File

@@ -4528,7 +4528,8 @@ declare namespace firebase.auth {
* @hidden
*/
class RecaptchaVerifier_Instance
implements firebase.auth.ApplicationVerifier {
implements firebase.auth.ApplicationVerifier
{
constructor(
container: any | string,
parameters?: Object | null,
@@ -9509,9 +9510,9 @@ declare namespace firebase.firestore {
* `exists` property will always be true and `data()` will never return
* 'undefined'.
*/
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<
T
> {
export class QueryDocumentSnapshot<
T = DocumentData,
> extends DocumentSnapshot<T> {
private constructor();
/**

View File

@@ -146,8 +146,8 @@ namespace gdjs {
? 'endAt'
: 'endBefore'
: includeSelf
? 'startAt'
: 'startAfter'
? 'startAt'
: 'startAfter'
](value)
);
};

View File

@@ -10,7 +10,8 @@ namespace gdjs {
* Set the interval between auto-config updates.
*/
export const setAutoUpdateInterval = (interval: integer) => {
firebase.remoteConfig().settings.minimumFetchIntervalMillis = interval;
firebase.remoteConfig().settings.minimumFetchIntervalMillis =
interval;
};
/**

View File

@@ -37,7 +37,7 @@ module.exports = {
extension
.registerProperty('FirebaseConfig')
.setLabel(_('Firebase configuration string'))
.setType('textarea');
.setType('multilinestring');
/* ====== ANALYTICS ====== */
extension
@@ -422,7 +422,7 @@ module.exports = {
_('Is the user email address verified'),
_('Checks if the email address of the user got verified.'),
_('The email of the user is verified'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -440,7 +440,7 @@ module.exports = {
'GetUserEmail',
_('User email address'),
_('Return the user email address.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -457,7 +457,7 @@ module.exports = {
'GetAccountCreationTime',
_('Accounts creation time'),
_('Return the accounts creation time.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -474,7 +474,7 @@ module.exports = {
'GetLastLoginTime',
_('User last login time'),
_('Return the user last login time.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -491,7 +491,7 @@ module.exports = {
'GetUserDisplayName',
_('User display name'),
_('Return the user display name.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -508,7 +508,7 @@ module.exports = {
'GetPhoneNumber',
_('User phone number'),
_('Return the user phone number.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -528,7 +528,7 @@ module.exports = {
'Return the user Unique IDentifier. Use that to link data to an ' +
'user instead of the name or email.'
),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -545,7 +545,7 @@ module.exports = {
'GetTenantID',
_('User tenant ID'),
_('Return the user tenant ID. For advanced usage only.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -562,7 +562,7 @@ module.exports = {
'GetRefreshToken',
_('User refresh token'),
_('Return the user refresh token. For advanced usage only.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -579,7 +579,7 @@ module.exports = {
'GetPhotoURL',
_('Profile picture URL'),
_('Gets an URL to the user profile picture.'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png'
)
.getCodeExtraInformation()
@@ -597,7 +597,7 @@ module.exports = {
_('Send a password reset email'),
_('Send a password reset link per email.'),
_('Send a password reset email'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -619,7 +619,7 @@ module.exports = {
_('Send a verification email'),
_('Send a link per email to verify the user email.'),
_('Send a verification email'),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -638,7 +638,7 @@ module.exports = {
_('Display name'),
_('Sets the user display name.'),
_("Set the user's display name to _PARAM0_"),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -658,7 +658,7 @@ module.exports = {
_('Profile picture'),
_('Change the user profile picture URL to a new one.'),
_("Change the user's profile picture URL to _PARAM0_"),
_('Authentication/User Management'),
_('Authentication User Management'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -684,7 +684,7 @@ module.exports = {
_(
"Change the user's email to _PARAM0_ and store result in _PARAM4_ (send verification email: _PARAM3_)"
),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -725,7 +725,7 @@ module.exports = {
_(
"Change the user's email to _PARAM0_ and store result in _PARAM2_ (send verification email: _PARAM1_)"
),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -764,7 +764,7 @@ module.exports = {
'Change the user password to _PARAM2_ and store result in ' +
'_PARAM4_ (send verification email: _PARAM3_)'
),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -806,7 +806,7 @@ module.exports = {
'Change the user password to _PARAM0_ and store result in ' +
'_PARAM2_ (send verification email: _PARAM1_)'
),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -842,7 +842,7 @@ module.exports = {
'Deletes the user account.'
),
_('Delete the user account and store result in _PARAM2_'),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)
@@ -873,7 +873,7 @@ module.exports = {
'This is the same as "Delete the user account" but reauthenticates via an external provider.'
),
_('Delete the user account and store result in _PARAM0_'),
_('Authentication/User Management/Advanced'),
_('Authentication User Management Advanced'),
'JsPlatform/Extensions/firebase.png',
'JsPlatform/Extensions/firebase.png'
)

View File

@@ -519,9 +519,9 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
// Delete the temporary namespace to not bloat the DB
after(async () =>
(
await firebase.firestore().collection(namespace).get()
).forEach(({ ref }) => ref.delete())
(await firebase.firestore().collection(namespace).get()).forEach(
({ ref }) => ref.delete()
)
);
});

View File

@@ -189,6 +189,7 @@ declare type ObjectsRenderingService = {
objectConfiguration: gd.ObjectConfiguration
) => string;
rgbOrHexToHexNumber: (value: string) => number;
hexNumberToRGBArray: (value: number) => [number, number, number];
registerClearCache: (clearCache: (_: any) => void) => void;
};

View File

@@ -223,9 +223,8 @@ namespace gdjs {
let _leaderboardViewIframeLoading: boolean = false;
let _leaderboardViewIframeLoaded: boolean = false;
let _errorTimeoutId: NodeJS.Timeout | null = null;
let _leaderboardMessageListener:
| ((event: MessageEvent) => void)
| null = null;
let _leaderboardMessageListener: ((event: MessageEvent) => void) | null =
null;
const _loaderContainer: HTMLDivElement = document.createElement('div');
_loaderContainer.style.backgroundColor = '#000000';
@@ -319,9 +318,8 @@ namespace gdjs {
};
let leaderboardEntryCreationUrl = `${baseUrl}/game/${gdjs.projectData.properties.projectUuid}/leaderboard/${leaderboardId}/entry`;
if (authenticatedPlayerData) {
headers[
'Authorization'
] = `player-game-token ${authenticatedPlayerData.playerToken}`;
headers['Authorization'] =
`player-game-token ${authenticatedPlayerData.playerToken}`;
leaderboardEntryCreationUrl += `?playerId=${authenticatedPlayerData.playerId}`;
} else {
// In case playerName is empty, the backend will generate a random name.
@@ -397,10 +395,8 @@ namespace gdjs {
new ScoreSavingState());
try {
const {
closeSaving,
closeSavingWithError,
} = scoreSavingState.startSaving({ playerName, score });
const { closeSaving, closeSavingWithError } =
scoreSavingState.startSaving({ playerName, score });
try {
const leaderboardEntry = await saveScore({
@@ -443,10 +439,8 @@ namespace gdjs {
new ScoreSavingState());
try {
const {
closeSaving,
closeSavingWithError,
} = scoreSavingState.startSaving({ playerId, score });
const { closeSaving, closeSavingWithError } =
scoreSavingState.startSaving({ playerId, score });
try {
const leaderboardEntryId = await saveScore({
@@ -866,9 +860,8 @@ namespace gdjs {
resetLeaderboardDisplayErrorTimeout(runtimeScene);
_leaderboardViewIframe = computeLeaderboardDisplayingIframe(
targetUrl
);
_leaderboardViewIframe =
computeLeaderboardDisplayingIframe(targetUrl);
if (typeof window !== 'undefined') {
_leaderboardMessageListener = (event: MessageEvent) => {
receiveMessageFromLeaderboardView(

View File

@@ -235,7 +235,8 @@ module.exports = {
class RenderedLightObjectInstance extends RenderedInstance {
_radius = 0;
_color = 0;
_radiusGraphics = null;
/** @type {PIXI.Graphics} The circle to show the radius of the light */
_radiusGraphics;
constructor(
project,
@@ -259,7 +260,6 @@ module.exports = {
lightIconSprite.anchor.x = 0.5;
lightIconSprite.anchor.y = 0.5;
// The circle to show the radius of the light.
this._radiusGraphics = new PIXI.Graphics();
this._pixiObject = new PIXI.Container();

View File

@@ -18,9 +18,8 @@ namespace gdjs {
if (!instanceContainer._lightObstaclesManager) {
// Create the shared manager if necessary.
// @ts-ignore
instanceContainer._lightObstaclesManager = new gdjs.LightObstaclesManager(
instanceContainer
);
instanceContainer._lightObstaclesManager =
new gdjs.LightObstaclesManager(instanceContainer);
}
// @ts-ignore
return instanceContainer._lightObstaclesManager;
@@ -70,9 +69,8 @@ namespace gdjs {
searchArea.maxX = x + radius;
// @ts-ignore
searchArea.maxY = y + radius;
const nearbyObstacles: gdjs.BehaviorRBushAABB<
gdjs.LightObstacleRuntimeBehavior
>[] = this._obstacleRBush.search(searchArea);
const nearbyObstacles: gdjs.BehaviorRBushAABB<gdjs.LightObstacleRuntimeBehavior>[] =
this._obstacleRBush.search(searchArea);
result.length = 0;
nearbyObstacles.forEach((nearbyObstacle) =>
result.push(nearbyObstacle.behavior)
@@ -85,9 +83,8 @@ namespace gdjs {
_oldY: float = 0;
_oldWidth: float = 0;
_oldHeight: float = 0;
currentRBushAABB: gdjs.BehaviorRBushAABB<
LightObstacleRuntimeBehavior
> | null = null;
currentRBushAABB: gdjs.BehaviorRBushAABB<LightObstacleRuntimeBehavior> | null =
null;
_manager: any;
_registeredInManager: boolean = false;

View File

@@ -197,11 +197,11 @@ namespace gdjs {
const texture = this._object.getTexture();
this._texture =
texture !== ''
? (this._instanceContainer
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
texture
)
? (
this._instanceContainer
.getGame()
.getImageManager() as gdjs.PixiImageManager
).getPIXITexture(texture)
: null;
}
@@ -466,35 +466,38 @@ namespace gdjs {
const xdiff = flattenVertices[i][0] - this._object.x;
const ydiff = flattenVertices[i][1] - this._object.y;
const angle = Math.atan2(ydiff, xdiff);
const closestVertex = LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle,
obstaclePolygons,
boundingSquareHalfDiag
);
const closestVertex =
LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertex) {
closestVertices.push({ vertex: closestVertex, angle: angle });
}
// TODO: Check whether we need to raycast these two extra rays or not.
const closestVertexOffsetLeft = LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle + 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
const closestVertexOffsetLeft =
LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle + 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertexOffsetLeft) {
closestVertices.push({
vertex: closestVertexOffsetLeft,
angle: angle + 0.0001,
});
}
const closestVertexOffsetRight = LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle - 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
const closestVertexOffsetRight =
LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle - 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertexOffsetRight) {
closestVertices.push({
vertex: closestVertexOffsetRight,

View File

@@ -47,9 +47,8 @@ namespace gdjs {
this._color = gdjs.rgbOrHexToRGBColor(lightObjectData.content.color);
this._debugMode = lightObjectData.content.debugMode;
this._texture = lightObjectData.content.texture;
this._obstaclesManager = gdjs.LightObstaclesManager.getManager(
runtimeScene
);
this._obstaclesManager =
gdjs.LightObstaclesManager.getManager(runtimeScene);
this._renderer = new gdjs.LightRuntimeObjectRenderer(this, runtimeScene);
this._instanceContainer = runtimeScene;

View File

@@ -194,12 +194,11 @@ namespace gdjs {
export namespace evtTools {
export namespace linkedObjects {
gdjs.registerObjectDeletedFromSceneCallback(function (
instanceContainer,
obj
) {
LinksManager.getManager(instanceContainer).removeAllLinksOf(obj);
});
gdjs.registerObjectDeletedFromSceneCallback(
function (instanceContainer, obj) {
LinksManager.getManager(instanceContainer).removeAllLinksOf(obj);
}
);
export const linkObjects = function (
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -245,9 +244,10 @@ namespace gdjs {
if (obj === null) {
return false;
}
const linkedObjectMap = LinksManager.getManager(
instanceContainer
)._getMapOfObjectsLinkedWith(obj);
const linkedObjectMap =
LinksManager.getManager(instanceContainer)._getMapOfObjectsLinkedWith(
obj
);
let pickedSomething = false;
for (const contextObjectName in objectsLists.items) {

View File

@@ -31,6 +31,128 @@ module.exports = {
.addInstructionOrExpressionGroupMetadata(_('Multiplayer'))
.setIcon('JsPlatform/Extensions/multiplayer.svg');
extension
.addAction(
'QuickJoinLobby',
_('Join the next available lobby'),
_(
'Join the next available lobby. The player will join the game instantly if this is possible.'
),
_('Join the next available lobby'),
_('Lobbies'),
'JsPlatform/Extensions/multiplayer.svg',
'JsPlatform/Extensions/multiplayer.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter(
'yesorno',
_('Display loader while searching for a lobby.'),
'',
true
)
.setDefaultValue('yes')
.addParameter(
'yesorno',
_('Display game lobbies if no lobby can be joined directly.'),
'',
true
)
.setDefaultValue('yes')
.setHelpPath('/all-features/multiplayer')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
.setFunctionName('gdjs.multiplayer.authenticateAndQuickJoinLobby');
extension
.addCondition(
'IsSearchingForLobbyToJoin',
_('Is searching for a lobby to join'),
_('Is searching for a lobby to join.'),
_('Is searching for a lobby to join'),
_('Lobbies'),
'JsPlatform/Extensions/multiplayer.svg',
'JsPlatform/Extensions/multiplayer.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/multiplayer')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
.setFunctionName('gdjs.multiplayer.isSearchingForLobbyToJoin');
extension
.addCondition(
'QuickJoinJustFailed',
_('Quick join failed to join a lobby'),
_('Quick join failed to join a lobby.'),
_('Quick join failed to join a lobby'),
_('Lobbies'),
'JsPlatform/Extensions/multiplayer.svg',
'JsPlatform/Extensions/multiplayer.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/multiplayer')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
.setFunctionName('gdjs.multiplayer.hasQuickJoinJustFailed');
extension
.addStrExpression(
'QuickJoinFailureReason',
_('Quick join action failure reason'),
_(
"Returns the reason why the Quick join action failed. It can either be 'FULL' if all lobbies were occupied, 'NOT_ENOUGH_PLAYERS' if the lobby's configuration requires more than 1 player to start the game and no other players were available. It can also take the value 'UNKNOWN'."
),
_('Lobbies'),
'JsPlatform/Extensions/multiplayer.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
.setFunctionName('gdjs.multiplayer.getQuickJoinFailureReason');
extension
.addAction(
'OpenGameLobbies',

View File

@@ -149,18 +149,16 @@ namespace gdjs {
let lastSceneSyncTimestamp = 0;
let lastSentSceneSyncData: LayoutNetworkSyncData | null = null;
let numberOfForcedSceneUpdates = 0;
let lastReceivedSceneSyncDataUpdates = new SavedSyncDataUpdates<
LayoutNetworkSyncData
>();
let lastReceivedSceneSyncDataUpdates =
new SavedSyncDataUpdates<LayoutNetworkSyncData>();
// The number of times per second the game data should be synchronized.
const gameSyncDataSyncRate = 1;
let lastGameSyncTimestamp = 0;
let lastSentGameSyncData: GameNetworkSyncData | null = null;
let numberOfForcedGameUpdates = 0;
let lastReceivedGameSyncDataUpdates = new SavedSyncDataUpdates<
GameNetworkSyncData
>();
let lastReceivedGameSyncDataUpdates =
new SavedSyncDataUpdates<GameNetworkSyncData>();
// Send heartbeat messages from host to players, ensuring their connection is still alive,
// measure the ping, and send other useful info.
@@ -261,9 +259,8 @@ namespace gdjs {
_lastClockReceivedByInstanceByScene[sceneNetworkId] = {};
}
_lastClockReceivedByInstanceByScene[sceneNetworkId][
instanceNetworkId
] = clock;
_lastClockReceivedByInstanceByScene[sceneNetworkId][instanceNetworkId] =
clock;
};
/**
@@ -407,7 +404,8 @@ namespace gdjs {
};
const changeInstanceOwnerMessageNamePrefix = '#changeInstanceOwner';
const changeInstanceOwnerMessageNameRegex = /#changeInstanceOwner#owner_(\d+)#object_(.+)#instance_(.+)/;
const changeInstanceOwnerMessageNameRegex =
/#changeInstanceOwner#owner_(\d+)#object_(.+)#instance_(.+)/;
const createChangeInstanceOwnerMessage = ({
objectOwner,
objectName,
@@ -446,15 +444,15 @@ namespace gdjs {
};
};
const instanceOwnerChangedMessageNamePrefix = '#instanceOwnerChanged';
const instanceOwnerChangedMessageNameRegex = /#instanceOwnerChanged#owner_(\d+)#object_(.+)#instance_(.+)/;
const createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage = (
messageName: string
): string => {
return messageName.replace(
changeInstanceOwnerMessageNamePrefix,
instanceOwnerChangedMessageNamePrefix
);
};
const instanceOwnerChangedMessageNameRegex =
/#instanceOwnerChanged#owner_(\d+)#object_(.+)#instance_(.+)/;
const createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage =
(messageName: string): string => {
return messageName.replace(
changeInstanceOwnerMessageNamePrefix,
instanceOwnerChangedMessageNamePrefix
);
};
const handleChangeInstanceOwnerMessagesReceived = (
runtimeScene: gdjs.RuntimeScene
) => {
@@ -524,7 +522,8 @@ namespace gdjs {
return;
}
const currentPlayerObjectOwnership = behavior.getPlayerObjectOwnership();
const currentPlayerObjectOwnership =
behavior.getPlayerObjectOwnership();
// Change is coherent if:
const ownershipChangeIsCoherent =
// the object is changing ownership from the same owner the host knew about,
@@ -550,9 +549,10 @@ namespace gdjs {
);
behavior.playerNumber = newOwner;
const instanceOwnerChangedMessageName = createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(
messageName
);
const instanceOwnerChangedMessageName =
createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(
messageName
);
debugLogger.info(
`Sending acknowledgment of ownership change of object ${objectName} from ${previousOwner} to ${newOwner} with instance network ID ${instanceNetworkId} to ${messageSender}.`
@@ -594,7 +594,8 @@ namespace gdjs {
};
const updateInstanceMessageNamePrefix = '#updateInstance';
const updateInstanceMessageNameRegex = /#updateInstance#owner_(\d+)#object_(.+)#instance_(.+)#scene_(.+)/;
const updateInstanceMessageNameRegex =
/#updateInstance#owner_(\d+)#object_(.+)#instance_(.+)#scene_(.+)/;
const createUpdateInstanceMessage = ({
objectOwner,
objectName,
@@ -757,7 +758,8 @@ namespace gdjs {
};
const changeVariableOwnerMessageNamePrefix = '#changeVariableOwner';
const changeVariableOwnerMessageNameRegex = /#changeVariableOwner#owner_(\d+)#variable_(.+)/;
const changeVariableOwnerMessageNameRegex =
/#changeVariableOwner#owner_(\d+)#variable_(.+)/;
const createChangeVariableOwnerMessage = ({
variableOwner,
variableNetworkId,
@@ -782,15 +784,15 @@ namespace gdjs {
};
};
const variableOwnerChangedMessageNamePrefix = '#variableOwnerChanged';
const variableOwnerChangedMessageNameRegex = /#variableOwnerChanged#owner_(\d+)#variable_(.+)/;
const createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage = (
messageName: string
): string => {
return messageName.replace(
changeVariableOwnerMessageNamePrefix,
variableOwnerChangedMessageNamePrefix
);
};
const variableOwnerChangedMessageNameRegex =
/#variableOwnerChanged#owner_(\d+)#variable_(.+)/;
const createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage =
(messageName: string): string => {
return messageName.replace(
changeVariableOwnerMessageNamePrefix,
variableOwnerChangedMessageNamePrefix
);
};
const handleChangeVariableOwnerMessagesReceived = (
runtimeScene: gdjs.RuntimeScene
) => {
@@ -885,9 +887,10 @@ namespace gdjs {
);
variable.setPlayerOwnership(newOwner);
const variableOwnerChangedMessageName = createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage(
messageName
);
const variableOwnerChangedMessageName =
createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage(
messageName
);
debugLogger.info(
`Sending acknowledgment of ownership change of variable with ID ${variableNetworkId} from ${previousOwner} to ${newOwner} to ${messageSender}.`
@@ -1081,9 +1084,10 @@ namespace gdjs {
changeInstanceOwnerMessageNamePrefix
)
) {
const matches = changeInstanceOwnerMessageNameRegex.exec(
originalMessageName
);
const matches =
changeInstanceOwnerMessageNameRegex.exec(
originalMessageName
);
if (!matches) {
// This should not happen, if it does, remove the acknowledgment and return.
delete expectedMessageAcknowledgements[
@@ -1146,9 +1150,10 @@ namespace gdjs {
changeVariableOwnerMessageNamePrefix
)
) {
const matches = changeVariableOwnerMessageNameRegex.exec(
originalMessageName
);
const matches =
changeVariableOwnerMessageNameRegex.exec(
originalMessageName
);
if (!matches) {
// This should not happen, if it does, remove the acknowledgment and return.
delete expectedMessageAcknowledgements[
@@ -1229,7 +1234,8 @@ namespace gdjs {
};
const destroyInstanceMessageNamePrefix = '#destroyInstance';
const destroyInstanceMessageNameRegex = /#destroyInstance#owner_(\d+)#object_(.+)#instance_(.+)#scene_(.+)/;
const destroyInstanceMessageNameRegex =
/#destroyInstance#owner_(\d+)#object_(.+)#instance_(.+)#scene_(.+)/;
const createDestroyInstanceMessage = ({
objectOwner,
objectName,
@@ -1250,7 +1256,8 @@ namespace gdjs {
};
};
const instanceDestroyedMessageNamePrefix = '#instanceDestroyed';
const instanceDestroyedMessageNameRegex = /#instanceDestroyed#owner_(\d+)#object_(.+)#instance_(.+)/;
const instanceDestroyedMessageNameRegex =
/#instanceDestroyed#owner_(\d+)#object_(.+)#instance_(.+)/;
const createInstanceDestroyedMessageNameFromDestroyInstanceMessage = (
messageName: string
): string => {
@@ -1315,9 +1322,10 @@ namespace gdjs {
instanceNetworkId,
});
const instanceDestroyedMessageName = createInstanceDestroyedMessageNameFromDestroyInstanceMessage(
messageName
);
const instanceDestroyedMessageName =
createInstanceDestroyedMessageNameFromDestroyInstanceMessage(
messageName
);
if (!instance) {
debugLogger.info(
@@ -1420,9 +1428,8 @@ namespace gdjs {
userMessageData,
senderPlayerNumber: currentPlayerNumber,
});
const acknowledgmentMessageName = createAcknowledgeCustomMessageNameFromCustomMessage(
messageName
);
const acknowledgmentMessageName =
createAcknowledgeCustomMessageNameFromCustomMessage(messageName);
addExpectedMessageAcknowledgement({
originalMessageName: messageName,
originalData: messageData,
@@ -1441,9 +1448,8 @@ namespace gdjs {
// If we are the host, we can consider this messaged as received
// and add it to the list of custom messages to process on top of the messages received.
if (gdjs.multiplayer.isCurrentPlayerHost()) {
const messagesList = gdjs.multiplayerPeerJsHelper.getOrCreateMessagesList(
messageName
);
const messagesList =
gdjs.multiplayerPeerJsHelper.getOrCreateMessagesList(messageName);
messagesList.pushMessage(
messageData,
gdjs.multiplayerPeerJsHelper.getCurrentId()
@@ -1467,9 +1473,8 @@ namespace gdjs {
};
const hasCustomMessageBeenReceived = (userMessageName: string) => {
const customMessageName = getCustomMessageNameFromUserMessageName(
userMessageName
);
const customMessageName =
getCustomMessageNameFromUserMessageName(userMessageName);
const p2pMessagesMap = gdjs.multiplayerPeerJsHelper.getAllMessagesMap();
const messagesList = p2pMessagesMap.get(customMessageName);
if (!messagesList) return; // No message received.
@@ -1499,9 +1504,8 @@ namespace gdjs {
};
const getCustomMessageData = (userMessageName: string) => {
const customMessageName = getCustomMessageNameFromUserMessageName(
userMessageName
);
const customMessageName =
getCustomMessageNameFromUserMessageName(userMessageName);
const p2pMessagesMap = gdjs.multiplayerPeerJsHelper.getAllMessagesMap();
const messagesList = p2pMessagesMap.get(customMessageName);
if (!messagesList) return; // No message received.
@@ -1531,9 +1535,8 @@ namespace gdjs {
};
const getCustomMessageSender = (userMessageName: string): number => {
const customMessageName = getCustomMessageNameFromUserMessageName(
userMessageName
);
const customMessageName =
getCustomMessageNameFromUserMessageName(userMessageName);
const p2pMessagesMap = gdjs.multiplayerPeerJsHelper.getAllMessagesMap();
const messagesList = p2pMessagesMap.get(customMessageName);
if (!messagesList) return 0;
@@ -1593,9 +1596,8 @@ namespace gdjs {
return;
}
const acknowledgmentMessageName = createAcknowledgeCustomMessageNameFromCustomMessage(
messageName
);
const acknowledgmentMessageName =
createAcknowledgeCustomMessageNameFromCustomMessage(messageName);
debugLogger.info(
`Sending acknowledgment of custom message ${messageName} to ${messageSender}.`
);
@@ -1681,9 +1683,8 @@ namespace gdjs {
return;
}
const isSceneSyncDataDifferent = isSceneDifferentFromLastSync(
sceneNetworkSyncData
);
const isSceneSyncDataDifferent =
isSceneDifferentFromLastSync(sceneNetworkSyncData);
const shouldSyncScene =
!hasSceneBeenSyncedRecently() ||
isSceneSyncDataDifferent ||
@@ -1845,9 +1846,8 @@ namespace gdjs {
return;
}
const isGameSyncDataDifferent = isGameDifferentFromLastSync(
gameNetworkSyncData
);
const isGameSyncDataDifferent =
isGameDifferentFromLastSync(gameNetworkSyncData);
const shouldSyncGame =
!hasGameBeenSyncedRecently() ||
isGameSyncDataDifferent ||
@@ -1945,12 +1945,12 @@ namespace gdjs {
};
const heartbeatMessageNamePrefix = '#heartbeat';
const heartbeastMessageRegex = /#heartbeat#(.+)/;
const heartbeatMessageRegex = /#heartbeat#(.+)/;
const createHeartbeatMessage = (): {
messageName: string;
messageData: any;
} => {
// If we create the heartbeat meassage, we are the host,
// If we create the heartbeat message, we are the host.
// Ensure our player number is correctly set when the first heartbeat is sent.
_playersInfo[gdjs.multiplayer.getCurrentPlayerNumber()] = {
ping: 0, // we are the host, so we don't need to compute the ping.
@@ -2028,7 +2028,7 @@ namespace gdjs {
messages.forEach((message) => {
const messageData = message.getData();
const messageSender = message.getSender();
const matches = heartbeastMessageRegex.exec(messageName);
const matches = heartbeatMessageRegex.exec(messageName);
if (!matches) {
return;
}
@@ -2039,10 +2039,11 @@ namespace gdjs {
// If we are not the host, save what the host told us about the other players info
// and respond with a heartbeat immediately, informing the host of our playerId and username.
if (!gdjs.multiplayer.isCurrentPlayerHost()) {
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
const currentlyKnownPlayerNumbers = Object.keys(
_playersInfo
).map((playerNumber) => parseInt(playerNumber, 10));
const currentPlayerNumber =
gdjs.multiplayer.getCurrentPlayerNumber();
const currentlyKnownPlayerNumbers = Object.keys(_playersInfo).map(
(playerNumber) => parseInt(playerNumber, 10)
);
const receivedPlayerNumbers = Object.keys(
messageData.playersInfo
).map((playerNumber) => parseInt(playerNumber, 10));
@@ -2061,18 +2062,19 @@ namespace gdjs {
);
_playerNumbersWhoJustJoined.push(...newPlayerNumbers);
// Or players who have disconnected.
const playerNumbersWhoHaveDisconnected = currentlyKnownPlayerNumbers.filter(
(playerNumber) => !receivedPlayerNumbers.includes(playerNumber)
);
const playerNumbersWhoHaveDisconnected =
currentlyKnownPlayerNumbers.filter(
(playerNumber) =>
!receivedPlayerNumbers.includes(playerNumber)
);
_playerNumbersWhoJustLeft.push(
...playerNumbersWhoHaveDisconnected
);
for (const playerNumber of playerNumbersWhoHaveDisconnected) {
// Temporarily save the username in another variable to be used for the notification,
// as we're deleting its playerInfo just after.
_temporaryPlayerNumberToUsername[
playerNumber
] = getPlayerUsername(playerNumber);
_temporaryPlayerNumberToUsername[playerNumber] =
getPlayerUsername(playerNumber);
}
}
@@ -2188,14 +2190,14 @@ namespace gdjs {
_playerNumbersWhoJustLeft.push(playerNumber);
// Temporarily save the username in another variable to be used for the notification,
// as we're deleting its playerInfo just after.
_temporaryPlayerNumberToUsername[playerNumber] = getPlayerUsername(
playerNumber
);
_temporaryPlayerNumberToUsername[playerNumber] =
getPlayerUsername(playerNumber);
clearPlayerTempData(playerNumber);
// If Host has disconnected, either switch host or stop the game.
if (peerId && peerId === gdjs.multiplayer.hostPeerId) {
const shouldEndLobbyGame = gdjs.multiplayer.shouldEndLobbyWhenHostLeaves();
const shouldEndLobbyGame =
gdjs.multiplayer.shouldEndLobbyWhenHostLeaves();
if (shouldEndLobbyGame) {
logger.info('Host has disconnected, ending the game.');
@@ -2243,7 +2245,8 @@ namespace gdjs {
peerId: string;
}[] = [];
const justDisconnectedPeers = gdjs.multiplayerPeerJsHelper.getJustDisconnectedPeers();
const justDisconnectedPeers =
gdjs.multiplayerPeerJsHelper.getJustDisconnectedPeers();
if (justDisconnectedPeers.length) {
for (const disconnectedPeer of justDisconnectedPeers) {
const disconnectedPlayerNumber =
@@ -2273,7 +2276,8 @@ namespace gdjs {
behavior &&
behavior.getPlayerObjectOwnership() === playerNumber
) {
const actionOnPlayerDisconnect = behavior.getActionOnPlayerDisconnect();
const actionOnPlayerDisconnect =
behavior.getActionOnPlayerDisconnect();
if (actionOnPlayerDisconnect === 'DestroyObject') {
// No need to remove the ownership, as the destroy message will be sent to all players.
instance.deleteFromScene(runtimeScene);

View File

@@ -78,9 +78,10 @@ namespace gdjs {
return;
}
const variableName = currentSceneVariables.getVariableNameInContainerByLoopingThroughAllVariables(
variable
);
const variableName =
currentSceneVariables.getVariableNameInContainerByLoopingThroughAllVariables(
variable
);
if (!variableName) {
logger.error('Variable is being synchronized but has no name.');
@@ -98,9 +99,10 @@ namespace gdjs {
// TODO: prevent returning a networkID if this is not a root variable.
const variableName = runtimeGameVariables.getVariableNameInContainerByLoopingThroughAllVariables(
variable
);
const variableName =
runtimeGameVariables.getVariableNameInContainerByLoopingThroughAllVariables(
variable
);
if (!variableName) {
logger.error('Variable is being synchronized but has no name.');
return;
@@ -171,9 +173,8 @@ namespace gdjs {
return;
}
const { type: variableType } = getVariableTypeAndNameFromNetworkId(
variableNetworkId
);
const { type: variableType } =
getVariableTypeAndNameFromNetworkId(variableNetworkId);
debugLogger.info(
`Adding variable to be synchronized: ${variableNetworkId} (type: ${variableType}) from owner ${previousVariablePlayerNumber} to ${newVariablePlayerNumber}.`
@@ -220,21 +221,20 @@ namespace gdjs {
for (const variableNetworkId in variableOwnershipChangesToSyncAtEndOfFrame) {
const variableData =
variableOwnershipChangesToSyncAtEndOfFrame[variableNetworkId];
const {
messageName,
messageData,
} = gdjs.multiplayerMessageManager.createChangeVariableOwnerMessage({
variableNetworkId,
variableOwner: variableData.previousVariableOwner,
newVariableOwner: variableData.newVariableOwner,
});
const { messageName, messageData } =
gdjs.multiplayerMessageManager.createChangeVariableOwnerMessage({
variableNetworkId,
variableOwner: variableData.previousVariableOwner,
newVariableOwner: variableData.newVariableOwner,
});
// Before sending the change owner message, if we are becoming the new owner,
// we want to ensure this message is acknowledged, by everyone we're connected to.
if (variableData.newVariableOwner === currentPlayerNumber) {
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
const variableOwnerChangedMessageName = gdjs.multiplayerMessageManager.createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage(
messageName
);
const variableOwnerChangedMessageName =
gdjs.multiplayerMessageManager.createVariableOwnerChangedMessageNameFromChangeVariableOwnerMessage(
messageName
);
gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({
originalMessageName: messageName,
originalData: messageData,

View File

@@ -1,6 +1,7 @@
namespace gdjs {
const logger = new gdjs.Logger('Multiplayer');
export namespace multiplayerComponents {
const loaderContainerId = 'loader-container';
const lobbiesRootContainerId = 'lobbies-root-container';
const lobbiesFrameContainerId = 'lobbies-frame-container';
const lobbiesCloseContainerId = 'lobbies-close-container';
@@ -117,6 +118,66 @@ namespace gdjs {
return lobbiesIframe;
};
export const displayLoader = (
runtimeScene: gdjs.RuntimeScene,
yesOrNo: boolean
) => {
const domElementContainer = getDomElementContainer(runtimeScene);
if (!domElementContainer) {
return;
}
if (yesOrNo) {
const loaderContainer: HTMLDivElement = document.createElement('div');
loaderContainer.id = loaderContainerId;
loaderContainer.style.backgroundColor = '#000000';
loaderContainer.style.display = 'flex';
loaderContainer.style.height = '100%';
loaderContainer.style.width = '100%';
loaderContainer.style.justifyContent = 'center';
loaderContainer.style.alignItems = 'center';
loaderContainer.style.position = 'relative';
loaderContainer.style.zIndex = '2';
const loader = document.createElement('img');
loader.setAttribute('width', '50px');
loader.setAttribute(
'src',
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iNCI+PC9jaXJjbGU+CjxwYXRoIG9wYWNpdHk9JzAuNzUnIGZpbGw9IiNGRkZGRkYiIGQ9Ik00IDEyYTggOCAwIDAxOC04VjBDNS4zNzMgMCAwIDUuMzczIDAgMTJoNHptMiA1LjI5MUE3Ljk2MiA3Ljk2MiAwIDAxNCAxMkgwYzAgMy4wNDIgMS4xMzUgNS44MjQgMyA3LjkzOGwzLTIuNjQ3eiI+PC9wYXRoPgo8L3N2Zz4='
);
try {
loader.animate(
[{ transform: 'rotate(0deg)' }, { transform: 'rotate(359deg)' }],
{
duration: 3000,
iterations: Infinity,
}
);
} catch {
logger.warn('Animation not supported, loader will be fixed.');
}
loaderContainer.appendChild(loader);
if (
domElementContainer.children &&
domElementContainer.children.length > 0
) {
domElementContainer.insertBefore(
loaderContainer,
domElementContainer.children[0]
);
} else {
domElementContainer.appendChild(loaderContainer);
}
} else {
const loaderContainer = domElementContainer.querySelector(
`#${loaderContainerId}`
);
if (!loaderContainer) return;
try {
domElementContainer.removeChild(loaderContainer);
} catch {}
}
};
/**
* Creates a DOM element that will contain the loader or a message if the game is not registered,
* and adds it to the dom container.
@@ -562,8 +623,8 @@ namespace gdjs {
type === 'success'
? '#0E062D'
: type === 'warning'
? '#FFA500'
: '#FF0000';
? '#FFA500'
: '#FF0000';
// Space the notifications vertically, based on how many there are.
divContainer.style.top = `${12 + notificationContainerIds.length * 32}px`;
divContainer.style.right = '16px';

View File

@@ -130,7 +130,8 @@ namespace gdjs {
}
private _hasObjectBeenSyncedWithinMaxRate() {
const objectMaxSyncRate = gdjs.multiplayer.getObjectsSynchronizationRate();
const objectMaxSyncRate =
gdjs.multiplayer.getObjectsSynchronizationRate();
return (
getTimeNow() - this._lastObjectSyncTimestamp < 1000 / objectMaxSyncRate
);
@@ -287,8 +288,8 @@ namespace gdjs {
// )}`
// );
const areBasicObjectNetworkSyncDataDifferent = this._isBasicObjectNetworkSyncDataDifferentFromLastSync(
{
const areBasicObjectNetworkSyncDataDifferent =
this._isBasicObjectNetworkSyncDataDifferentFromLastSync({
x: objectNetworkSyncData.x,
y: objectNetworkSyncData.y,
z: objectNetworkSyncData.z,
@@ -299,8 +300,7 @@ namespace gdjs {
if: objectNetworkSyncData.if,
pfx: objectNetworkSyncData.pfx,
pfy: objectNetworkSyncData.pfy,
}
);
});
const shouldSyncObjectBasicInfo =
!this._hasObjectBasicInfoBeenSyncedRecently() ||
areBasicObjectNetworkSyncDataDifferent ||
@@ -348,16 +348,14 @@ namespace gdjs {
return;
}
const {
messageName: updateMessageName,
messageData: updateMessageData,
} = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData,
sceneNetworkId,
});
const { messageName: updateMessageName, messageData: updateMessageData } =
gdjs.multiplayerMessageManager.createUpdateInstanceMessage({
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData,
sceneNetworkId,
});
this._sendDataToPeersWithIncreasedClock(
updateMessageName,
updateMessageData
@@ -440,16 +438,14 @@ namespace gdjs {
}
// Ensure we send a final update before the object is destroyed, if it had a networkId.
const {
messageName: updateMessageName,
messageData: updateMessageData,
} = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData: this.owner.getNetworkSyncData(),
sceneNetworkId,
});
const { messageName: updateMessageName, messageData: updateMessageData } =
gdjs.multiplayerMessageManager.createUpdateInstanceMessage({
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData: this.owner.getNetworkSyncData(),
sceneNetworkId,
});
this._sendDataToPeersWithIncreasedClock(
updateMessageName,
updateMessageData
@@ -470,9 +466,10 @@ namespace gdjs {
instanceNetworkId,
sceneNetworkId,
});
const destroyedMessageName = gdjs.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(
destroyMessageName
);
const destroyedMessageName =
gdjs.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(
destroyMessageName
);
gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({
originalMessageName: destroyMessageName,
originalData: {
@@ -552,18 +549,16 @@ namespace gdjs {
// When changing the ownership of an object with a networkId, we send a message to the host to ensure it is aware of the change,
// and can either accept it and broadcast it to other players, or reject it and do nothing with it.
// We expect an acknowledgment from the host, if not, we will retry and eventually revert the ownership.
const {
messageName,
messageData,
} = gdjs.multiplayerMessageManager.createChangeInstanceOwnerMessage({
objectOwner: previousObjectPlayerNumber,
objectName,
instanceNetworkId,
newObjectOwner: newObjectPlayerNumber,
instanceX: this.owner.getX(),
instanceY: this.owner.getY(),
sceneNetworkId,
});
const { messageName, messageData } =
gdjs.multiplayerMessageManager.createChangeInstanceOwnerMessage({
objectOwner: previousObjectPlayerNumber,
objectName,
instanceNetworkId,
newObjectOwner: newObjectPlayerNumber,
instanceX: this.owner.getX(),
instanceY: this.owner.getY(),
sceneNetworkId,
});
// Before sending the changeOwner message, if we are becoming the new owner,
// we want to ensure this message is acknowledged, by everyone we're connected to.
// If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.
@@ -571,9 +566,10 @@ namespace gdjs {
// In both cases, this represents the list of peers the current user is connected to.
if (newObjectPlayerNumber === currentPlayerNumber) {
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
const changeOwnerAcknowledgedMessageName = gdjs.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(
messageName
);
const changeOwnerAcknowledgedMessageName =
gdjs.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(
messageName
);
gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({
originalMessageName: messageName,
originalData: {
@@ -583,7 +579,8 @@ namespace gdjs {
expectedMessageName: changeOwnerAcknowledgedMessageName,
otherPeerIds,
// If we are not the host, we should revert the ownership if the host does not acknowledge the change.
shouldCancelMessageIfTimesOut: !gdjs.multiplayer.isCurrentPlayerHost(),
shouldCancelMessageIfTimesOut:
!gdjs.multiplayer.isCurrentPlayerHost(),
});
}

View File

@@ -17,6 +17,17 @@ namespace gdjs {
}[];
};
type Lobby = {
id: string;
status: 'waiting' | 'starting' | 'playing' | 'migrating' | 'migrated';
};
type QuickJoinLobbyResponse =
| { status: 'join-game'; lobby: Lobby }
| { status: 'join-lobby'; lobby: Lobby }
| { status: 'not-enough-players' }
| { status: 'full' };
const getTimeNow =
window.performance && typeof window.performance.now === 'function'
? window.performance.now.bind(window.performance)
@@ -90,6 +101,12 @@ namespace gdjs {
let _hasLobbyGameJustStarted = false;
export let _isLobbyGameRunning = false;
let _hasLobbyGameJustEnded = false;
let _quickJoinLobbyJustFailed = false;
let _quickJoinLobbyFailureReason:
| 'FULL'
| 'NOT_ENOUGH_PLAYERS'
| 'UNKNOWN'
| null = null;
let _lobbyId: string | null = null;
let _connectionId: string | null = null;
@@ -98,6 +115,13 @@ namespace gdjs {
let _lobbyChangeHostRequestInitiatedAt: number | null = null;
let _isChangingHost = false;
let _lobbyNewHostPickedAt: number | null = null;
let _actionAfterJoiningLobby:
| 'OPEN_LOBBY_PAGE'
| 'JOIN_GAME'
| 'START_GAME'
| null = null;
let _isQuickJoiningOrStartingAGame = false;
let _lastQuickJoinRequestDoneAt: number | null = null;
// Communication methods.
let _lobbiesMessageCallback: ((event: MessageEvent) => void) | null = null;
@@ -206,6 +230,7 @@ namespace gdjs {
_hasLobbyGameJustStarted = false;
_hasLobbyGameJustEnded = false;
_quickJoinLobbyJustFailed = false;
});
const getLobbiesWindowUrl = ({
@@ -253,6 +278,11 @@ namespace gdjs {
if (playerToken) {
url.searchParams.set('playerToken', playerToken);
}
const platformInfo = runtimeGame.getPlatformInfo();
url.searchParams.set(
'scm',
platformInfo.supportedCompressionMethods.join(',')
);
// Increment this value when a new feature is introduced so we can
// adapt the interface of the lobbies.
url.searchParams.set('multiplayerVersion', '2');
@@ -362,7 +392,8 @@ namespace gdjs {
};
const handleLeavingPlayer = (runtimeScene: gdjs.RuntimeScene) => {
const lastestPlayerWhoJustLeft = gdjs.multiplayerMessageManager.getLatestPlayerWhoJustLeft();
const lastestPlayerWhoJustLeft =
gdjs.multiplayerMessageManager.getLatestPlayerWhoJustLeft();
if (lastestPlayerWhoJustLeft) {
const playerUsername = getPlayerUsername(lastestPlayerWhoJustLeft);
gdjs.multiplayerComponents.displayPlayerLeftNotification(
@@ -386,7 +417,8 @@ namespace gdjs {
};
const handleJoiningPlayer = (runtimeScene: gdjs.RuntimeScene) => {
const lastestPlayerWhoJustJoined = gdjs.multiplayerMessageManager.getLatestPlayerWhoJustJoined();
const lastestPlayerWhoJustJoined =
gdjs.multiplayerMessageManager.getLatestPlayerWhoJustJoined();
if (lastestPlayerWhoJustJoined) {
const playerUsername = getPlayerUsername(lastestPlayerWhoJustJoined);
gdjs.multiplayerComponents.displayPlayerJoinedNotification(
@@ -592,8 +624,16 @@ namespace gdjs {
logger.error('Malformed message received');
return;
}
handlePeerIdEvent({ peerId, compressionMethod });
const retryData = { times: 2, delayInMs: 500 };
try {
gdjs.evtTools.network.retryIfFailed(retryData, async () => {
handlePeerIdEvent({ peerId, compressionMethod });
});
} catch (error) {
logger.error(
`Handling peerId message from websocket failed (after {${retryData.times}} times with a delay of ${retryData.delayInMs}ms). Not trying anymore.`
);
}
break;
}
}
@@ -616,9 +656,8 @@ namespace gdjs {
return;
}
const lobbiesIframe = gdjs.multiplayerComponents.getLobbiesIframe(
runtimeScene
);
const lobbiesIframe =
gdjs.multiplayerComponents.getLobbiesIframe(runtimeScene);
if (!lobbiesIframe || !lobbiesIframe.contentWindow) {
return;
@@ -634,6 +673,17 @@ namespace gdjs {
};
};
const onPeerUnavailable = (runtimeScene: gdjs.RuntimeScene) => {
gdjs.multiplayerComponents.displayConnectionErrorNotification(
runtimeScene
);
handleLeaveLobbyEvent();
_actionAfterJoiningLobby = null;
_quickJoinLobbyFailureReason = null;
if (_isQuickJoiningOrStartingAGame)
onLobbyQuickJoinFinished(runtimeScene);
};
const handleConnectionIdReceived = function ({
runtimeScene,
connectionId,
@@ -679,10 +729,13 @@ namespace gdjs {
brokerServerConfig.port,
brokerServerConfig.path,
brokerServerConfig.key,
brokerServerConfig.secure
brokerServerConfig.secure,
{ onPeerUnavailable: () => onPeerUnavailable(runtimeScene) }
);
} else {
gdjs.multiplayerPeerJsHelper.useDefaultBrokerServer();
gdjs.multiplayerPeerJsHelper.useDefaultBrokerServer({
onPeerUnavailable: () => onPeerUnavailable(runtimeScene),
});
}
_connectionId = connectionId;
@@ -690,10 +743,31 @@ namespace gdjs {
// We save the lobbyId here as this is the moment when the player is really connected to the lobby.
_lobbyId = lobbyId;
if (_actionAfterJoiningLobby === 'OPEN_LOBBY_PAGE') {
openLobbiesWindow(runtimeScene);
onLobbyQuickJoinFinished(runtimeScene);
return;
} else if (_actionAfterJoiningLobby === 'JOIN_GAME') {
handleJoinGameMessage();
return;
} else if (_actionAfterJoiningLobby === 'START_GAME') {
const retryData = { times: 2, delayInMs: 500 };
try {
gdjs.evtTools.network.retryIfFailed(retryData, async () => {
sendPeerId();
handleStartGameMessage();
});
} catch (error) {
logger.error(
`Sending of peerId message from websocket failed (after {${retryData.times}} times with a delay of ${retryData.delayInMs}ms). Not trying anymore.`
);
}
return;
}
// Then we inform the lobbies window that the player has joined.
const lobbiesIframe = gdjs.multiplayerComponents.getLobbiesIframe(
runtimeScene
);
const lobbiesIframe =
gdjs.multiplayerComponents.getLobbiesIframe(runtimeScene);
if (!lobbiesIframe || !lobbiesIframe.contentWindow) {
logger.error(
@@ -742,9 +816,8 @@ namespace gdjs {
// If the player is in the lobby, tell the lobbies window that the lobby has been updated,
// as well as the player position.
const lobbiesIframe = gdjs.multiplayerComponents.getLobbiesIframe(
runtimeScene
);
const lobbiesIframe =
gdjs.multiplayerComponents.getLobbiesIframe(runtimeScene);
if (!lobbiesIframe || !lobbiesIframe.contentWindow) {
return;
@@ -776,9 +849,8 @@ namespace gdjs {
}
// Just pass along the message to the iframe so that it can display the countdown.
const lobbiesIframe = gdjs.multiplayerComponents.getLobbiesIframe(
runtimeScene
);
const lobbiesIframe =
gdjs.multiplayerComponents.getLobbiesIframe(runtimeScene);
if (!lobbiesIframe || !lobbiesIframe.contentWindow) {
logger.info('The lobbies iframe is not opened, not sending message.');
@@ -818,6 +890,8 @@ namespace gdjs {
}),
dev: isUsingGDevelopDevelopmentEnvironment,
});
// TODO: if 404, there's chance that it means the lobby is now closed. Display a message
// to the player?
} catch (error) {
logger.error('Error while sending heartbeat, retrying:', error);
try {
@@ -873,6 +947,8 @@ namespace gdjs {
logger.info('Lobby game has started.');
// In case we're joining an existing lobby, read the saved messages to catch-up with the game state.
gdjs.multiplayerMessageManager.handleSavedUpdateMessages(runtimeScene);
if (_isQuickJoiningOrStartingAGame)
onLobbyQuickJoinFinished(runtimeScene);
_isReadyToSendOrReceiveGameUpdateMessages = true;
_hasLobbyGameJustStarted = true;
_isLobbyGameRunning = true;
@@ -926,7 +1002,7 @@ namespace gdjs {
logger.error(
'No peerId found, the player does not seem connected to the broker server.'
);
return;
throw new Error('Missing player peerId.');
}
if (currentPeerId === peerId) {
@@ -990,10 +1066,15 @@ namespace gdjs {
const handleJoinGameMessage = function () {
if (!_websocket) {
logger.error(
'No connection to send the start countdown message. Are you connected to a lobby?'
'No connection to send the join game message. Are you connected to a lobby?'
);
return;
}
// TODO: When the message is sent, it is expected to then receive a "peerId" message
// from the websocket. This "peerId" message might not be sent for different reasons.
// Should there be a security that checks if the "peerId" message has been received
// in the next 10s or something more global that checks the lobby status after the player
// has committed to open a connection with it?
_websocket.send(
JSON.stringify({
@@ -1199,13 +1280,15 @@ namespace gdjs {
);
// First look for players who left during the migration.
const playerNumbersConnectedBeforeMigration = gdjs.multiplayerMessageManager
.getConnectedPlayers()
.map((player) => player.playerNumber);
const playerNumbersWhoLeftDuringMigration = playerNumbersConnectedBeforeMigration.filter(
(playerNumberBeforeMigration) =>
!expectedNewOtherPlayerNumbers.includes(playerNumberBeforeMigration)
);
const playerNumbersConnectedBeforeMigration =
gdjs.multiplayerMessageManager
.getConnectedPlayers()
.map((player) => player.playerNumber);
const playerNumbersWhoLeftDuringMigration =
playerNumbersConnectedBeforeMigration.filter(
(playerNumberBeforeMigration) =>
!expectedNewOtherPlayerNumbers.includes(playerNumberBeforeMigration)
);
playerNumbersWhoLeftDuringMigration.map((playerNumberWhoLeft) => {
logger.info(
`Player ${playerNumberWhoLeft} left during the host migration. Marking as disconnected.`
@@ -1217,13 +1300,14 @@ namespace gdjs {
});
// Then check if all expected players are connected.
const playerNumbersWhoDidNotConnect = expectedNewOtherPlayerNumbers.filter(
(otherPlayerNumber) =>
otherPlayerNumber !== playerNumber && // We don't look for ourselves
!gdjs.multiplayerMessageManager.hasReceivedHeartbeatFromPlayer(
otherPlayerNumber
)
);
const playerNumbersWhoDidNotConnect =
expectedNewOtherPlayerNumbers.filter(
(otherPlayerNumber) =>
otherPlayerNumber !== playerNumber && // We don't look for ourselves
!gdjs.multiplayerMessageManager.hasReceivedHeartbeatFromPlayer(
otherPlayerNumber
)
);
if (playerNumbersWhoDidNotConnect.length === 0) {
logger.info('All expected players are connected. Resuming the game.');
@@ -1388,7 +1472,7 @@ namespace gdjs {
logger.error(
"No peerId found, the player doesn't seem connected to the broker server."
);
return;
throw new Error('Missing player peerId.');
}
_websocket.send(
@@ -1433,7 +1517,7 @@ namespace gdjs {
if (!event.data.lobbyId) {
throw new Error('Malformed message.');
}
_actionAfterJoiningLobby = null;
handleJoinLobbyEvent(runtimeScene, event.data.lobbyId);
break;
}
@@ -1469,9 +1553,8 @@ namespace gdjs {
};
const sendSessionInformation = (runtimeScene: gdjs.RuntimeScene) => {
const lobbiesIframe = gdjs.multiplayerComponents.getLobbiesIframe(
runtimeScene
);
const lobbiesIframe =
gdjs.multiplayerComponents.getLobbiesIframe(runtimeScene);
if (!lobbiesIframe || !lobbiesIframe.contentWindow) {
// Cannot send the message if the iframe is not opened.
return;
@@ -1519,6 +1602,159 @@ namespace gdjs {
);
};
const onLobbyQuickJoinFinished = (runtimeScene: gdjs.RuntimeScene) => {
_isQuickJoiningOrStartingAGame = false;
_actionAfterJoiningLobby = null;
gdjs.multiplayerComponents.displayLoader(runtimeScene, false);
};
const quickJoinLobby = async (
runtimeScene: gdjs.RuntimeScene,
displayLoader: boolean,
openLobbiesPageIfFailure: boolean
) => {
if (_isQuickJoiningOrStartingAGame) return;
const _gameId = gdjs.projectData.properties.projectUuid;
if (!_gameId) {
handleLobbiesError(
runtimeScene,
'The game ID is missing, the quick join lobby action cannot continue.'
);
return;
}
_quickJoinLobbyFailureReason = null;
_isQuickJoiningOrStartingAGame = true;
if (displayLoader) {
gdjs.multiplayerComponents.displayLoader(runtimeScene, true);
}
const quickJoinLobbyRelativeUrl = `/play/game/${_gameId}/public-lobby/action/quick-join`;
const platformInfo = runtimeScene.getGame().getPlatformInfo();
try {
const quickJoinLobbyResponse: QuickJoinLobbyResponse =
await gdjs.evtTools.network.retryIfFailed({ times: 2 }, () =>
fetchAsPlayer({
relativeUrl: quickJoinLobbyRelativeUrl,
method: 'POST',
dev: isUsingGDevelopDevelopmentEnvironment,
body: JSON.stringify({
isPreview: runtimeScene.getGame().isPreview(),
gameVersion: runtimeScene.getGame().getGameData().properties
.version,
supportedCompressionMethods:
platformInfo.supportedCompressionMethods,
}),
})
);
if (
quickJoinLobbyResponse.status === 'full' ||
quickJoinLobbyResponse.status === 'not-enough-players'
) {
_quickJoinLobbyJustFailed = true;
_quickJoinLobbyFailureReason =
quickJoinLobbyResponse.status === 'full'
? 'FULL'
: 'NOT_ENOUGH_PLAYERS';
onLobbyQuickJoinFinished(runtimeScene);
if (openLobbiesPageIfFailure) {
openLobbiesWindow(runtimeScene);
}
return;
}
if (quickJoinLobbyResponse.status === 'join-game') {
if (quickJoinLobbyResponse.lobby.status === 'waiting') {
_actionAfterJoiningLobby = 'START_GAME';
} else if (quickJoinLobbyResponse.lobby.status === 'playing') {
_actionAfterJoiningLobby = 'JOIN_GAME';
} else {
throw new Error(
`Lobby in wrong status: ${quickJoinLobbyResponse.status}`
);
}
} else {
if (_connectionId) {
// Already connected to a lobby.
onLobbyQuickJoinFinished(runtimeScene);
openLobbiesWindow(runtimeScene);
return;
} else {
_actionAfterJoiningLobby = 'OPEN_LOBBY_PAGE';
}
}
handleJoinLobbyEvent(runtimeScene, quickJoinLobbyResponse.lobby.id);
} catch (error) {
logger.error('An error occurred while joining a lobby:', error);
_quickJoinLobbyJustFailed = true;
_quickJoinLobbyFailureReason = 'UNKNOWN';
onLobbyQuickJoinFinished(runtimeScene);
if (openLobbiesPageIfFailure) {
openLobbiesWindow(runtimeScene);
}
}
};
export const authenticateAndQuickJoinLobby = async (
runtimeScene: gdjs.RuntimeScene,
displayLoader: boolean,
openLobbiesPageIfFailure: boolean
) => {
const requestDoneAt = Date.now();
if (_lastQuickJoinRequestDoneAt) {
if (requestDoneAt - _lastQuickJoinRequestDoneAt < 500) {
_lastQuickJoinRequestDoneAt = requestDoneAt;
logger.warn(
'Last request to quick join a lobby was sent too little time ago. Ignoring this one.'
);
return;
}
} else {
_lastQuickJoinRequestDoneAt = requestDoneAt;
}
const playerId = gdjs.playerAuthentication.getUserId();
const playerToken = gdjs.playerAuthentication.getUserToken();
if (!playerId || !playerToken) {
_isWaitingForLogin = true;
const { status } =
await gdjs.playerAuthentication.openAuthenticationWindow(runtimeScene)
.promise;
_isWaitingForLogin = false;
if (status === 'logged') {
await quickJoinLobby(
runtimeScene,
displayLoader,
openLobbiesPageIfFailure
);
}
return;
}
await quickJoinLobby(
runtimeScene,
displayLoader,
openLobbiesPageIfFailure
);
};
export const isSearchingForLobbyToJoin = (
runtimeScene: gdjs.RuntimeScene
) => {
return _isQuickJoiningOrStartingAGame;
};
export const hasQuickJoinJustFailed = (runtimeScene: gdjs.RuntimeScene) => {
return _quickJoinLobbyJustFailed;
};
export const getQuickJoinFailureReason = () => {
return _quickJoinLobbyFailureReason;
};
/**
* Action to display the lobbies window to the user.
*/
@@ -1567,11 +1803,9 @@ namespace gdjs {
const playerToken = gdjs.playerAuthentication.getUserToken();
if (!playerId || !playerToken) {
_isWaitingForLogin = true;
const {
status,
} = await gdjs.playerAuthentication.openAuthenticationWindow(
runtimeScene
).promise;
const { status } =
await gdjs.playerAuthentication.openAuthenticationWindow(runtimeScene)
.promise;
_isWaitingForLogin = false;
if (status === 'logged') {
@@ -1640,9 +1874,8 @@ namespace gdjs {
export const isLobbiesWindowOpen = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const lobbiesRootContainer = gdjs.multiplayerComponents.getLobbiesRootContainer(
runtimeScene
);
const lobbiesRootContainer =
gdjs.multiplayerComponents.getLobbiesRootContainer(runtimeScene);
return !!lobbiesRootContainer;
};

View File

@@ -11,6 +11,10 @@ namespace gdjs {
data: Uint8Array | string;
};
type PeerJSInitOptions = {
onPeerUnavailable?: () => void;
};
export type CompressionMethod = 'none' | 'cs:gzip' | 'cs:deflate';
/**
@@ -100,6 +104,8 @@ namespace gdjs {
*/
let ready = false;
let _peerIdToConnectToOnceReady: string | null = null;
/**
* List of IDs of peers that just disconnected.
*/
@@ -228,6 +234,14 @@ namespace gdjs {
return newMessagesList;
};
const _onReady = () => {
ready = true;
if (_peerIdToConnectToOnceReady) {
connect(_peerIdToConnectToOnceReady);
_peerIdToConnectToOnceReady = null;
}
};
/**
* Internal function called when a connection with a remote peer is initiated.
* @param connection The DataConnection of the peer
@@ -289,14 +303,28 @@ namespace gdjs {
* Internal function called to initialize PeerJS after it
* has been configured.
*/
const loadPeerJS = () => {
const initializePeerJS = (initOptions: PeerJSInitOptions = {}) => {
if (peer !== null) return;
peer = new Peer(peerConfig);
peer.on('open', () => {
ready = true;
_onReady();
});
peer.on('error', (errorMessage) => {
logger.error('PeerJS error:', errorMessage);
peer.on('error', (error) => {
// TODO: Support other error types listed in https://peerjs.com/docs/#peeron-error
if (
initOptions.onPeerUnavailable &&
// @ts-ignore - PeerJS adds `type` on errors, but it doesn't show in their TS types.
error.type === 'peer-unavailable'
) {
logger.error('Peer is unavailable.');
initOptions.onPeerUnavailable();
} else {
logger.error(
// @ts-ignore - PeerJS adds `type` on errors, but it doesn't show in their TS types.
`PeerJS error (${error.type || 'unknown'}):`,
error
);
}
});
peer.on('connection', (connection) => {
connection.on('open', () => {
@@ -306,17 +334,21 @@ namespace gdjs {
});
peer.on('close', () => {
peer = null;
loadPeerJS();
initializePeerJS(initOptions);
});
peer.on('disconnected', peer.reconnect);
};
export const useDefaultBrokerServer = initializePeerJS;
/**
* Connects to another p2p client.
* @param id - The other client's ID.
*/
export const connect = (id: string) => {
if (peer === null) return;
if (peer === null || !ready) {
_peerIdToConnectToOnceReady = id;
return;
}
const connection = peer.connect(id);
connection.on('open', () => {
_onConnect(connection);
@@ -365,13 +397,15 @@ namespace gdjs {
* @param path The path (part of the url after the host) to the broker server.
* @param key Optional password to connect to the broker server.
* @param ssl Use ssl?
* @param peerJSInitOptions @see PeerJSInitOptions
*/
export const useCustomBrokerServer = (
host: string,
port: number,
path: string,
key: string,
ssl: boolean
ssl: boolean,
peerJSInitOptions: PeerJSInitOptions = {}
) => {
Object.assign(peerConfig, {
host,
@@ -381,11 +415,9 @@ namespace gdjs {
// All servers have "peerjs" as default key
key: key.length === 0 ? 'peerjs' : key,
});
loadPeerJS();
initializePeerJS(peerJSInitOptions);
};
export const useDefaultBrokerServer = loadPeerJS;
/**
* Adds an ICE server candidate, and removes the default ones provided by PeerJs. Must be called before connecting to a broker.
* @param urls The URL of the STUN/TURN server.
@@ -422,8 +454,8 @@ namespace gdjs {
* @see Peer.id
*/
export const getCurrentId = (): string => {
if (peer == undefined) return '';
return peer.id || '';
if (peer === null) return '';
return peer.id;
};
/**

View File

@@ -4,7 +4,7 @@
*/
declare class EventEmitter<
EventTypes extends EventEmitter.ValidEventTypes = string | symbol,
Context extends any = any
Context extends any = any,
> {
static prefixed: string | boolean;
@@ -87,7 +87,7 @@ declare namespace EventEmitter {
export interface EventEmitterStatic {
new <
EventTypes extends ValidEventTypes = string | symbol,
Context = any
Context = any,
>(): EventEmitter<EventTypes, Context>;
}
@@ -110,13 +110,13 @@ declare namespace EventEmitter {
[K in keyof T]: T[K] extends (...args: any[]) => void
? Parameters<T[K]>
: T[K] extends any[]
? T[K]
: any[];
? T[K]
: any[];
};
export type EventListener<
T extends ValidEventTypes,
K extends EventNames<T>
K extends EventNames<T>,
> = T extends string | symbol
? (...args: any[]) => void
: (
@@ -125,7 +125,7 @@ declare namespace EventEmitter {
export type EventArgs<
T extends ValidEventTypes,
K extends EventNames<T>
K extends EventNames<T>,
> = Parameters<EventListener<T, K>>;
export const EventEmitter: EventEmitterStatic;
@@ -170,9 +170,7 @@ declare namespace Peer {
validateId(id: string): boolean;
pack: any;
unpack: any;
chunk(
blob: Blob
): {
chunk(blob: Blob): {
__peerData: number;
n: number;
total: number;
@@ -267,7 +265,7 @@ declare namespace Peer {
};
abstract class BaseConnection<
T extends EventEmitter.ValidEventTypes,
TT
TT,
> extends EventEmitter<T & BaseConnectionEvents> {
readonly peer: string;
provider: Peer<TT>;

View File

@@ -324,12 +324,10 @@ describe('Multiplayer', () => {
// Create the instances of the MultiplayerMessageManager and MultiplayerVariablesManager
// for each player.
for (const player of players) {
peerMultiplayerMessageManager[
player.peerId
] = gdjs.makeMultiplayerMessageManager();
peerMultiplayerVariablesManager[
player.peerId
] = gdjs.makeMultiplayerVariablesManager();
peerMultiplayerMessageManager[player.peerId] =
gdjs.makeMultiplayerMessageManager();
peerMultiplayerVariablesManager[player.peerId] =
gdjs.makeMultiplayerVariablesManager();
// Define the host for everyone.
gdjs.multiplayer.hostPeerId = host.peerId;
@@ -1051,12 +1049,11 @@ describe('Multiplayer', () => {
const p1RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p1RuntimeScene.createObject('MySpriteObject');
const {
object: p1SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
p1SpriteObjectOriginal.setX(142);
p1SpriteObjectOriginal.setY(143);
@@ -1075,12 +1072,11 @@ describe('Multiplayer', () => {
p2RuntimeScene.renderAndStep(1000 / 60);
markAllPeerMessagesAsProcessed();
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
expect(p2SpriteObject.getX()).to.be(142);
expect(p2SpriteObject.getY()).to.be(143);
@@ -1091,12 +1087,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
p1SpriteObject.setX(242);
p1SpriteObject.setY(243);
@@ -1112,12 +1107,11 @@ describe('Multiplayer', () => {
p2RuntimeScene.renderAndStep(1000 / 60);
markAllPeerMessagesAsProcessed();
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
expect(p2SpriteObject.getX()).to.be(242);
expect(p2SpriteObject.getY()).to.be(243);
@@ -1130,12 +1124,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p1SpriteObject1,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject1 } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
p1SpriteObject1.deleteFromScene(p1RuntimeScene);
p1RuntimeScene.renderAndStep(1000 / 60);
@@ -1204,12 +1197,11 @@ describe('Multiplayer', () => {
expect(p1Objects.length).to.be(0);
p1RuntimeScene.renderAndStep(1000 / 60);
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1SpriteObject.getX()).to.be(142);
expect(p1SpriteObject.getY()).to.be(143);
@@ -1226,12 +1218,11 @@ describe('Multiplayer', () => {
expect(p3Objects.length).to.be(0);
p3RuntimeScene.renderAndStep(1000 / 60);
const {
object: p3SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObject.getX()).to.be(142);
expect(p3SpriteObject.getY()).to.be(143);
@@ -1245,12 +1236,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
p2SpriteObject.setX(242);
p2SpriteObject.setY(243);
@@ -1265,12 +1255,11 @@ describe('Multiplayer', () => {
});
p1RuntimeScene.renderAndStep(1000 / 60);
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1SpriteObject.getX()).to.be(242);
expect(p1SpriteObject.getY()).to.be(243);
@@ -1285,12 +1274,11 @@ describe('Multiplayer', () => {
p3RuntimeScene.renderAndStep(1000 / 60);
markAllPeerMessagesAsProcessed();
const {
object: p3SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObject.getX()).to.be(242);
expect(p3SpriteObject.getY()).to.be(243);
@@ -1303,12 +1291,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p2SpriteObject1,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject1 } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
p2SpriteObject1.deleteFromScene(p2RuntimeScene);
p2RuntimeScene.renderAndStep(1000 / 60);
@@ -1327,10 +1314,11 @@ describe('Multiplayer', () => {
);
expect(p1ObjectsAndBehaviors.length).to.be(1);
p1RuntimeScene.renderAndStep(1000 / 60);
const p1ObjectsAndBehaviorsUpdated = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
);
const p1ObjectsAndBehaviorsUpdated =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
);
expect(p1ObjectsAndBehaviorsUpdated.length).to.be(0);
}
@@ -1347,10 +1335,11 @@ describe('Multiplayer', () => {
);
expect(p3ObjectsAndBehaviors.length).to.be(1);
p3RuntimeScene.renderAndStep(1000 / 60);
const p3ObjectsAndBehaviorsUpdated = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
);
const p3ObjectsAndBehaviorsUpdated =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
);
expect(p3ObjectsAndBehaviorsUpdated.length).to.be(0);
}
@@ -1380,12 +1369,11 @@ describe('Multiplayer', () => {
const p1RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p1RuntimeScene.createObject('MySpriteObject');
const {
object: p1SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
p1SpriteObjectOriginal.setX(142);
p1SpriteObjectOriginal.setY(143);
@@ -1399,12 +1387,11 @@ describe('Multiplayer', () => {
const p2RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
expect(p2SpriteObjectOriginal.getX()).to.be(142);
expect(p2SpriteObjectOriginal.getY()).to.be(143);
@@ -1415,12 +1402,11 @@ describe('Multiplayer', () => {
const p3RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p3RuntimeScene.renderAndStep(1000 / 60);
const {
object: p3SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObjectOriginal.getX()).to.be(142);
expect(p3SpriteObjectOriginal.getY()).to.be(143);
@@ -1526,22 +1512,20 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p1MultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p1MultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1MultiplayerObjectBehavior.getPlayerObjectOwnership()).to.be(3);
p1RuntimeScene.renderAndStep(1000 / 60);
const {
behavior: p1MultiplayerObjectBehaviorUpdated,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p1MultiplayerObjectBehaviorUpdated } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(
p1MultiplayerObjectBehaviorUpdated.getPlayerObjectOwnership()
).to.be(2);
@@ -1554,22 +1538,20 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p3MultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3MultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3MultiplayerObjectBehavior.getPlayerObjectOwnership()).to.be(3);
p3RuntimeScene.renderAndStep(1000 / 60);
const {
behavior: p3MultiplayerObjectBehaviorUpdated,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3MultiplayerObjectBehaviorUpdated } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(
p3MultiplayerObjectBehaviorUpdated.getPlayerObjectOwnership()
).to.be(2);
@@ -1584,21 +1566,19 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p1MultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p1MultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1MultiplayerObjectBehavior.getPlayerObjectOwnership()).to.be(2);
p1RuntimeScene.renderAndStep(1000 / 60);
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1SpriteObject.getX()).to.be(242);
expect(p1SpriteObject.getY()).to.be(243);
@@ -1607,21 +1587,19 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p3MultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3MultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3MultiplayerObjectBehavior.getPlayerObjectOwnership()).to.be(2);
p3RuntimeScene.renderAndStep(1000 / 60);
const {
object: p3SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObject.getX()).to.be(242);
expect(p3SpriteObject.getY()).to.be(243);
}
@@ -1652,13 +1630,11 @@ describe('Multiplayer', () => {
const p2RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p2RuntimeScene.createObject('MySpriteObject');
const {
object: p2SpriteObject,
behavior: p2SpriteObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject, behavior: p2SpriteObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
p2SpriteObject.setX(142);
p2SpriteObject.setY(143);
p2SpriteObjectBehavior.setPlayerObjectOwnership(2);
@@ -1678,12 +1654,11 @@ describe('Multiplayer', () => {
p1RuntimeScene.renderAndStep(1000 / 60);
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1SpriteObject.getX()).to.be(142);
expect(p1SpriteObject.getY()).to.be(143);
@@ -1701,12 +1676,11 @@ describe('Multiplayer', () => {
p3RuntimeScene.renderAndStep(1000 / 60);
const {
object: p3SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObject.getX()).to.be(142);
expect(p3SpriteObject.getY()).to.be(143);
@@ -1723,12 +1697,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
p1RuntimeScene.createObject('MySpriteObject');
const {
behavior: p1MultiplayerBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[1]; // The new instance
const { behavior: p1MultiplayerBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[1]; // The new instance
p1MultiplayerBehavior.setPlayerObjectOwnership(2);
p1RuntimeScene.renderAndStep(1000 / 60);
@@ -1749,12 +1722,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
p3RuntimeScene.createObject('MySpriteObject');
const {
behavior: p3MultiplayerBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[1]; // The new instance
const { behavior: p3MultiplayerBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[1]; // The new instance
p3MultiplayerBehavior.setPlayerObjectOwnership(2);
p3RuntimeScene.renderAndStep(1000 / 60);
@@ -1784,10 +1756,8 @@ describe('Multiplayer', () => {
);
expect(p2ObjectsAndBehaviors.length).to.be(2);
const { object: p2Object1 } = p2ObjectsAndBehaviors[0];
const {
object: p2Object2,
behavior: p2MultiplayerBehavior2,
} = p2ObjectsAndBehaviors[1];
const { object: p2Object2, behavior: p2MultiplayerBehavior2 } =
p2ObjectsAndBehaviors[1];
expect(p2Object1.getX()).to.be(142);
expect(p2Object1.getY()).to.be(143);
expect(p2Object2.getX()).to.be(0);
@@ -1842,10 +1812,8 @@ describe('Multiplayer', () => {
});
it('deletes an instance owned by another player after a bit (if not "reconciled" in the meantime)', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -1901,10 +1869,8 @@ describe('Multiplayer', () => {
});
it('deletes an instance owned by another player instantly if not belonging to an existing player', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -1991,12 +1957,11 @@ describe('Multiplayer', () => {
expect(p2Objects.length).to.be(0);
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
expect(p2SpriteObject.getX()).to.be(142);
expect(p2SpriteObject.getY()).to.be(143);
@@ -2013,12 +1978,11 @@ describe('Multiplayer', () => {
expect(p3Objects.length).to.be(0);
p3RuntimeScene.renderAndStep(1000 / 60);
const {
object: p3SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { object: p3SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(p3SpriteObject.getX()).to.be(142);
expect(p3SpriteObject.getY()).to.be(143);
@@ -2032,12 +1996,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p2SpriteMultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p2SpriteMultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
p2SpriteMultiplayerObjectBehavior.setPlayerObjectOwnership(2);
p2RuntimeScene.renderAndStep(1000 / 60);
@@ -2047,12 +2010,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
behavior: p3SpriteMultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3SpriteMultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
p3SpriteMultiplayerObjectBehavior.setPlayerObjectOwnership(3);
p3RuntimeScene.renderAndStep(1000 / 60);
@@ -2064,24 +2026,22 @@ describe('Multiplayer', () => {
playerNumber: 1,
allConnectedPlayers,
});
const {
behavior: p1SpriteMultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p1SpriteMultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(
p1SpriteMultiplayerObjectBehavior.getPlayerObjectOwnership()
).to.be(0);
p1RuntimeScene.renderAndStep(1000 / 60);
const {
behavior: p1SpriteMultiplayerObjectBehaviorUpdated,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p1SpriteMultiplayerObjectBehaviorUpdated } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(
p1SpriteMultiplayerObjectBehaviorUpdated.getPlayerObjectOwnership()
).to.be(2);
@@ -2102,12 +2062,11 @@ describe('Multiplayer', () => {
p3RuntimeScene.renderAndStep(1000 / 60);
const {
behavior: p3SpriteMultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3SpriteMultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(
p3SpriteMultiplayerObjectBehavior.getPlayerObjectOwnership()
).to.be(3);
@@ -2127,12 +2086,11 @@ describe('Multiplayer', () => {
p3RuntimeScene.renderAndStep(1000 / 60);
const {
behavior: p3SpriteMultiplayerObjectBehavior,
} = getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
const { behavior: p3SpriteMultiplayerObjectBehavior } =
getObjectAndMultiplayerBehaviorsFromScene(
p3RuntimeScene,
'MySpriteObject'
)[0];
expect(
p3SpriteMultiplayerObjectBehavior.getPlayerObjectOwnership()
).to.be(0);
@@ -2146,12 +2104,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
p2SpriteObject.setX(242);
p2SpriteObject.setY(243);
p2RuntimeScene.renderAndStep(1000 / 60);
@@ -2165,12 +2122,11 @@ describe('Multiplayer', () => {
});
p1RuntimeScene.renderAndStep(1000 / 60);
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
expect(p1SpriteObject.getX()).to.be(242);
expect(p1SpriteObject.getY()).to.be(243);
@@ -2202,10 +2158,8 @@ describe('Multiplayer', () => {
});
it('synchronizes object behaviors from the host to other players', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -2221,18 +2175,16 @@ describe('Multiplayer', () => {
const p1RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p1RuntimeScene.createObject('MySpriteObject');
const {
object: p1SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
// Ensure dummy behavior is there.
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p1DummyBehaviorOriginal = p1SpriteObjectOriginal.getBehavior(
'DummyBehavior'
);
const p1DummyBehaviorOriginal =
p1SpriteObjectOriginal.getBehavior('DummyBehavior');
if (!p1DummyBehaviorOriginal) throw new Error('No dummy behavior found');
expect(p1DummyBehaviorOriginal._activated).to.be(true);
@@ -2246,18 +2198,16 @@ describe('Multiplayer', () => {
const p2RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
// Ensure dummy behavior is there.
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p2DummyBehaviorOriginal = p2SpriteObjectOriginal.getBehavior(
'DummyBehavior'
);
const p2DummyBehaviorOriginal =
p2SpriteObjectOriginal.getBehavior('DummyBehavior');
if (!p2DummyBehaviorOriginal) throw new Error('No dummy behavior found');
expect(p2DummyBehaviorOriginal._activated).to.be(true);
@@ -2268,12 +2218,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p2DummyBehavior = p2SpriteObject.getBehavior('DummyBehavior');
@@ -2304,12 +2253,11 @@ describe('Multiplayer', () => {
await delay(20);
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p2DummyBehavior = p2SpriteObject.getBehavior('DummyBehavior');
@@ -2324,12 +2272,11 @@ describe('Multiplayer', () => {
allConnectedPlayers,
});
const {
object: p1SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
const { object: p1SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p1RuntimeScene,
'MySpriteObject'
)[0];
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p1DummyBehavior = p1SpriteObject.getBehavior('DummyBehavior');
@@ -2350,12 +2297,11 @@ describe('Multiplayer', () => {
// As the object is not moving, it will not be synced a lot, so we need to wait a bit.
await delay(20);
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObject,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObject } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p2DummyBehavior = p2SpriteObject.getBehavior('DummyBehavior');
@@ -2365,10 +2311,8 @@ describe('Multiplayer', () => {
});
it('does not synchronize object behaviors if defined as not synchronized', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -2394,9 +2338,8 @@ describe('Multiplayer', () => {
// Ensure dummy behavior is there.
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p1DummyBehaviorOriginal = p1SpriteObjectOriginal.getBehavior(
'DummyBehavior'
);
const p1DummyBehaviorOriginal =
p1SpriteObjectOriginal.getBehavior('DummyBehavior');
if (!p1DummyBehaviorOriginal) throw new Error('No dummy behavior found');
expect(p1DummyBehaviorOriginal._activated).to.be(true);
// Deactivate it and mark it as not synchronized.
@@ -2416,18 +2359,16 @@ describe('Multiplayer', () => {
const p2RuntimeScene = makeTestRuntimeSceneWithNetworkId();
p2RuntimeScene.renderAndStep(1000 / 60);
const {
object: p2SpriteObjectOriginal,
} = getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
const { object: p2SpriteObjectOriginal } =
getObjectAndMultiplayerBehaviorsFromScene(
p2RuntimeScene,
'MySpriteObject'
)[0];
// Ensure dummy behavior is there.
/** @type {gdjs.DummyRuntimeBehavior | null} */
// @ts-ignore - We know this returns an DummyRuntimeBehavior
const p2DummyBehaviorOriginal = p2SpriteObjectOriginal.getBehavior(
'DummyBehavior'
);
const p2DummyBehaviorOriginal =
p2SpriteObjectOriginal.getBehavior('DummyBehavior');
if (!p2DummyBehaviorOriginal) throw new Error('No dummy behavior found');
// It is activated as it is not synchronized.
@@ -2450,10 +2391,8 @@ describe('Multiplayer', () => {
};
it('synchronizes scenes from the host to other players', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -2676,10 +2615,8 @@ describe('Multiplayer', () => {
describe('Player joins and leaves', () => {
it('detects a player leaving and send it to other players', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -2708,14 +2645,13 @@ describe('Multiplayer', () => {
allConnectedPlayers: newConnectedPlayers,
});
runtimeScene.renderAndStep(1000 / 60);
const p2PlayersLeft = gdjs.multiplayerMessageManager.getPlayersWhoJustLeft();
const p2PlayersLeft =
gdjs.multiplayerMessageManager.getPlayersWhoJustLeft();
expect(p2PlayersLeft).to.be.eql([2]);
});
it('detects a player joining and send it to other players', async () => {
const {
switchToPeer,
initiateGameWithPlayers,
} = createMultiplayerManagersMock();
const { switchToPeer, initiateGameWithPlayers } =
createMultiplayerManagersMock();
const allConnectedPlayers = [
{ playerNumber: 1, peerId: 'player-1', isHost: true },
@@ -2754,7 +2690,8 @@ describe('Multiplayer', () => {
allConnectedPlayers: newConnectedPlayers,
});
runtimeScene.renderAndStep(1000 / 60);
const p1PlayersJoined = gdjs.multiplayerMessageManager.getPlayersWhoJustJoined();
const p1PlayersJoined =
gdjs.multiplayerMessageManager.getPlayersWhoJustJoined();
expect(p1PlayersJoined).to.be.eql([2]);
// Player 3 also sees the player 2 joining.
@@ -2763,7 +2700,8 @@ describe('Multiplayer', () => {
allConnectedPlayers: newConnectedPlayers,
});
runtimeScene.renderAndStep(1000 / 60);
const p3PlayersJoined = gdjs.multiplayerMessageManager.getPlayersWhoJustJoined();
const p3PlayersJoined =
gdjs.multiplayerMessageManager.getPlayersWhoJustJoined();
expect(p3PlayersJoined).to.be.eql([2]);
});
});

View File

@@ -4,7 +4,7 @@
*/
declare class EventEmitter<
EventTypes extends EventEmitter.ValidEventTypes = string | symbol,
Context extends any = any
Context extends any = any,
> {
static prefixed: string | boolean;
@@ -87,7 +87,7 @@ declare namespace EventEmitter {
export interface EventEmitterStatic {
new <
EventTypes extends ValidEventTypes = string | symbol,
Context = any
Context = any,
>(): EventEmitter<EventTypes, Context>;
}
@@ -110,13 +110,13 @@ declare namespace EventEmitter {
[K in keyof T]: T[K] extends (...args: any[]) => void
? Parameters<T[K]>
: T[K] extends any[]
? T[K]
: any[];
? T[K]
: any[];
};
export type EventListener<
T extends ValidEventTypes,
K extends EventNames<T>
K extends EventNames<T>,
> = T extends string | symbol
? (...args: any[]) => void
: (
@@ -125,7 +125,7 @@ declare namespace EventEmitter {
export type EventArgs<
T extends ValidEventTypes,
K extends EventNames<T>
K extends EventNames<T>,
> = Parameters<EventListener<T, K>>;
export const EventEmitter: EventEmitterStatic;
@@ -170,9 +170,7 @@ declare namespace Peer {
validateId(id: string): boolean;
pack: any;
unpack: any;
chunk(
blob: Blob
): {
chunk(blob: Blob): {
__peerData: number;
n: number;
total: number;
@@ -267,7 +265,7 @@ declare namespace Peer {
};
abstract class BaseConnection<
T extends EventEmitter.ValidEventTypes,
TT
TT,
> extends EventEmitter<T & BaseConnectionEvents> {
readonly peer: string;
provider: Peer<TT>;

View File

@@ -26,7 +26,8 @@ PanelSpriteObject::PanelSpriteObject()
leftMargin(0),
topMargin(0),
rightMargin(0),
bottomMargin(0) {}
bottomMargin(0),
tiled(false) {}
PanelSpriteObject::~PanelSpriteObject() {}

View File

@@ -24,11 +24,9 @@ namespace gdjs {
tiled: boolean
) {
this._object = runtimeObject;
const texture = (instanceContainer
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
textureName
);
const texture = (
instanceContainer.getGame().getImageManager() as gdjs.PixiImageManager
).getPIXITexture(textureName);
const StretchedSprite = !tiled ? PIXI.Sprite : PIXI.TilingSprite;
this._spritesContainer = new PIXI.Container();
this._wrapperContainer = new PIXI.Container();
@@ -420,6 +418,8 @@ namespace gdjs {
}
}
export const PanelSpriteRuntimeObjectRenderer = PanelSpriteRuntimeObjectPixiRenderer;
export type PanelSpriteRuntimeObjectRenderer = PanelSpriteRuntimeObjectPixiRenderer;
export const PanelSpriteRuntimeObjectRenderer =
PanelSpriteRuntimeObjectPixiRenderer;
export type PanelSpriteRuntimeObjectRenderer =
PanelSpriteRuntimeObjectPixiRenderer;
}

View File

@@ -39,7 +39,8 @@ namespace gdjs {
*/
export class PanelSpriteRuntimeObject
extends gdjs.RuntimeObject
implements gdjs.Resizable, gdjs.OpacityHandler {
implements gdjs.Resizable, gdjs.OpacityHandler
{
_rBorder: integer;
_lBorder: integer;
_tBorder: integer;

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