Compare commits

...

73 Commits

Author SHA1 Message Date
AlexandreS
78cbe48718 Fix points editor not updating correctly when deleting points 2022-10-06 12:40:04 +02:00
Florian Rival
e153d295de Bump newIDE version 2022-10-06 10:35:33 +02:00
AlexandreS
1b8510655e Revert "Add expression and condition to get highest z order of a layer (#4346)" (#4359)
Don't show in changelog
2022-10-06 10:34:41 +02:00
github-actions[bot]
bd88127563 Update translations [skip ci] (#4342)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-10-06 10:25:39 +02:00
AlexandreS
01b9f09604 Fix: Add parameter to file reading actions to remove CR characters from files (#4352)
⚠️ If you had encountered an issue linked to this and changed your events to fix it, you might need to update your project and set this new parameter to "No"
2022-10-05 14:11:21 +02:00
Florian Rival
0db30f02c9 Add support for opening onboarding directly on the web-app
https://editor.gdevelop.io/?initial-dialog=onboarding

Don't show in changelog
2022-10-04 16:03:12 +02:00
D8H
a852e91690 Add missing expression group icons (#4353) 2022-10-04 14:56:59 +02:00
AlexandreS
79d6281061 Add expression and condition to get highest z order of a layer (#4346) 2022-10-04 14:31:49 +02:00
AlexandreS
eba6b2540c Add price to assets home private asset packs thumbnails and display them in their dialog (#4350)
Don't show in changelog
2022-10-04 12:13:25 +02:00
D8H
0706a54305 Reorganize extensions categories (#4345) 2022-10-04 10:37:27 +02:00
D8H
d929fd6e48 Add a test on sprite hit-boxes after a camera displacement (#4349)
* Don't show in changelog
2022-10-03 14:17:01 +02:00
Florian Rival
4dbabab052 Bump newIDE version 2022-09-30 15:43:18 +02:00
D8H
827c5d6442 Add an extension category filter (#4341) 2022-09-30 15:14:23 +02:00
Florian Rival
46be0e0ffc Change previews so that they use the development environment for GDevelop APIs if running GDevelop development version (#4343)
Only show in developer changelog
2022-09-30 14:55:50 +02:00
github-actions[bot]
72e3cf5b99 Update translations (#4313) 2022-09-30 10:12:34 +02:00
D8H
54f32a2542 Add new categories for extensions (#4331) 2022-09-29 18:24:06 +02:00
D8H
b826f66455 Make the custom object renderer works better with 9-patch and text child-objects (#4335)
* Don't show in changelogs.
2022-09-29 17:16:41 +02:00
D8H
6fc03cccc6 Add help buttons in the expression editor and the extension details dialog (#4337) 2022-09-29 15:44:16 +02:00
AlexandreS
ed7313a330 Add invert condition shortcut (J key by default - configurable in your preferences) (#4334) 2022-09-29 11:18:33 +02:00
D8H
7390f7cd6a Ensure required behavior properties can't be hidden (#4336) 2022-09-29 09:54:01 +02:00
D8H
4619ae824b Allow event-based objects to define a default name for created objects (#4329)
* Don't show in changelogs
2022-09-28 17:43:51 +02:00
D8H
0f69ee435f Add tests on behavior properties initialization and unserialization (#4314)
* Don't show in changelogs
2022-09-28 17:43:18 +02:00
AlexandreS
f46241d5a2 Fix bug that prevented converting a variable to JSON when one previously tried to access an out-of-index child in a variable array (#4333) 2022-09-28 17:39:53 +02:00
D8H
4c8ec48004 Add subsections for extensions categories in the Wiki (#4332)
* Add a warning message on pages for extensions from the community list.
2022-09-28 15:34:03 +02:00
AlexandreS
3ac121be4c Add asset packs that can be purchased in the Asset store home (#4328)
Do not show in changelog
2022-09-28 15:29:45 +02:00
D8H
3aa636861c Custom objects take the icon of one of their sprite child (#4316)
* Don't show in changelogs.
2022-09-27 16:19:12 +02:00
D8H
6d4b422be6 Fix extension description links on the Wiki (#4325) 2022-09-27 16:18:18 +02:00
Clément Pasteau
b8ee27f62c Improve player authentication
* Improve player authentication by indicating when the game is not registered
* Show a link to open the window if blocked

Do not show in changelog
2022-09-27 15:19:06 +02:00
AlexandreS
6996ff452d Fix various memory leaks when using the app (#4323) 2022-09-23 18:34:34 +02:00
AlexandreS
da7934c6ac Add context menu items to manipulate the view on the scene editor (#4307)
- Return to initial position (view matches the game resolution)
- Fit zoom to selected instances
- Fit zoom to the whole scene
- Select all instances of an object on the scene (in the context menu of an object)
2022-09-23 13:27:40 +02:00
Clément Pasteau
90bebcb404 Create GDevelop Authentication extension
* This is an experimental extension!
* It allows you to provide a login/register form to your players, with 1 action
* It connects the player automatically when they launch your game again
* It also provides a new action to submit a leaderboard entry without having to enter a username
* This is the beginning of Player Authentication and more features will come allowing creators to interact with their players
2022-09-23 10:44:58 +02:00
D8H
a29e7aae44 Bump newIDE version (#4317) 2022-09-22 12:22:11 +02:00
D8H
52201e2a36 Fix tiles rotation that was no longer applied in tile maps (#4315) 2022-09-22 10:25:38 +02:00
Florian Rival
50465badd7 Bump newIDE version 2022-09-21 10:01:17 +02:00
D8H
6e1bfb0190 Fix properties on the scene of the Physics behavior not working ("shared data" of behaviors not handled properly) (#4310) 2022-09-21 09:56:38 +02:00
Aurélien Vivet
40c7c57670 Add issue template for Asset Store Submission [skip ci] (#4309)
Don't show in changelog
2022-09-20 21:43:43 +02:00
github-actions[bot]
e51c73b293 Update translations [skip ci] (#4306)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-09-19 11:16:17 +02:00
Florian Rival
29d5d5fe75 Bump newIDE version 2022-09-19 10:23:17 +02:00
AlexandreS
6e1f2c4eee Fix parameter fields being reset to their old value after being changed (#4308) 2022-09-19 09:28:50 +02:00
Florian Rival
2f2a89faf6 Use a purple heart instead of a red one to look less like an error
Don't show in changelog
2022-09-16 12:23:15 +02:00
AlexandreS
4100b24dfd Improve display of black icons on dark themes (#4302) 2022-09-16 12:22:11 +02:00
AlexandreS
c616abe1c5 Bump newIDE version 2022-09-16 10:45:10 +02:00
Aurélien Vivet
35084de4f6 Add a new expression: Pi number (3.1415...) (#4304) 2022-09-16 10:30:46 +02:00
github-actions[bot]
6ecb5e9d8c Update translations [skip ci] (#4291)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-09-16 10:09:03 +02:00
D8H
9532a8f6de Fix extension upgrades that was leaving duplicated behaviors. (#4301)
- Don't show in changelogs (the regression is between the 140 and the 141)
2022-09-15 15:43:28 +02:00
D8H
00a5b0b402 Fix events-based behavior default name. (#4300)
* Don't show in changelogs (the regression is between the 140 and the 141)
2022-09-15 11:57:28 +02:00
D8H
629567ad21 Fix tile maps with rectangular tiles (#4299) 2022-09-14 19:04:42 +02:00
D8H
5f21229ccc Remove a file commited by mistake (#4296) 2022-09-14 18:40:48 +02:00
Florian Rival
27cf2ef596 Remove shadow on home page menu buttons
Don't show in changelog
2022-09-14 16:44:47 +02:00
AlexandreS
2bc9a6d19d Fix previews on private navigation for cloud projects
Do not show in changelog
2022-09-14 16:29:15 +02:00
Florian Rival
c6c586459c Update the subscriptions plans to reflect the new ones and their updated benefits (#4290)
* Read [the blog post](https://gdevelop.io/blog/new-premium-subscriptions-online-services) for a full description of the updates and online services unlocked by the updated (and the existing) plans.
2022-09-14 11:33:37 +02:00
AlexandreS
a930011d8d Bump newIDE version 2022-09-14 10:24:54 +02:00
github-actions[bot]
51d723bd3d Update translations [skip ci] (#4284) 2022-09-14 10:21:23 +02:00
D8H
b63f968011 Fix resource refactoring for child-objects (#4289)
* Don't show in changelogs.
2022-09-14 09:41:07 +02:00
Florian Rival
3387c553d8 Fix previews not loading audio/font/json resources uploaded with the web-app (#4285)
Don't show in changelog
2022-09-13 16:41:33 +02:00
D8H
441cd20846 Fix a crash when renaming a child-object (#4281)
* Add tests for object renaming in events.
* Don't show in changelogs.
2022-09-13 12:46:20 +02:00
AlexandreS
f9871bd63d Add supported formats mimtypes to resource selector input accept attributes (#4283)
[skip-ci]
2022-09-13 11:51:22 +02:00
github-actions[bot]
bac11b3818 Update translations [skip ci] (#4230)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-09-13 10:37:01 +02:00
D8H
3e32cb8cea Fix the events-based objects editor that were never showing up in dev mode (#4279)
* Don't show in changelogs
2022-09-12 10:48:22 +02:00
Florian Rival
db74a59730 Add support for markdown in announcements (#4280)
Don't show in changelog
2022-09-12 10:28:32 +02:00
D8H
e7d09531b7 Fix event-based behavior properties default values that were ignored (#4276)
* Don't show in changelogs
2022-09-09 17:56:27 +02:00
Florian Rival
97cf19180b Rename Behaviors/Functions to Extensions in the project manager (#4272)
Don't show in changelog
2022-09-09 14:08:13 +02:00
Florian Rival
5cc999c0a3 Add announcements and news in the homepage community tab (#4273)
* Also display "urgent" announcements at the top of the home page (which can be dismissed).
2022-09-09 10:50:49 +02:00
D8H
5eb0aa9e14 Add an instance render for custom objects (#4251)
* Don't show in changelogs
2022-09-06 12:41:38 +02:00
D8H
7f528649d7 Revert the RendererInstance parameter for the tile map extension (#4260)
* Don't show in changelogs
2022-09-05 19:38:47 +02:00
D8H
c4f44daa8c Expose a gdObject constructor for the asset script (#4268)
* Don't show in changelogs.
2022-09-05 16:14:54 +02:00
AlexandreS
7d00e78628 Change the name of exports to be based on the name and version of the game 2022-09-05 12:12:39 +02:00
Aurélien Vivet
887ced270a Add the possibility to receive the GDevelop newsletter from the Profile and on Signup (#4256) 2022-09-05 10:32:30 +02:00
D8H
b8e9bc801a Add a properties editor for custom objects (#4227)
* Don't show in changelogs
2022-09-02 14:13:41 +02:00
D8H
7f023e1a58 Remove useless SetIncludeFile call. (#4255)
- Most of them has a wrong path anyway.
- Don't show in changelogs.
2022-09-01 14:30:24 +02:00
AlexandreS
6606ddb260 Improve SVG display in Resource Store for dark themes 2022-09-01 09:31:04 +02:00
Clément Pasteau
a682c1baa8 Update "Submit New Extension" link with new format (#4254) 2022-08-31 14:47:32 +02:00
Clément Pasteau
d581af20e1 Improve Signup and Edit profile to show when a username is not available 2022-08-31 13:59:14 +02:00
444 changed files with 11524 additions and 4904 deletions

View File

@@ -0,0 +1,19 @@
---
name: "📦 Asset Store submission"
about: Submit a free asset pack for the GDevelop Asset Store.
title: ''
labels: "📦 Asset Store submission"
assignees: ''
---
BEFORE opening a new submission, please make sure that you:
- You have packaged the asset pack according [these rules](https://wiki.gdevelop.io/gdevelop5/community/contribute-to-the-assets-store). Otherwise, your package may be rejected or we will ask you to do the changes.
## Description
- License:
- Author:
- Link to the original website:
- Zip file:

View File

@@ -28,7 +28,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension.AddInstructionOrExpressionGroupMetadata(_("Movement using forces"))
.SetIcon("res/actions/force24.png");
gd::ObjectMetadata& obj = extension.AddObject<gd::Object>(
gd::ObjectMetadata& obj = extension.AddObject<gd::ObjectConfiguration>(
"", _("Base object"), _("Base object"), "res/objeticon24.png");
obj.AddCondition("PosX",
@@ -36,8 +36,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Compare the X position of the object."),
_("the X position"),
_("Position"),
"res/conditions/position24.png",
"res/conditions/position.png")
"res/conditions/position24_black.png",
"res/conditions/position_black.png")
.AddParameter("object", _("Object"))
.UseStandardRelationalOperatorParameters("number")
@@ -48,8 +48,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Change the X position of an object."),
_("the X position"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.UseStandardOperatorParameters("number")
@@ -60,8 +60,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Compare the Y position of an object."),
_("the Y position"),
_("Position"),
"res/conditions/position24.png",
"res/conditions/position.png")
"res/conditions/position24_black.png",
"res/conditions/position_black.png")
.AddParameter("object", _("Object"))
.UseStandardRelationalOperatorParameters("number")
@@ -72,8 +72,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Change the Y position of an object."),
_("the Y position"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.UseStandardOperatorParameters("number")
@@ -85,8 +85,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Change the position of _PARAM0_: _PARAM1_ _PARAM2_ (x "
"axis), _PARAM3_ _PARAM4_ (y axis)"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"), "number")
@@ -102,8 +102,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"_PARAM2_ (x "
"axis), _PARAM3_ _PARAM4_ (y axis)"),
_("Position/Center"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"), "number")
.AddParameter("expression", _("X position"))
@@ -118,7 +118,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the X position of the center of rotation"),
_("the X position of the center"),
_("Position/Center"),
"res/actions/position24.png")
"res/actions/position24_black.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -129,7 +129,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the Y position of the center of rotation"),
_("the Y position of the center"),
_("Position/Center"),
"res/actions/position24.png")
"res/actions/position24_black.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -140,7 +140,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the object) left position"),
_("the bounding box left position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-left.svg")
"res/conditions/bounding-box-left_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -151,7 +151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("the bounding box (the area encapsulating the object) top position"),
_("the bounding box top position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-top.svg")
"res/conditions/bounding-box-top_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -162,7 +162,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the object) right position"),
_("the bounding box right position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-right.svg")
"res/conditions/bounding-box-right_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -173,7 +173,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the object) bottom position"),
_("the bounding box bottom position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-bottom.svg")
"res/conditions/bounding-box-bottom_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -184,7 +184,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the object) center X position"),
_("the bounding box center X position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-center.svg")
"res/conditions/bounding-box-center_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -195,7 +195,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the object) center Y position"),
_("the bounding box center Y position"),
_("Position/Bounding Box"),
"res/conditions/bounding-box-center.svg")
"res/conditions/bounding-box-center_black.svg")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
@@ -222,8 +222,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Change the angle of rotation of an object (in degrees)."),
_("the angle"),
_("Angle"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/direction24_black.png",
"res/actions/direction_black.png")
.AddParameter("object", _("Object"))
.UseStandardOperatorParameters("number");
@@ -234,8 +234,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"counterclockwise otherwise."),
_("Rotate _PARAM0_ at speed _PARAM1_ deg/second"),
_("Angle"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angular speed (in degrees per second)"))
@@ -248,8 +248,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Rotate an object towards an angle with the specified speed."),
_("Rotate _PARAM0_ towards _PARAM1_ at speed _PARAM2_ deg/second"),
_("Angle"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle to rotate towards (in degrees)"))
@@ -264,8 +264,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Rotate _PARAM0_ towards _PARAM1_;_PARAM2_ at speed "
"_PARAM3_ deg/second"),
_("Angle"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position"))
@@ -512,8 +512,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Compare the angle of the specified object."),
_("the angle (in degrees)"),
_("Angle"),
"res/conditions/direction24.png",
"res/conditions/direction.png")
"res/conditions/direction24_black.png",
"res/conditions/direction_black.png")
.AddParameter("object", _("Object"))
.UseStandardRelationalOperatorParameters("number")
@@ -965,21 +965,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("X position"),
_("X position of the object"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Y",
_("Y position"),
_("Y position of the object"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Angle",
_("Angle"),
_("Current angle, in degrees, of the object"),
_("Angle"),
"res/actions/direction.png")
"res/actions/direction_black.png")
.AddParameter("object", _("Object"));
obj.AddExpression("ForceX",
@@ -1022,14 +1022,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Width"),
_("Width of the object"),
_("Size"),
"res/actions/scaleWidth.png")
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Largeur",
_("Width"),
_("Width of the object"),
_("Size"),
"res/actions/scaleWidth.png")
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"))
.SetHidden();
@@ -1037,14 +1037,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Height"),
_("Height of the object"),
_("Size"),
"res/actions/scaleHeight.png")
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Hauteur",
_("Height"),
_("Height of the object"),
_("Size"),
"res/actions/scaleHeight.png")
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"))
.SetHidden();
@@ -1135,7 +1135,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"If you need the angle to an arbitrary position, "
"use AngleToPosition."),
_("Angle"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Object"));
@@ -1146,7 +1146,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
@@ -1158,7 +1158,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
@@ -1169,7 +1169,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"\"target\" position (in degrees). If you need the angle "
"between two objects, use AngleToObject."),
_("Angle"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Target X position"))
.AddParameter("expression", _("Target Y position"));
@@ -1565,7 +1565,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Object name"),
_("Return the name of the object"),
"",
"res/conditions/text.png")
"res/conditions/text_black.png")
.AddParameter("object", _("Object"));
obj.AddStrExpression("Layer",

View File

@@ -22,6 +22,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"object or a position.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Camera")
.SetExtensionHelpPath("/interface/scene-editor/layers-and-cameras");
extension.AddInstructionOrExpressionGroupMetadata(_("Layers and cameras"))
.SetIcon("res/conditions/camera24.png");

View File

@@ -21,14 +21,14 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/common-conversions");
extension.AddInstructionOrExpressionGroupMetadata(_("Conversion"))
.SetIcon("res/conditions/toujours24.png");
.SetIcon("res/conditions/toujours24_black.png");
extension
.AddExpression("ToNumber",
_("Text > Number"),
_("Convert the text to a number"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text to convert to a number"));
extension
@@ -36,7 +36,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Number > Text"),
_("Convert the result of the expression to text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("expression", _("Expression to be converted to text"));
extension
@@ -45,7 +45,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Convert the result of the expression to text, "
"without using the scientific notation"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("expression", _("Expression to be converted to text"));
extension
@@ -54,7 +54,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Degrees > Radians"),
_("Converts the angle, expressed in degrees, into radians"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("expression", _("Angle, in degrees"));
extension
@@ -63,7 +63,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Radians > Degrees"),
_("Converts the angle, expressed in radians, into degrees"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("expression", _("Angle, in radians"));
extension
@@ -71,7 +71,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Convert scene variable to JSON"),
_("Convert a scene variable to JSON"),
_("JSON"),
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("scenevar", _("Scene variable to be stringified"));
extension
@@ -79,7 +79,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Convert global variable to JSON"),
_("Convert a global variable to JSON"),
_("JSON"),
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("globalvar", _("The global variable to be stringified"));
extension
@@ -87,7 +87,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Convert object variable to JSON"),
_("Convert an object variable to JSON"),
_("JSON"),
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("objectPtr", _("The object with the variable"))
.AddParameter("objectvar", _("The object variable to be stringified"));

View File

@@ -33,7 +33,7 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
.SetExtensionHelpPath("/all-features/advanced-conditions");
extension
.AddInstructionOrExpressionGroupMetadata(_("Events and control flow"))
.SetIcon("res/conditions/toujours24.png");
.SetIcon("res/conditions/toujours24_black.png");
extension
.AddCondition("Always",
@@ -42,8 +42,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
"the condition is inverted)."),
_("Always"),
"",
"res/conditions/toujours24.png",
"res/conditions/toujours.png")
"res/conditions/toujours24_black.png",
"res/conditions/toujours_black.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
@@ -61,8 +61,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
_("Check if one of the sub conditions is true"),
_("If one of these conditions is true:"),
"",
"res/conditions/or24.png",
"res/conditions/or.png")
"res/conditions/or24_black.png",
"res/conditions/or_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
@@ -72,8 +72,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
_("Check if all sub conditions are true"),
_("If all of these conditions are true:"),
"",
"res/conditions/and24.png",
"res/conditions/and.png")
"res/conditions/and24_black.png",
"res/conditions/and_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
@@ -84,8 +84,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
_("Return the contrary of the result of the sub conditions"),
_("Invert the logical result of these conditions:"),
"",
"res/conditions/not24.png",
"res/conditions/not.png")
"res/conditions/not24_black.png",
"res/conditions/not_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
@@ -104,8 +104,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
_("Compare the two numbers."),
_("_PARAM0_ _PARAM1_ _PARAM2_"),
"",
"res/conditions/egal24.png",
"res/conditions/egal.png")
"res/conditions/egal24_black.png",
"res/conditions/egal_black.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddParameter("expression", _("First expression"))
.AddParameter("relationalOperator", _("Sign of the test"), "number")
@@ -125,8 +125,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
_("Compare the two strings."),
_("_PARAM0_ _PARAM1_ _PARAM2_"),
"",
"res/conditions/egal24.png",
"res/conditions/egal.png")
"res/conditions/egal24_black.png",
"res/conditions/egal_black.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddParameter("string", _("First string expression"))
.AddParameter("relationalOperator", _("Sign of the test"), "string")

View File

@@ -21,7 +21,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/storage")
.SetCategory("Device");
.SetCategory("Advanced");
extension.AddInstructionOrExpressionGroupMetadata(_("Storage"))
.SetIcon("res/conditions/fichier24.png");

View File

@@ -485,6 +485,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
extension
.AddExpression("Pi",
_("Number Pi (3.1415...)"),
_("The number Pi (3.1415...)"),
"",
"res/mathfunction.png")
.SetHelpPath("/all-features/expressions");
}
} // namespace gd

View File

@@ -22,6 +22,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/sprite");
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
.SetIcon("CppPlatform/Extensions/spriteicon.png");
gd::ObjectMetadata& obj =
extension
@@ -79,8 +81,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"is in 8 directions mode, the valid directions are 0..7"),
_("the direction"),
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/direction24_black.png",
"res/actions/direction_black.png")
.SetHidden() // Hide as 8 direction is not supported officially in the
// interface.
.AddParameter("object", _("Object"), "Sprite")
@@ -140,8 +142,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"Rotate an object towards a position.",
"Rotate _PARAM0_ towards _PARAM1_;_PARAM2_",
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object to be rotated"), "Sprite")
.AddParameter("expression", _("X position"))
@@ -156,8 +158,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Modify the scale of the specified object."),
_("the scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardOperatorParameters("number")
@@ -168,8 +170,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Modify the scale of the width of an object."),
_("the width's scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardOperatorParameters("number")
@@ -180,8 +182,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Modify the scale of the height of an object."),
_("the height's scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardOperatorParameters("number")
@@ -192,8 +194,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Change the width of a Sprite object."),
_("the width"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardOperatorParameters("number")
@@ -204,8 +206,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Compare the width of a Sprite object."),
_("the width"),
_("Size"),
"res/conditions/scaleWidth24.png",
"res/conditions/scaleWidth.png")
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters("number")
@@ -216,8 +218,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Change the height of a Sprite object."),
_("the height"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardOperatorParameters("number")
@@ -228,8 +230,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Compare the height of a Sprite object."),
_("the height"),
_("Size"),
"res/conditions/scaleHeight24.png",
"res/conditions/scaleHeight.png")
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters("number")
@@ -240,8 +242,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Change the size of an object."),
_("Change the size of _PARAM0_: set to _PARAM1_x_PARAM2_"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Width"))
@@ -281,8 +283,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"from 0 to 7. Otherwise, the direction is in degrees."),
_("the direction"),
_("Direction"),
"res/conditions/direction24.png",
"res/conditions/direction.png")
"res/conditions/direction24_black.png",
"res/conditions/direction_black.png")
.SetHidden() // Hide as 8 direction is not supported officially in the
// interface.
.AddParameter("object", _("Object"), "Sprite")
@@ -330,8 +332,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Compare the scale of the width of an object."),
_("the width's scale"),
_("Size"),
"res/conditions/scaleWidth24.png",
"res/conditions/scaleWidth.png")
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters("number")
@@ -342,8 +344,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Compare the scale of the height of an object."),
_("the height's scale"),
_("Size"),
"res/conditions/scaleHeight24.png",
"res/conditions/scaleHeight.png")
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters("number")
@@ -449,8 +451,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"Rotate an object towards another.",
"Rotate _PARAM0_ towards _PARAM1_",
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPtr", "Rotate toward this object")
@@ -461,7 +463,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("X position of a point"),
_("X position of a point"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPointName", _("Name of the point"), "", true);
@@ -470,7 +472,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Y position of a point"),
_("Y position of a point"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPointName", _("Name of the point"), "", true);
@@ -479,7 +481,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("X position of a point"),
_("X position of a point"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPointName", _("Name of the point"));
@@ -488,7 +490,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Y position of a point"),
_("Y position of a point"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPointName", _("Name of the point"));
@@ -497,7 +499,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Direction"),
_("Direction of the object"),
_("Direction"),
"res/actions/direction.png")
"res/actions/direction_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite");
@@ -505,7 +507,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Direction"),
_("Direction of the object"),
_("Direction"),
"res/actions/direction.png")
"res/actions/direction_black.png")
.SetHidden() // Hide as 8 direction is not supported officially in the
// interface.
.AddParameter("object", _("Object"), "Sprite");
@@ -550,14 +552,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Scale of the width of an object"),
_("Scale of the width of an object"),
_("Size"),
"res/actions/scaleWidth.png")
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Sprite");
obj.AddExpression("ScaleY",
_("Scale of the height of an object"),
_("Scale of the height of an object"),
_("Size"),
"res/actions/scaleHeight.png")
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Sprite");
obj.AddExpression("Opacity",

View File

@@ -25,8 +25,7 @@ namespace gd {
Animation SpriteObject::badAnimation;
SpriteObject::SpriteObject(gd::String name_)
: Object(name_), updateIfNotVisible(false) {}
SpriteObject::SpriteObject() : updateIfNotVisible(false) {}
SpriteObject::~SpriteObject(){};

View File

@@ -36,11 +36,11 @@ namespace gd {
* \see gd::BuiltinExtensionsImplementer::ImplementsSpriteExtension
* \ingroup SpriteObjectExtension
*/
class GD_CORE_API SpriteObject : public gd::Object {
class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
public:
SpriteObject(gd::String name_);
SpriteObject();
virtual ~SpriteObject();
std::unique_ptr<gd::Object> Clone() const override {
std::unique_ptr<gd::ObjectConfiguration> Clone() const override {
return gd::make_unique<SpriteObject>(*this);
}

View File

@@ -21,20 +21,20 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
extension.AddInstructionOrExpressionGroupMetadata(_("Text manipulation"))
.SetIcon("res/actions/text24.png");
.SetIcon("res/actions/text24_black.png");
extension.AddStrExpression("NewLine",
_("Insert a new line"),
_("Insert a new line"),
"",
"res/conditions/toujours24.png");
"res/conditions/toujours24_black.png");
extension
.AddStrExpression("FromCodePoint",
_("Get character from code point"),
_("Get character from code point"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("expression", _("Code point"));
@@ -43,7 +43,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Uppercase a text"),
_("Uppercase a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"));
@@ -52,7 +52,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Lowercase a text"),
_("Lowercase a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"));
@@ -61,7 +61,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Get a portion of a text"),
_("Get a portion of a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("expression",
@@ -74,7 +74,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Get a character from a text"),
_("Get a character from a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter(
@@ -86,7 +86,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Repeat a text"),
_("Repeat a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text to repeat"))
.AddParameter("expression", _("Repetition count"));
@@ -96,7 +96,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Length of a text"),
_("Length of a text"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"));
@@ -106,7 +106,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Search in a text (return the position of the result or "
"-1 if not found)"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"));
@@ -117,7 +117,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
"Search in a text from the end (return the position of "
"the result or -1 if not found)",
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"))
@@ -131,7 +131,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
"the result, from the beginning of the string, or -1 if not "
"found)"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"));
@@ -142,7 +142,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Search in a text, starting from a position (return the "
"position of the result or -1 if not found)"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"))
@@ -157,7 +157,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
"Search in a text from the end, starting from a position (return "
"the position of the result or -1 if not found)",
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"))
@@ -175,7 +175,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
" the position of the result, from the beginning of the string, or "
"-1 if not found)"),
"",
"res/conditions/toujours24.png")
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"))

View File

@@ -153,8 +153,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"the next actions (and sub-events)."),
_("Wait _PARAM0_ seconds"),
"",
"res/timer.svg",
"res/timer.svg")
"res/timer_black.svg",
"res/timer_black.svg")
.AddParameter("expression", "Time to wait in seconds")
.SetHelpPath("/all-features/timers-and-time/wait-action");

View File

@@ -20,6 +20,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
"these features can be applied.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("User interface")
.SetExtensionHelpPath("/all-features/window");
extension
.AddInstructionOrExpressionGroupMetadata(

View File

@@ -21,7 +21,7 @@ namespace gd {
BehaviorMetadata::BehaviorMetadata(
const gd::String& extensionNamespace_,
const gd::String& name_,
const gd::String& nameWithNamespace,
const gd::String& fullname_,
const gd::String& defaultName_,
const gd::String& description_,
@@ -45,32 +45,32 @@ BehaviorMetadata::BehaviorMetadata(
gd::LogFatalError(
"Trying to create a BehaviorMetadata that has no "
"behavior. This will crash - please double check that the "
"BehaviorMetadata is valid for: " + name_);
"BehaviorMetadata is valid for: " + nameWithNamespace);
}
if (instance) instance->SetTypeName(name_);
if (sharedDatasInstance) sharedDatasInstance->SetTypeName(name_);
if (instance) instance->SetTypeName(nameWithNamespace);
if (sharedDatasInstance) sharedDatasInstance->SetTypeName(nameWithNamespace);
}
BehaviorMetadata::BehaviorMetadata(
const gd::String& extensionNamespace,
const gd::String& name_,
const gd::String& nameWithNamespace,
const gd::String& fullname_,
const gd::String& defaultName_,
const gd::String& description_,
const gd::String& group_,
const gd::String& icon24x24_): BehaviorMetadata(
extensionNamespace,
name_,
nameWithNamespace,
fullname_,
// Default name is the name
name_,
defaultName_,
description_,
group_,
icon24x24_,
// Class name is the name, actually unused
name_,
defaultName_,
// It is only used to get the name for GetName.
gd::make_unique<gd::Behavior>("", name_),
gd::make_unique<gd::Behavior>("", nameWithNamespace),
nullptr){
isEventBased = true;
};

View File

@@ -29,7 +29,7 @@ class GD_CORE_API BehaviorMetadata {
public:
BehaviorMetadata(
const gd::String& extensionNamespace,
const gd::String& name_,
const gd::String& nameWithNamespace,
const gd::String& fullname_,
const gd::String& defaultName_,
const gd::String& description_,
@@ -46,8 +46,9 @@ class GD_CORE_API BehaviorMetadata {
*/
BehaviorMetadata(
const gd::String& extensionNamespace,
const gd::String& name_,
const gd::String& nameWithNamespace,
const gd::String& fullname_,
const gd::String& defaultName_,
const gd::String& description_,
const gd::String& group_,
const gd::String& icon24x24_);

View File

@@ -361,7 +361,7 @@ class GD_CORE_API InstructionMetadata {
* _("the string"),
* _("Text"),
* "CppPlatform/Extensions/text24.png",
* "CppPlatform/Extensions/text.png");
* "CppPlatform/Extensions/text_black.png");
*
* .AddParameter("object", _("Object"), "Text", false)
* .AddParameter("operator", _("Modification operator"), "string")

View File

@@ -23,23 +23,20 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24,
std::shared_ptr<gd::Object> blueprintObject_)
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24,
[blueprintObject_](gd::String name) -> std::unique_ptr<gd::Object> {
if (blueprintObject_ == std::shared_ptr<gd::Object>()) {
[blueprintObject_]() -> std::unique_ptr<gd::ObjectConfiguration> {
if (blueprintObject_ == std::shared_ptr<gd::ObjectConfiguration>()) {
gd::LogFatalError(
"Error: Unable to create object. Have you declared an extension "
"(or ObjectMetadata) without specifying an object as blueprint?");
return nullptr;
}
std::unique_ptr<gd::Object> newObject = blueprintObject_->Clone();
newObject->SetName(name);
return newObject;
return blueprintObject_->Clone();
}) {
blueprintObject = blueprintObject_;
}
@@ -54,7 +51,7 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
fullname_,
description_,
icon24x24,
[](gd::String name) -> std::unique_ptr<gd::Object> {
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
gd::LogFatalError(
"Error: Event-based objects don't have blueprint. "
"This method should not never be called.");

View File

@@ -13,6 +13,7 @@
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/String.h"
namespace gd {
class InstructionMetadata;
@@ -20,7 +21,7 @@ class MultipleInstructionMetadata;
class ExpressionMetadata;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
namespace gd {
@@ -42,7 +43,7 @@ class GD_CORE_API ObjectMetadata {
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24_,
std::shared_ptr<gd::Object> blueprintObject_);
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_);
/**
* \brief Construct an object metadata, without "blueprint" object
*
@@ -314,7 +315,7 @@ class GD_CORE_API ObjectMetadata {
gd::String categoryFullName;
std::set<gd::String> unsupportedBaseObjectCapabilities;
std::shared_ptr<gd::Object>
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new
///< object is asked. Can be null in case a creation
///< function is passed or for events based objects

View File

@@ -7,6 +7,7 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
@@ -92,8 +93,8 @@ std::shared_ptr<gd::PlatformExtension> Platform::GetExtension(
return std::shared_ptr<gd::PlatformExtension>();
}
std::unique_ptr<gd::Object> Platform::CreateObject(
gd::String type, const gd::String& name) const {
std::unique_ptr<gd::ObjectConfiguration> Platform::CreateObjectConfiguration(
gd::String type) const {
if (creationFunctionTable.find(type) == creationFunctionTable.end()) {
gd::LogWarning("Tried to create an object with an unknown type: " + type
+ " for platform " + GetName() + "!");
@@ -105,11 +106,9 @@ std::unique_ptr<gd::Object> Platform::CreateObject(
}
// Create a new object with the type we want.
std::unique_ptr<gd::Object> object =
(creationFunctionTable.find(type)->second)(name);
object->SetType(type);
return std::unique_ptr<gd::Object>(std::move(object));
auto objectConfiguration = (creationFunctionTable.find(type)->second)();
objectConfiguration->SetType(type);
return objectConfiguration;
}
#if defined(GD_IDE_ONLY)

View File

@@ -16,6 +16,7 @@ namespace gd {
class InstructionsMetadataHolder;
class Project;
class Object;
class ObjectConfiguration;
class Behavior;
class BehaviorMetadata;
class ObjectMetadata;
@@ -26,7 +27,7 @@ class LayoutEditorCanvas;
class ProjectExporter;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
#undef CreateEvent
@@ -146,8 +147,8 @@ class GD_CORE_API Platform {
/**
* \brief Create an object of given type with the specified name.
*/
std::unique_ptr<gd::Object> CreateObject(gd::String type,
const gd::String& name) const;
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
gd::String type) const;
/**
* \brief Create an event of given type

View File

@@ -18,6 +18,7 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Tools/Localization.h"
@@ -231,7 +232,7 @@ gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& fullname,
const gd::String& description,
const gd::String& icon24x24,
std::shared_ptr<gd::Object> instance) {
std::shared_ptr<gd::ObjectConfiguration> instance) {
gd::String nameWithNamespace = GetNameSpace() + name;
objectsInfos[nameWithNamespace] = ObjectMetadata(GetNameSpace(),
nameWithNamespace,
@@ -294,6 +295,8 @@ gd::BehaviorMetadata& PlatformExtension::AddEventsBasedBehavior(
behaviorsInfo[nameWithNamespace] = BehaviorMetadata(GetNameSpace(),
nameWithNamespace,
fullname,
// Default name is the name
name,
description,
group,
icon24x24)

View File

@@ -37,9 +37,10 @@ class ArbitraryResourceWorker;
class BehaviorsSharedData;
class Behavior;
class Object;
class ObjectConfiguration;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
namespace gd {
@@ -242,7 +243,7 @@ class GD_CORE_API PlatformExtension {
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::Object> instance);
std::shared_ptr<gd::ObjectConfiguration> instance);
/**
* \brief Declare a new events based object as being part of the extension.

View File

@@ -25,8 +25,8 @@ gd::ObjectMetadata& PlatformExtension::AddObject(const gd::String& name,
fullname,
description,
icon24x24,
[](gd::String name) -> std::unique_ptr<gd::Object> {
return gd::make_unique<T>(name);
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
return gd::make_unique<T>();
})
.SetHelpPath(GetHelpPath());

View File

@@ -1628,19 +1628,18 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
gd::Project& project,
gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::EventsBasedObject& eventsBasedObject,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
for (auto &functionUniquePtr : eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
auto function = functionUniquePtr.get();
auto *function = functionUniquePtr.get();
WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project,
*function,
globalObjectsContainer,
objectsContainer,
eventsBasedObject,
oldName,
newName,
isObjectGroup);

View File

@@ -333,9 +333,8 @@ class GD_CORE_API WholeProjectRefactorer {
*/
static void ObjectOrGroupRenamedInEventsBasedObject(
gd::Project& project,
gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::EventsBasedObject& eventsBasedObject,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);

View File

@@ -1,3 +1,8 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomBehavior.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
@@ -99,7 +104,7 @@ void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto &&property : properties.GetInternalVector()) {
auto element = behaviorContent.AddChild(property->GetName());
auto &element = behaviorContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||

View File

@@ -1,3 +1,11 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMBEHAVIOR_H
#define GDCORE_CUSTOMBEHAVIOR_H
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
@@ -7,6 +15,7 @@
using namespace gd;
namespace gd {
/**
* \brief A gd::Behavior that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
@@ -37,3 +46,6 @@ private:
const Project &project; ///< The project is used to get the
///< EventBasedBehavior from the fullType.
};
} // namespace gd
#endif // GDCORE_CUSTOMBEHAVIOR_H

View File

@@ -1,49 +0,0 @@
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
/**
* \brief A gd::Object that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
*
* It also implements "ExposeResources" to expose the properties of type
* "resource".
*/
class CustomObject : public gd::Object {
public:
CustomObject(const gd::String &name, const Project& project_, const gd::String &fullType)
: Object(name),
project(project_) {
SetType(fullType);
}
std::unique_ptr<gd::Object> Clone() const override;
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) override;
bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
private:
const Project& project; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
};

View File

@@ -1,4 +1,9 @@
#include "CustomObject.h"
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomObjectConfiguration.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Object.h"
@@ -6,27 +11,62 @@
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include <map>
#include "GDCore/Tools/Log.h"
using namespace gd;
std::unique_ptr<gd::Object> CustomObject::Clone() const {
CustomObject* clone = new CustomObject(*this);
return std::unique_ptr<gd::Object>(clone);
void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& objectConfiguration) {
project = objectConfiguration.project;
objectContent = objectConfiguration.objectContent;
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
childObjectConfigurations.clear();
for (auto& it : objectConfiguration.childObjectConfigurations) {
childObjectConfigurations[it.first] =
gd::make_unique<gd::ObjectConfiguration>(*it.second);
}
}
// TODO EBO Extract a class from Object for the object configuration.
// This will allow CustomObject to have a ObjectConfiguration composed of
// ObjectConfiguration for their children in addition to its own properties.
// This will be used by the GUI to display custom editors (for sprites for
// instance)
std::map<gd::String, gd::PropertyDescriptor> CustomObject::GetProperties() const {
gd::ObjectConfiguration CustomObjectConfiguration::badObjectConfiguration;
std::unique_ptr<gd::ObjectConfiguration> CustomObjectConfiguration::Clone() const {
CustomObjectConfiguration* clone = new CustomObjectConfiguration(*this);
return std::unique_ptr<gd::ObjectConfiguration>(clone);
}
gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(const gd::String &objectName) {
if (!project->HasEventsBasedObject(GetType())) {
return badObjectConfiguration;
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
if (!eventsBasedObject.HasObjectNamed(objectName)) {
gd::LogError("Tried to get the configuration of a child-object:" + objectName
+ " that doesn't exist in the event-based object: " + GetType());
return badObjectConfiguration;
}
auto &childObject = eventsBasedObject.GetObject(objectName);
auto configurationPosition = childObjectConfigurations.find(objectName);
if (configurationPosition == childObjectConfigurations.end()) {
childObjectConfigurations.insert(std::make_pair(
objectName,
project->CreateObjectConfiguration(childObject.GetType())));
return *(childObjectConfigurations[objectName]);
}
else {
auto &pair = *configurationPosition;
auto &configuration = pair.second;
return *configuration;
}
}
std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetProperties() const {
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project.HasEventsBasedObject(GetType())) {
if (!project->HasEventsBasedObject(GetType())) {
return objectProperties;
}
const auto &eventsBasedObject = project.GetEventsBasedObject(GetType());
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
for (auto &property : properties.GetInternalVector()) {
@@ -75,12 +115,12 @@ std::map<gd::String, gd::PropertyDescriptor> CustomObject::GetProperties() const
return objectProperties;
}
bool CustomObject::UpdateProperty(const gd::String& propertyName,
bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
const gd::String& newValue) {
if (!project.HasEventsBasedObject(GetType())) {
if (!project->HasEventsBasedObject(GetType())) {
return false;
}
const auto &eventsBasedObject = project.GetEventsBasedObject(GetType());
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
if (!properties.Has(propertyName)) {
return false;
@@ -106,14 +146,14 @@ bool CustomObject::UpdateProperty(const gd::String& propertyName,
}
std::map<gd::String, gd::PropertyDescriptor>
CustomObject::GetInitialInstanceProperties(
CustomObjectConfiguration::GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) {
return std::map<gd::String, gd::PropertyDescriptor>();
}
bool CustomObject::UpdateInitialInstanceProperty(
bool CustomObjectConfiguration::UpdateInitialInstanceProperty(
gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
@@ -122,15 +162,29 @@ bool CustomObject::UpdateInitialInstanceProperty(
return false;
}
void CustomObject::DoSerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = objectContent;
void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const {
element.AddChild("content") = objectContent;
auto &childrenContentElement = element.AddChild("childrenContent");
for (auto &pair : childObjectConfigurations) {
auto &childName = pair.first;
auto &childConfiguration = pair.second;
auto &childElement = childrenContentElement.AddChild(childName);
childConfiguration->SerializeTo(childElement);
}
}
void CustomObject::DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) {
objectContent = arg1.GetChild("content");
void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
const SerializerElement& element) {
objectContent = element.GetChild("content");
auto &childrenContentElement = element.GetChild("childrenContent");
for (auto &pair : childrenContentElement.GetAllChildren()) {
auto &childName = pair.first;
auto &childElement = pair.second;
auto &childConfiguration = GetChildObjectConfiguration(childName);
childConfiguration.UnserializeFrom(project, *childElement);
}
}
void CustomObject::ExposeResources(
void CustomObjectConfiguration::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
@@ -162,4 +216,15 @@ void CustomObject::ExposeResources(
}
}
}
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project->HasEventsBasedObject(GetType())) {
return;
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
for (auto& childObject : eventsBasedObject.GetObjects()) {
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
configuration.ExposeResources(worker);
}
}

View File

@@ -0,0 +1,100 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMOBJECTCONFIGURATION_H
#define GDCORE_CUSTOMOBJECTCONFIGURATION_H
#include "GDCore/Project/ObjectConfiguration.h"
#include <map>
#include <memory>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief A gd::ObjectConfiguration that stores its content in JSON and is
* composed of other configuration according to it's object children.
*
* It also implements "ExposeResources" to expose the properties of type
* "resource".
*/
class CustomObjectConfiguration : public gd::ObjectConfiguration {
public:
CustomObjectConfiguration(const Project& project_, const String& type_)
: project(&project_) {
SetType(type_);
}
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
/**
* Copy constructor. Calls Init().
*/
CustomObjectConfiguration(const gd::CustomObjectConfiguration& object)
: ObjectConfiguration(object) {
Init(object);
};
/**
* Assignment operator. Calls Init().
*/
CustomObjectConfiguration& operator=(const gd::CustomObjectConfiguration& object){
if ((this) != &object) {
ObjectConfiguration::operator=(object);
Init(object);
}
return *this;
}
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) override;
bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
gd::ObjectConfiguration &GetChildObjectConfiguration(const gd::String& objectName);
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
private:
const Project* project; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
static gd::ObjectConfiguration badObjectConfiguration;
/**
* Initialize configuration using another configuration. Used by copy-ctor
* and assign-op.
*
* Don't forget to update me if members were changed!
*
* It's needed because there is no default copy for childObjectConfigurations
* and it must be a deep copy.
*/
void Init(const gd::CustomObjectConfiguration& object);
};
} // namespace gd
#endif // GDCORE_CUSTOMOBJECTCONFIGURATION_H

View File

@@ -23,12 +23,16 @@ EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObj
}
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
element.SetAttribute("defaultName", defaultName);
AbstractEventsBasedEntity::SerializeTo(element);
SerializeObjectsTo(element.AddChild("objects"));
}
void EventsBasedObject::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
defaultName = element.GetStringAttribute("defaultName");
AbstractEventsBasedEntity::UnserializeFrom(project, element);
UnserializeObjectsFrom(project, element.GetChild("objects"));
}

View File

@@ -16,7 +16,7 @@ class Project;
} // namespace gd
namespace gd {
// TODO EBO Add a way to mark some parts of children configuration as readonly.
/**
* \brief Represents an object that is implemented with events.
*
@@ -38,6 +38,19 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
*/
EventsBasedObject* Clone() const { return new EventsBasedObject(*this); };
/**
* \brief Get the default name for created objects.
*/
const gd::String& GetDefaultName() const { return defaultName; };
/**
* \brief Set the default name for created objects.
*/
EventsBasedObject& SetDefaultName(const gd::String& defaultName_) {
defaultName = defaultName_;
return *this;
}
EventsBasedObject& SetDescription(const gd::String& description_) override {
AbstractEventsBasedEntity::SetDescription(description_);
return *this;
@@ -65,6 +78,7 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
const SerializerElement& element) override;
private:
gd::String defaultName;
};
} // namespace gd

View File

@@ -168,24 +168,10 @@ void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
gd::Project& project,
const SerializerElement& element) {
UnserializeEventsFunctionsFrom(project, element.GetChild("eventsFunctions"));
auto &behaviorsElement = element.GetChild("eventsBasedBehaviors");
behaviorsElement.ConsiderAsArrayOf("eventsBasedBehavior");
for (std::size_t i = 0; i < behaviorsElement.GetChildrenCount(); ++i) {
const gd::String &behaviorName =
behaviorsElement.GetChild(i).GetStringAttribute("name");
if (eventsBasedBehaviors.Has(behaviorName)) {
eventsBasedBehaviors.Get(behaviorName).UnserializeFrom(project, behaviorsElement.GetChild(i));
}
}
auto &objectsElement = element.GetChild("eventsBasedObjects");
objectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t i = 0; i < objectsElement.GetChildrenCount(); ++i) {
const gd::String &objectName =
objectsElement.GetChild(i).GetStringAttribute("name");
if (eventsBasedObjects.Has(objectName)) {
eventsBasedObjects.Get(objectName).UnserializeFrom(project, objectsElement.GetChild(i));
}
}
eventsBasedBehaviors.UnserializeElementsFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
eventsBasedObjects.UnserializeElementsFrom(
"eventsBasedObject", project, element.GetChild("eventsBasedObjects"));
}
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(

View File

@@ -117,10 +117,10 @@ std::map<gd::String, gd::PropertyDescriptor>
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
// Find an object
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName())
return layout.GetObject(GetObjectName()).GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName())
return project.GetObject(GetObjectName()).GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
std::map<gd::String, gd::PropertyDescriptor> nothing;
@@ -132,10 +132,10 @@ bool InitialInstance::UpdateCustomProperty(const gd::String& name,
gd::Project& project,
gd::Layout& layout) {
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName())
return layout.GetObject(GetObjectName()).GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName())
return project.GetObject(GetObjectName()).GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
return false;

View File

@@ -218,32 +218,8 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
if (behaviorsSharedData.find(name) != behaviorsSharedData.end()) continue;
if (project.HasEventsBasedBehavior(allBehaviorsTypes[i])) {
// Events based behaviors don't have shared data yet.
auto sharedData =
gd::make_unique<gd::BehaviorsSharedData>(name, allBehaviorsTypes[i]);
sharedData->InitializeContent();
behaviorsSharedData[name] = std::move(sharedData);
}
else {
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(),
allBehaviorsTypes[i]);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
continue;
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) continue;
auto sharedData =
gd::make_unique<gd::BehaviorsSharedData>(
*behaviorsSharedDataBluePrint);
sharedData->SetName(name);
sharedData->SetTypeName(allBehaviorsTypes[i]);
sharedData->InitializeContent();
auto sharedData = CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
if (sharedData) {
behaviorsSharedData[name] = std::move(sharedData);
}
}
@@ -264,6 +240,33 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
}
}
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
// Events based behaviors don't have shared data yet.
auto sharedData =
gd::make_unique<gd::BehaviorsSharedData>(name, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
return nullptr;
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) return nullptr;
auto sharedData = behaviorsSharedDataBluePrint->Clone();
sharedData->SetName(name);
sharedData->SetTypeName(behaviorsType);
sharedData->InitializeContent();
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
}
void Layout::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("mangledName", GetMangledName());
@@ -367,20 +370,23 @@ void Layout::UnserializeFrom(gd::Project& project,
"Behavior"); // Compatibility with GD <= 4
gd::String name = sharedDataElement.GetStringAttribute("name", "", "Name");
auto behavior = gd::make_unique<gd::BehaviorsSharedData>(name, type);
// Compatibility with GD <= 4.0.98
// If there is only one child called "content" (in addition to "type" and
// "name"), it's the content of a JavaScript behavior. Move the content
// out of the "content" object (to put it directly at the root of the
// behavior shared data element).
if (sharedDataElement.HasChild("content")) {
behavior->UnserializeFrom(sharedDataElement.GetChild("content"));
auto sharedData = CreateBehaviorsSharedData(project, name, type);
if (sharedData) {
// Compatibility with GD <= 4.0.98
// If there is only one child called "content" (in addition to "type" and
// "name"), it's the content of a JavaScript behavior. Move the content
// out of the "content" object (to put it directly at the root of the
// behavior shared data element).
if (sharedDataElement.HasChild("content")) {
sharedData->UnserializeFrom(sharedDataElement.GetChild("content"));
}
// end of compatibility code
else {
sharedData->UnserializeFrom(sharedDataElement);
}
behaviorsSharedData[name] = std::move(sharedData);
}
// end of compatibility code
else {
behavior->UnserializeFrom(sharedDataElement);
}
behaviorsSharedData[name] = std::move(behavior);
}
}
@@ -402,7 +408,7 @@ void Layout::Init(const Layout& other) {
behaviorsSharedData.clear();
for (const auto& it : other.behaviorsSharedData) {
behaviorsSharedData[it.first] =
gd::make_unique<gd::BehaviorsSharedData>(*(it.second->Clone()));
std::unique_ptr<gd::BehaviorsSharedData>(it.second->Clone());
}
events = other.events;

View File

@@ -404,6 +404,11 @@ class GD_CORE_API Layout : public ObjectsContainer {
* Don't forget to update me if members were changed!
*/
void Init(const gd::Layout& other);
std::unique_ptr<gd::BehaviorsSharedData> CreateBehaviorsSharedData(
gd::Project& project,
const gd::String& name,
const gd::String& behaviorsType);
};
/**

View File

@@ -20,12 +20,23 @@ namespace gd {
Object::~Object() {}
Object::Object(const gd::String& name_) : name(name_) {}
Object::Object(const gd::String& name_,
const gd::String& type_,
std::unique_ptr<gd::ObjectConfiguration> configuration_)
: name(name_), configuration(std::move(configuration_)) {
SetType(type_);
}
Object::Object(const gd::String& name_,
const gd::String& type_,
gd::ObjectConfiguration* configuration_)
: name(name_), configuration(configuration_) {
SetType(type_);
}
void Object::Init(const gd::Object& object) {
name = object.name;
assetStoreId = object.assetStoreId;
type = object.type;
objectVariables = object.objectVariables;
tags = object.tags;
effectsContainer = object.effectsContainer;
@@ -34,6 +45,16 @@ void Object::Init(const gd::Object& object) {
for (auto& it : object.behaviors) {
behaviors[it.first] = gd::make_unique<gd::Behavior>(*it.second);
}
configuration = object.configuration->Clone();
}
gd::ObjectConfiguration& Object::GetConfiguration() {
return *configuration;
}
const gd::ObjectConfiguration& Object::GetConfiguration() const {
return *configuration;
}
std::vector<gd::String> Object::GetAllBehaviorNames() const {
@@ -72,11 +93,6 @@ bool Object::HasBehaviorNamed(const gd::String& name) const {
return behaviors.find(name) != behaviors.end();
}
std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name) {
@@ -109,17 +125,9 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
}
}
std::map<gd::String, gd::PropertyDescriptor>
Object::GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout) {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
void Object::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
type = element.GetStringAttribute("type");
SetType(element.GetStringAttribute("type"));
assetStoreId = element.GetStringAttribute("assetStoreId");
name = element.GetStringAttribute("name", name, "nom");
tags = element.GetStringAttribute("tags");
@@ -186,7 +194,7 @@ void Object::UnserializeFrom(gd::Project& project,
}
}
DoUnserializeFrom(project, element);
configuration->UnserializeFrom(project, element);
}
void Object::SerializeTo(SerializerElement& element) const {
@@ -212,7 +220,7 @@ void Object::SerializeTo(SerializerElement& element) const {
behaviorElement.SetAttribute("name", behavior.GetName());
}
DoSerializeTo(element);
configuration->SerializeTo(element);
}
} // namespace gd

View File

@@ -11,10 +11,12 @@
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class PropertyDescriptor;
class Project;
@@ -28,7 +30,7 @@ class EffectsContainer;
namespace gd {
/**
* \brief Base class used to represent an object of a platform
* \brief Represent an object of a platform
*
* \ingroup PlatformDefinition
*/
@@ -36,9 +38,19 @@ class GD_CORE_API Object {
public:
/**
* Create a new object with the name passed as argument.
* \param name Object's name
*/
Object(const gd::String& name);
Object(const gd::String& name,
const gd::String& type,
std::unique_ptr<gd::ObjectConfiguration> configuration);
/**
* Create a new object with the name passed as argument.
*
* Object takes the ownership of the configuration.
*/
Object(const gd::String& name,
const gd::String& type,
gd::ObjectConfiguration* configuration);
/**
* Copy constructor. Calls Init().
@@ -70,6 +82,13 @@ class GD_CORE_API Object {
return gd::make_unique<gd::Object>(*this);
}
/**
* \brief Return the object configuration.
*/
gd::ObjectConfiguration& GetConfiguration();
const gd::ObjectConfiguration& GetConfiguration() const;
/** \name Common properties
* Members functions related to common properties
*/
@@ -93,11 +112,15 @@ class GD_CORE_API Object {
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
void SetType(const gd::String& type_) {
configuration->SetType(type_);
}
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
const gd::String& GetType() const {
return configuration->GetType();
}
/** \brief Change the tags of the object.
*/
@@ -108,92 +131,6 @@ class GD_CORE_API Object {
const gd::String& GetTags() const { return tags; }
///@}
/** \name Resources management
* Members functions related to managing resources used by the object
*/
///@{
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources and sometimes update their filename. Implementation example:
* \code
* worker.ExposeImage(myImage);
* worker.ExposeFile(myResourceFile);
* \endcode
*
* \see ArbitraryResourceWorker
*/
virtual void ExposeResources(gd::ArbitraryResourceWorker& worker) { return; };
/**
* Redefine this function to return true if your object can use shaders.
*/
virtual bool SupportShaders() { return false; }
///@}
/** \name Object properties
* Reading and updating object properties
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of the
object.
*
* Usage example:
\code
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[ToString(_("Text"))].SetValue("Hello world!");
return properties;
\endcode
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the object
*
* \return false if the new value cannot be set
*/
virtual bool UpdateProperty(const gd::String& name, const gd::String& value) {
return false;
};
///@}
/** \name Drawing and editing initial instances
* Members functions related to drawing and editing initial instances of this
* object
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of an
* initial instance of this object.
*
* \return a std::map with properties names as key and values.
* \see gd::InitialInstance
*/
virtual std::map<gd::String, gd::PropertyDescriptor>
GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout);
/**
* \brief Called when the IDE wants to update a custom property of an initial
* instance of this object.
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& layout) {
return false;
};
///@}
/** \name Behaviors management
* Members functions related to behaviors management.
*/
@@ -309,8 +246,7 @@ class GD_CORE_API Object {
protected:
gd::String name; ///< The full name of the object
gd::String assetStoreId; ///< The ID of the asset if the object comes from the store.
gd::String type; ///< Which type is the object. ( To test if we can do
///< something reserved to some objects with it )
std::unique_ptr<gd::ObjectConfiguration> configuration;
std::map<gd::String, std::unique_ptr<gd::Behavior>>
behaviors; ///< Contains all behaviors and their properties for the
///< object. Behavior contents are the ownership of the
@@ -321,20 +257,12 @@ class GD_CORE_API Object {
gd::EffectsContainer
effectsContainer; ///< The effects container for the object.
/**
* \brief Derived objects can redefine this method to load custom attributes.
*/
virtual void DoUnserializeFrom(gd::Project& project,
const SerializerElement& element){};
/**
* \brief Derived objects can redefine this method to save custom attributes.
*/
virtual void DoSerializeTo(SerializerElement& element) const {};
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
* Don't forget to update me if members were changed!
*
* It's needed because there is no default copy for a map of unique_ptr like
* behaviors and it must be a deep copy.
*/
void Init(const gd::Object& object);
};

View File

@@ -0,0 +1,47 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Tools/Log.h"
namespace gd {
ObjectConfiguration::~ObjectConfiguration() {}
ObjectConfiguration::ObjectConfiguration() {}
std::map<gd::String, gd::PropertyDescriptor> ObjectConfiguration::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectConfiguration::GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout) {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
void ObjectConfiguration::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
DoUnserializeFrom(project, element);
}
void ObjectConfiguration::SerializeTo(SerializerElement& element) const {
DoSerializeTo(element);
}
} // namespace gd

View File

@@ -0,0 +1,194 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_OBJECTCONFIGURATION_H
#define GDCORE_OBJECTCONFIGURATION_H
#include "GDCore/Vector2.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class PropertyDescriptor;
class Project;
class Layout;
class ArbitraryResourceWorker;
class InitialInstance;
class SerializerElement;
class EffectsContainer;
} // namespace gd
namespace gd {
/**
* \brief Base class used to represent an object configuration.
* For example, this can be the animations in a sprite, the text, its font,
* its color in a Text object, etc...
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API ObjectConfiguration {
public:
/**
* Create a new object configuration.
*/
ObjectConfiguration();
/**
* Destructor.
*/
virtual ~ObjectConfiguration();
/**
* Must return a pointer to a copy of the configuration. This method is
* needed to do polymorphic copies. Just redefine this method in your derived
* object class like this:
* \code
* return gd::make_unique<MyObjectConfiguration>(*this);
* \endcode
*/
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<gd::ObjectConfiguration>(*this);
}
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
/** \name Object properties
* Reading and updating object configuration properties
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of the
object configuration.
*
* Usage example:
\code
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[ToString(_("Text"))].SetValue("Hello world!");
return properties;
\endcode
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the object
* configuration.
*
* \return false if the new value cannot be set
*/
virtual bool UpdateProperty(const gd::String& name, const gd::String& value) {
return false;
};
///@}
/** \name Drawing and editing initial instances
* Members functions related to drawing and editing initial instances of this
* object configuration
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of an
* initial instance of this object configuration.
*
* \return a std::map with properties names as key and values.
* \see gd::InitialInstance
*/
virtual std::map<gd::String, gd::PropertyDescriptor>
GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout);
/**
* \brief Called when the IDE wants to update a custom property of an initial
* instance of this object configuration.
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& layout) {
return false;
};
///@}
/** \name Resources management
* Members functions related to managing resources used by the object configuration
*/
///@{
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources and sometimes update their filename. Implementation example:
* \code
* worker.ExposeImage(myImage);
* worker.ExposeFile(myResourceFile);
* \endcode
*
* \see ArbitraryResourceWorker
*/
virtual void ExposeResources(gd::ArbitraryResourceWorker& worker) { return; };
///@}
/** \name Serialization
* Members functions related to serialization of the object configuration
*/
///@{
/**
* \brief Serialize the object configuration.
* \see DoSerializeTo
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the object configuration.
* \see DoUnserializeFrom
*/
void UnserializeFrom(gd::Project& project, const SerializerElement& element);
///@}
protected:
gd::String type; ///< Which type of object is represented by this
///< configuration.
/**
* \brief Derived object configuration can redefine this method to load
* custom attributes.
*/
virtual void DoUnserializeFrom(gd::Project& project,
const SerializerElement& element){};
/**
* \brief Derived object configuration can redefine this method to save
* custom attributes.
*/
virtual void DoSerializeTo(SerializerElement& element) const {};
};
} // namespace gd
/**
* Object configurations are usually managed thanks to (smart) pointers.
*/
using ObjConfSPtr = std::unique_ptr<gd::ObjectConfiguration>;
#endif // GDCORE_OBJECT_H

View File

@@ -23,11 +23,12 @@
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/CustomObject.h"
#include "GDCore/Project/CustomObjectConfiguration.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/SourceFile.h"
@@ -82,12 +83,17 @@ void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& type,
const gd::String& name) const {
return gd::make_unique<Object>(name, type, CreateObjectConfiguration(type));
}
std::unique_ptr<gd::ObjectConfiguration> Project::CreateObjectConfiguration(
const gd::String& type) const {
if (Project::HasEventsBasedObject(type)) {
return gd::make_unique<CustomObject>(name, *this, type);
return gd::make_unique<CustomObjectConfiguration>(*this, type);
}
else {
// Create a base object if the type can't be found in the platform.
return currentPlatform->CreateObject(type, name);
return currentPlatform->CreateObjectConfiguration(type);
}
}
@@ -940,8 +946,9 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// Add layouts resources
for (std::size_t s = 0; s < GetLayoutsCount(); s++) {
for (std::size_t j = 0; j < GetLayout(s).GetObjectsCount();
++j) // Add objects resources
GetLayout(s).GetObject(j).ExposeResources(worker);
++j) { // Add objects resources
GetLayout(s).GetObject(j).GetConfiguration().ExposeResources(worker);
}
LaunchResourceWorkerOnEvents(*this, GetLayout(s).GetEvents(), worker);
}
@@ -960,7 +967,7 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// Add global objects resources
for (std::size_t j = 0; j < GetObjectsCount(); ++j) {
GetObject(j).ExposeResources(worker);
GetObject(j).GetConfiguration().ExposeResources(worker);
}
// Add loading screen background image if present

View File

@@ -27,6 +27,7 @@ class EventsFunctionsExtension;
class EventsBasedObject;
class EventsBasedBehavior;
class Object;
class ObjectConfiguration;
class VariablesContainer;
class ArbitraryResourceWorker;
class SourceFile;
@@ -464,6 +465,14 @@ class GD_CORE_API Project : public ObjectsContainer {
std::unique_ptr<gd::Object> CreateObject(const gd::String& type,
const gd::String& name) const;
/**
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
/**
* Create an event of the given type.
*

View File

@@ -13,12 +13,14 @@
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "DummyPlatform.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "catch.hpp"
class ArbitraryResourceWorkerTest : public gd::ArbitraryResourceWorker {
@@ -63,14 +65,15 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
"path/to/file4.png") != worker.files.end());
SECTION("Object using a resource") {
gd::SpriteObject obj("myObject");
gd::SpriteObject spriteConfiguration;
gd::Animation anim;
gd::Sprite sprite;
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
obj.AddAnimation(anim);
spriteConfiguration.AddAnimation(anim);
gd::Object obj("myObject", "", spriteConfiguration.Clone());
project.InsertObject(obj, 0);
worker.files.clear();

View File

@@ -120,12 +120,14 @@ void CheckBehaviorProperty(ObjectsContainer &container) {
};
} // namespace
TEST_CASE("ProjectSerialization", "[common]") {
// TODO EBO Add similar test cases for events-based objects.
TEST_CASE("BehaviorSerialization", "[common]") {
SECTION("Save and load a project with a custom behavior property value") {
gd::Platform platform;
gd::Project writtenProject;
SetupProject(writtenProject, platform);
CheckBehaviorProperty(writtenProject.GetLayout("Scene"));
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);

View File

@@ -7,6 +7,8 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
@@ -98,7 +100,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
// Create the base object. All objects "inherits" from it.
baseObjectExtension->SetExtensionInformation(
"BuiltinObject", "Base Object dummy extension", "", "", "");
auto& baseObject = baseObjectExtension->AddObject<gd::Object>(
auto& baseObject = baseObjectExtension->AddObject<gd::ObjectConfiguration>(
"", "Dummy Base Object", "Dummy Base Object", "");
// Add this expression for all objects. But it requires a "capability".
@@ -130,6 +132,18 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("expression", "Parameter 1 (a number)")
.SetFunctionName("doSomething");
extension
->AddAction("DoSomethingWithObjects",
"Do something",
"This does something",
"Do something please",
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("object", _("Object 2 parameter"))
.SetFunctionName("doSomethingWithObjects");
extension
->AddAction("DoSomethingWithResources",
"Do something with resources",
@@ -214,7 +228,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("objectvar", _("Variable for object 2"))
.SetFunctionName("getStringWith1ObjectParamAnd2ObjectVarParam");
auto& object = extension->AddObject<gd::Object>(
auto& object = extension->AddObject<gd::SpriteObject>(
"Sprite", "Dummy Sprite", "Dummy sprite object", "");
object
.AddExpression("GetObjectVariableAsNumber",
@@ -351,7 +365,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
{
auto& object = extension
->AddObject<gd::Object>(
->AddObject<gd::ObjectConfiguration>(
"FakeObjectWithUnsupportedCapability",
"FakeObjectWithUnsupportedCapability",
"This is FakeObjectWithUnsupportedCapability",

View File

@@ -0,0 +1,76 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering events-based extensions
*/
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include <algorithm>
#include <stdexcept>
#include "DummyPlatform.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/UnfilledRequiredBehaviorPropertyProblem.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "catch.hpp"
namespace {
gd::EventsFunctionsExtension &
SetupProjectWithEventsFunctionExtension(gd::Project &project) {
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
// Add an events-based behavior
{
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().InsertNew(
"MyEventsBasedBehavior", 0);
eventsBasedBehavior.SetFullName("My events based behavior");
eventsBasedBehavior.SetDescription("An events based behavior for test");
// Add a property
eventsBasedBehavior.GetPropertyDescriptors()
.InsertNew("MyProperty", 0)
.SetValue("123")
.SetType("Number");
}
return eventsExtension;
}
} // namespace
TEST_CASE("Events-based extension", "[common]") {
SECTION("Behavior property default value") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &object = layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
// Attach a behavior to an object.
auto *behavior = object.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyEventsBasedBehavior");
behavior->InitializeContent();
// The behavior has the default value.
REQUIRE(behavior->GetProperties().size() == 1);
REQUIRE(behavior->GetProperties().at("MyProperty").GetValue() == "123");
}
}

View File

@@ -0,0 +1,111 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering serialization to JSON.
*/
#include "DummyPlatform.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
using namespace gd;
namespace {
void SetupProject(gd::Project &project, gd::Platform &platform) {
SetupProjectWithDummyPlatform(project, platform);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object =
layout.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
auto &configuration = object.GetConfiguration();
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration != nullptr);
gd::Animation animation;
animation.SetName("Idle");
spriteConfiguration->AddAnimation(animation);
};
void CheckSpriteConfiguration(
SerializerElement &objectContainerElement) {
};
void CheckSpriteConfigurationInElement(SerializerElement &projectElement) {
auto &layoutsElement = projectElement.GetChild("layouts");
layoutsElement.ConsiderAsArrayOf("layout");
REQUIRE(layoutsElement.GetChildrenCount() == 1);
auto &layoutElement = layoutsElement.GetChild(0);
REQUIRE(layoutElement.GetStringAttribute("name") == "Scene");
REQUIRE(layoutElement.HasChild("objects"));
auto &objectsElement = layoutElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
REQUIRE(objectsElement.GetChildrenCount() == 1);
auto &objectElement = objectsElement.GetChild(0);
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") == "MyExtension::Sprite");
REQUIRE(objectElement.HasChild("animations"));
auto &animationsElement = objectElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") ==
"Idle");
};
void CheckSpriteConfiguration(gd::Project &project) {
auto &layout = project.GetLayout("Scene");
auto &object = layout.GetObject("MyObject");
REQUIRE(object.GetName() == "MyObject");
REQUIRE(object.GetType() == "MyExtension::Sprite");
auto &configuration = object.GetConfiguration();
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration);
REQUIRE(spriteConfiguration->GetAnimationsCount() == 1);
auto &animation = spriteConfiguration->GetAnimation(0);
REQUIRE(animation.GetName() == "Idle");
};
} // namespace
TEST_CASE("ObjectSerialization", "[common]") {
SECTION("Save and load a project with a sprite configuration") {
gd::Platform platform;
gd::Project writtenProject;
SetupProject(writtenProject, platform);
CheckSpriteConfiguration(writtenProject);
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
CheckSpriteConfigurationInElement(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
CheckSpriteConfiguration(readProject);
}
}

View File

@@ -67,7 +67,9 @@ const gd::String &GetEventFirstActionType(const gd::BaseEvent &event) {
enum TestEvent {
FreeFunctionAction,
FreeFunctionExpression,
FreeFunctionWithExpression,
FreeFunctionWithObjects,
FreeFunctionWithObjectExpression,
BehaviorAction,
BehaviorPropertyAction,
@@ -113,7 +115,7 @@ const void SetupEvents(gd::EventsList &eventList) {
if (eventList.GetEventsCount() != FreeFunctionAction) {
throw std::logic_error("Invalid events setup");
}
// Create an event in the layout referring to
// Create an event referring to
// MyEventsExtension::MyEventsFunction
{
gd::StandardEvent event;
@@ -128,10 +130,10 @@ const void SetupEvents(gd::EventsList &eventList) {
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionExpression) {
if (eventList.GetEventsCount() != FreeFunctionWithExpression) {
throw std::logic_error("Invalid events setup");
}
// Create an event in the external events referring to
// Create an event referring to
// MyEventsExtension::MyEventsFunctionExpression
{
gd::StandardEvent event;
@@ -145,6 +147,38 @@ const void SetupEvents(gd::EventsList &eventList) {
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjects) {
throw std::logic_error("Invalid events setup");
}
// Create an event referring to objects
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomethingWithObjects");
action.SetParametersCount(2);
action.SetParameter(0, gd::Expression("ObjectWithMyBehavior"));
action.SetParameter(1, gd::Expression("MyCustomObject"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjectExpression) {
throw std::logic_error("Invalid events setup");
}
// Create an event referring to objects in an expression
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
"ObjectWithMyBehavior.GetObjectNumber()"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
}
// Add some events based behavior usages in events
@@ -854,6 +888,30 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject(
"GlobalObject3") == true);
}
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout, "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
}
SECTION("Object renamed (in events function)") {
@@ -894,6 +952,43 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Events are not tested
}
SECTION("Object renamed (in events-based object)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
project.GetEventsFunctionsExtension("MyEventsExtension")
.GetEventsBasedObjects()
.Get("MyOtherEventsBasedObject");
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
project, globalObjectsContainer, eventsBasedObject,
"ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
auto &objectFunctionEvents =
eventsBasedObject
.GetEventsFunctions()
.GetEventsFunction("MyObjectEventsFunction")
.GetEvents();
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(
objectFunctionEvents.GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
objectFunctionEvents.GetEvent(FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
SECTION("Object deleted (in events function)") {
gd::Project project;
gd::Platform platform;
@@ -948,7 +1043,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Check that events function calls in expressions have been renamed
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyRenamedExtension::MyEventsFunctionExpression(123, 456)");
// Check that the type of the behavior was changed in the behaviors of
@@ -1137,7 +1232,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been renamed
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyEventsExtension::MyRenamedFunctionExpression(123, 456)");
}
}
@@ -1177,7 +1272,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been updated
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyEventsExtension::MyEventsFunctionExpression(456, 123)");
}
}

View File

@@ -28,7 +28,7 @@ module.exports = {
'Arthur Pacaud (arthuro555)',
'MIT'
)
.setCategory('Device');
.setCategory('User interface');
extension
.addInstructionOrExpressionGroupMetadata(_('Advanced window management'))
.setIcon('res/actions/window24.png');

View File

@@ -17,6 +17,7 @@ void DeclareAnchorBehaviorExtension(gd::PlatformExtension& extension) {
_("Anchor objects to the window's bounds."),
"Victor Levasseur",
"Open source (MIT License)")
.SetCategory("User interface")
.SetExtensionHelpPath("/behaviors/anchor");
gd::BehaviorMetadata& aut = extension.AddBehavior(

View File

@@ -33,7 +33,10 @@ module.exports = {
'Todor Imreorov',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bbtext');
.setExtensionHelpPath('/objects/bbtext')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("BBCode Text Object"))
.setIcon("JsPlatform/Extensions/bbcode32.png");
var objectBBText = new gd.ObjectJsImplementation();
// $FlowExpectedError
@@ -168,7 +171,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd.js'
)
.setCategoryFullName(_('Texts'));
.setCategoryFullName(_('User interface'));
/**
* Utility function to add both a setter and a getter to a property from a list.
@@ -289,7 +292,7 @@ module.exports = {
const setterAndGetterProperties = [
{
functionName: 'BBText',
iconPath: 'res/actions/text24.png',
iconPath: 'res/actions/text24_black.png',
type: 'string',
paramLabel: _('BBCode text'),
conditionDescription: _('Compare the value of the BBCode text.'),
@@ -364,7 +367,7 @@ module.exports = {
},
{
functionName: 'WordWrap',
iconPath: 'res/actions/scaleWidth24.png',
iconPath: 'res/actions/scaleWidth24_black.png',
type: 'boolean',
paramLabel: _('Word wrap'),
conditionDescription: _('Check if word wrap is enabled.'),
@@ -376,7 +379,7 @@ module.exports = {
},
{
functionName: 'WrappingWidth',
iconPath: 'res/actions/scaleWidth24.png',
iconPath: 'res/actions/scaleWidth24_black.png',
type: 'number',
paramLabel: _('Wrapping width'),
conditionDescription: _(
@@ -454,7 +457,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -463,7 +466,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -498,7 +501,7 @@ module.exports = {
RenderedBBTextInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/bbcode24.png';
};
@@ -507,7 +510,8 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
RenderedBBTextInstance.prototype.update = function () {
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {

View File

@@ -35,7 +35,10 @@ module.exports = {
'Aurélien Vivet',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bitmap_text');
.setExtensionHelpPath('/objects/bitmap_text')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Bitmap Text"))
.setIcon("JsPlatform/Extensions/bitmapfont32.png");
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
@@ -171,7 +174,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
)
.setCategoryFullName(_('Texts'));
.setCategoryFullName(_('User interface'));
object
.addExpressionAndConditionAndAction(
@@ -181,7 +184,7 @@ module.exports = {
_('the text'),
_('the text'),
'',
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('string')
@@ -225,7 +228,7 @@ module.exports = {
_('the scale (1 by default)'),
_('the scale'),
'',
'res/actions/scale24.png'
'res/actions/scale24_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
@@ -331,8 +334,8 @@ module.exports = {
_('Check if word wrap is enabled.'),
_('_PARAM0_ word wrap is enabled'),
'',
'res/conditions/wordWrap24.png',
'res/conditions/wordWrap.png'
'res/conditions/wordWrap24_black.png',
'res/conditions/wordWrap_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.getCodeExtraInformation()
@@ -345,8 +348,8 @@ module.exports = {
_('De/activate word wrapping.'),
_('Activate word wrap of _PARAM0_: _PARAM1_'),
'',
'res/actions/wordWrap24.png',
'res/actions/wordWrap.png'
'res/actions/wordWrap24_black.png',
'res/actions/wordWrap_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter('yesorno', _('Activate word wrap'), '', false)
@@ -361,7 +364,7 @@ module.exports = {
_('the width, in pixels, after which the text is wrapped on next line'),
_('the wrapping width'),
'',
'res/actions/scaleWidth24.png'
'res/actions/scaleWidth24_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
@@ -589,7 +592,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -598,7 +601,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -627,14 +630,15 @@ module.exports = {
RenderedBitmapTextInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/bitmapfont24.png';
};
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).

View File

@@ -20,6 +20,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
"or other short-lived objects."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Game mechanic")
.SetExtensionHelpPath("/behaviors/destroyoutside");
gd::BehaviorMetadata& aut =

View File

@@ -31,7 +31,7 @@ module.exports = {
"Matthias Meike",
"Open source (MIT License)"
).setExtensionHelpPath("/all-features/device-sensors")
.setCategory('Device');
.setCategory('Input');
extension.addInstructionOrExpressionGroupMetadata(_("Device sensors"))
.setIcon("JsPlatform/Extensions/orientation_active32.png");

View File

@@ -35,7 +35,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/device-vibration')
.setCategory('Device');
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Device vibration"))
.setIcon("JsPlatform/Extensions/vibration_start32.png");

View File

@@ -34,7 +34,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/dialogue-tree')
.setCategory('Advanced');
.setCategory('Game mechanic');
extension
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree (experimental)'))
.setIcon('JsPlatform/Extensions/yarn32.png');

View File

@@ -20,6 +20,7 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
"or disable the behavior when needed."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("User interface")
.SetExtensionHelpPath("/behaviors/draggable");
gd::BehaviorMetadata& aut = extension.AddBehavior(

View File

@@ -31,7 +31,9 @@ module.exports = {
'Lots of different effects to be used in your game.',
'Various contributors from PixiJS, PixiJS filters and GDevelop',
'MIT'
).setExtensionHelpPath('/interface/scene-editor/layer-effects');
)
.setCategory('Visual effect')
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
// You can declare an effect here. Please order the effects by alphabetical order.
// This file is for common effects that are well-known/"battle-tested". If you have an

View File

@@ -506,7 +506,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -515,7 +515,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -539,7 +539,7 @@ module.exports = {
RenderedDummyObjectInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'CppPlatform/Extensions/texticon24.png';
};
@@ -549,7 +549,7 @@ module.exports = {
*/
RenderedDummyObjectInstance.prototype.update = function () {
// Read a property from the object
const property1Value = this._associatedObject
const property1Value = this._associatedObjectConfiguration
.getProperties()
.get('My first property')
.getValue();

View File

@@ -34,7 +34,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/filesystem')
.setCategory('Device');
.setCategory('Advanced');
extension
.addInstructionOrExpressionGroupMetadata(_('File system'))
.setIcon('JsPlatform/Extensions/filesystem_create_folder32.png');
@@ -203,6 +203,18 @@ module.exports = {
'',
true
)
.addParameter(
'yesorno',
_('Normalize the file content (recommended)'),
'',
true
)
.setParameterLongDescription(
_(
'This replaces Windows new lines characters ("CRLF") by a single new line character.'
)
)
.setDefaultValue('yes')
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.loadStringFromFileAsync');
@@ -229,6 +241,18 @@ module.exports = {
'',
true
)
.addParameter(
'yesorno',
_('Normalize the file content (recommended)'),
'',
true
)
.setParameterLongDescription(
_(
'This replaces Windows new lines characters ("CRLF") by a single new line character.'
)
)
.setDefaultValue('yes')
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.loadStringFromFile');
@@ -255,6 +279,18 @@ module.exports = {
'',
true
)
.addParameter(
'yesorno',
_('Normalize the file content (recommended)'),
'',
true
)
.setParameterLongDescription(
_(
'This replaces Windows new lines characters ("CRLF") by a single new line character.'
)
)
.setDefaultValue('yes')
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.loadVariableFromJSONFile');
@@ -281,6 +317,18 @@ module.exports = {
'',
true
)
.addParameter(
'yesorno',
_('Normalize the file content (recommended)'),
'',
true
)
.setParameterLongDescription(
_(
'This replaces Windows new lines characters ("CRLF") by a single new line character.'
)
)
.setDefaultValue('yes')
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.loadVariableFromJSONFileAsync');

View File

@@ -338,11 +338,13 @@ namespace gdjs {
* @param stringVar Variable where to store the content
* @param loadPath Path to the file
* @param resultVar The variable where to store the result of the operation
* @param removeCRCharacters If true, will remove \r characters usually added by Windows when editing files
*/
export const loadStringFromFile = function (
stringVar: gdjs.Variable,
loadPath: string,
resultVar: gdjs.Variable
resultVar: gdjs.Variable,
removeCRCharacters: boolean
) {
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
@@ -350,7 +352,9 @@ namespace gdjs {
try {
const data = fileSystem.readFileSync(loadPath, 'utf8');
if (data) {
stringVar.setString(data);
stringVar.setString(
removeCRCharacters ? data.replace(/\r/g, '') : data
);
result = 'ok';
}
} catch (err) {
@@ -368,11 +372,13 @@ namespace gdjs {
* @param variable Variable to store the variable
* @param loadPath Path to the file
* @param resultVar The variable where to store the result of the operation
* @param removeCRCharacters If true, will remove \r characters usually added by Windows when editing files
*/
export const loadVariableFromJSONFile = function (
variable: gdjs.Variable,
loadPath: string,
resultVar: gdjs.Variable
resultVar: gdjs.Variable,
removeCRCharacters: boolean
) {
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
@@ -380,7 +386,9 @@ namespace gdjs {
try {
const data = fileSystem.readFileSync(loadPath, 'utf8');
if (data) {
variable.fromJSON(data);
variable.fromJSON(
removeCRCharacters ? data.replace(/\r/g, '') : data
);
result = 'ok';
}
} catch (err) {
@@ -400,17 +408,21 @@ namespace gdjs {
* @param variable Variable to store the variable
* @param loadPath Path to the file
* @param resultVar The variable where to store the result of the operation
* @param removeCRCharacters If true, will remove \r characters usually added by Windows when editing files
*/
export const loadVariableFromJSONFileAsync = function (
variable: gdjs.Variable,
loadPath: string,
resultVar: gdjs.Variable
resultVar: gdjs.Variable,
removeCRCharacters: boolean
) {
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
fileSystem.readFile(loadPath, 'utf8', (err, data) => {
if (data) {
variable.fromJSON(data);
variable.fromJSON(
removeCRCharacters ? data.replace(/\r/g, '') : data
);
resultVar.setString('ok');
}
if (err) {
@@ -431,17 +443,21 @@ namespace gdjs {
* @param stringVar Variable where to store the content
* @param loadPath Path to the file
* @param resultVar The variable where to store the result of the operation
* @param removeCRCharacters If true, will remove \r characters usually added by Windows when editing files
*/
export const loadStringFromFileAsync = function (
stringVar: gdjs.Variable,
loadPath: string,
resultVar: gdjs.Variable
resultVar: gdjs.Variable,
removeCRCharacters: boolean
) {
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
fileSystem.readFile(loadPath, 'utf8', (err, data) => {
if (data) {
stringVar.setString(data);
stringVar.setString(
removeCRCharacters ? data.replace(/\r/g, '') : data
);
resultVar.setString('ok');
}
if (err) {

View File

@@ -19,7 +19,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/inventory")
.SetCategory("Advanced");
.SetCategory("Game mechanic");
extension
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");

View File

@@ -20,7 +20,7 @@ export type ObjectsRenderingService = {
RenderedInstance: any,
registerInstanceRenderer: (objectType: string, renderer: any) => void,
requireModule: (dirname: string, moduleName: string) => any,
getThumbnail: (project: gdProject, object: gdObject) => string,
getThumbnail: (project: gdProject, objectConfiguration: gdObjectConfiguration) => string,
rgbOrHexToHexNumber: (value: string) => number,
};
export type ObjectsEditorService = {

View File

@@ -34,7 +34,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/leaderboards')
.setCategory('Leaderboards')
.setCategory('Players')
.addInstructionOrExpressionGroupMetadata(_('Leaderboards (experimental)'))
.setIcon('JsPlatform/Extensions/leaderboard.svg');
@@ -65,6 +65,32 @@ module.exports = {
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.savePlayerScore');
extension
.addAction(
'SaveConnectedPlayerScore',
_('Save connected player score'),
_("Save the connected player's score to the given leaderboard."),
_(
'Send to leaderboard _PARAM1_ the score _PARAM2_ for the connected player'
),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('leaderboardId', _('Leaderboard'), '', false)
.addParameter(
'expression',
_('Score to register for the player'),
'',
false
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/sha256.js')
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore');
extension
.addCondition(
'HasLastSaveErrored',

View File

@@ -16,8 +16,10 @@ namespace gdjs {
lastScoreSavingSucceededAt: number | null;
currentlySavingScore: number | null;
currentlySavingPlayerName: string | null;
currentlySavingPlayerId: string | null;
lastSavedScore: number | null;
lastSavedPlayerName: string | null;
lastSavedPlayerId: string | null;
lastSaveError: string | null;
isScoreSaving: boolean;
hasScoreBeenSaved: boolean;
@@ -28,25 +30,45 @@ namespace gdjs {
this.lastScoreSavingSucceededAt = null;
this.currentlySavingScore = null;
this.currentlySavingPlayerName = null;
this.currentlySavingPlayerId = null;
this.lastSavedScore = null;
this.lastSavedPlayerName = null;
this.lastSavedPlayerId = null;
this.lastSaveError = null;
this.isScoreSaving = false;
this.hasScoreBeenSaved = false;
this.hasScoreSavingErrored = false;
}
isSameAsLastScore(playerName: string, score: number): boolean {
isSameAsLastScore({
playerName,
playerId,
score,
}: {
playerName?: string;
playerId?: string;
score: number;
}): boolean {
return (
this.lastSavedPlayerName === playerName &&
((!!playerName && this.lastSavedPlayerName === playerName) ||
(!!playerId && this.lastSavedPlayerId === playerId)) &&
this.lastSavedScore === score
);
}
isAlreadySavingThisScore(playerName: string, score: number): boolean {
isAlreadySavingThisScore({
playerName,
playerId,
score,
}: {
playerName?: string;
playerId?: string;
score: number;
}): boolean {
return (
((!!playerName && this.currentlySavingPlayerName === playerName) ||
(!!playerId && this.currentlySavingPlayerId === playerId)) &&
this.isScoreSaving &&
this.currentlySavingPlayerName === playerName &&
this.currentlySavingScore === score
);
}
@@ -58,19 +80,29 @@ namespace gdjs {
);
}
startSaving(playerName: string, score: number): void {
startSaving({
playerName,
playerId,
score,
}: {
playerName?: string;
playerId?: string;
score: number;
}): void {
this.lastScoreSavingStartedAt = Date.now();
this.isScoreSaving = true;
this.hasScoreBeenSaved = false;
this.hasScoreSavingErrored = false;
this.currentlySavingScore = score;
this.currentlySavingPlayerName = playerName;
if (playerName) this.currentlySavingPlayerName = playerName;
if (playerId) this.currentlySavingPlayerId = playerId;
}
closeSaving(): void {
this.lastScoreSavingSucceededAt = Date.now();
this.lastSavedScore = this.currentlySavingScore;
this.lastSavedPlayerName = this.currentlySavingPlayerName;
this.lastSavedPlayerId = this.currentlySavingPlayerId;
this.isScoreSaving = false;
this.hasScoreBeenSaved = true;
}
@@ -155,50 +187,31 @@ namespace gdjs {
return lastScoreSavingState;
};
export const savePlayerScore = function (
runtimeScene: gdjs.RuntimeScene,
leaderboardId: string,
score: float,
playerName: string
) {
let scoreSavingState: ScoreSavingState;
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
if (scoreSavingState.isAlreadySavingThisScore(playerName, score)) {
logger.warn(
'There is already a request to save with this player name and this score. Ignoring this one.'
);
return;
}
if (scoreSavingState.isSameAsLastScore(playerName, score)) {
logger.warn(
'The player and score to be sent are the same as previous one. Ignoring this one.'
);
const errorCode = 'SAME_AS_PREVIOUS';
scoreSavingState.setError(errorCode);
return;
}
if (scoreSavingState.isTooSoonToSaveAnotherScore()) {
logger.warn(
'Last entry was sent too little time ago. Ignoring this one.'
);
const errorCode = 'TOO_FAST';
scoreSavingState.setError(errorCode);
return;
}
} else {
scoreSavingState = new ScoreSavingState();
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
}
scoreSavingState.startSaving(playerName, score);
const baseUrl = 'https://api.gdevelop-app.com/play';
const saveScore = function ({
leaderboardId,
playerName,
playerId,
playerToken,
score,
scoreSavingState,
runtimeScene,
}: {
leaderboardId: string;
playerName?: string;
playerId?: string;
playerToken?: string;
score: number;
scoreSavingState: ScoreSavingState;
runtimeScene: gdjs.RuntimeScene;
}) {
const rootApi = runtimeScene
.getGame()
.isUsingGDevelopDevelopmentEnvironment()
? 'https://api-dev.gdevelop.io'
: 'https://api.gdevelop.io';
const baseUrl = `${rootApi}/play`;
const game = runtimeScene.getGame();
const payload = JSON.stringify({
playerName: formatPlayerName(playerName),
const payloadObject = {
score: score,
sessionId: game.getSessionId(),
clientPlayerId: game.getPlayerId(),
@@ -206,18 +219,25 @@ namespace gdjs {
typeof window !== 'undefined' && (window as any).location
? (window as any).location.href
: '',
});
fetch(
`${baseUrl}/game/${gdjs.projectData.properties.projectUuid}/leaderboard/${leaderboardId}/entry`,
{
body: payload,
method: 'POST',
headers: {
'Content-Type': 'application/json',
Digest: computeDigest(payload),
},
}
).then(
};
if (playerName)
payloadObject['playerName'] = formatPlayerName(playerName);
const payload = JSON.stringify(payloadObject);
const headers = {
'Content-Type': 'application/json',
Digest: computeDigest(payload),
};
if (playerToken)
headers['Authorization'] = `player-game-token ${playerToken}`;
let leaderboardEntryCreationUrl = `${baseUrl}/game/${gdjs.projectData.properties.projectUuid}/leaderboard/${leaderboardId}/entry`;
if (playerId) {
leaderboardEntryCreationUrl += `?playerId=${playerId}`;
}
fetch(leaderboardEntryCreationUrl, {
body: payload,
method: 'POST',
headers: headers,
}).then(
(response) => {
if (!response.ok) {
const errorCode = response.status.toString();
@@ -250,6 +270,114 @@ namespace gdjs {
);
};
export const savePlayerScore = function (
runtimeScene: gdjs.RuntimeScene,
leaderboardId: string,
score: float,
playerName: string
) {
let scoreSavingState: ScoreSavingState;
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
if (
scoreSavingState.isAlreadySavingThisScore({ playerName, score })
) {
logger.warn(
'There is already a request to save with this player name and this score. Ignoring this one.'
);
return;
}
if (scoreSavingState.isSameAsLastScore({ playerName, score })) {
logger.warn(
'The player and score to be sent are the same as previous one. Ignoring this one.'
);
const errorCode = 'SAME_AS_PREVIOUS';
scoreSavingState.setError(errorCode);
return;
}
if (scoreSavingState.isTooSoonToSaveAnotherScore()) {
logger.warn(
'Last entry was sent too little time ago. Ignoring this one.'
);
const errorCode = 'TOO_FAST';
scoreSavingState.setError(errorCode);
return;
}
} else {
scoreSavingState = new ScoreSavingState();
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
}
scoreSavingState.startSaving({ playerName, score });
saveScore({
leaderboardId,
playerName,
score,
scoreSavingState,
runtimeScene,
});
};
export const saveConnectedPlayerScore = function (
runtimeScene: gdjs.RuntimeScene,
leaderboardId: string,
score: float
) {
let scoreSavingState: ScoreSavingState;
const playerId = gdjs.playerAuthentication.getUserId();
const playerToken = gdjs.playerAuthentication.getUserToken();
if (!playerId || !playerToken) {
logger.warn(
'Cannot save a score for a connected player if the player is not connected.'
);
return;
}
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
if (scoreSavingState.isAlreadySavingThisScore({ playerId, score })) {
logger.warn(
'There is already a request to save with this player ID and this score. Ignoring this one.'
);
return;
}
if (scoreSavingState.isSameAsLastScore({ playerId, score })) {
logger.warn(
'The player and score to be sent are the same as previous one. Ignoring this one.'
);
const errorCode = 'SAME_AS_PREVIOUS';
scoreSavingState.setError(errorCode);
return;
}
if (scoreSavingState.isTooSoonToSaveAnotherScore()) {
logger.warn(
'Last entry was sent too little time ago. Ignoring this one.'
);
const errorCode = 'TOO_FAST';
scoreSavingState.setError(errorCode);
return;
}
} else {
scoreSavingState = new ScoreSavingState();
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
}
scoreSavingState.startSaving({ playerId, score });
saveScore({
leaderboardId,
playerId,
playerToken,
score,
scoreSavingState,
runtimeScene,
});
};
export const isSaving = function (leaderboardId?: string): boolean {
if (leaderboardId) {
return _scoreSavingStateByLeaderboard[leaderboardId]
@@ -512,7 +640,12 @@ namespace gdjs {
}
const gameId = gdjs.projectData.properties.projectUuid;
const targetUrl = `https://liluo.io/games/${gameId}/leaderboard/${leaderboardId}?inGameEmbedded=true`;
const isDev = runtimeScene
.getGame()
.isUsingGDevelopDevelopmentEnvironment();
const targetUrl = `https://liluo.io/games/${gameId}/leaderboard/${leaderboardId}?inGameEmbedded=true${
isDev ? '&dev=true' : ''
}`;
checkLeaderboardAvailability(targetUrl).then(
(isAvailable) => {
if (leaderboardId !== _requestedLeaderboardId) {

View File

@@ -32,7 +32,8 @@ module.exports = {
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
'Harsimran Virk',
'MIT'
);
)
.setCategory('Visual effect');
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
@@ -195,7 +196,7 @@ module.exports = {
.setIncludeFile('Extensions/Lighting/lightruntimeobject.js')
.addIncludeFile('Extensions/Lighting/lightruntimeobject-pixi-renderer.js')
.addIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
.setCategoryFullName(_('Lights'));
.setCategoryFullName(_('Visual effect'));
object
.addAction(
@@ -269,7 +270,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -278,19 +279,19 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
this._radius = parseFloat(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const colorHex = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('color')
.getValue()
@@ -370,7 +371,7 @@ module.exports = {
RenderedLightObjectInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'CppPlatform/Extensions/lightIcon32.png';
};

View File

@@ -24,6 +24,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
"Victor Levasseur and Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/panel_sprite");
extension.AddInstructionOrExpressionGroupMetadata(_("Panel Sprite (9-patch) Object"))
.SetIcon("CppPlatform/Extensions/PanelSpriteIcon.png");
gd::ObjectMetadata& obj =
extension
@@ -85,8 +87,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
_("Modify the width of a Panel Sprite."),
_("the width"),
_("Size and angle"),
"res/actions/scaleWidth24.png",
"res/actions/scaleWidth.png")
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardOperatorParameters("number")
@@ -98,8 +100,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
_("Check the width of a Panel Sprite."),
_("the width"),
_("Size and angle"),
"res/conditions/scaleWidth24.png",
"res/conditions/scaleWidth.png")
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardRelationalOperatorParameters("number")
@@ -110,8 +112,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
_("Modify the height of a Panel Sprite."),
_("the height"),
_("Size and angle"),
"res/actions/scaleHeight24.png",
"res/actions/scaleHeight.png")
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardOperatorParameters("number")
@@ -123,8 +125,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
_("Check the height of a Panel Sprite."),
_("the height"),
_("Size and angle"),
"res/conditions/scaleHeight24.png",
"res/conditions/scaleHeight.png")
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardRelationalOperatorParameters("number")
@@ -136,8 +138,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
"Modify the angle of a Panel Sprite.",
"the angle",
_("Size and angle"),
"res/actions/rotate24.png",
"res/actions/rotate.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.SetHidden() // Deprecated
.AddParameter("object", _("Object"), "PanelSprite")
@@ -150,8 +152,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
"Check the angle of a Panel Sprite.",
"the angle",
_("Size and angle"),
"res/conditions/rotate24.png",
"res/conditions/rotate.png")
"res/conditions/rotate24_black.png",
"res/conditions/rotate_black.png")
.SetHidden() // Deprecated
.AddParameter("object", _("Object"), "PanelSprite")

View File

@@ -33,24 +33,16 @@ class PanelSpriteObjectJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::SetOpacity"]
.SetFunctionName("setOpacity")
.SetGetter("getOpacity")
.SetIncludeFile(
"Extensions/PanelSpriteObject/panelspriteruntimeobject.js");
.SetGetter("getOpacity");
GetAllConditionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("getOpacity");
GetAllExpressionsForObject(
"PanelSpriteObject::PanelSprite")["Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("getOpacity");
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::SetColor"]
.SetFunctionName("setColor")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("setColor");
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::Width"]

View File

@@ -19,9 +19,8 @@ This project is released under the MIT License.
using namespace std;
PanelSpriteObject::PanelSpriteObject(gd::String name_)
: Object(name_),
textureName(""),
PanelSpriteObject::PanelSpriteObject()
: textureName(""),
width(32),
height(32),
leftMargin(0),

View File

@@ -10,7 +10,7 @@ This project is released under the MIT License.
#include <memory>
#include "GDCore/Project/Object.h"
namespace gd {
class Object;
class ObjectConfiguration;
class InitialInstance;
class Project;
}
@@ -18,12 +18,12 @@ class Project;
/**
* PanelSprite Object
*/
class GD_EXTENSION_API PanelSpriteObject : public gd::Object {
class GD_EXTENSION_API PanelSpriteObject : public gd::ObjectConfiguration {
public:
PanelSpriteObject(gd::String name_);
PanelSpriteObject();
virtual ~PanelSpriteObject();
virtual std::unique_ptr<gd::Object> Clone() const {
return std::unique_ptr<gd::Object>(new PanelSpriteObject(*this));
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return std::unique_ptr<gd::ObjectConfiguration>(new PanelSpriteObject(*this));
}
#if defined(GD_IDE_ONLY)

View File

@@ -25,7 +25,10 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
"explosions, magical effects, etc...",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Visual effect")
.SetExtensionHelpPath("/objects/particles_emitter");
extension.AddInstructionOrExpressionGroupMetadata(_("Particle system"))
.SetIcon("CppPlatform/Extensions/particleSystemicon.png");
// Declaration of all objects available
{
@@ -37,7 +40,7 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
_("Displays a large number of small particles to create visual "
"effects."),
"CppPlatform/Extensions/particleSystemicon.png")
.SetCategoryFullName(_("General"));
.SetCategoryFullName(_("Visual effect"));
// Declaration is too big to be compiled by GCC in one file, unless you have
// 4GB+ ram. :/

View File

@@ -55,8 +55,7 @@ ParticleEmitterBase::ParticleEmitterBase()
maxParticleNb(300),
destroyWhenNoParticles(true) {}
ParticleEmitterObject::ParticleEmitterObject(gd::String name_)
: Object(name_) {}
ParticleEmitterObject::ParticleEmitterObject() {}
void ParticleEmitterObject::DoUnserializeFrom(
gd::Project& project, const gd::SerializerElement& element) {

View File

@@ -8,7 +8,7 @@ This project is released under the MIT License.
#ifndef PARTICLEEMITTEROBJECT_H
#define PARTICLEEMITTEROBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class InitialInstance;
class Project;
@@ -211,12 +211,12 @@ class GD_EXTENSION_API ParticleEmitterBase {
/**
* \brief Particle Emitter object used for storage and for the IDE.
*/
class GD_EXTENSION_API ParticleEmitterObject : public gd::Object,
class GD_EXTENSION_API ParticleEmitterObject : public gd::ObjectConfiguration,
public ParticleEmitterBase {
public:
ParticleEmitterObject(gd::String name_);
ParticleEmitterObject();
virtual ~ParticleEmitterObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<ParticleEmitterObject>(*this);
}

View File

@@ -21,7 +21,10 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"avoiding obstacles on the way.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Movement")
.SetExtensionHelpPath("/behaviors/pathfinding");
extension.AddInstructionOrExpressionGroupMetadata(_("Pathfinding behavior"))
.SetIcon("CppPlatform/Extensions/AStaricon16.png");
{
gd::BehaviorMetadata& aut =

View File

@@ -34,7 +34,7 @@ module.exports = {
'MIT'
)
.setExtensionHelpPath('/behaviors/physics2')
.setCategory('Advanced');
.setCategory('Movement');
extension
.addInstructionOrExpressionGroupMetadata(_('Physics Engine 2.0'))
.setIcon('res/physics32.png');
@@ -3816,6 +3816,23 @@ module.exports = {
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
const dummyBehavior = extension
.getBehaviorMetadata('Physics2::Physics2Behavior')
.get();
const sharedData = extension
.getBehaviorMetadata('Physics2::Physics2Behavior')
.getSharedDataInstance();
return [
gd.ProjectHelper.sanityCheckBehaviorProperty(
dummyBehavior,
'density',
'123'
),
gd.ProjectHelper.sanityCheckBehaviorsSharedDataProperty(
sharedData,
'gravityY',
'456'
),
];
},
};

View File

@@ -21,7 +21,10 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
"This is the old, deprecated physics engine. Prefer to use the Physics Engine 2.0.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Movement")
.SetExtensionHelpPath("/behaviors/physics");
extension.AddInstructionOrExpressionGroupMetadata(_("Physics Engine (deprecated)"))
.SetIcon("res/physics16.png");
{
gd::BehaviorMetadata& aut = extension.AddBehavior(

View File

@@ -27,6 +27,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
"could be used.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Movement")
.SetExtensionHelpPath("/behaviors/platformer");
extension.AddInstructionOrExpressionGroupMetadata(_("Platform behavior"))
.SetIcon("CppPlatform/Extensions/platformerobjecticon.png");

View File

@@ -0,0 +1,197 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'PlayerAuthentication',
_('Player Authentication (experimental)'),
_('Allow your game to authenticate players.'),
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/player-authentication')
.setCategory('Players');
extension
.addInstructionOrExpressionGroupMetadata(
_('Player Authentication (experimental)')
)
.setIcon('JsPlatform/Extensions/authentication.svg');
extension
.addDependency()
.setName('InAppBrowser Cordova plugin')
.setDependencyType('cordova')
.setExportName('cordova-plugin-inappbrowser');
extension
.addAction(
'DisplayAuthenticationBanner',
_('Display authentication banner'),
_(
'Display an authentication banner at the top of the game screen, for the player to log in.'
),
_('Display an authentication banner'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/player-authentication')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.displayAuthenticationBanner');
extension
.addAction(
'OpenAuthenticationWindow',
_('Open authentication window'),
_('Open an authentication window for the player to log in.'),
_('Open an authentication window'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/player-authentication')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.openAuthenticationWindow');
extension
.addCondition(
'IsAuthenticationWindowOpen',
_('Authentication window is open'),
_('Check if the authentication window is open.'),
_('Authentication window is open'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.isAuthenticationWindowOpen');
extension
.addAction(
'LogOut',
_('Log out the player'),
_('Log out the player.'),
_('Log out the player'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/player-authentication')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.logout');
extension
.addStrExpression(
'Username',
_('Username'),
_('Get the username of the authenticated player.'),
'',
'JsPlatform/Extensions/authentication.svg'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.getUsername');
extension
.addCondition(
'IsPlayerAuthenticated',
_('Player is authenticated'),
_('Check if the player is authenticated.'),
_('Player is authenticated'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.isAuthenticated');
extension
.addCondition(
'HasPlayerLoggedIn',
_('Player has logged in'),
_('Check if the player has just logged in.'),
_('Player has logged in'),
'',
'JsPlatform/Extensions/authentication.svg',
'JsPlatform/Extensions/authentication.svg'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.hasLoggedIn');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

@@ -0,0 +1,448 @@
namespace gdjs {
const logger = new gdjs.Logger('Player Authentication');
export namespace playerAuthenticationComponents {
const getPlayerLoginMessages = ({
platform,
isGameRegistered,
}: {
platform: 'cordova' | 'electron' | 'web';
isGameRegistered: boolean;
}) =>
isGameRegistered
? {
title: 'Logging in...',
text1:
platform === 'cordova'
? "One moment, we're opening a window for you to log in."
: "One moment, we're opening a new page with your web browser for you to log in.",
text2:
'If the window did not open, please check your pop-up blocker and click the button below to try again.',
}
: {
title: 'Your game is not registered!',
text1:
'In order to use player authentication, this game must be registered with GDevelop Services first.',
text2: 'Head to your Game Dashboard, then try again.',
};
/**
* Creates a DOM element that will contain the loader or a message if the game is not registered.
*/
export const computeAuthenticationContainer = function (
onCloseAuthenticationContainer: () => void
): {
rootContainer: HTMLDivElement;
loaderContainer: HTMLDivElement;
} {
const rootContainer = document.createElement('div');
rootContainer.id = 'authentication-root-container';
rootContainer.style.position = 'relative';
rootContainer.style.backgroundColor = 'rgba(14, 6, 45, 0.5)';
rootContainer.style.opacity = '1';
rootContainer.style.width = '100%';
rootContainer.style.height = '100%';
rootContainer.style.zIndex = '2';
rootContainer.style.pointerEvents = 'all';
const subContainer = document.createElement('div');
subContainer.id = 'authentication-sub-container';
subContainer.style.backgroundColor = '#FFFFFF';
subContainer.style.position = 'absolute';
subContainer.style.top = '16px';
subContainer.style.bottom = '16px';
subContainer.style.left = '16px';
subContainer.style.right = '16px';
subContainer.style.borderRadius = '8px';
subContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';
subContainer.style.padding = '16px';
const _closeContainer: HTMLDivElement = document.createElement('div');
_closeContainer.style.cursor = 'pointer';
_closeContainer.style.display = 'flex';
_closeContainer.style.justifyContent = 'right';
_closeContainer.style.alignItems = 'center';
_closeContainer.style.zIndex = '3';
addTouchAndClickEventListeners(
_closeContainer,
onCloseAuthenticationContainer
);
const _close = document.createElement('img');
_close.setAttribute('width', '15px');
_close.setAttribute(
'src',
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuODUzNTUgMC4xNDY0NDdDOC4wNDg4MiAwLjM0MTcwOSA4LjA0ODgyIDAuNjU4MjkxIDcuODUzNTUgMC44NTM1NTNMMC44NTM1NTMgNy44NTM1NUMwLjY1ODI5MSA4LjA0ODgyIDAuMzQxNzA5IDguMDQ4ODIgMC4xNDY0NDcgNy44NTM1NUMtMC4wNDg4MTU1IDcuNjU4MjkgLTAuMDQ4ODE1NSA3LjM0MTcxIDAuMTQ2NDQ3IDcuMTQ2NDVMNy4xNDY0NSAwLjE0NjQ0N0M3LjM0MTcxIC0wLjA0ODgxNTUgNy42NTgyOSAtMC4wNDg4MTU1IDcuODUzNTUgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMC4xNDY0NDcgMC4xNDY0NDdDMC4zNDE3MDkgLTAuMDQ4ODE1NSAwLjY1ODI5MSAtMC4wNDg4MTU1IDAuODUzNTUzIDAuMTQ2NDQ3TDcuODUzNTUgNy4xNDY0NUM4LjA0ODgyIDcuMzQxNzEgOC4wNDg4MiA3LjY1ODI5IDcuODUzNTUgNy44NTM1NUM3LjY1ODI5IDguMDQ4ODIgNy4zNDE3MSA4LjA0ODgyIDcuMTQ2NDUgNy44NTM1NUwwLjE0NjQ0NyAwLjg1MzU1M0MtMC4wNDg4MTU1IDAuNjU4MjkxIC0wLjA0ODgxNTUgMC4zNDE3MDkgMC4xNDY0NDcgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+Cjwvc3ZnPgo='
);
_closeContainer.appendChild(_close);
const loaderContainer: HTMLDivElement = document.createElement('div');
loaderContainer.id = 'authentication-container-loader';
loaderContainer.style.display = 'flex';
loaderContainer.style.flexDirection = 'column';
loaderContainer.style.height = '100%';
loaderContainer.style.width = '100%';
loaderContainer.style.justifyContent = 'center';
loaderContainer.style.alignItems = 'center';
const _loader = document.createElement('img');
_loader.setAttribute('width', '28px');
_loader.setAttribute(
'src',
'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iYW5pbWF0ZS1zcGluIC1tbC0xIG1yLTMgaC01IHctNSB0ZXh0LXdoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT4KPHBhdGggb3BhY2l0eT0nMC43NScgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD4KPC9zdmc+'
);
_loader.style.marginTop = '50px';
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);
subContainer.appendChild(_closeContainer);
subContainer.appendChild(loaderContainer);
rootContainer.appendChild(subContainer);
return { rootContainer, loaderContainer };
};
/**
* Helper to add the texts to the authentication container
* based on the platform or if the game is registered.
*/
export const addAuthenticationTextsToLoadingContainer = (
loaderContainer: HTMLDivElement,
platform,
isGameRegistered
) => {
const textContainer: HTMLDivElement = document.createElement('div');
textContainer.id = 'authentication-container-texts';
textContainer.style.display = 'flex';
textContainer.style.flexDirection = 'column';
textContainer.style.width = '100%';
textContainer.style.justifyContent = 'center';
textContainer.style.alignItems = 'center';
textContainer.style.position = 'relative';
textContainer.style.zIndex = '3';
textContainer.style.fontSize = '11pt';
textContainer.style.fontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
const messages = getPlayerLoginMessages({
platform,
isGameRegistered,
});
const title = document.createElement('h1');
title.innerText = messages.title;
title.style.fontSize = '20pt';
title.style.fontWeight = 'bold';
const text1 = document.createElement('p');
text1.innerText = messages.text1;
const text2 = document.createElement('p');
text2.innerText = messages.text2;
textContainer.appendChild(title);
textContainer.appendChild(text1);
textContainer.appendChild(text2);
if (!isGameRegistered) {
// Remove the loader.
loaderContainer.innerHTML = '';
}
loaderContainer.prepend(textContainer);
return textContainer;
};
/**
* Helper to add the authentication link in case the window hasn't opened properly.
* Useful for Electron & Web platforms.
*/
export const addAuthenticationUrlToTextsContainer = (
onClick: () => void,
textContainer: HTMLDivElement
) => {
const link = document.createElement('a');
addTouchAndClickEventListeners(link, onClick);
link.innerText = 'Click here to authenticate';
link.style.color = '#0078d4';
link.style.textDecoration = 'none';
link.style.textDecoration = 'underline';
link.style.cursor = 'pointer';
textContainer.appendChild(link);
};
/**
* Creates a DOM element to display a dismissable banner.
*/
export const computeDismissableBanner = function (
onDismissBanner: () => void
): HTMLDivElement {
const divContainer = document.createElement('div');
divContainer.id = 'authenticated-banner';
divContainer.style.position = 'absolute';
divContainer.style.pointerEvents = 'all';
divContainer.style.backgroundColor = '#0E062D';
divContainer.style.top = '0px';
divContainer.style.height = '48px';
divContainer.style.left = '0px';
divContainer.style.width = '100%';
divContainer.style.padding = '6px 16px';
// Use zIndex 1 to make sure it is below the authentication iframe or webview.
divContainer.style.zIndex = '1';
divContainer.style.display = 'flex';
divContainer.style.flexDirection = 'row-reverse';
divContainer.style.justifyContent = 'space-between';
divContainer.style.alignItems = 'center';
divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';
divContainer.style.fontSize = '11pt';
divContainer.style.color = '#FFFFFF';
divContainer.style.fontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
const _closeContainer: HTMLDivElement = document.createElement('div');
_closeContainer.style.cursor = 'pointer';
_closeContainer.style.display = 'flex';
_closeContainer.style.justifyContent = 'center';
_closeContainer.style.alignItems = 'center';
_closeContainer.style.zIndex = '3';
_closeContainer.style.marginRight = '32px';
_closeContainer.style.height = '100%';
addTouchAndClickEventListeners(_closeContainer, onDismissBanner);
const _close = document.createElement('img');
_close.setAttribute('width', '30px');
_close.setAttribute(
'src',
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjc1IDguMDEyNUwyMS45ODc1IDYuMjVMMTUgMTMuMjM3NUw4LjAxMjUgNi4yNUw2LjI1IDguMDEyNUwxMy4yMzc1IDE1TDYuMjUgMjEuOTg3NUw4LjAxMjUgMjMuNzVMMTUgMTYuNzYyNUwyMS45ODc1IDIzLjc1TDIzLjc1IDIxLjk4NzVMMTYuNzYyNSAxNUwyMy43NSA4LjAxMjVaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K'
);
_closeContainer.appendChild(_close);
divContainer.appendChild(_closeContainer);
return divContainer;
};
/**
* Creates a DOM element representing a banner for the user to know which account
* they're using and also to allow switching to another account.
*/
export const computeAuthenticatedBanner = function (
onOpenAuthenticationWindow: () => void,
onDismissBanner: () => void,
username: string | null
): HTMLDivElement {
const divContainer = computeDismissableBanner(onDismissBanner);
const playerUsername = username || 'Anonymous';
const _textContainer: HTMLDivElement = document.createElement('div');
const loggedText = document.createElement('p');
loggedText.id = 'loggedText';
loggedText.innerHTML = `<img style="margin-right:4px" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K" />
Logged as ${playerUsername}`;
loggedText.style.margin = '0px';
const changeAccountText = document.createElement('p');
changeAccountText.id = 'changeAccountText';
changeAccountText.innerText = `Click here to switch to another account.`;
changeAccountText.style.margin = '0px';
changeAccountText.style.marginTop = '4px';
changeAccountText.style.textDecoration = 'underline';
changeAccountText.style.cursor = 'pointer';
addTouchAndClickEventListeners(
changeAccountText,
onOpenAuthenticationWindow
);
_textContainer.appendChild(loggedText);
_textContainer.appendChild(changeAccountText);
divContainer.appendChild(_textContainer);
return divContainer;
};
/**
* Creates a DOM element representing a banner for the user to know
* they are not connected and to allow logging in.
*/
export const computeNotAuthenticatedBanner = function (
onOpenAuthenticationWindow: () => void,
onDismissBanner: () => void
): HTMLDivElement {
const divContainer = computeDismissableBanner(onDismissBanner);
const _textContainer: HTMLDivElement = document.createElement('div');
const loggedText = document.createElement('p');
loggedText.id = 'loggedText';
loggedText.innerHTML = `You are not authenticated.`;
loggedText.style.margin = '0px';
const changeAccountText = document.createElement('p');
changeAccountText.id = 'changeAccountText';
changeAccountText.innerText = `Click here to log in.`;
changeAccountText.style.margin = '0px';
changeAccountText.style.marginTop = '4px';
changeAccountText.style.textDecoration = 'underline';
changeAccountText.style.cursor = 'pointer';
addTouchAndClickEventListeners(
changeAccountText,
onOpenAuthenticationWindow
);
_textContainer.appendChild(loggedText);
_textContainer.appendChild(changeAccountText);
divContainer.appendChild(_textContainer);
return divContainer;
};
/**
* Create, display, and hide the logged in confirmation.
*/
export const displayLoggedInNotification = function (
domContainer: HTMLDivElement,
username: string
) {
showNotification(
domContainer,
'authenticated-notification',
`<img style="margin-right:4px" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K" />
Logged as ${username}`,
'success'
);
};
/**
* Create, display, and hide the logged in confirmation.
*/
export const displayLoggedOutNotification = function (
domContainer: HTMLDivElement
) {
showNotification(
domContainer,
'authenticated-notification',
`<img style="margin-right:4px" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K" />
Logged out`,
'success'
);
};
/**
* Create, display, and hide an error notification.
*/
export const displayErrorNotification = function (
domContainer: HTMLDivElement
) {
showNotification(
domContainer,
'error-notification',
'An error occurred while authenticating, please try again.',
'error'
);
};
/**
* Helper to show a notification to the user, that disappears automatically.
*/
export const showNotification = function (
domContainer: HTMLDivElement,
id: string,
content: string,
type: 'success' | 'error'
) {
const divContainer = document.createElement('div');
divContainer.id = id;
divContainer.style.position = 'absolute';
divContainer.style.pointerEvents = 'all';
divContainer.style.backgroundColor =
type === 'success' ? '#0E062D' : 'red';
divContainer.style.top = '12px';
divContainer.style.right = '16px';
divContainer.style.padding = '6px 32px 6px 6px';
// Use zIndex 1 to make sure it is below the authentication iframe or webview.
divContainer.style.zIndex = '1';
divContainer.style.display = 'flex';
divContainer.style.flexDirection = 'row-reverse';
divContainer.style.justifyContent = 'space-between';
divContainer.style.alignItems = 'center';
divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';
divContainer.style.borderRadius = '4px';
divContainer.style.fontSize = '11pt';
divContainer.style.color = '#FFFFFF';
divContainer.style.fontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
try {
divContainer.animate(
[
{ transform: 'translateY(-30px)', opacity: 0 },
{ transform: 'translateY(0px)', opacity: 1 },
],
{
duration: 700,
easing: 'ease-out',
}
);
} catch {
logger.warn('Animation not supported, div will be fixed.');
}
const loggedText = document.createElement('p');
loggedText.id = 'loggedText';
loggedText.innerHTML = content;
loggedText.style.margin = '0px';
divContainer.appendChild(loggedText);
domContainer.appendChild(divContainer);
const animationTime = 700;
const notificationTime = 5000;
setTimeout(() => {
try {
divContainer.animate(
[
{ transform: 'translateY(0px)', opacity: 1 },
{ transform: 'translateY(-30px)', opacity: 0 },
],
{
duration: animationTime,
easing: 'ease-in',
}
);
} catch {
logger.warn('Animation not supported, div will be fixed.');
}
}, notificationTime);
// Use timeout because onanimationend listener does not work.
setTimeout(() => {
divContainer.remove();
}, notificationTime + animationTime);
};
/**
* Helper to add event listeners on a pressable/clickable element
* to work on both desktop and mobile.
*/
export const addTouchAndClickEventListeners = function (
element: HTMLElement,
action: () => void
) {
// Touch start event listener for mobile.
element.addEventListener('touchstart', (event) => {
action();
});
// Click event listener for desktop.
element.addEventListener('click', (event) => {
action();
});
};
}
}

View File

@@ -0,0 +1,739 @@
namespace gdjs {
declare var cordova: any;
const logger = new gdjs.Logger('Player Authentication');
const authComponents = gdjs.playerAuthenticationComponents;
export namespace playerAuthentication {
// Authentication information.
let _username: string | null = null;
let _userId: string | null = null;
let _userToken: string | null = null;
let _justLoggedIn = false;
let _checkedLocalStorage: boolean = false;
// Authentication display
let _authenticationWindow: Window | null = null; // For Web.
let _authenticationInAppWindow: Window | null = null; // For Cordova.
let _authenticationRootContainer: HTMLDivElement | null = null;
let _authenticationLoaderContainer: HTMLDivElement | null = null;
let _authenticationTextContainer: HTMLDivElement | null = null;
let _authenticationBanner: HTMLDivElement | null = null;
let _authenticationTimeoutId: NodeJS.Timeout | null = null;
// Communication methods.
let _authenticationMessageCallback:
| ((event: MessageEvent) => void)
| null = null;
let _cordovaAuthenticationMessageCallback:
| ((event: MessageEvent) => void)
| null = null;
let _websocket: WebSocket | null = null;
// Ensure that the condition "just logged in" is valid only for one frame.
gdjs.registerRuntimeScenePostEventsCallback(() => {
_justLoggedIn = false;
});
const getLocalStorageKey = (gameId: string) =>
`${gameId}_authenticatedUser`;
const getAuthWindowUrl = ({
runtimeGame,
gameId,
connectionId,
}: {
runtimeGame: gdjs.RuntimeGame;
gameId: string;
connectionId?: string;
}) =>
`https://liluo.io/auth?gameId=${gameId}${
connectionId ? `&connectionId=${connectionId}` : ''
}${
runtimeGame.isUsingGDevelopDevelopmentEnvironment() ? '&dev=true' : ''
}`;
/**
* Helper returning the platform.
*/
const getPlatform = (
runtimeScene: RuntimeScene
): 'electron' | 'cordova' | 'web' => {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return 'electron';
}
if (typeof cordova !== 'undefined') return 'cordova';
return 'web';
};
/**
* Returns true if a user token is present in the local storage.
*/
export const isAuthenticated = () => {
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
return _userToken !== null;
};
/**
* Returns true if the user just logged in.
* Useful to update username or trigger messages in the game.
*/
export const hasLoggedIn = () => _justLoggedIn;
/**
* Returns the username from the local storage.
*/
export const getUsername = () => {
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
return _username || '';
};
/**
* Returns the user token from the local storage.
*/
export const getUserToken = () => {
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
return _userToken || null;
};
/**
* Returns the username from the local storage.
*/
export const getUserId = () => {
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
return _userId || null;
};
/**
* Returns true if the game is registered, false otherwise.
* Useful to display a message to the user to register the game before logging in.
*/
const checkIfGameIsRegistered = (
runtimeGame: gdjs.RuntimeGame,
gameId: string,
tries: number = 0
): Promise<boolean> => {
const rootApi = runtimeGame.isUsingGDevelopDevelopmentEnvironment()
? 'https://api-dev.gdevelop.io'
: 'https://api.gdevelop.io';
const url = `${rootApi}/game/public-game/${gameId}`;
return fetch(url, { method: 'HEAD' }).then(
(response) => {
if (response.status !== 200) {
logger.warn(
`Error while fetching the game: ${response.status} ${response.statusText}`
);
// If the response is not 404, it may be a timeout, so retry a few times.
if (response.status === 404 || tries > 2) {
return false;
}
return checkIfGameIsRegistered(runtimeGame, gameId, tries + 1);
}
return true;
},
(err) => {
logger.error('Error while fetching game:', err);
return false;
}
);
};
/**
* Remove the user information from the local storage.
*/
export const logout = (runtimeScene: RuntimeScene) => {
_username = null;
_userToken = null;
_userId = null;
const gameId = gdjs.projectData.properties.projectUuid;
if (!gameId) {
logger.error('Missing game id in project properties.');
return;
}
window.localStorage.removeItem(getLocalStorageKey(gameId));
cleanUpAuthWindowAndCallbacks(runtimeScene);
removeAuthenticationBanner(runtimeScene);
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleAuthenticationError(
runtimeScene,
"The div element covering the game couldn't be found, the authentication banner cannot be displayed."
);
return;
}
authComponents.displayLoggedOutNotification(domElementContainer);
};
/**
* Retrieves the user information from the local storage, and store
* them in the extension variables.
*/
const readAuthenticatedUserFromLocalStorage = () => {
const gameId = gdjs.projectData.properties.projectUuid;
if (!gameId) {
logger.error('Missing game id in project properties.');
return;
}
const authenticatedUserStorageItem = window.localStorage.getItem(
getLocalStorageKey(gameId)
);
if (!authenticatedUserStorageItem) {
_checkedLocalStorage = true;
return;
}
const authenticatedUser = JSON.parse(authenticatedUserStorageItem);
_username = authenticatedUser.username;
_userId = authenticatedUser.userId;
_userToken = authenticatedUser.userToken;
_checkedLocalStorage = true;
};
/**
* Helper to be called on login or error.
* Removes all the UI and callbacks.
*/
const cleanUpAuthWindowAndCallbacks = (runtimeScene: RuntimeScene) => {
removeAuthenticationContainer(runtimeScene);
clearAuthenticationWindowTimeout();
if (_websocket) {
_websocket.close();
_websocket = null;
}
// If a new window was opened (web), close it.
if (_authenticationWindow) {
_authenticationWindow.close();
_authenticationWindow = null;
}
// If an in-app browser was used (cordova), close it.
if (_authenticationInAppWindow) {
_authenticationInAppWindow.close();
_authenticationInAppWindow = null;
}
};
/**
* When the websocket receives the authentication result, close all the
* authentication windows, display the notification and focus on the game.
*/
const handleLoggedInEvent = function (
runtimeScene: gdjs.RuntimeScene,
userId: string,
username: string | null,
userToken: string
) {
if (!username) {
logger.warn('The authenticated player does not have a username');
}
_username = username;
_userId = userId;
_userToken = userToken;
_justLoggedIn = true;
const gameId = gdjs.projectData.properties.projectUuid;
if (!gameId) {
logger.error('Missing game id in project properties.');
return;
}
window.localStorage.setItem(
getLocalStorageKey(gameId),
JSON.stringify({
username: _username,
userId: _userId,
userToken: _userToken,
})
);
cleanUpAuthWindowAndCallbacks(runtimeScene);
removeAuthenticationBanner(runtimeScene);
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleAuthenticationError(
runtimeScene,
"The div element covering the game couldn't be found, the authentication banner cannot be displayed."
);
return;
}
authComponents.displayLoggedInNotification(
domElementContainer,
_username || 'Anonymous'
);
focusOnGame(runtimeScene);
};
/**
* Reads the event sent by the authentication window and
* display the appropriate banner.
*/
const receiveMessageFromAuthenticationWindow = function (
runtimeScene: gdjs.RuntimeScene,
event: MessageEvent,
{ checkOrigin }: { checkOrigin: boolean }
) {
const allowedOrigin = 'https://liluo.io';
// Check origin of message.
if (checkOrigin && event.origin !== allowedOrigin) {
throw new Error(`Unexpected origin: ${event.origin}`);
}
// Check that message is not malformed.
if (!event.data.id) {
throw new Error('Malformed message');
}
// Handle message.
switch (event.data.id) {
case 'authenticationResult': {
if (!(event.data.body && event.data.body.token)) {
throw new Error('Malformed message.');
}
handleLoggedInEvent(
runtimeScene,
event.data.body.userId,
event.data.body.username,
event.data.body.token
);
break;
}
}
};
/**
* Handle any error that can occur as part of the authentication process.
*/
const handleAuthenticationError = function (
runtimeScene: gdjs.RuntimeScene,
message: string
) {
logger.error(message);
cleanUpAuthWindowAndCallbacks(runtimeScene);
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleAuthenticationError(
runtimeScene,
"The div element covering the game couldn't be found, the authentication banner cannot be displayed."
);
return;
}
authComponents.displayErrorNotification(domElementContainer);
focusOnGame(runtimeScene);
};
/**
* If after 5min, no message has been received from the authentication window,
* show a notification and remove the authentication container.
*/
const startAuthenticationWindowTimeout = (
runtimeScene: gdjs.RuntimeScene
) => {
clearAuthenticationWindowTimeout();
const time = 12 * 60 * 1000; // 12 minutes, in case the user needs time to authenticate.
_authenticationTimeoutId = setTimeout(() => {
logger.info(
'Authentication window did not send message in time. Closing it.'
);
cleanUpAuthWindowAndCallbacks(runtimeScene);
focusOnGame(runtimeScene);
}, time);
};
/**
* Clear the authentication window timeout.
* Useful when:
* - the authentication succeeded
* - the authentication window is closed
*/
const clearAuthenticationWindowTimeout = () => {
if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);
};
/**
* Action to display the banner to the user, depending on their authentication status.
*/
export const displayAuthenticationBanner = function (
runtimeScene: gdjs.RuntimeScene
) {
if (_authenticationBanner) {
// Banner already displayed, ensure it's visible.
_authenticationBanner.style.opacity = '1';
return;
}
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleAuthenticationError(
runtimeScene,
"The div element covering the game couldn't be found, the authentication banner cannot be displayed."
);
return;
}
const onDismissBanner = () => {
removeAuthenticationBanner(runtimeScene);
};
const onOpenAuthenticationWindow = () => {
openAuthenticationWindow(runtimeScene);
};
// We display the corresponding banner depending on the authentication status.
_authenticationBanner = _userToken
? authComponents.computeAuthenticatedBanner(
onOpenAuthenticationWindow,
onDismissBanner,
_username
)
: authComponents.computeNotAuthenticatedBanner(
onOpenAuthenticationWindow,
onDismissBanner
);
domElementContainer.appendChild(_authenticationBanner);
};
/**
* Helper to handle authentication window on Electron.
* We open a new window, and create a websocket to know when the user is logged in.
*/
const openAuthenticationWindowForElectron = (
runtimeScene: gdjs.RuntimeScene,
gameId: string
) => {
const wsPlayApi = runtimeScene
.getGame()
.isUsingGDevelopDevelopmentEnvironment()
? 'wss://api-ws-dev.gdevelop.io/play'
: 'wss://api-ws.gdevelop.io/play';
_websocket = new WebSocket(wsPlayApi);
_websocket.onopen = () => {
// When socket is open, ask for the connectionId, so that we can open the authentication window.
if (_websocket) {
_websocket.send(JSON.stringify({ action: 'getConnectionId' }));
}
};
_websocket.onerror = () => {
handleAuthenticationError(
runtimeScene,
'Error while connecting to the authentication server.'
);
};
_websocket.onmessage = (event) => {
if (event.data) {
const messageContent = JSON.parse(event.data);
switch (messageContent.type) {
case 'authenticationResult': {
const messageData = messageContent.data;
handleLoggedInEvent(
runtimeScene,
messageData.userId,
messageData.username,
messageData.token
);
break;
}
case 'connectionId': {
const messagegeData = messageContent.data;
const connectionId = messagegeData.connectionId;
if (!connectionId) {
logger.error('No connectionId received');
return;
}
const targetUrl = getAuthWindowUrl({
runtimeGame: runtimeScene.getGame(),
gameId,
connectionId,
});
const electron = runtimeScene
.getGame()
.getRenderer()
.getElectron();
const openWindow = () => electron.shell.openExternal(targetUrl);
openWindow();
// Add the link to the window in case a popup blocker is preventing the window from opening.
if (_authenticationTextContainer) {
authComponents.addAuthenticationUrlToTextsContainer(
openWindow,
_authenticationTextContainer
);
}
break;
}
}
}
};
};
/**
* Helper to handle authentication window on Cordova.
* We open an InAppBrowser window, and listen to messages posted on this window.
*/
const openAuthenticationWindowForCordova = (
runtimeScene: gdjs.RuntimeScene,
gameId: string
) => {
const targetUrl = getAuthWindowUrl({
runtimeGame: runtimeScene.getGame(),
gameId,
});
_authenticationInAppWindow = cordova.InAppBrowser.open(
targetUrl,
'authentication',
'location=yes' // location=yes is important to show the URL bar to the user.
);
// Listen to messages posted on the authentication window, so that we can
// know when the user is authenticated.
if (_authenticationInAppWindow) {
_cordovaAuthenticationMessageCallback = (event: MessageEvent) => {
receiveMessageFromAuthenticationWindow(runtimeScene, event, {
checkOrigin: false, // For Cordova we don't check the origin, as the message is read from the InAppBrowser directly.
});
};
_authenticationInAppWindow.addEventListener(
'message',
_cordovaAuthenticationMessageCallback,
true
);
}
};
/**
* Helper to handle authentication window on web.
* We open a new window, and listen to messages posted back to the game window.
*/
const openAuthenticationWindowForWeb = (
runtimeScene: gdjs.RuntimeScene,
gameId: string
) => {
// If we're on a browser, open a new window.
const targetUrl = getAuthWindowUrl({
runtimeGame: runtimeScene.getGame(),
gameId,
});
// Listen to messages posted by the authentication window, so that we can
// know when the user is authenticated.
_authenticationMessageCallback = (event: MessageEvent) => {
receiveMessageFromAuthenticationWindow(runtimeScene, event, {
checkOrigin: true,
});
};
window.addEventListener('message', _authenticationMessageCallback, true);
const left = screen.width / 2 - 500 / 2;
const top = screen.height / 2 - 600 / 2;
const windowFeatures = `left=${left},top=${top},width=500,height=600`;
const openWindow = () =>
window.open(targetUrl, 'authentication', windowFeatures);
_authenticationWindow = openWindow();
// Add the link to the window in case a popup blocker is preventing the window from opening.
if (_authenticationTextContainer) {
authComponents.addAuthenticationUrlToTextsContainer(
openWindow,
_authenticationTextContainer
);
}
};
/**
* Action to display the authentication window to the user.
*/
export const openAuthenticationWindow = function (
runtimeScene: gdjs.RuntimeScene
) {
// Create the authentication container for the player to wait.
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleAuthenticationError(
runtimeScene,
"The div element covering the game couldn't be found, the authentication window cannot be displayed."
);
return;
}
const onAuthenticationContainerDismissed = () => {
cleanUpAuthWindowAndCallbacks(runtimeScene);
displayAuthenticationBanner(runtimeScene);
};
const _gameId = gdjs.projectData.properties.projectUuid;
if (!_gameId) {
handleAuthenticationError(
runtimeScene,
'The game ID is missing, the authentication window cannot be opened.'
);
return;
}
// If the banner is displayed, hide it, so that it can be shown again if the user closes the window.
if (_authenticationBanner) _authenticationBanner.style.opacity = '0';
const platform = getPlatform(runtimeScene);
const {
rootContainer,
loaderContainer,
} = authComponents.computeAuthenticationContainer(
onAuthenticationContainerDismissed
);
_authenticationRootContainer = rootContainer;
_authenticationLoaderContainer = loaderContainer;
// Display the authentication window right away, to show a loader
// while the call for game registration is happening.
domElementContainer.appendChild(_authenticationRootContainer);
// If the game is registered, open the authentication window.
// Otherwise, open the window indicating that the game is not registered.
checkIfGameIsRegistered(runtimeScene.getGame(), _gameId)
.then((isGameRegistered) => {
if (_authenticationLoaderContainer) {
_authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(
_authenticationLoaderContainer,
platform,
isGameRegistered
);
}
if (isGameRegistered) {
startAuthenticationWindowTimeout(runtimeScene);
// Based on which platform the game is running, we open the authentication window
// with a different window, with or without a websocket.
switch (platform) {
case 'electron':
openAuthenticationWindowForElectron(runtimeScene, _gameId);
break;
case 'cordova':
openAuthenticationWindowForCordova(runtimeScene, _gameId);
break;
case 'web':
default:
openAuthenticationWindowForWeb(runtimeScene, _gameId);
break;
}
}
})
.catch((error) => {
handleAuthenticationError(
runtimeScene,
'Error while checking if the game is registered.'
);
console.error(error);
});
};
/**
* Condition to check if the window is open, so that the game can be paused in the background.
*/
export const isAuthenticationWindowOpen = function (): boolean {
return !!_authenticationRootContainer;
};
/**
* Remove the container displaying the authentication window and the callback.
*/
export const removeAuthenticationContainer = function (
runtimeScene: gdjs.RuntimeScene
) {
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
logger.info(
"The div element covering the game couldn't be found, the authentication must be already closed."
);
return;
}
// Remove the authentication root container.
if (_authenticationRootContainer) {
domElementContainer.removeChild(_authenticationRootContainer);
}
// Remove the authentication callbacks.
if (_authenticationMessageCallback) {
window.removeEventListener(
'message',
_authenticationMessageCallback,
true
);
_authenticationMessageCallback = null;
// No need to detach the callback from the InAppBrowser, as it's destroyed when the window is closed.
_cordovaAuthenticationMessageCallback = null;
}
_authenticationRootContainer = null;
_authenticationLoaderContainer = null;
_authenticationTextContainer = null;
};
/**
* Remove the banner displaying the authentication status.
*/
const removeAuthenticationBanner = function (
runtimeScene: gdjs.RuntimeScene
) {
if (!_authenticationBanner) {
logger.info(
"The authentication banner couldn't be found, the authentication banner must be already closed."
);
return;
}
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
logger.info(
"The div element covering the game couldn't be found, the authentication must be already closed."
);
return;
}
domElementContainer.removeChild(_authenticationBanner);
_authenticationBanner = null;
};
/**
* Focus on game canvas to allow user to interact with it.
*/
const focusOnGame = function (runtimeScene: gdjs.RuntimeScene) {
const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();
if (gameCanvas) gameCanvas.focus();
};
}
}

View File

@@ -20,6 +20,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"Florian Rival and Aurélien Vivet",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/shape_painter");
extension.AddInstructionOrExpressionGroupMetadata(_("Shape painter"))
.SetIcon("CppPlatform/Extensions/primitivedrawingicon.png");
gd::ObjectMetadata& obj =
extension
@@ -256,8 +258,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Move the drawing position of the path to _PARAM1_;_PARAM2_ "
"with _PARAM0_"),
_("Advanced"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
@@ -474,8 +476,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Modify the size of the outline of future drawings."),
_("the size of the outline"),
_("Setup"),
"res/actions/outlineSize24.png",
"res/actions/outlineSize.png")
"res/actions/outlineSize24_black.png",
"res/actions/outlineSize_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardOperatorParameters("number")
@@ -487,8 +489,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Test the size of the outline."),
_("the size of the outline"),
_("Setup"),
"res/conditions/outlineSize24.png",
"res/conditions/outlineSize.png")
"res/conditions/outlineSize24_black.png",
"res/conditions/outlineSize_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardRelationalOperatorParameters("number")
@@ -498,7 +500,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Outline size"),
_("Outline size"),
"",
"res/conditions/outlineSize.png")
"res/conditions/outlineSize_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineSize");
@@ -576,8 +578,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"not. It's recommended to use relative coordinates."),
_("Use relative coordinates for _PARAM0_: _PARAM1_"),
_("Setup"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("yesorno", _("Use relative coordinates?"), "", false)
.SetDefaultValue("true")
@@ -589,8 +591,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Check if the coordinates of the shape painter is relative."),
_("_PARAM0_ is using relative coordinates"),
_("Setup"),
"res/conditions/position24.png",
"res/conditions/position.png")
"res/conditions/position24_black.png",
"res/conditions/position_black.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("AreCoordinatesRelative");
@@ -600,8 +602,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Modify the scale of the specified object."),
_("the scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"), "Drawer")
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
@@ -612,7 +614,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("the width's scale of an object"),
_("the width's scale"),
_("Size"),
"res/actions/scaleWidth24.png")
"res/actions/scaleWidth24_black.png")
.AddParameter("object", _("Object"), "Drawer")
.UseStandardParameters("number")
.MarkAsAdvanced();
@@ -623,7 +625,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("the height's scale of an object"),
_("the height's scale"),
_("Size"),
"res/actions/scaleHeight24.png")
"res/actions/scaleHeight24_black.png")
.AddParameter("object", _("Object"), "Drawer")
.UseStandardParameters("number")
.MarkAsAdvanced();
@@ -673,8 +675,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Change the width of an object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24.png",
"res/actions/scale.png")
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Drawer")
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
@@ -684,8 +686,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Change the height of an object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24.png",
"res/actions/scale.png")
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Drawer")
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
@@ -697,8 +699,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"object origin."),
_("Change the center of rotation of _PARAM0_: _PARAM1_; _PARAM2_"),
_("Angle"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
@@ -711,8 +713,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Change the collision mask of _PARAM0_ to a rectangle from "
"_PARAM1_; _PARAM2_ to _PARAM3_; _PARAM4_"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
"res/actions/position24_black.png",
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("Left X position"))
.AddParameter("expression", _("Top Y position"))
@@ -724,7 +726,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("X drawing coordinate of a point from the scene"),
_("X drawing coordinate of a point from the scene"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("X scene position"))
.AddParameter("expression", _("Y scene position"));
@@ -733,7 +735,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Y drawing coordinate of a point from the scene"),
_("Y drawing coordinate of a point from the scene"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("X scene position"))
.AddParameter("expression", _("Y scene position"));
@@ -742,7 +744,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("X scene coordinate of a point from the drawing"),
_("X scene coordinate of a point from the drawing"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("X drawing position"))
.AddParameter("expression", _("Y drawing position"));
@@ -751,7 +753,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
_("Y scene coordinate of a point from the drawing"),
_("Y scene coordinate of a point from the drawing"),
_("Position"),
"res/actions/position.png")
"res/actions/position_black.png")
.AddParameter("object", _("Object"), "Drawer")
.AddParameter("expression", _("X drawing position"))
.AddParameter("expression", _("Y drawing position"));

View File

@@ -33,7 +33,7 @@ ShapePainterObjectBase::ShapePainterObjectBase()
clearBetweenFrames(true),
absoluteCoordinates(false) {}
ShapePainterObject::ShapePainterObject(gd::String name_) : gd::Object(name_) {}
ShapePainterObject::ShapePainterObject() {}
void ShapePainterObjectBase::DoUnserializeFrom(
gd::Project& project, const gd::SerializerElement& element) {

View File

@@ -9,7 +9,7 @@ This project is released under the MIT License.
#define SHAPEPAINTEROBJECT_H
#include <vector>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class Object;
class InitialInstance;
@@ -88,12 +88,12 @@ class GD_EXTENSION_API ShapePainterObjectBase {
/**
* \brief The Shape Painter object used for storage and by the IDE.
*/
class GD_EXTENSION_API ShapePainterObject : public gd::Object,
class GD_EXTENSION_API ShapePainterObject : public gd::ObjectConfiguration,
public ShapePainterObjectBase {
public:
ShapePainterObject(gd::String name_);
ShapePainterObject();
virtual ~ShapePainterObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<ShapePainterObject>(*this);
}

View File

@@ -34,7 +34,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/screenshot')
.setCategory('Device');
.setCategory('Advanced');
extension
.addInstructionOrExpressionGroupMetadata(_('Screenshot'))
.setIcon('JsPlatform/Extensions/take_screenshot32.png');

View File

@@ -15,7 +15,7 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
_("Get information about the system and device running the game."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Device");
.SetCategory("Advanced");
extension.AddInstructionOrExpressionGroupMetadata(_("System information"))
.SetIcon("CppPlatform/Extensions/systeminfoicon.png");

View File

@@ -18,6 +18,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
"entered with a keyboard by a player."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("User interface")
.SetExtensionHelpPath("/objects/text_entry");
gd::ObjectMetadata& obj =
@@ -27,7 +28,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
_("Invisible object used to get the text "
"entered with the keyboard."),
"CppPlatform/Extensions/textentry.png")
.SetCategoryFullName(_("Advanced"));
.SetCategoryFullName(_("User interface"));
obj.AddAction("String",
_("Text in memory"),

View File

@@ -10,4 +10,4 @@ This project is released under the MIT License.
using namespace std;
TextEntryObject::TextEntryObject(gd::String name_) : Object(name_) {}
TextEntryObject::TextEntryObject() {}

View File

@@ -7,16 +7,16 @@ This project is released under the MIT License.
#ifndef TEXTENTRYOBJECT_H
#define TEXTENTRYOBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
/**
* \brief Simple object which stores user keyboard input.
*/
class GD_EXTENSION_API TextEntryObject : public gd::Object {
class GD_EXTENSION_API TextEntryObject : public gd::ObjectConfiguration {
public:
TextEntryObject(gd::String name_);
TextEntryObject();
virtual ~TextEntryObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<TextEntryObject>(*this);
}

View File

@@ -31,7 +31,10 @@ module.exports = {
_('A text field the player can type text into.'),
'Florian Rival',
'MIT'
);
)
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Text Input"))
.setIcon("JsPlatform/Extensions/text_input.svg");
const textInputObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
@@ -277,7 +280,7 @@ module.exports = {
'JsPlatform/Extensions/text_input.svg',
textInputObject
)
.setCategoryFullName(_('Form control'))
.setCategoryFullName(_('User interface'))
.addUnsupportedBaseObjectCapability('effect')
.setIncludeFile('Extensions/TextInput/textinputruntimeobject.js')
.addIncludeFile(
@@ -293,7 +296,7 @@ module.exports = {
_('the text'),
_('the text'),
'',
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('string')
@@ -308,7 +311,7 @@ module.exports = {
_('the placeholder'),
_('the placeholder'),
'',
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('string')
@@ -368,7 +371,7 @@ module.exports = {
_('the input type'),
_('the input type'),
_('Type'),
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('string') // TODO: stringWithSelector?
@@ -458,7 +461,7 @@ module.exports = {
_('the border width'),
_('the border width'),
_('Field appearance'),
'res/conditions/outlineSize24.png'
'res/conditions/outlineSize24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('number')
@@ -475,7 +478,7 @@ module.exports = {
_('the text input is read-only'),
_('read-only'),
'',
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('boolean')
@@ -490,7 +493,7 @@ module.exports = {
_('the text input is disabled'),
_('disabled'),
'',
'res/conditions/text24.png'
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('boolean')
@@ -582,7 +585,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -590,7 +593,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -612,13 +615,14 @@ module.exports = {
this.update();
}
static getThumbnail(project, resourcesLoader, object) {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/text_input.svg';
}
update() {
const instance = this._instance;
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
const placeholder =
instance.getRawStringProperty('placeholder') ||

View File

@@ -23,7 +23,10 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"some indicators, menu buttons, dialogues..."),
"Florian Rival and Victor Levasseur",
"Open source (MIT License)")
.SetCategory("User interface")
.SetExtensionHelpPath("/objects/text");
extension.AddInstructionOrExpressionGroupMetadata(_("Text object"))
.SetIcon("CppPlatform/Extensions/texticon.png");
gd::ObjectMetadata& obj =
extension
@@ -31,15 +34,15 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Text"),
_("Displays a text on the screen."),
"CppPlatform/Extensions/texticon.png")
.SetCategoryFullName(_("Texts"));
.SetCategoryFullName(_("User interface"));
obj.AddAction("String",
_("Modify the text"),
_("Modify the text of a Text object."),
_("the text"),
"",
"res/actions/text24.png",
"res/actions/text.png")
"res/actions/text24_black.png",
"res/actions/text_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("string")
@@ -51,8 +54,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Compare the text of a Text object."),
_("the text"),
"",
"res/conditions/text24.png",
"res/conditions/text.png")
"res/conditions/text24_black.png",
"res/conditions/text_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("string")
@@ -75,8 +78,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Compare the scale of the text on the X axis"),
_("the scale on the X axis"),
"Scale",
"res/conditions/scaleWidth24.png",
"res/conditions/scaleWidth.png")
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
@@ -88,8 +91,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Modify the scale of the text on the X axis (default scale is 1)"),
_("the scale on the X axis"),
_("Scale"),
"res/actions/scaleWidth24.png",
"res/actions/scaleWidth.png")
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
@@ -100,8 +103,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Compare the scale of the text on the Y axis"),
_("the scale on the Y axis"),
"Scale",
"res/conditions/scaleHeight24.png",
"res/conditions/scaleHeight.png")
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
@@ -113,8 +116,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Modify the scale of the text on the Y axis (default scale is 1)"),
_("the scale on the Y axis"),
_("Scale"),
"res/actions/scaleHeight24.png",
"res/actions/scaleHeight.png")
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
@@ -126,8 +129,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Modify the scale of the specified object (default scale is 1)"),
_("the scale"),
_("Scale"),
"res/actions/scale24.png",
"res/actions/scale.png")
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
@@ -329,8 +332,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Modify the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/actions/rotate24.png",
"res/actions/rotate.png")
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
@@ -342,8 +345,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Compare the value of the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/conditions/rotate24.png",
"res/conditions/rotate.png")
"res/conditions/rotate24_black.png",
"res/conditions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
@@ -356,8 +359,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"cropped, raise this value."),
_("the padding"),
_("Style"),
"res/conditions/textPadding24.png",
"res/conditions/textPadding.png")
"res/conditions/textPadding24_black.png",
"res/conditions/textPadding_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number");
@@ -369,8 +372,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"raise this value."),
_("the padding"),
_("Style"),
"res/actions/textPadding24.png",
"res/actions/textPadding.png")
"res/actions/textPadding24_black.png",
"res/actions/textPadding_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number");
@@ -409,8 +412,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"option,\nyou can't get the number of lines displayed"),
_("Activate wrapping style of _PARAM0_: _PARAM1_"),
_("Style"),
"res/actions/wordWrap24.png",
"res/actions/wordWrap.png")
"res/actions/wordWrap24_black.png",
"res/actions/wordWrap_black.png")
.AddParameter("object", _("Object"), "Text")
.AddParameter("yesorno", _("Wrapping"));
@@ -420,8 +423,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Test if the word wrapping style of an object is set."),
_("_PARAM0_ word wrapping style is activated"),
_("Style"),
"res/conditions/wordWrap24.png",
"res/conditions/wordWrap.png")
"res/conditions/wordWrap24_black.png",
"res/conditions/wordWrap_black.png")
.AddParameter("object", _("Object"), "Text");
@@ -430,8 +433,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Modify the word wrapping width of a Text object."),
_("the wrapping width"),
_("Style"),
"res/actions/wordWrap24.png",
"res/actions/wordWrap.png")
"res/actions/wordWrap24_black.png",
"res/actions/wordWrap_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number");
@@ -441,8 +444,8 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Test the word wrapping width of a Text object."),
_("the wrapping width"),
_("Style"),
"res/conditions/wordWrap24.png",
"res/conditions/wordWrap.png")
"res/conditions/wordWrap24_black.png",
"res/conditions/wordWrap_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number");
@@ -451,14 +454,14 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Padding"),
_("Padding"),
_("Style"),
"res/actions/textPadding.png")
"res/actions/textPadding_black.png")
.AddParameter("object", _("Object"), "Text");
obj.AddExpression("ScaleX",
_("X Scale of a Text object"),
_("X Scale of a Text object"),
_("Scale"),
"res/actions/scaleWidth.png")
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetScaleX");
@@ -466,7 +469,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Y Scale of a Text object"),
_("Y Scale of a Text object"),
_("Scale"),
"res/actions/scaleHeight.png")
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetScaleY");
@@ -482,7 +485,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Angle"),
_("Angle"),
_("Rotation"),
"res/actions/rotate.png")
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetAngle");

View File

@@ -19,9 +19,8 @@ This project is released under the MIT License.
using namespace std;
TextObject::TextObject(gd::String name_)
: Object(name_),
text("Text"),
TextObject::TextObject()
: text("Text"),
characterSize(20),
fontName(""),
smoothed(true),

View File

@@ -7,7 +7,7 @@ This project is released under the MIT License.
#ifndef TEXTOBJECT_H
#define TEXTOBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class Project;
class Object;
@@ -17,11 +17,11 @@ class InitialInstance;
/**
* Text Object
*/
class GD_EXTENSION_API TextObject : public gd::Object {
class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
public:
TextObject(gd::String name_);
TextObject();
virtual ~TextObject();
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<TextObject>(*this);
}

View File

@@ -26,7 +26,7 @@ const defineTileMap = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */) {
var objectTileMap = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.updateProperty = function (
@@ -435,8 +435,8 @@ const defineTileMap = function (
_("Modify the scale of the specified object."),
_("the scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png"
"res/actions/scale24_black.png",
"res/actions/scale_black.png"
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardOperatorParameters("number")
@@ -452,7 +452,7 @@ object
_("the width's scale of an object"),
_("the width's scale"),
_("Size"),
"res/actions/scaleWidth24.png"
"res/actions/scaleWidth24_black.png"
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters("number")
@@ -468,7 +468,7 @@ object
_("the height's scale of an object"),
_("the height's scale"),
_("Size"),
"res/actions/scaleHeight24.png"
"res/actions/scaleHeight24_black.png"
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters("number")
@@ -483,8 +483,8 @@ object
_("Change the width of an object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24.png",
"res/actions/scale.png"
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png"
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardOperatorParameters("number")
@@ -499,8 +499,8 @@ object
_("Change the height of an object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24.png",
"res/actions/scale.png"
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png"
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardOperatorParameters("number")
@@ -514,7 +514,7 @@ const defineCollisionMask = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */) {
var collisionMaskObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.updateProperty = function (
@@ -768,15 +768,15 @@ const defineCollisionMask = function (
_("Modify the scale of the specified object."),
_("the scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png"
"res/actions/scale24_black.png",
"res/actions/scale_black.png"
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.useStandardOperatorParameters("number")
.markAsAdvanced()
.getCodeExtraInformation()
.setFunctionName('setScale');
object
.addExpressionAndConditionAndAction(
"number",
@@ -785,7 +785,7 @@ const defineCollisionMask = function (
_("the width's scale of an object"),
_("the width's scale"),
_("Size"),
"res/actions/scaleWidth24.png"
"res/actions/scaleWidth24_black.png"
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.useStandardParameters("number")
@@ -801,7 +801,7 @@ const defineCollisionMask = function (
_("the height's scale of an object"),
_("the height's scale"),
_("Size"),
"res/actions/scaleHeight24.png"
"res/actions/scaleHeight24_black.png"
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.useStandardParameters("number")
@@ -816,15 +816,15 @@ const defineCollisionMask = function (
_("Change the width of an object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24.png",
"res/actions/scale.png"
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png"
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.useStandardOperatorParameters("number")
.markAsAdvanced()
.getCodeExtraInformation()
.setFunctionName('setWidth');
object
.addAction(
"Height",
@@ -832,15 +832,15 @@ const defineCollisionMask = function (
_("Change the height of an object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24.png",
"res/actions/scale.png"
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png"
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.useStandardOperatorParameters("number")
.markAsAdvanced()
.getCodeExtraInformation()
.setFunctionName('setHeight');
};
module.exports = {
@@ -858,8 +858,11 @@ module.exports = {
'Todor Imreorov',
'Open source (MIT License)'
)
.setCategory('Advanced')
.setExtensionHelpPath('/objects/tilemap');
extension.addInstructionOrExpressionGroupMetadata(_("Tilemap"))
.setIcon("JsPlatform/Extensions/tile_map.svg");
defineTileMap(extension, _, gd);
defineCollisionMask(extension, _, gd);
@@ -940,26 +943,23 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
pixiRenderer
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
pixiRenderer
pixiResourcesLoader
);
// This setting allows tile maps with more than 16K tiles.
Tilemap.settings.use32bitIndex = true;
pixiRenderer.plugins.tilemap =
pixiRenderer.plugins.tilemap || new Tilemap.TileRenderer();
this.tileMapPixiObject = new Tilemap.CompositeTilemap();
this._pixiObject = this.tileMapPixiObject;
@@ -1010,7 +1010,7 @@ module.exports = {
RenderedTileMapInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/tile_map.svg';
};
@@ -1020,26 +1020,26 @@ module.exports = {
*/
RenderedTileMapInstance.prototype.updateTileMap = function () {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObject
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObject
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObject
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const layerIndex = parseInt(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('layerIndex')
.getValue(),
10
);
const displayMode = this._associatedObject
const displayMode = this._associatedObjectConfiguration
.getProperties(this.project)
.get('displayMode')
.getValue();
@@ -1187,7 +1187,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -1196,7 +1196,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -1252,7 +1252,7 @@ module.exports = {
RenderedCollisionMaskInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/tile_map_collision_mask24.svg';
};
@@ -1262,39 +1262,39 @@ module.exports = {
*/
RenderedCollisionMaskInstance.prototype.updateTileMap = function () {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObject
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObject
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObject
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const collisionMaskTag = this._associatedObject
const collisionMaskTag = this._associatedObjectConfiguration
.getProperties(this.project)
.get('collisionMaskTag')
.getValue();
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineColor')
.getValue()
);
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillColor')
.getValue()
);
const outlineOpacity = this._associatedObject
const outlineOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineOpacity')
.getValue() / 255;
const fillOpacity = this._associatedObject
const fillOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillOpacity')
.getValue() / 255;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAGpC,qBAAa,iBAAiB;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CACf,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI;IA6F1B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,iBAAiB,CACtB,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI;IAiFP;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI;CAiER"}
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAGpC,qBAAa,iBAAiB;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CACf,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI;IA6F1B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,iBAAiB,CACtB,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI;IAgFP;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI;CAiER"}

View File

@@ -19,23 +19,29 @@ export declare class TileTextureCache {
flippedDiagonally: boolean,
texture: PIXI.Texture
): void;
/**
* Return the texture to use for the tile with the specified id.
*
* @param tileId The tile identifier
* @returns The texture for the given tile identifier.
*/
findTileTexture(tileId: integer): PIXI.Texture | undefined;
/**
* Return the texture to use for the tile with the specified uid, which can contains
* information about rotation in bits 32, 31 and 30
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
*
* @param tileId The tile identifier
* @param flippedHorizontally true if the tile is flipped horizontally.
* @param flippedVertically true if the tile is flipped vertically.
* @param flippedDiagonally true if the tile is flipped diagonally.
* @returns The texture for the given tile identifier and orientation.
* @returns the rotation "D8" number used by Pixi.
* @see https://pixijs.io/examples/#/textures/texture-rotate.js
*/
findTileTexture(
tileId: integer,
getPixiRotate(
flippedHorizontally: boolean,
flippedVertically: boolean,
flippedDiagonally: boolean
): PIXI.Texture | undefined;
): number;
/**
* @return the Tiled tile global uniq identifier.
*/

View File

@@ -1 +1 @@
{"version":3,"file":"TileTextureCache.d.ts","sourceRoot":"","sources":["../../src/render/TileTextureCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAc;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAE3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;;IAMvD,UAAU,CACR,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,GACpB,IAAI;IAUP;;;;;;;;;;OAUG;IACH,eAAe,CACb,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,GACzB,IAAI,CAAC,OAAO,GAAG,SAAS;IA8D3B;;OAEG;IACH,OAAO,CAAC,YAAY;CAkBrB"}
{"version":3,"file":"TileTextureCache.d.ts","sourceRoot":"","sources":["../../src/render/TileTextureCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAc;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAE3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;;IAMvD,UAAU,CACR,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,GACpB,IAAI;IAUP;;;;;OAKG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,SAAS;IAI1D;;;;;;;;;;OAUG;IACH,aAAa,CACX,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,GACzB,MAAM;IAwBT;;OAEG;IACH,OAAO,CAAC,YAAY;CAkBrB"}

View File

@@ -121,10 +121,10 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
// TODO find a clean way to wait for the json to be read.
for (
let index = 0;
index < 200 && tileMap._collisionTileMap.getDimensionX() === 0;
index < 100 && tileMap._collisionTileMap.getDimensionX() === 0;
index++
) {
await delay(5);
await delay(20);
}
if (tileMap._collisionTileMap.getDimensionX() === 0) {
throw new Error('Timeout reading the tile map JSON file.');

View File

@@ -10,7 +10,6 @@ namespace gdjs {
*/
export class TileMapRuntimeObjectPixiRenderer {
private _object: any;
private _runtimeScene: gdjs.RuntimeScene;
private _tileMap: TileMapHelper.EditableTileMap | null = null;
private _pixiObject: PIXI.tilemap.CompositeTilemap;
@@ -24,20 +23,9 @@ namespace gdjs {
runtimeScene: gdjs.RuntimeScene
) {
this._object = runtimeObject;
this._runtimeScene = runtimeScene;
const pixiRenderer = runtimeScene
.getGame()
.getRenderer()
.getPIXIRenderer();
// This setting allows tile maps with more than 16K tiles.
PIXI.tilemap.settings.use32bitIndex = true;
if (pixiRenderer) {
pixiRenderer.plugins.tilemap =
// @ts-ignore - pixi-tilemap types to be added.
pixiRenderer.plugins.tilemap || new PIXI.tilemap.TileRenderer();
}
// Load (or reset)
this._pixiObject = new PIXI.tilemap.CompositeTilemap();

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