Compare commits

...

167 Commits

Author SHA1 Message Date
Clément Pasteau
ba4a37813e Refactor into usage 2023-02-14 15:37:14 +01:00
Clément Pasteau
3f36054d54 Reduce allowed size for online web build to avoid abuse 2023-02-14 15:04:25 +01:00
AlexandreS
cdfd7a7ab1 Autotranslate improvements (#4951)
Don't show in changelog
2023-02-14 14:08:57 +01:00
Florian Rival
a944ac43db Update README
Don't show in changelog
2023-02-14 12:40:59 +01:00
Clément Pasteau
b7a1a96e7b Update storybook urls (#4949)
Do not show in changelog
2023-02-14 09:54:02 +01:00
Clément Pasteau
29d71796cc Remove unused ravenjs (sentry) (#4940)
Do not show in changelog
2023-02-13 16:41:28 +01:00
AlexandreS
78a1361897 Use form in account related actions to simplify mobile experience (#4917)
Don't show in changelog
2023-02-13 14:27:05 +01:00
AlexandreS
e340784ad6 Prevent google translate issue (#4937)
Don't show in changelog
2023-02-13 14:22:08 +01:00
github-actions[bot]
511466f0b8 Update translations [skip ci] (#4909)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-02-13 10:39:08 +01:00
Florian Rival
3f4e372acb Fix check of subscriptions
Don't show in changelog
2023-02-10 17:01:18 +01:00
D8H
a73a92d748 Fix tile map loading for collision mask objects (#4916) 2023-02-10 15:52:06 +01:00
AlexandreS
6e131d8a17 Display asset pack categories in asset store homepage (#4905) 2023-02-10 14:57:24 +01:00
D8H
9ac4c021e0 Fix the number parameter color in the events (#4915) 2023-02-10 11:37:38 +01:00
AlexandreS
a1fbf91ac7 Add Eye icon to switch password visibility (#4914) 2023-02-09 17:16:13 +01:00
Clément Pasteau
3f8d01e25e Improve subscription messages when a redeem code has been used (#4912) 2023-02-09 16:56:54 +01:00
AlexandreS
03b4170d74 Fix resource store display for extension icon (#4913)
Don't show in changelog
2023-02-09 15:34:13 +01:00
AlexandreS
0146ad9c38 Add error boundary at the app level (#4911)
Don't show in changelog
2023-02-09 12:19:26 +01:00
Clément Pasteau
fbb23f86cd Rename and add more actions, conditions and expressions to manipulate array variables (#4904) 2023-02-08 16:53:57 +01:00
github-actions[bot]
1fdaeea4f8 Update translations [skip ci] (#4893)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-02-08 11:21:06 +01:00
Clément Pasteau
32b97f2c40 Improve search accuracy in the app (instructions, examples, objects, tags) (#4906)
* Use tokenized search to improve search with multiple words looking into multiple keys (ex: name & description)
2023-02-08 09:41:27 +01:00
AlexandreS
d61f8336a8 Fix firebase extension tests (#4907)
Don't show in changelog
2023-02-07 17:11:16 +01:00
Clément Pasteau
3448cd57fe Allow showing an alertMessage without an icon (#4903) 2023-02-06 16:41:55 +01:00
Clément Pasteau
b7333612aa Prompt for email verification when logging in or signing up (#4898) 2023-02-06 11:45:32 +01:00
Aurélien Vivet
de82182b37 fix typo (#4899)
Do not show in changelog
2023-02-06 08:47:53 +01:00
Clément Pasteau
a8177b0e6f Upgrade electron-builder (#4892)
* This should fix the auto-updater uninstalling GDevelop without re-installing it on Windows, if installed "for all users"
2023-02-03 13:48:43 +01:00
AlexandreS
9716ff14ed Fix: Use correct default values for effects with color parameters (#4891) 2023-02-03 12:06:26 +01:00
Clément Pasteau
f0e1d7a6d9 Bump to 5.1.157 (#4886)
Do not show in changelog
2023-02-02 15:48:45 +01:00
github-actions[bot]
dd89562405 Update translations [skip ci] (#4887)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-02-02 15:48:29 +01:00
Clément Pasteau
f0e56c4513 Update tutorials for offline use (#4888)
Do not show in changelog
2023-02-02 15:47:59 +01:00
Clément Pasteau
ad2ea0abb8 Improve search accuracy for conditions and actions (#4885) 2023-02-02 15:38:42 +01:00
D8H
7ff71ea11f Make the button to open condition/action extensions easier to see (#4884) 2023-02-02 15:35:01 +01:00
github-actions[bot]
cd1d82f7ee Update translations [skip ci] (#4839)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-02-02 15:34:09 +01:00
Clément Pasteau
0724b7d113 Move save to toolbar (#4883)
Do not show in changelog
2023-02-02 15:26:26 +01:00
Clément Pasteau
c58ea73631 Upgrade AdMob extension (#4881)
* Updated internal Google Ads SDK version
* Added "App open" ads
* Added "Rewarded Interstitial" ads
2023-02-02 13:03:41 +01:00
Arthur Pacaud
b1039c6146 Add support for asynchronous actions in extensions in the editor (#4819)
* By marking an action as asynchronous, the actions and sub-events following it will wait for it to end. This is similar to the "Wait X seconds" action.
* Don't forget to use the action *"End asynchronous function"* in the events to mark the end of the action. Otherwise, the action will never end.
2023-02-02 12:30:17 +01:00
D8H
1b5acc9fa9 Fix the instruction group of event functions with operator (#4882) 2023-02-02 11:38:16 +01:00
Clément Pasteau
54cf91f180 Display new objects in the New Object Dialog (#4871)
* Buttons
* Toggles, Switches and Sliders
* Multitouch Joysticks
* Health and loading bars
* (Those objects were previously available through the asset store and are now visible when creating an object from scratch)
2023-02-02 11:27:49 +01:00
D8H
a434e4d9a9 Add a button to jump to behavior events (#4878)
---------

Co-authored-by: Florian Rival <Florian.Rival@gmail.com>
2023-02-02 10:45:36 +01:00
Florian Rival
2f339dd170 Add a save icon button to the top left next to the app main menu 2023-02-01 22:33:07 +01:00
Florian Rival
e843ca67ad Update comment [skip ci]
Don't show in changelog
2023-02-01 15:32:30 +01:00
Florian Rival
e4286b2021 Show expiration date and clearer explanations when using a redemption code (#4879) 2023-02-01 09:37:45 +01:00
Florian Rival
4579568866 Fix formatting and improve namings
Don't show in changelog
2023-02-01 09:35:14 +01:00
Florian Rival
4543a03530 Render the grid of the scene in realtime when a change is made to the grid setup (#4880) 2023-02-01 09:26:33 +01:00
Florian Rival
5aed25a02f Hide labels from buttons to add objects (or other items) when a list is too narrow
Don't show in changelog
2023-01-31 22:16:09 +01:00
Florian Rival
6fd9cfe4c3 Fix Events Sheet crashes and inconsistent order or location of pasting (#4876)
* Fix extracting events to a function or to a group crashing sometimes the app
* Also ensure events are copied/pasted or moved in their order of selection
* Events or actions/conditions are now only pasted at a single location
  * We previously allowed pasted events/actions/conditions to be inserted multiple times for each selected event, but this was more confusing than useful.
  * Now, the pasted elements are only pasted on the last selected event/action/condition (usually the one you just right clicked)
2023-01-31 17:56:35 +01:00
D8H
091324d93b Add a condition to check if the player has just closed the leaderboard view (#4875) 2023-01-31 15:16:28 +01:00
D8H
d802bd14b9 Add an action to hide the player login banner (#4874) 2023-01-31 12:55:12 +01:00
D8H
f46a6dc421 Optimize the z-order action (#4868)
- Scenes should be faster to load
- Games that y-sort a lot of moving objects should run much faster.
2023-01-30 13:29:50 +01:00
Florian Rival
b09e783b06 Remove help buttons for each accordion in the editor of a custom object
Don't show in changelog
2023-01-30 12:43:27 +01:00
Florian Rival
e2a050b717 Reduce size of text alignment icons 2023-01-30 12:41:32 +01:00
Florian Rival
ae22d1b876 Fix warning
Don't show in changelog
2023-01-28 17:26:34 +01:00
Florian Rival
9e6e7166ef Allow to retry uploading a file that failed to be uploaded
Don't show in changelog
2023-01-28 16:56:29 +01:00
Florian Rival
22c71a8aef Fix regression in font loading
Don't show in changelog
2023-01-27 19:32:08 +01:00
Florian Rival
4908c84494 Adapt loading of resources to allow a token instead of a cookie (#4870)
Don't show in changelog
2023-01-27 18:11:39 +01:00
AlexandreS
e4f44f7899 Add possibility to display GDevelop watermark that opens Liluo creator page on click/touch (#4811) 2023-01-24 18:26:39 +01:00
AlexandreS
c441610123 Improve in app tutorial element highlighting (#4865)
Don't show in changelog
2023-01-24 17:48:55 +01:00
D8H
d724712aa4 Fix direction actions on Sprites after an animation change with custom origin (#4860)
* Impacted actions are "Turn toward a position" and "Add a force toward a position".
2023-01-24 15:04:36 +01:00
Florian Rival
08fc7d3e9f Fix typos in description of StartedTouchOrMouseId and StartedTouchOrMouseCount 2023-01-22 20:49:29 +01:00
Florian Rival
ba30665811 Open the file chooser when adding a sprite or choosing a resource with less clicks for Cloud projects (#4859)
*Choosing a file from the device/computer is also the default choice when adding any resource, like for local projects.
2023-01-22 19:17:27 +01:00
Florian Rival
c72658e2d6 Fix some missing extensions accepted for files for Cloud projects (#4858) 2023-01-22 18:35:19 +01:00
AlexandreS
a3fc7585b4 Show the number of instances on a layer in the dialog confirming its deletion (#4854) 2023-01-21 18:33:46 +01:00
Florian Rival
244bc7e274 Fix error when trying to save in Piskel immediately after saving a project in the cloud for the first time (#4857) 2023-01-21 17:06:35 +01:00
Florian Rival
5a5bf60aac Add a selector in external editors to choose if new files must be created, or existing files be edited (#4855) 2023-01-21 14:26:07 +01:00
Florian Rival
33a69849d9 Temporarily revert game with login template to use gdevelop-app.com to prevent CORS issue
Don't show in changelog
2023-01-20 16:56:07 +01:00
Florian Rival
131ee1534a Fix performance issue when pinching to zoom on the scene editor (#4852) 2023-01-20 16:54:40 +01:00
AlexandreS
614c324a63 Change SortableVirtualizedItemList ItemRow to use text field with same fontsize as when not editing (#4851)
Don't show in changelog
2023-01-20 15:12:31 +01:00
AlexandreS
ca4ff1fa6d Decrease line height in comments (#4850)
Don't show in changelog
2023-01-20 14:53:05 +01:00
AlexandreS
0e9ed86b4d Improved comment and group events display (#4846)
* Fix bug that added a new line when one typed Enter key to edit a comment event
* Focus comment or group event after adding it (so that one doesn't have to click on it after adding it)
* Keep the same style when editing a group or comment event
* Submit modifications of group or comment event with Ctrl/Cmd + Enter
2023-01-20 13:53:26 +01:00
Florian Rival
3961c2af4a Remove useless analytics event
Don't show in changelog
2023-01-20 10:33:22 +01:00
Daniel R
cc4d78b08a Don't snap to grid when resizing an instance if Alt key is pressed (#4847)
* This is similar to pressing Alt key when moving an object - it won't snap to the grid.
2023-01-20 09:12:46 +01:00
AlexandreS
8d13285c97 Fix GDJS tests (#4845)
Don't show in changelog
2023-01-19 09:36:21 +01:00
Florian Rival
7b7e13817c Hide the old, deprecated Text Entry object when creating a new object from scratch
* Instead, use the Text Input object to display a field to let the player write something.
2023-01-18 14:44:13 +01:00
Florian Rival
129ebeb4e4 Fix previews not automatically opened on iOS and Safari for the web-app (#4843) 2023-01-18 13:58:14 +01:00
Florian Rival
808f2bc802 Fix new images made with Piskel wrongly smoothed when the project is configured for pixel-art (#4844) 2023-01-18 13:35:43 +01:00
AlexandreS
c5935232f7 Add legacy flag on former plans (#4842)
Don't show in changelog
2023-01-18 12:37:06 +01:00
AlexandreS
35a8341964 Display former subscriptions (indie and pro) in user profile (#4841) 2023-01-18 11:02:24 +01:00
AlexandreS
95783fd58f Bump newIDE version 2023-01-17 15:49:19 +01:00
github-actions[bot]
a78aa4af6e Update translations [skip ci] (#4794) 2023-01-17 15:48:47 +01:00
D8H
4f4b2b7901 Replace touch conditions to also handle the mouse (#4785)
* Add expressions to get the mouse cursor position without touches moving it.
2023-01-17 15:27:58 +01:00
Tristan Rhodes
957f672b90 Add LinearVelocityAngle() expression for the Physics behavior (#4798) 2023-01-17 14:11:21 +01:00
AlexandreS
b9e932d0de Fix flaky animation frame setting action (#4835) 2023-01-17 14:04:11 +01:00
Florian Rival
fdd2362dae Show link to business plans in the subscription dialog (#4838)
As we're having more and more professionals (game designers, game studios), startups and large-scale companies using GDevelop, we're now providing dedicated plans for businesses: https://gdevelop.io/pricing. They come with dedicated support, ability to apply custom branding and more things for usage by enterprises or game studios.
2023-01-17 12:28:48 +01:00
AlexandreS
47583e3092 Fix: Display new resource created with Jfxr or Yarn in resource selector dropdown (#4834) 2023-01-16 15:24:58 +01:00
AlexandreS
8d7967d71a Commit variable name changes after build (#4833)
Do not show in changelog
2023-01-16 15:08:25 +01:00
AlexandreS
72142a62c4 Fix bug that prevented to close parameter popover on the event sheet (#4832) 2023-01-16 14:49:48 +01:00
D8H
619890a25f Fix extension life-cycle functions were not exported (#4830) 2023-01-16 14:39:55 +01:00
AlexandreS
debcd3045f Add autocompletions for some fields that can contain object groups (#4831)
* Effect name
* Effect parameter name
* Sprite point name
2023-01-16 14:37:46 +01:00
D8H
86635e4f66 Add a condition to check if the game has just resumed (#4824)
* It can be useful to display a pause menu when the player come back to the game.
2023-01-16 13:34:38 +01:00
D8H
eedc68b0f2 Fix pathfinding movement angle condition (#4825)
* Add a condition to check the movement angle of the top-down movement behavior.
2023-01-16 10:30:00 +01:00
Florian Rival
b1a6425ab8 Display, when available, video tutorials translated in other languages (#4829)
* For example, some French tutorials have been translated by https://www.youtube.com/@videogamecoder6395
* If you have some translated tutorials, please send them to us using this form: https://airtable.com/shrv295oHlsuS69el - the GDevelop team will review them and add them for your language.
2023-01-15 21:24:45 +01:00
Florian Rival
3d1ee5efcc Update yarn.lock
Don't show in changelog
2023-01-15 12:00:19 +01:00
Florian Rival
4d1fde608b Fix missing translation marker
Don't show in changelog
2023-01-14 14:19:06 +01:00
Florian Rival
e40666bd73 Improve wording of the action to set a Text Input read-only 2023-01-14 14:15:44 +01:00
Florian Rival
4a6c075e41 Fix plural in variable child count in the variables editor 2023-01-14 14:04:14 +01:00
Florian Rival
82426fa564 Fix tinting of deleted sprites sometimes wrongly used to render newly created objects (#4823)
Fix #4822
2023-01-13 14:35:37 +01:00
Florian Rival
a21eef6930 Rename the signing options to make clearer that the legacy options must not be used
Some users were trying to change the upload key when they wanted to update their app,
because they thought "new upload key" would generate a new upload key at every packaging,
instead of using the "default" key - while the other option uses a legacy key that must not
be used in 99.9% of cases

Don't show in changelog
2023-01-13 10:41:55 +01:00
D8H
c6112a1549 Allow custom objects to define an origin point (#4821)
*  Fix camera renderer position update when the center stays the same but dimensions change.
* Don't show in changelog.
2023-01-12 18:47:57 +01:00
D8H
d8350396df Add an action to toggle boolean property values (#4800) 2023-01-12 12:10:47 +01:00
Florian Rival
a8282aaf83 Add conditions to check if the game is exported as a native mobile or desktop app (#4820) 2023-01-12 11:27:47 +01:00
D8H
7de7fc7193 Allow anchored child-objects to scale with the child-object they target (#4818)
* Don't show in changelogs.
2023-01-11 23:49:08 +01:00
AlexandreS
81c5829267 Fix selection rectangle around an instance not displaying correctly for sprites with custom center (#4817) 2023-01-11 16:04:41 +01:00
D8H
07cf1a90a4 Fix a crash when an event-based object tries to access scene variables (#4816)
* Internal changes (for developers)
2023-01-11 14:11:19 +01:00
Florian Rival
3213985a81 Add two expressions to replace one or all occurrences of a text in another (#4809)
* `StrReplaceOne` can be used to replace one occurrence of a "pattern" in a text by another.
* `StrReplaceAll` can be used to replace all occurrences of a "pattern" in a text by another.
2023-01-10 12:39:59 +01:00
Clément Pasteau
d4441fda99 Disable player auth checkbox if offline and improve error message (#4810)
Do not show in changelog
2023-01-10 11:54:39 +01:00
Florian Rival
91c55fcf94 Ask for company name/size or goal when asking about GDevelop usage (#4807)
* Also ask for what's the goal for people using GDevelop for a personal/student usage. Useful to better understand the motivation of the user base.
2023-01-10 11:09:57 +01:00
D8H
6d7b5ac7d3 Add missing icons in the instruction tree (#4797) 2023-01-09 13:45:50 +01:00
Florian Rival
903b353b7b Fix LDtk maps not properly loaded when not having a IntGrid layer (#4806)
Fix #4803
2023-01-09 13:20:31 +01:00
Clément Pasteau
4dd189512b Show contact information from a feedback (#4804) 2023-01-09 12:42:09 +01:00
Clément Pasteau
535fa39a7b Fix renaming children objects of event-based Object (#4805)
Do not show in changelog
2023-01-09 12:41:41 +01:00
Daniel R
78c0cb944f Use F2 as a shortcut for renaming object in the Objects List Panel (#4682)
Thanks @danired!
Co-authored-by: Daniel Redondo <dani@easydevel.com>
2023-01-09 11:02:29 +01:00
Clément Pasteau
55e037484f Allow accessing user profile of a game feedback made by a connected player (#4796) 2023-01-09 10:58:49 +01:00
D8H
84392c08b2 Add an event that returns true when a new condition is added to an extension (#4802) 2023-01-09 09:25:47 +01:00
Florian Rival
0061b1cb8b Allow to close the Home page menu drawer by tapping outside
Don't show in changelog
2023-01-08 21:25:40 +01:00
github-actions[bot]
9e19aa8886 Update translations [skip ci] (#4750)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-01-06 14:40:33 +01:00
Clément Pasteau
f4b812ab29 Small fixes player auth (#4792)
* Prevent listening for liluo connection if not on web
* Catch any error when interacting with localstorage

Do not show in changelog
2023-01-06 14:39:21 +01:00
Florian Rival
106d9de47c Fix naming of filenames created from resources in an external editor (#4791)
Don't show in changelog
2023-01-05 22:52:29 +01:00
Florian Rival
ecbafe60d1 Fix crash sometimes happening when an action/condition was stored with more parameters than expected (#4786)
Don't show in changelog
2023-01-05 16:11:28 +01:00
D8H
c0ba26f1d5 Allow to add a vertex to a collision mask by pulling an edge (#4778)
* Vertices can also be removed by dropping them next to one of their neighbors.
2023-01-05 12:58:02 +01:00
AlexandreS
089be66e82 Fix top down behavior "Rotation speed" property keeping the value 180° for no reason (#4764) 2023-01-05 10:52:20 +01:00
Florian Rival
ffb8a399cd Add support for Piskel, Jfxr and Yarn on the web-app (#4779)
* When you're using the web version of GDevelop, be it on a mobile phone, tablet, Chromebook, traditional computer, on iOS, Android, Windows, Linux or macOS, you can now create and edit images with Piskel, sounds with Jfxr and dialogue trees with Yarn.
2023-01-04 22:42:50 +01:00
Clément Pasteau
63482a0cda Allow automatic player login when the game is hosted on Liluo (#4776)
Do not show in changelog
2023-01-03 16:49:25 +01:00
Clément Pasteau
4fe8dcaf0c Improve default build name (#4777)
Do not show in changelog
2023-01-03 15:35:22 +01:00
Florian Rival
9729461aa1 Add support for using Piskel (image editor), Jfxr (sound editor) and Yarn (dialogue editor) for Cloud projects on desktop (#4769) 2023-01-03 11:41:40 +01:00
D8H
26fcc9770b Add new collision mask vertices on the middle of edges (#4774) 2023-01-03 11:10:42 +01:00
D8H
c62f9c5c65 Fix missing export files for extensions (#4773) 2023-01-02 19:11:11 +01:00
Daniel R
9b199d5aa0 Fix missing space between "Add action" and "paste actions" labels (#4772) 2023-01-02 09:01:48 +01:00
AlexandreS
46e1a5c6e8 Fix Text object Scale X condition and action (#4768) 2022-12-30 11:39:03 +01:00
Aurélien Vivet
052a6240c5 Fix typo (#4766)
Don't show in changelog
2022-12-30 09:10:24 +01:00
AlexandreS
d081711838 Allow to use Escape key as way to cancel rename of object/group/scene/external layout (#4765)
* Also fix drag and drop of project manager items preventing to select text when renaming item
2022-12-30 09:05:03 +01:00
AlexandreS
f0151b2e1f Improve subscription panel and user experience (#4755) 2022-12-29 11:03:37 +01:00
AlexandreS
e2f42ca290 Fix unwelcome opening of keyboard on mobile on the app 2022-12-27 17:59:32 +01:00
Clément Pasteau
b7a3d85287 Allow opening the Game Dashboard from the Home Build page (#4730)
* This will work by default for new games
* For existing games, the option will appear the next time you save your project
2022-12-27 12:43:30 +01:00
Clément Pasteau
4b542e071a Allow navigating through asset packs of an Asset Creator from their profile (#4697) 2022-12-27 11:59:29 +01:00
Florian Rival
64cad25b81 Bump newIDE version (#4752) 2022-12-26 16:24:35 +01:00
Florian Rival
8472e30342 Show a warning about Piskel/Jfxr/Yarn not being available yet for cloud projects on desktop (#4751) 2022-12-26 16:23:29 +01:00
Clément Pasteau
1b7d258727 Remove mentions of "experimental" for features and tools widely used and supported (#4749) 2022-12-26 15:02:56 +01:00
github-actions[bot]
946b77093d Update translations [skip ci] (#4742)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-12-26 14:10:34 +01:00
Florian Rival
74c882f219 Fix wrong scrollbars in the Sprite editor 2022-12-26 12:50:40 +01:00
Florian Rival
abd417c494 Fix games crash because of some extensions not included in the generated games
Revert "Fix missing export files by including the free functions of every extension that is actually used (#4672)" (#4746) (commit 93121d1a1c)
2022-12-26 12:36:39 +01:00
Florian Rival
2005f9c37b Bump newIDE version 2022-12-24 11:30:25 +01:00
github-actions[bot]
b749ac1106 Update translations [skip ci] (#4736)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-12-24 11:21:40 +01:00
Baptiste Augrain
f34f901fa7 Add support for opacity of layers for Tiled tilemaps (#4739) 2022-12-24 11:21:03 +01:00
Florian Rival
c74110c058 Fix the storage provider default choice not being always the optimal one when running the tutorial 2022-12-23 23:29:44 +01:00
Baptiste Augrain
2a38be827c Add support for Tileset resources with .tsj extension (#4737) 2022-12-23 23:09:16 +01:00
D8H
7cd97e8cc4 Disable "missing shared data" error logs for custom objects. (#4735)
* Don't in changelog
2022-12-23 18:31:19 +01:00
github-actions[bot]
4c9445f6f5 Update translations [skip ci] (#4734)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-12-23 18:08:28 +01:00
Baptiste Augrain
b7772520b5 Add support for selecting directly .ldtk and .tmj files from the interface (#4733)
* Files saved from LDtk or from Tiled (using the .tmj format) can be now selected directly when choosing a Tilemap to be displayed by a Tilemap object.
2022-12-23 17:46:17 +01:00
D8H
93121d1a1c Fix missing export files by including the free functions of every extension that is actually used (#4672) 2022-12-23 17:33:52 +01:00
D8H
9f7c1a1748 Better display custom objects in the editor (#4643) 2022-12-23 17:21:47 +01:00
Florian Rival
f6fc2406a5 Fix missing menu items on web-app
Don't show in changelog
2022-12-23 15:32:37 +01:00
github-actions[bot]
daede19d0d Update translations [skip ci] (#4718)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-12-23 12:42:26 +01:00
Florian Rival
36e835c461 Don't enable player authentication if a tutorial is running when creating a new project
Don't show in changelog
2022-12-23 10:49:16 +01:00
Florian Rival
8d0b3ef53e Fix MarkdownText using a div instead of a span when not a standalone text
* Also add a missing translation
* Also enable player authentication by default when creating a new game
2022-12-23 10:13:58 +01:00
Florian Rival
7ccd2cd146 Update README with link to GDevelop-tutorials repository [skip ci]
* This is where the step-by-step onboarding is stored and future tutorials can be built by the community: https://github.com/GDevelopApp/GDevelop-tutorials

Only show in developer changelog
2022-12-22 22:59:40 +01:00
D8H
49974131d0 Improve installation of assets containing custom objects, like menu buttons, so that if an extension needs an update, a confirmation dialog is shown (#4724) 2022-12-22 19:27:54 +01:00
Baptiste Augrain
77320ce12e Add support for LDtk Tilemaps (#4575)
* Tilemaps made with [LDtk](https://ldtk.io/), a modern 2D level editor, can be now used directly. Make sure to save your map made in LDtk as a JSON file, create a new Tilemap object in GDevelop and in the Tilemap field, choose the LDtk file.
* Tilesets used by the LDtk Tilemap are automatically imported in your game - you don't need to import them separately.
* For more information, read about the [Tilemap object on the wiki](https://wiki.gdevelop.io/gdevelop5/objects/tilemap).

Co-authored-by: Davy Hélard <davy.helard@gmail.com>
Co-authored-by: Florian Rival <Florian.Rival@gmail.com>
2022-12-22 18:44:27 +01:00
Clément Pasteau
35833e6e23 Allow adding default player authentication when creating a project (#4714)
* New checkbox in the Project Creation dialog, which will apply best practices for player authentication on the empty project
2022-12-22 16:20:13 +01:00
AlexandreS
7436032787 Fix asset store preview (#4729)
Do not show in changelog
2022-12-22 15:48:15 +01:00
AlexandreS
aa4fd1bda9 Prefer scroll in homepage header rather than uncontrolled wrap (#4728)
Do not show in changelog
2022-12-22 11:46:04 +01:00
AlexandreS
e10dd57fed In the scene editor, paste cut instances at highest z order (#4726) 2022-12-22 10:53:26 +01:00
Florian Rival
893257491a Launch automatically the search in the asset store when typing (#4725) 2022-12-21 22:12:05 +01:00
AlexandreS
b8f04515f3 Do not close context menu when clicking on checkbox items (#4690) 2022-12-21 12:46:45 +01:00
AlexandreS
4a807b6053 Fix editor that was setting the z order of a new instance a bit randomly 2022-12-21 12:41:49 +01:00
AlexandreS
dbfef27d2e Fix leaderboard admin closing when trying to remove a leaderboard (#4691) 2022-12-21 11:08:24 +01:00
D8H
72b69e4cdf Add a sprite editor for custom objects with a locked animation list (#4719) 2022-12-21 09:32:24 +01:00
D8H
2f11cd667b Add an expression to get the number of frames in the current animation of a sprite object (#4722) 2022-12-21 09:30:03 +01:00
Florian Rival
d9de2a3177 Fix the asset store search
Don't show in changelog
2022-12-20 17:52:21 +01:00
764 changed files with 31250 additions and 10502 deletions

View File

@@ -57,8 +57,8 @@ jobs:
- name: Log urls to the Storybook
run: |
echo "Find the latest Storybook for this branch on http://gdevelop-storybook.s3-website-us-east-1.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/latest/index.html"
echo "Find the Storybook for this commit on http://gdevelop-storybook.s3-website-us-east-1.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/index.html"
echo "Find the latest Storybook for this branch on https://gdevelop-storybook.s3.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/latest/index.html"
echo "Find the Storybook for this commit on https://gdevelop-storybook.s3.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/index.html"
# Publish on Chromatic, only when manually launched (too costly to run on every commit).
- name: Publish Storybook to Chromatic

View File

@@ -726,6 +726,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
metadata.GetType() == "fontResource" ||
metadata.GetType() == "imageResource" ||
metadata.GetType() == "jsonResource" ||
metadata.GetType() == "tilemapResource" ||
metadata.GetType() == "tilesetResource" ||
metadata.GetType() == "videoResource" ||
// Deprecated, old parameter names:
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||

View File

@@ -15,11 +15,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAsyncExtension(
extension
.SetExtensionInformation(
"BuiltinAsync",
_("Async functions"),
_("Asynchronous functions"),
_("Functions that defer the execution of the events after it."),
"Arthur Pacaud (arthuro555)",
"Open source (MIT License)")
.SetCategory("Advanced");
extension.AddInstructionOrExpressionGroupMetadata(_("Asynchronous functions"))
.SetIcon("res/function32.png");
extension.AddEvent("Async",
_("Async event"),
@@ -27,6 +29,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAsyncExtension(
"",
"res/eventaddicon.png",
std::make_shared<gd::AsyncEvent>());
extension
.AddAction(
"ResolveAsyncEventsFunction",
_("End asynchronous function"),
_("Mark an asynchronous function as finished. This will allow the "
"actions and subevents following it to be run."),
"Mark asynchronous function as ended",
"",
"res/actions/quit24.png",
"res/actions/quit.png")
.AddCodeOnlyParameter("eventsFunctionContext", "");
}
} // namespace gd

View File

@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
.SetCategory("Audio");
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds and music"))
.SetIcon("res/actions/music24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds on channels"))
.SetIcon("res/actions/son24.png");
extension
.AddAction("PlaySoundCanal",

View File

@@ -406,8 +406,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddAction("ModVarObjet",
_("Value of an object variable"),
_("Change the value of an object variable."),
_("Change number variable"),
_("Modify the number value of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
@@ -419,8 +419,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
ParameterOptions::MakeNewOptions());
obj.AddAction("ModVarObjetTxt",
_("Text of an object variable"),
_("Change the text of an object variable."),
_("Change text variable"),
_("Modify the text of an object variable."),
_("the text of variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
@@ -432,8 +432,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
ParameterOptions::MakeNewOptions());
obj.AddAction("SetObjectVariableAsBoolean",
_("Boolean value of an object variable"),
_("Change the boolean value of an object variable."),
_("Change boolean variable"),
_("Modify the boolean value of an object variable."),
_("Set the boolean value of variable _PARAM1_ of "
"_PARAM0_ to _PARAM2_"),
_("Variables"),
@@ -446,7 +446,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction(
"ToggleObjectVariableAsBoolean",
_("Toggle the boolean value of an object variable"),
_("Toggle boolean variable"),
_("Toggles the boolean value of an object variable.") + "\n" +
_("If it was true, it will become false, and if it was false "
"it will become true."),
@@ -461,37 +461,39 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("ObjectVariableChildExists",
_("Child existence"),
_("Check if the specified child of the variable exists."),
_("Check if the specified child of the object "
"structure variable exists."),
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
_("Variables/Collections/Structures"),
_("Variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("objectvar", _("Structure variable"))
.AddParameter("string", _("Name of the child"))
.MarkAsAdvanced();
obj.AddAction("ObjectVariableRemoveChild",
_("Remove a child"),
_("Remove a child from an object variable."),
_("Remove a child from an object structure variable."),
_("Remove child _PARAM2_ from variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections/Structures"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("objectvar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.MarkAsAdvanced();
obj.AddAction("ObjectVariableClearChildren",
_("Clear variable"),
_("Remove all the children from the object variable."),
_("Clear children"),
_("Remove all the children from the object array or structure "
"variable."),
_("Clear children from variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("objectvar", _("Array or structure variable"))
.MarkAsAdvanced();
obj.AddAction("Cache",
@@ -619,8 +621,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("VarObjet",
_("Value of an object variable"),
_("Compare the value of an object variable."),
_("Number variable"),
_("Compare the number value of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/conditions/var24.png",
@@ -632,7 +634,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"number", ParameterOptions::MakeNewOptions());
obj.AddCondition("VarObjetTxt",
_("Text of an object variable"),
_("Text variable"),
_("Compare the text of an object variable."),
_("the text of variable _PARAM1_"),
_("Variables"),
@@ -645,7 +647,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"string", ParameterOptions::MakeNewOptions());
obj.AddCondition("ObjectVariableAsBoolean",
_("Boolean value of an object variable"),
_("Boolean variable"),
_("Compare the boolean value of an object variable."),
_("The boolean value of variable _PARAM1_ of object "
"_PARAM0_ is _PARAM2_"),
@@ -659,7 +661,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("VarObjetDef",
"Variable defined",
"Check if the variable is defined.",
"Check if the object variable is defined.",
"Variable _PARAM1 of _PARAM0_ is defined",
_("Variables"),
"res/conditions/var24.png",
@@ -667,78 +669,131 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("string", _("Variable"))
.SetHidden();
.SetHidden(); // Deprecated.
obj.AddAction(
"ObjectVariablePush",
_("Append variable to an object array"),
_("Appends a variable to the end of an object array variable."),
_("Append variable _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections/Arrays"),
_("Add existing variable"),
_("Adds an existing variable to the end of an object array variable."),
_("Add variable _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("scenevar", _("Scene variable with the content to append"))
.SetParameterLongDescription(
_("The content of the variable will *be copied* and appended at the "
"end of the array."))
.AddParameter("scenevar", _("Scene variable with the content to add"))
.SetParameterLongDescription(_("The content of the object variable will "
"*be copied* and added at the "
"end of the array."))
.MarkAsAdvanced();
obj.AddAction(
"ObjectVariablePushString",
_("Append a string to an object array"),
_("Appends a string to the end of an object array variable."),
_("Append string _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections/Arrays"),
_("Add text variable"),
_("Adds a text (string) to the end of an object array variable."),
_("Add text _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("string", _("String to append"))
.AddParameter("string", _("Text to add"))
.MarkAsAdvanced();
obj.AddAction(
"ObjectVariablePushNumber",
_("Append a number to an object array"),
_("Appends a number to the end of an object array variable."),
_("Append number _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
obj.AddAction("ObjectVariablePushNumber",
_("Add number variable"),
_("Adds a number to the end of an object array variable."),
_("Add number _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Number to append"))
.AddParameter("expression", _("Number to add"))
.MarkAsAdvanced();
obj.AddAction(
"ObjectVariablePushBool",
_("Append a boolean to an object array"),
_("Appends a boolean to the end of an object array variable."),
_("Append boolean _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Collections/Arrays"),
_("Add boolean variable"),
_("Adds a boolean to the end of an object array variable."),
_("Add boolean _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to append"))
.AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced();
obj.AddAction(
"ObjectVariableRemoveAt",
_("Remove variable from an object array (by index)"),
_("Remove variable by index"),
_("Removes a variable at the specified index of an object array "
"variable."),
_("Remove variable at index _PARAM2_ from array variable _PARAM1_ of "
"_PARAM0_"),
_("Variables/Collections/Arrays"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.MarkAsAdvanced();
obj.AddCondition(
"ObjectVariableChildCount",
_("Number of children"),
_("Compare the number of children in an object array variable."),
_("The number of children in the array variable _PARAM1_"),
_("Variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
obj.AddStrExpression(
"ArrayVariableFirstString",
_("First text child"),
_("Get the value of the first element of an object array variable, if "
"it is a text (string) variable."),
_("Variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
obj.AddExpression(
"ArrayVariableFirstNumber",
_("First number child"),
_("Get the value of the first element of an object array variable, if "
"it is a number variable."),
_("Variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
obj.AddStrExpression(
"ArrayVariableLastString",
_("Last text child"),
_("Get the value of the last element of an object array variable, if "
"it is a text (string) variable."),
_("Variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
obj.AddExpression(
"ArrayVariableLastNumber",
_("Last number child"),
_("Get the value of the last element of an object array variable, if "
"it is a number variable."),
_("Variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"));
obj.AddCondition("BehaviorActivated",
_("Behavior activated"),
_("Check if the behavior is activated for the object."),
@@ -1115,23 +1170,24 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Target Y position"));
obj.AddExpression("Variable",
_("Value of an object variable"),
_("Value of an object variable"),
_("Number variable"),
_("Number value of an object variable"),
_("Variables"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"));
obj.AddExpression("VariableChildCount",
_("Number of children of an object variable"),
_("Number of children of an object variable"),
_("Variables"),
"res/actions/var.png")
obj.AddExpression(
"VariableChildCount",
_("Number of children"),
_("Number of children in an object array or structure variable"),
_("Variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"));
.AddParameter("objectvar", _("Array or structure variable"));
obj.AddStrExpression("VariableString",
_("Text of an object variable"),
_("Text variable"),
_("Text of an object variable"),
_("Variables"),
"res/actions/var.png")
@@ -1293,7 +1349,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListOrEmptyIfJustDeclared", _("Group of potential objects"))
.AddParameter("objectListOrEmptyIfJustDeclared",
_("Group of potential objects"))
.SetParameterLongDescription(
_("Group containing objects that can be created by the action."))
.AddParameter("string", _("Name of the object to create"))

View File

@@ -26,6 +26,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.SetExtensionHelpPath("/interface/scene-editor/layers-and-cameras");
extension.AddInstructionOrExpressionGroupMetadata(_("Layers and cameras"))
.SetIcon("res/conditions/camera24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
.SetIcon("res/actions/effect24.png");
extension
.AddExpressionAndConditionAndAction(
@@ -453,8 +455,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
"res/actions/effect24.png",
"res/actions/effect.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
@@ -472,8 +474,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
"res/actions/effect24.png",
"res/actions/effect.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
@@ -491,8 +493,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Enable _PARAM3_ for effect _PARAM2_ of layer _PARAM1_: _PARAM4_"),
_("Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
"res/actions/effect24.png",
"res/actions/effect.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
@@ -506,9 +508,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("Layer effect is enabled"),
_("The effect on a layer is enabled"),
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
_("Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
_(""),
"res/actions/effect24.png",
"res/actions/effect.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
@@ -521,8 +523,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("Enable an effect on a layer"),
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
_("Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
"res/actions/effect24.png",
"res/actions/effect.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
@@ -601,7 +603,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("Set the ambient light color of the lighting layer in format "
"\"R;G;B\" string."),
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),
_("Lighting"),
_(""),
"res/actions/color24.png",
"res/actions/color.png")
.AddCodeOnlyParameter("currentScene", "")

View File

@@ -27,6 +27,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.SetCategory("Input");
extension.AddInstructionOrExpressionGroupMetadata(_("Mouse and touch"))
.SetIcon("res/actions/mouse24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Multitouch"))
.SetIcon("res/conditions/touch24.png");
extension
.AddCondition(
@@ -153,7 +155,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
extension
.AddExpressionAndCondition(
"number",
"MouseX",
"CursorX",
_("Cursor X position"),
_("the X position of the cursor or of a touch"),
_("the cursor (or touch) X position"),
@@ -167,13 +169,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.SetDefaultValue("0");
// Support for deprecated names:
extension.AddDuplicatedCondition("SourisX", "MouseX").SetHidden();
extension.AddDuplicatedExpression("SourisX", "MouseX").SetHidden();
extension.AddDuplicatedCondition("MouseX", "CursorX").SetHidden();
extension.AddDuplicatedExpression("MouseX", "CursorX").SetHidden();
extension.AddDuplicatedCondition("SourisX", "CursorX").SetHidden();
extension.AddDuplicatedExpression("SourisX", "CursorX").SetHidden();
extension
.AddExpressionAndCondition(
"number",
"MouseY",
"CursorY",
_("Cursor Y position"),
_("the Y position of the cursor or of a touch"),
_("the cursor (or touch) Y position"),
@@ -187,8 +191,44 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.SetDefaultValue("0");
// Support for deprecated names:
extension.AddDuplicatedCondition("SourisY", "MouseY").SetHidden();
extension.AddDuplicatedExpression("SourisY", "MouseY").SetHidden();
extension.AddDuplicatedCondition("MouseY", "CursorY").SetHidden();
extension.AddDuplicatedExpression("MouseY", "CursorY").SetHidden();
extension.AddDuplicatedCondition("SourisY", "CursorY").SetHidden();
extension.AddDuplicatedExpression("SourisY", "CursorY").SetHidden();
extension
.AddExpressionAndCondition("number",
"MouseOnlyCursorX",
_("Mouse cursor X position"),
_("the X position of the mouse cursor"),
_("the mouse cursor X position"),
"",
"res/conditions/mouse24.png")
.AddCodeOnlyParameter("currentScene", "")
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
// It's only useful for extensions as they can't use TouchSimulateMouse.
.SetHidden();
extension
.AddExpressionAndCondition("number",
"MouseOnlyCursorY",
_("Mouse cursor Y position"),
_("the Y position of the mouse cursor"),
_("the mouse cursor Y position"),
"",
"res/conditions/mouse24.png")
.AddCodeOnlyParameter("currentScene", "")
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
// It's only useful for extensions as they can't use TouchSimulateMouse.
.SetHidden();
extension
.AddCondition("IsMouseInsideCanvas",
@@ -334,44 +374,85 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition(
"HasAnyTouchStarted",
_("A new touch has started"),
_("Check if a touch has just started on this frame. The touch identifiers can be "
_("Check if a touch has just started on this frame. The touch "
"identifiers can be "
"accessed using StartedTouchId() and StartedTouchCount()."),
_("A new touch has started"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
extension
.AddExpression("StartedTouchCount",
_("Started touch count"),
_("The number of touches that have just started on this "
"frame. The touch identifiers can be "
"accessed using StartedTouchId()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
extension
.AddExpression("StartedTouchId",
_("Started touch identifier"),
_("The identifier of the touch that has just started on "
"this frame. The number of touches can be "
"accessed using StartedTouchCount()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch index"))
.SetHidden();
extension
.AddCondition(
"HasAnyTouchOrMouseStarted",
_("A new touch has started"),
_("Check if a touch has just started or the mouse left button has "
"been pressed on this frame. The touch identifiers can be "
"accessed using StartedTouchOrMouseId() and "
"StartedTouchOrMouseCount()."),
_("A new touch has started"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddExpression(
"StartedTouchCount",
"StartedTouchOrMouseCount",
_("Started touch count"),
_("The number of touches that have just started on this frame. The touch identifiers can be "
"accessed using StartedTouchId()."),
_("The number of touches (including the mouse) that have just "
"started on this frame. The touch identifiers can be "
"accessed using StartedTouchOrMouseId()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddExpression(
"StartedTouchId",
"StartedTouchOrMouseId",
_("Started touch identifier"),
_("The identifier of the touch that has just started on this frame. The touch number of touches can be "
"accessed using StartedTouchCount()."),
_("The identifier of the touch or mouse that has just started on "
"this frame. The number of touches can be "
"accessed using StartedTouchOrMouseCount()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch index"));
extension
.AddCondition(
"HasTouchEnded",
_("A touch has ended"),
_("Check if a touch has ended."),
_("The touch with identifier _PARAM1_ has ended"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCondition("HasTouchEnded",
_("A touch has ended"),
_("Check if a touch has ended or a mouse left button has "
"been released."),
_("The touch with identifier _PARAM1_ has ended"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch identifier"));

View File

@@ -136,6 +136,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("yesorno", _("Deactivate input when focus is lost"))
.MarkAsAdvanced();
extension
.AddCondition(
"HasGameJustResumed",
_("Game has just resumed"),
_("Check if the game has just resumed from being hidden. It "
"happens when the game tab is selected, a minimized window is "
"restored or the application is put back on front."),
_("Game has just resumed"),
"",
"res/actions/window24.png",
"res/actions/window.png")
.SetHelpPath("/interface/scene-editor/events")
.AddCodeOnlyParameter("currentScene", "");
}
} // namespace gd

View File

@@ -583,7 +583,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddExpression("Sprite",
_("Image"),
_("Animation frame of the object"),
_("Current frame of the animation of the object"),
_("Animations and images"),
"res/actions/sprite.png")
.AddParameter("object", _("Object"), "Sprite");
obj.AddExpression("AnimationFrameCount",
_("Number of frames"),
_("Number of frames in the current animation of the object"),
_("Animations and images"),
"res/actions/sprite.png")
.AddParameter("object", _("Object"), "Sprite");

View File

@@ -183,6 +183,26 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Position of the last character in the string to be "
"considered in the search"));
extension
.AddStrExpression("StrReplaceOne",
_("Replace the first occurrence of a text by another."),
_("Replace the first occurrence of a text by another."),
"",
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text in which the replacement must be done"))
.AddParameter("string", _("Text to find inside the first text"))
.AddParameter("string", _("Replacement to put instead of the text to find"));
extension
.AddStrExpression("StrReplaceAll",
_("Replace all occurrences of a text by another."),
_("Replace all occurrences of a text by another."),
"",
"res/conditions/toujours24_black.png")
.AddParameter("string", _("Text in which the replacement(s) must be done"))
.AddParameter("string", _("Text to find inside the first text"))
.AddParameter("string", _("Replacement to put instead of the text to find"));
}
} // namespace gd

View File

@@ -151,7 +151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddAction("Wait",
_("Wait X seconds (experimental)"),
_("Wait X seconds"),
_("Waits a number of seconds before running "
"the next actions (and sub-events)."),
_("Wait _PARAM0_ seconds"),

View File

@@ -27,9 +27,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarScene",
_("Value of a scene variable"),
_("Compare the value of a scene variable."),
_("the scene variable _PARAM0_"),
_("Number variable"),
_("Compare the number value of a scene variable."),
_("The number of scene variable _PARAM0_"),
_("Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
@@ -39,9 +39,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarSceneTxt",
_("Text of a scene variable"),
_("Compare the text of a scene variable."),
_("the text of scene variable _PARAM0_"),
_("Text variable"),
_("Compare the text (string) of a scene variable."),
_("The text of scene variable _PARAM0_"),
_("Scene variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
@@ -52,7 +52,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition(
"SceneVariableAsBoolean",
_("Boolean value of a scene variable"),
_("Boolean variable"),
_("Compare the boolean value of a scene variable."),
_("The boolean value of scene variable _PARAM0_ is _PARAM1_"),
_("Scene variables"),
@@ -63,14 +63,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetDefaultValue("true");
extension
.AddCondition(
"VariableChildExists",
_("Child existence"),
_("Check if the specified child of the scene variable exists."),
_("Child _PARAM1_ of scene variable _PARAM0_ exists"),
_("Scene variables/Collections/Structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddCondition("VariableChildExists",
_("Child existence"),
_("Check if the specified child of the scene structure "
"variable exists."),
_("Child _PARAM1_ of scene variable _PARAM0_ exists"),
_("Scene variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("string", _("Name of the child"))
.MarkAsAdvanced();
@@ -78,10 +78,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("GlobalVariableChildExists",
_("Child existence"),
_("Check if the specified child of the global "
_("Check if the specified child of the global structure "
"variable exists."),
_("Child _PARAM1_ of global variable _PARAM0_ exists"),
_("Global variables/Collections/Structures"),
_("Global variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
@@ -90,7 +90,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarSceneDef",
"Test if a scene variable is defined",
"Variable defined",
"Test if the scene variable exists.",
"Scene variable _PARAM0_ is defined",
_("Scene variables"),
@@ -98,12 +98,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"res/conditions/var.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Variable"))
.SetHidden();
.SetHidden(); // Deprecated.
extension
.AddCondition("VarGlobal",
_("Value of a global variable"),
_("Compare the value of a global variable."),
_("Number variable"),
_("Compare the number value of a global variable."),
_("the global variable _PARAM0_"),
_("Global variables"),
"res/conditions/var24.png",
@@ -115,8 +115,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarGlobalTxt",
_("Text of a global variable"),
_("Compare the text of a global variable."),
_("Text variable"),
_("Compare the text (string) of a global variable."),
_("the text of the global variable _PARAM0_"),
_("Global variables"),
"res/conditions/var24.png",
@@ -129,7 +129,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition(
"GlobalVariableAsBoolean",
_("Boolean value of a global variable"),
_("Boolean variable"),
_("Compare the boolean value of a global variable."),
_("The boolean value of global variable _PARAM0_ is _PARAM1_"),
_("Global variables"),
@@ -141,8 +141,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarGlobalDef",
"Test if a global variable is defined",
"Test if a global variable exists",
"Variable defined",
"Test if a global variable exists.",
"Global variable _PARAM0_ is defined",
_("Global variables"),
"res/conditions/var24.png",
@@ -150,12 +150,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Variable"))
.MarkAsAdvanced()
.SetHidden();
.SetHidden(); // Deprecated.
extension
.AddAction("ModVarScene",
_("Value of a scene variable"),
_("Change the value of a scene variable."),
_("Change number variable"),
_("Modify the number value of a scene variable."),
_("the scene variable _PARAM0_"),
_("Scene variables"),
"res/actions/var24.png",
@@ -166,8 +166,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction("ModVarSceneTxt",
_("String of a scene variable"),
_("Modify the text of a scene variable."),
_("Change text variable"),
_("Modify the text (string) of a scene variable."),
_("the text of scene variable _PARAM0_"),
_("Scene variables"),
"res/actions/var24.png",
@@ -179,7 +179,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction(
"SetSceneVariableAsBoolean",
_("Boolean value of a scene variable"),
_("Change boolean variable"),
_("Modify the boolean value of a scene variable."),
_("Set the boolean value of scene variable _PARAM0_ to _PARAM1_"),
_("Scene variables"),
@@ -190,7 +190,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction("ToggleSceneVariableAsBoolean",
_("Toggle boolean value of a scene variable"),
_("Toggle boolean variable"),
_("Toggle the boolean value of a scene variable.") + "\n" +
_("If it was true, it will become false, and if it was "
"false it will become true."),
@@ -202,8 +202,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction("ModVarGlobal",
_("Value of a global variable"),
_("Change the value of a global variable"),
_("Change number variable"),
_("Modify the number value of a global variable."),
_("the global variable _PARAM0_"),
_("Global variables"),
"res/actions/var24.png",
@@ -215,8 +215,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction("ModVarGlobalTxt",
_("String of a global variable"),
_("Modify the text of a global variable."),
_("Change text variable"),
_("Modify the text (string) of a global variable."),
_("the text of global variable _PARAM0_"),
_("Global variables"),
"res/actions/var24.png",
@@ -229,7 +229,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction(
"SetGlobalVariableAsBoolean",
_("Boolean value of a global variable"),
_("Change boolean variable"),
_("Modify the boolean value of a global variable."),
_("Set the boolean value of global variable _PARAM0_ to _PARAM1_"),
_("Global variables"),
@@ -240,7 +240,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddAction("ToggleGlobalVariableAsBoolean",
_("Toggle boolean value of a global variable"),
_("Toggle boolean variable"),
_("Toggle the boolean value of a global variable.") + "\n" +
_("If it was true, it will become false, and if it was "
"false it will become true."),
@@ -251,202 +251,324 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddParameter("globalvar", _("Variable"));
extension
.AddAction("VariableRemoveChild",
_("Remove a child"),
_("Remove a child from a scene variable."),
_("Remove child _PARAM1_ from scene variable _PARAM0_"),
_("Scene variables/Collections/Structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddAction(
"VariableRemoveChild",
_("Remove a child"),
_("Remove a child from a scene structure variable."),
_("Remove child _PARAM1_ from scene structure variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariableRemoveChild",
_("Remove a child"),
_("Remove a child from a global variable."),
_("Remove child _PARAM1_ from global variable _PARAM0_"),
_("Global variables/Collections/Structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddAction(
"GlobalVariableRemoveChild",
_("Remove a child"),
_("Remove a child from a global structure variable."),
_("Remove child _PARAM1_ from global structure variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.MarkAsAdvanced();
extension
.AddAction("VariableClearChildren",
_("Clear scene variable"),
_("Remove all the children from the scene variable."),
_("Clear children"),
_("Remove all the children from the scene structure or array "
"variable."),
_("Clear children from scene variable _PARAM0_"),
_("Scene variables/Collections"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("scenevar", _("Structure or array variable"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariableClearChildren",
_("Clear global variable"),
_("Remove all the children from the global variable."),
_("Clear children"),
_("Remove all the children from the global structure or array "
"variable."),
_("Clear children from global variable _PARAM0_"),
_("Global variables/Collections"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("globalvar", _("Structure or array variable"))
.MarkAsAdvanced();
extension
.AddAction("SceneVariablePush",
_("Append variable to a scene array"),
_("Appends a variable at the end of a scene array variable."),
_("Append variable _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Collections/Arrays"),
_("Add existing variable"),
_("Adds an existing variable at the end of a scene array "
"variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("scenevar", _("Scene variable with the content to append"))
.SetParameterLongDescription(_("The content of the variable will *be copied* and appended at the end of the array."))
.AddParameter("scenevar", _("Scene variable with the content to add"))
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.MarkAsAdvanced();
extension
.AddAction("SceneVariablePushString",
_("Append a string to a scene array"),
_("Appends a string at the end of a scene array variable."),
_("Append string _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
.AddAction(
"SceneVariablePushString",
_("Add text variable"),
_("Adds a text (string) at the end of a scene array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("string", _("String to append"))
.AddParameter("string", _("Text to add"))
.MarkAsAdvanced();
extension
.AddAction("SceneVariablePushNumber",
_("Append a number to a scene array"),
_("Appends a number at the end of a scene array variable."),
_("Append number _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Collections/Arrays"),
_("Add number variable"),
_("Adds a number at the end of a scene array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Number to append"))
.AddParameter("expression", _("Number to add"))
.MarkAsAdvanced();
extension
.AddAction("SceneVariablePushBool",
_("Append a boolean to a scene array"),
_("Appends a boolean at the end of a scene array variable."),
_("Append boolean _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Collections/Arrays"),
_("Add boolean variable"),
_("Adds a boolean at the end of a scene array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to append"))
.AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced();
extension
.AddAction(
"SceneVariableRemoveAt",
_("Remove variable from a scene array (by index)"),
_("Removes a variable at the specified index of a scene array variable."),
_("Remove variable at index _PARAM1_ from scene array variable _PARAM0_"),
_("Scene variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddAction("SceneVariableRemoveAt",
_("Remove variable by index"),
_("Removes a variable at the specified index of a scene array "
"variable."),
_("Remove variable at index _PARAM1_ from scene array "
"variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariablePush",
_("Append variable to a global array"),
_("Appends a variable at the end of a global array variable."),
_("Append variable _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("scenevar", _("Scene variable with the content to append"))
.SetParameterLongDescription(_("The content of the variable will *be copied* and appended at the end of the array."))
.AddCondition(
"SceneVariableChildCount",
_("Number of children"),
_("Compare the number of children in a scene array variable."),
_("The number of children in the array variable _PARAM0_"),
_("Scene variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
extension
.AddStrExpression(
"SceneVariableFirstString",
_("First text child"),
_("Get the value of the first element of a scene array variable, if "
"it is a text (string)."),
_("Scene variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"));
extension
.AddExpression(
"SceneVariableFirstNumber",
_("First number child"),
_("Get the value of the first element of a scene array variable, if "
"it is a number."),
_("Scene variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"));
extension
.AddStrExpression(
"SceneVariableLastString",
_("Last text child"),
_("Get the value of the last element of a scene array variable, if "
"it is a text (string)."),
_("Scene variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"));
extension
.AddExpression(
"SceneVariableLastNumber",
_("Last number child"),
_("Get the value of the last element of a scene array variable, if "
"it is a number."),
_("Scene variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Array variable"));
extension
.AddAction(
"GlobalVariableRemoveAt",
_("Remove variable from a global array (by index)"),
_("Removes a variable at the specified index of a global array variable."),
_("Remove variable at index _PARAM1_ from global array variable _PARAM0_"),
_("Global variables/Collections/Arrays"),
"GlobalVariablePush",
_("Add existing variable"),
_("Adds an existing variable at the end of a global array variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("globalvar", _("Array variable"))
.AddParameter("scenevar", _("Scene variable with the content to add"))
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariableRemoveAt",
_("Remove variable by index"),
_("Removes a variable at the specified index of a global "
"array variable."),
_("Remove variable at index _PARAM1_ from global array "
"variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariablePushString",
_("Append a string to a global array"),
_("Appends a string at the end of a global array variable."),
_("Append string _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
.AddAction(
"GlobalVariablePushString",
_("Add text variable"),
_("Adds a text (string) at the end of a global array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("string", _("String to append"))
.AddParameter("string", _("Text to add"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariablePushNumber",
_("Append a number to a global array"),
_("Appends a number at the end of a global array variable."),
_("Append number _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Collections/Arrays"),
_("Add number variable"),
_("Adds a number at the end of a global array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Number to append"))
.AddParameter("expression", _("Number to add"))
.MarkAsAdvanced();
extension
.AddAction("GlobalVariablePushBool",
_("Append a boolean to a global array"),
_("Appends a boolean at the end of a global array variable."),
_("Append boolean _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Collections/Arrays"),
_("Add boolean variable"),
_("Adds a boolean at the end of a global array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to append"))
.AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced();
extension
.AddExpression("GlobalVariableChildCount",
_("Number of children of a global variable"),
_("Number of children of a global variable"),
_("Global variables"),
.AddCondition(
"GlobalVariableChildCount",
_("Number of children"),
_("Compare the number of children in a global array variable."),
_("The number of children of the array variable _PARAM0_"),
_("Global variables/Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
extension
.AddStrExpression("GlobalVariableFirstString",
_("First text child"),
_("Value of the first element of a global array "
"variable, if it is a text (string) variable."),
_("Global variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"));
extension
.AddExpression("GlobalVariableFirstNumber",
_("First number child"),
_("Value of the first element of a global array "
"variable, if it is a number variable"),
_("Global variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"));
.AddParameter("globalvar", _("Array variable"));
extension
.AddStrExpression(
"GlobalVariableLastString",
_("Last text child"),
_("Value of the last element of a global array variable, if "
"it is a text (string) variable."),
_("Global variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"));
extension
.AddExpression(
"GlobalVariableLastNumber",
_("Last number child"),
_("Value of the last element of a global array variable, if "
"it is a number variable"),
_("Global variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array variable"));
extension
.AddExpression("GlobalVariableChildCount",
_("Number of children"),
_("Number of children in a global array or "
"structure variable"),
_("Global variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("globalvar", _("Array or structure variable"));
extension
.AddExpression("VariableChildCount",
_("Number of children of a scene variable"),
_("Number of children of a scene variable"),
_("Scene variables"),
_("Number of children"),
_("Number of children in a scene array or "
"structure variable"),
_("Scene variables/Arrays and structures"),
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"));
.AddParameter("scenevar", _("Array or structure variable"));
extension
.AddExpression("Variable",
_("Value of a scene variable"),
_("Value of a scene variable"),
_("Number variable"),
_("Number value of a scene variable"),
_("Scene variables"),
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"));
extension
.AddStrExpression("VariableString",
_("Text of a scene variable"),
_("Text variable"),
_("Text of a scene variable"),
_("Scene variables"),
"res/actions/var.png")
@@ -454,15 +576,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddExpression("GlobalVariable",
_("Value of a global variable"),
_("Value of a global variable"),
_("Number variable"),
_("Number value of a global variable"),
_("Global variables"),
"res/actions/var.png")
.AddParameter("globalvar", _("Name of the global variable"));
extension
.AddStrExpression("GlobalVariableString",
_("Text of a global variable"),
_("Text variable"),
_("Text of a global variable"),
_("Global variables"),
"res/actions/var.png")

View File

@@ -46,7 +46,7 @@ class GD_CORE_API ObjectMetadata {
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_);
/**
* \brief Construct an object metadata, without "blueprint" object
*
*
* \note This is used by events based objects.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
@@ -295,6 +295,22 @@ class GD_CORE_API ObjectMetadata {
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() { return strExpressionsInfos; };
/**
* \brief Set the object to be hidden in the IDE.
*
* Used mainly when an object is deprecated.
*/
ObjectMetadata &SetHidden() {
hidden = true;
return *this;
}
/**
* \brief Return true if the instruction must be hidden in the IDE.
*/
bool IsHidden() const { return hidden; }
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
@@ -314,6 +330,7 @@ class GD_CORE_API ObjectMetadata {
gd::String iconFilename;
gd::String categoryFullName;
std::set<gd::String> unsupportedBaseObjectCapabilities;
bool hidden = false;
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new

View File

@@ -22,7 +22,7 @@ class EventsList;
namespace gd {
/**
* \brief List the values of the parameters of events and their type.
* \brief Allow to safely remove a bunch of events.
*
* \ingroup IDE
*/

View File

@@ -2,6 +2,7 @@
#include "GDCore/Events/Instruction.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
@@ -11,30 +12,36 @@
namespace gd {
std::set<gd::String> UsedExtensionsFinder::ScanProject(gd::Project& project) {
const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::WholeProjectRefactorer::ExposeProjectObjects(project, worker);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, worker);
return worker.usedExtensions;
return worker.result;
};
// Objects scanner
void UsedExtensionsFinder::DoVisitObject(gd::Object& object) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType())
.GetExtension()
.GetName());
void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
};
// Behaviors scanner
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior& behavior) {
usedExtensions.insert(
gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName())
.GetExtension()
.GetName());
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
for (auto &&includeFile : metadata.GetMetadata().requiredFiles) {
result.GetUsedRequiredFiles().insert(includeFile);
}
};
// Instructions scanner
@@ -46,23 +53,29 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
usedExtensions.insert(metadata.GetExtension().GetName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
}
size_t i = 0;
for (auto expression : instruction.GetParameters()) {
const gd::String& parameterType =
metadata.GetMetadata().GetParameter(i).GetType();
i++;
gd::ParameterMetadataTools::IterateOverParameters(
instruction.GetParameters(),
metadata.GetMetadata().GetParameters(),
[this](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
const gd::String& lastObjectName) {
const gd::String& parameterType = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsExpression("string", parameterType)) {
rootType = "string";
expression.GetRootNode()->Visit(*this);
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("number", parameterType)) {
rootType = "number";
expression.GetRootNode()->Visit(*this);
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
usedExtensions.insert("BuiltinVariables");
}
result.GetUsedExtensions().insert("BuiltinVariables");
});
return false;
}
@@ -93,31 +106,35 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
result.GetUsedExtensions().insert("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
// Add extensions bound to Objects/Behaviors/Functions
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(project.GetCurrentPlatform(), GetGlobalObjectsContainer(), GetObjectsContainer(), rootType, node);
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetGlobalObjectsContainer(),
GetObjectsContainer(), rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName)
.GetExtension()
.GetName());
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
}
};
@@ -138,7 +155,10 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
usedExtensions.insert(metadata.GetExtension().GetName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
}
};
} // namespace gd

View File

@@ -21,18 +21,62 @@ class Behavior;
namespace gd {
class GD_CORE_API UsedExtensionsResult {
public:
/**
* The extensions used by the project (or part of it).
*/
const std::set<gd::String> &GetUsedExtensions() const {
return usedExtensions;
}
/**
* The include files used at runtime by the project (or part of it).
*/
const std::set<gd::String> &GetUsedIncludeFiles() const {
return usedIncludeFiles;
}
/**
* The additional files required at runtime by the project (or part of it).
*/
const std::set<gd::String> &GetUsedRequiredFiles() const {
return usedRequiredFiles;
}
/**
* The extensions used by the project (or part of it).
*/
std::set<gd::String> &GetUsedExtensions() { return usedExtensions; }
/**
* The include files used at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedIncludeFiles() { return usedIncludeFiles; }
/**
* The additional files required at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
};
class GD_CORE_API UsedExtensionsFinder
: public ArbitraryObjectsWorker,
public ArbitraryEventsWorkerWithContext,
public ExpressionParser2NodeWorker {
public:
static std::set<gd::String> ScanProject(gd::Project& project);
static const UsedExtensionsResult ScanProject(gd::Project& project);
private:
UsedExtensionsFinder(gd::Project& project_) : project(project_){};
gd::Project& project;
gd::String rootType;
std::set<gd::String> usedExtensions;
UsedExtensionsResult result;
// Object Visitor
void DoVisitObject(gd::Object& object) override;

View File

@@ -35,6 +35,16 @@ void ArbitraryResourceWorker::ExposeJson(gd::String& jsonName){
// do.
};
void ArbitraryResourceWorker::ExposeTilemap(gd::String& tilemapName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeTileset(gd::String& tilesetName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
// Nothing to do by default - each child class can define here the action to
// do.
@@ -92,6 +102,67 @@ void ArbitraryResourceWorker::ExposeResources(
}
}
void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
if (resourcesManagers.empty()) return;
gd::ResourcesManager* resourcesManager = resourcesManagers[0];
gd::Resource& resource = resourcesManager->GetResource(resourceName);
if (!resource.GetMetadata().empty()) {
gd::SerializerElement serializerElement =
gd::Serializer::FromJSON(resource.GetMetadata());
if (serializerElement.HasChild("embeddedResourcesMapping")) {
bool anyEmbeddedResourceNameWasRenamed = false;
gd::SerializerElement& embeddedResourcesMappingElement =
serializerElement.GetChild("embeddedResourcesMapping");
for (const auto& child :
embeddedResourcesMappingElement.GetAllChildren()) {
const gd::String& targetResourceName =
child.second->GetValue().GetString();
if (resourcesManager->HasResource(targetResourceName)) {
std::cout << targetResourceName << std::endl;
gd::Resource& targetResource =
resourcesManager->GetResource(targetResourceName);
const gd::String& targetResourceKind = targetResource.GetKind();
gd::String potentiallyUpdatedTargetResourceName = targetResourceName;
if (targetResourceKind == "audio") {
ExposeAudio(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "bitmapFont") {
ExposeBitmapFont(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "font") {
ExposeFont(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "image") {
ExposeImage(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "json") {
ExposeJson(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "tilemap") {
ExposeTilemap(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "tileset") {
ExposeTileset(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "video") {
ExposeVideo(potentiallyUpdatedTargetResourceName);
}
if (potentiallyUpdatedTargetResourceName != targetResourceName) {
// The resource name was renamed. Also update the mapping.
child.second->SetStringValue(potentiallyUpdatedTargetResourceName);
anyEmbeddedResourceNameWasRenamed = true;
}
}
}
if (anyEmbeddedResourceNameWasRenamed) {
resource.SetMetadata(gd::Serializer::ToJSON(serializerElement));
}
}
}
}
void ArbitraryResourceWorker::ExposeResource(gd::Resource& resource) {
if (!resource.UseFile()) return;
@@ -154,6 +225,14 @@ class ResourceWorkerInEventsWorker : public ArbitraryEventsWorker {
gd::String updatedParameterValue = parameterValue;
worker.ExposeJson(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilemapResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeTilemap(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilesetResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeTileset(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
}
});

View File

@@ -75,6 +75,16 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeJson(gd::String &jsonName);
/**
* \brief Expose a Tilemap, which is always a reference to a "tilemap" resource.
*/
virtual void ExposeTilemap(gd::String &tilemapName);
/**
* \brief Expose a Tileset, which is always a reference to a "tileset" resource.
*/
virtual void ExposeTileset(gd::String &tilesetName);
/**
* \brief Expose a video, which is always a reference to a "video" resource.
*/
@@ -96,6 +106,11 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeFile(gd::String &resourceFileName) = 0;
/**
* \brief Expose the embedded resources of the specified resource.
*/
virtual void ExposeEmbeddeds(gd::String &resourceName);
protected:
const std::vector<gd::ResourcesManager *> &GetResources() {
return resourcesManagers;

View File

@@ -40,6 +40,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String>& GetAllAudios() { return GetAll("audio"); };
std::set<gd::String>& GetAllFonts() { return GetAll("font"); };
std::set<gd::String>& GetAllJsons() { return GetAll("json"); };
std::set<gd::String>& GetAllTilemaps() { return GetAll("tilemap"); };
std::set<gd::String>& GetAllTilesets() { return GetAll("tileset"); };
std::set<gd::String>& GetAllVideos() { return GetAll("video"); };
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
std::set<gd::String>& GetAll(const gd::String& resourceType) {
@@ -47,6 +49,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
if (resourceType == "audio") return allAudios;
if (resourceType == "font") return allFonts;
if (resourceType == "json") return allJsons;
if (resourceType == "tilemap") return allTilemaps;
if (resourceType == "tileset") return allTilesets;
if (resourceType == "video") return allVideos;
if (resourceType == "bitmapFont") return allBitmapFonts;
@@ -68,6 +72,12 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
virtual void ExposeJson(gd::String& jsonResourceName) override {
allJsons.insert(jsonResourceName);
};
virtual void ExposeTilemap(gd::String& tilemapResourceName) override {
allTilemaps.insert(tilemapResourceName);
};
virtual void ExposeTileset(gd::String& tilesetResourceName) override {
allTilesets.insert(tilesetResourceName);
};
virtual void ExposeVideo(gd::String& videoResourceName) override {
allVideos.insert(videoResourceName);
};
@@ -80,6 +90,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String> allAudios;
std::set<gd::String> allFonts;
std::set<gd::String> allJsons;
std::set<gd::String> allTilemaps;
std::set<gd::String> allTilesets;
std::set<gd::String> allVideos;
std::set<gd::String> allBitmapFonts;
std::set<gd::String> emptyResources;

View File

@@ -49,6 +49,12 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeJson(gd::String& jsonResourceName) override {
RenameIfNeeded(jsonResourceName);
};
virtual void ExposeTilemap(gd::String& tilemapResourceName) override {
RenameIfNeeded(tilemapResourceName);
};
virtual void ExposeTileset(gd::String& tilesetResourceName) override {
RenameIfNeeded(tilesetResourceName);
};
virtual void ExposeVideo(gd::String& videoResourceName) override {
RenameIfNeeded(videoResourceName);
};

View File

@@ -12,6 +12,7 @@
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
@@ -298,4 +299,16 @@ PropertyFunctionGenerator::UnCapitalizeFirstLetter(const gd::String &string) {
return string.substr(0, 1).LowerCase() + string.substr(1);
}
void PropertyFunctionGenerator::GenerateConditionSkeleton(
gd::Project &project, gd::EventsFunction &eventFunction) {
auto &event = dynamic_cast<gd::StandardEvent &>(
eventFunction.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction action;
action.SetType("SetReturnBoolean");
action.AddParameter("True");
event.GetActions().Insert(action, 0);
}
} // namespace gd

View File

@@ -10,6 +10,7 @@ namespace gd {
class String;
class Project;
class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
class AbstractEventsBasedEntity;
@@ -43,6 +44,12 @@ public:
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property);
/**
* \brief Generate an event with a "return" action.
*/
static void GenerateConditionSkeleton(gd::Project &project,
gd::EventsFunction &eventFunction);
~PropertyFunctionGenerator();
private:

View File

@@ -125,6 +125,11 @@ class GD_CORE_API AbstractEventsBasedEntity {
*/
static gd::String GetPropertyExpressionName(const gd::String& propertyName) { return "Property" + propertyName; };
/**
* \brief Get the name of the action to toggle a boolean property.
*/
static gd::String GetPropertyToggleActionName(const gd::String& propertyName) { return "ToggleProperty" + propertyName; };
/** \name Serialization
*/
///@{

View File

@@ -38,7 +38,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
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());
@@ -78,7 +78,7 @@ bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
objectContent,
@@ -125,8 +125,7 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
}
}
void CustomObjectConfiguration::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& worker) {
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
for (auto& property : properties) {
@@ -148,6 +147,10 @@ void CustomObjectConfiguration::ExposeResources(
worker.ExposeVideo(newPropertyValue);
} else if (resourceType == "json") {
worker.ExposeJson(newPropertyValue);
} else if (resourceType == "tilemap") {
worker.ExposeTilemap(newPropertyValue);
} else if (resourceType == "tileset") {
worker.ExposeTileset(newPropertyValue);
} else if (resourceType == "bitmapFont") {
worker.ExposeBitmapFont(newPropertyValue);
}

View File

@@ -87,9 +87,9 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
/**
* 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.
*/

View File

@@ -125,6 +125,13 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
return "SharedProperty" + propertyName;
};
/**
* \brief Get the name of the action to toggle a boolean shared property.
*/
static gd::String GetSharedPropertyToggleActionName(const gd::String &propertyName) {
return "ToggleSharedProperty" + propertyName;
};
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(gd::Project& project,

View File

@@ -72,6 +72,9 @@ void EventsFunction::SerializeTo(SerializerElement& element) const {
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
if (isAsync) {
element.SetBoolAttribute("async", isAsync);
}
events.SerializeTo(element.AddChild("events"));
gd::String functionTypeStr = "Action";
@@ -115,6 +118,7 @@ void EventsFunction::UnserializeFrom(gd::Project& project,
group = element.GetStringAttribute("group");
getterName = element.GetStringAttribute("getterName");
isPrivate = element.GetBoolAttribute("private");
isAsync = element.GetBoolAttribute("async");
events.UnserializeFrom(project, element.GetChild("events"));
gd::String functionTypeStr = element.GetStringAttribute("functionType");

View File

@@ -211,6 +211,19 @@ class GD_CORE_API EventsFunction {
return *this;
}
/**
* \brief Returns true if the function is async.
*/
bool IsAsync() const { return isAsync; }
/**
* \brief Sets the asycronity of the function.
*/
EventsFunction& SetAsync(bool _isAsync) {
isAsync = _isAsync;
return *this;
}
/**
* \brief Return the events.
*/
@@ -291,6 +304,7 @@ class GD_CORE_API EventsFunction {
mutable std::vector<gd::ParameterMetadata> actionWithOperationParameters;
gd::ObjectGroupsContainer objectGroups;
bool isPrivate = false;
bool isAsync = false;
};
} // namespace gd

View File

@@ -11,7 +11,7 @@
namespace gd {
LoadingScreen::LoadingScreen()
: showGDevelopSplash(true),
: showGDevelopLogoDuringLoadingScreen(false),
gdevelopLogoStyle("light"),
backgroundImageResourceName(""),
backgroundColor(0),
@@ -27,7 +27,7 @@ LoadingScreen::LoadingScreen()
progressBarColor(0xFFFFFF){};
void LoadingScreen::SerializeTo(SerializerElement& element) const {
element.SetAttribute("showGDevelopSplash", showGDevelopSplash);
element.SetAttribute("showGDevelopSplash", showGDevelopLogoDuringLoadingScreen);
element.SetAttribute("gdevelopLogoStyle",
gdevelopLogoStyle);
element.SetAttribute("backgroundImageResourceName",
@@ -46,7 +46,7 @@ void LoadingScreen::SerializeTo(SerializerElement& element) const {
}
void LoadingScreen::UnserializeFrom(const SerializerElement& element) {
showGDevelopSplash = element.GetBoolAttribute("showGDevelopSplash", true);
showGDevelopLogoDuringLoadingScreen = element.GetBoolAttribute("showGDevelopSplash", true);
gdevelopLogoStyle =
element.GetStringAttribute("gdevelopLogoStyle", "light");
backgroundImageResourceName =

View File

@@ -29,13 +29,13 @@ class GD_CORE_API LoadingScreen {
* \brief Return true if the GDevelop logo should be shown while loading
* assets.
*/
bool IsGDevelopSplashShown() const { return showGDevelopSplash; };
bool IsGDevelopLogoShownDuringLoadingScreen() const { return showGDevelopLogoDuringLoadingScreen; };
/**
* \brief Set if the GDevelop logo should be shown while loading assets.
*/
LoadingScreen& ShowGDevelopSplash(bool show) {
showGDevelopSplash = show;
LoadingScreen& ShowGDevelopLogoDuringLoadingScreen(bool show) {
showGDevelopLogoDuringLoadingScreen = show;
return *this;
};
@@ -157,7 +157,7 @@ class GD_CORE_API LoadingScreen {
///@}
private:
bool showGDevelopSplash;
bool showGDevelopLogoDuringLoadingScreen;
gd::String gdevelopLogoStyle;
gd::String backgroundImageResourceName;
int backgroundColor;

View File

@@ -12,6 +12,7 @@
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"

View File

@@ -636,6 +636,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
platformSpecificAssets.UnserializeFrom(
propElement.GetChild("platformSpecificAssets"));
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
watermark.UnserializeFrom(propElement.GetChild("watermark"));
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
@@ -646,6 +647,13 @@ void Project::UnserializeFrom(const SerializerElement& element) {
for (std::size_t i = 0; i < authorIdsElement.GetChildrenCount(); ++i) {
authorIds.push_back(authorIdsElement.GetChild(i).GetStringValue());
}
authorUsernames.clear();
auto& authorUsernamesElement = propElement.GetChild("authorUsernames");
authorUsernamesElement.ConsiderAsArray();
for (std::size_t i = 0; i < authorUsernamesElement.GetChildrenCount(); ++i) {
authorUsernames.push_back(
authorUsernamesElement.GetChild(i).GetStringValue());
}
categories.clear();
auto& categoriesElement = propElement.GetChild("categories");
@@ -875,6 +883,7 @@ void Project::SerializeTo(SerializerElement& element) const {
platformSpecificAssets.SerializeTo(
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
watermark.SerializeTo(propElement.AddChild("watermark"));
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
auto& authorIdsElement = propElement.AddChild("authorIds");
@@ -882,6 +891,11 @@ void Project::SerializeTo(SerializerElement& element) const {
for (const auto& authorId : authorIds) {
authorIdsElement.AddChild("").SetStringValue(authorId);
}
auto& authorUsernamesElement = propElement.AddChild("authorUsernames");
authorUsernamesElement.ConsiderAsArray();
for (const auto& authorUsername : authorUsernames) {
authorUsernamesElement.AddChild("").SetStringValue(authorUsername);
}
auto& categoriesElement = propElement.AddChild("categories");
categoriesElement.ConsiderAsArray();
@@ -982,8 +996,10 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// (this time for effects). Ideally, this method could be moved outside of
// gd::Project.
gd::ResourcesManager* resourcesManager = &GetResourcesManager();
// Add project resources
worker.ExposeResources(&GetResourcesManager());
worker.ExposeResources(resourcesManager);
platformSpecificAssets.ExposeResources(worker);
// Add layouts resources
@@ -1095,6 +1111,7 @@ void Project::Init(const gd::Project& game) {
author = game.author;
authorIds = game.authorIds;
authorUsernames = game.authorUsernames;
isPlayableWithKeyboard = game.isPlayableWithKeyboard;
isPlayableWithGamepad = game.isPlayableWithGamepad;
isPlayableWithMobile = game.isPlayableWithMobile;
@@ -1105,6 +1122,7 @@ void Project::Init(const gd::Project& game) {
latestCompilationDirectory = game.latestCompilationDirectory;
platformSpecificAssets = game.platformSpecificAssets;
loadingScreen = game.loadingScreen;
watermark = game.watermark;
objectGroups = game.objectGroups;
extensionProperties = game.extensionProperties;

View File

@@ -11,6 +11,7 @@
#include "GDCore/Project/ExtensionProperties.h"
#include "GDCore/Project/LoadingScreen.h"
#include "GDCore/Project/Watermark.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/PlatformSpecificAssets.h"
@@ -120,6 +121,16 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
std::vector<gd::String>& GetAuthorIds() { return authorIds; };
/**
* \brief Get the author usernames of the project.
*/
const std::vector<gd::String>& GetAuthorUsernames() const { return authorUsernames; };
/**
* \brief Get the author usernames of the project, to modify them (non-const).
*/
std::vector<gd::String>& GetAuthorUsernames() { return authorUsernames; };
/**
* Define the project as playable with a keyboard.
* \param enable True to define the project as playable with a keyboard.
@@ -256,6 +267,16 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::LoadingScreen& GetLoadingScreen() const { return loadingScreen; }
/**
* \brief Return a reference to watermark setup for the project
*/
gd::Watermark& GetWatermark() { return watermark; }
/**
* \brief Return a reference to watermark setup for the project
*/
const gd::Watermark& GetWatermark() const { return watermark; }
/**
* Change game's main window default width.
*
@@ -1043,6 +1064,8 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.
std::vector<gd::String>
authorUsernames; ///< Game author usernames, from GDevelop users DB.
std::vector<gd::String>
categories; ///< Game categories
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
@@ -1062,6 +1085,7 @@ class GD_CORE_API Project : public ObjectsContainer {
currentPlatform; ///< The platform being used to edit the project.
gd::PlatformSpecificAssets platformSpecificAssets;
gd::LoadingScreen loadingScreen;
gd::Watermark watermark;
std::vector<std::unique_ptr<gd::ExternalEvents> >
externalEvents; ///< List of all externals events
ExtensionProperties

View File

@@ -85,6 +85,10 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<VideoResource>();
else if (kind == "json")
return std::make_shared<JsonResource>();
else if (kind == "tilemap")
return std::make_shared<TilemapResource>();
else if (kind == "tileset")
return std::make_shared<TilesetResource>();
else if (kind == "bitmapFont")
return std::make_shared<BitmapFontResource>();
@@ -650,6 +654,74 @@ bool JsonResource::UpdateProperty(const gd::String& name,
return true;
}
void TilemapResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void TilemapResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
DisablePreload(element.GetBoolAttribute("disablePreload", false));
}
void TilemapResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
element.SetAttribute("disablePreload", IsPreloadDisabled());
}
std::map<gd::String, gd::PropertyDescriptor> TilemapResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["disablePreload"]
.SetValue(disablePreload ? "true" : "false")
.SetType("Boolean")
.SetLabel(_("Disable preloading at game startup"));
return properties;
}
bool TilemapResource::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == "disablePreload") disablePreload = value == "1";
return true;
}
void TilesetResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void TilesetResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
DisablePreload(element.GetBoolAttribute("disablePreload", false));
}
void TilesetResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
element.SetAttribute("disablePreload", IsPreloadDisabled());
}
std::map<gd::String, gd::PropertyDescriptor> TilesetResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["disablePreload"]
.SetValue(disablePreload ? "true" : "false")
.SetType("Boolean")
.SetLabel(_("Disable preloading at game startup"));
return properties;
}
bool TilesetResource::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == "disablePreload") disablePreload = value == "1";
return true;
}
void BitmapFontResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}

View File

@@ -373,6 +373,88 @@ class GD_CORE_API JsonResource : public Resource {
gd::String file;
};
/**
* \brief Describe a tilemap file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API TilemapResource : public Resource {
public:
TilemapResource() : Resource(), disablePreload(false) { SetKind("tilemap"); };
virtual ~TilemapResource(){};
virtual TilemapResource* Clone() const override {
return new TilemapResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
/**
* \brief Return true if the loading at game startup must be disabled
*/
bool IsPreloadDisabled() const { return disablePreload; }
/**
* \brief Set if the tilemap preload at game startup must be disabled
*/
void DisablePreload(bool disable = true) { disablePreload = disable; }
private:
bool disablePreload; ///< If "true", don't load the tilemap at game startup
gd::String file;
};
/**
* \brief Describe a tileset file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API TilesetResource : public Resource {
public:
TilesetResource() : Resource(), disablePreload(false) { SetKind("tileset"); };
virtual ~TilesetResource(){};
virtual TilesetResource* Clone() const override {
return new TilesetResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
/**
* \brief Return true if the loading at game startup must be disabled
*/
bool IsPreloadDisabled() const { return disablePreload; }
/**
* \brief Set if the tilemap preload at game startup must be disabled
*/
void DisablePreload(bool disable = true) { disablePreload = disable; }
private:
bool disablePreload; ///< If "true", don't load the tilemap at game startup
gd::String file;
};
/**
* \brief Describe a bitmap font file used by a project.
*

View File

@@ -0,0 +1,24 @@
/*
* GDevelop Core
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "Watermark.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
Watermark::Watermark() : showWatermark(true), placement("bottom-left"){};
void Watermark::SerializeTo(SerializerElement& element) const {
element.SetAttribute("showWatermark", showWatermark);
element.SetAttribute("placement", placement);
}
void Watermark::UnserializeFrom(const SerializerElement& element) {
showWatermark = element.GetBoolAttribute("showWatermark", true);
placement = element.GetStringAttribute("placement", "bottom-left");
}
} // namespace gd

View File

@@ -0,0 +1,70 @@
/*
* GDevelop Core
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_WATERMARK_H
#define GDCORE_WATERMARK_H
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief Describe the content and set up of the watermark
*
* \see gd::Watermark
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API Watermark {
public:
Watermark();
virtual ~Watermark(){};
/**
* \brief Return true if the GDevelop watermark should be shown after
* the game has loaded its assets.
*/
bool IsGDevelopWatermarkShown() const { return showWatermark; };
/**
* \brief Set if the GDevelop watermark should be shown after the game
* has loaded its assets.
*/
Watermark& ShowGDevelopWatermark(bool show) {
showWatermark = show;
return *this;
};
const gd::String& GetPlacement() const { return placement; };
Watermark& SetPlacement(const gd::String& value) {
placement = value;
return *this;
}
/** \name Saving and loading
*/
///@{
/**
* \brief Serialize the watermark setup.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the watermark setup.
*/
void UnserializeFrom(const SerializerElement& element);
///@}
private:
bool showWatermark;
gd::String placement;
};
} // namespace gd
#endif // GDCORE_WATERMARK_H

View File

@@ -187,7 +187,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.SetDefaultValue("\"\"")
.AddParameter("camera", _("Camera"), "", true)
.SetDefaultValue("0")
.SetFunctionName("getMouseX");
.SetFunctionName("getCursorX");
extension
->AddExpression("GetGlobalVariableAsNumber",
"Get me a global variable value",

View File

@@ -217,7 +217,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 2 + 2)");
"getCursorX(\"\", \"layer1\", 2 + 2)");
// (first argument is the currentScene)
}
SECTION("with last optional parameter omit") {
@@ -231,7 +231,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 0)");
"getCursorX(\"\", \"layer1\", 0)");
// (first argument is the currentScene)
}
SECTION("with last optional parameter omit (deprecated way)") {
@@ -245,7 +245,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"layer1\", 0)");
"getCursorX(\"\", \"layer1\", 0)");
// (first argument is the currentScene)
}
SECTION("with explicit comma (deprecated way)") {
@@ -258,7 +258,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getMouseX(\"\", \"\", 0)");
"getCursorX(\"\", \"\", 0)");
// (first argument is the currentScene)
}
}
@@ -624,7 +624,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"-(+(-(getMouseX(\"\", \"\", 0))))");
"-(+(-(getCursorX(\"\", \"\", 0))))");
// (first argument is the currentScene)
}
}

View File

@@ -19,15 +19,77 @@ TEST_CASE("ResourcesRenamer", "[common]") {
gd::ResourcesRenamer resourcesRenamer(renamings);
gd::Project project;
// Add "classic", plain resources.
gd::ImageResource resource1;
resource1.SetName("Resource1");
project.GetResourcesManager().AddResource(resource1);
gd::ImageResource resource2;
resource2.SetName("Resource2");
project.GetResourcesManager().AddResource(resource2);
// Add usage of some resources.
project.GetPlatformSpecificAssets().Set(
"android", "some-icon", "Resource1");
project.GetPlatformSpecificAssets().Set(
"android", "some-other-icon", "Resource2");
project.ExposeResources(resourcesRenamer);
// Check that resources were renamed were used.
REQUIRE(project.GetPlatformSpecificAssets().Get("android", "some-icon") ==
"RenamedResource1");
REQUIRE(project.GetPlatformSpecificAssets().Get(
"android", "some-other-icon") == "Resource2");
}
SECTION("It renames embedded resources") {
std::map<gd::String, gd::String> renamings = {
{"Resource1", "RenamedResource1"}};
gd::ResourcesRenamer resourcesRenamer(renamings);
gd::Project project;
// Add "classic", plain resources.
gd::ImageResource resource1;
resource1.SetName("Resource1");
project.GetResourcesManager().AddResource(resource1);
gd::ImageResource resource2;
resource2.SetName("Resource2");
project.GetResourcesManager().AddResource(resource2);
// Add a resource containing a mapping to other resources.
gd::JsonResource jsonResourceWithEmbeddeds;
jsonResourceWithEmbeddeds.SetName("Resource3");
jsonResourceWithEmbeddeds.SetMetadata(
"{ \"embeddedResourcesMapping\": {\"some-resource-name\": "
"\"Resource1\", \"some-other-resource-name\": \"Resource2\"} }");
project.GetResourcesManager().AddResource(jsonResourceWithEmbeddeds);
// Add usage of some resources.
project.GetPlatformSpecificAssets().Set(
"android", "some-icon", "Resource1");
project.GetPlatformSpecificAssets().Set(
"android", "some-other-icon", "Resource2");
project.ExposeResources(resourcesRenamer);
// TODO: This should not be necessary, but for now not all resources support embeddeds,
// so we must call it manually:
gd::String resource3Name = "Resource3";
resourcesRenamer.ExposeEmbeddeds(resource3Name);
// Check that resources were renamed were used.
REQUIRE(project.GetPlatformSpecificAssets().Get("android", "some-icon") ==
"RenamedResource1");
REQUIRE(project.GetPlatformSpecificAssets().Get(
"android", "some-other-icon") == "Resource2");
// Check that the names were also updated in the embedded resources mapping.
REQUIRE(project.GetResourcesManager().HasResource("Resource3") == true);
REQUIRE(
project.GetResourcesManager().GetResource("Resource3").GetMetadata() ==
"{\"embeddedResourcesMapping\":{\"some-resource-name\":"
"\"RenamedResource1\",\"some-other-resource-name\":\"Resource2\"}}");
}
}

View File

@@ -30,7 +30,7 @@ module.exports = {
'AdMob',
_('AdMob'),
_(
'Allow to display AdMob banners, interstitials and reward video ads.'
'Allow to display AdMob banners, app open, interstitials, rewarded interstitials and rewarded video ads.'
),
'Florian Rival',
'MIT'
@@ -57,8 +57,8 @@ module.exports = {
.addDependency()
.setName('AdMob Cordova plugin')
.setDependencyType('cordova')
.setExportName('gdevelop-cordova-admob-plus')
.setVersion('0.45.0')
.setExportName('admob-plus-cordova')
.setVersion('1.28.0')
.setExtraSetting(
'APP_ID_ANDROID',
new gd.PropertyDescriptor('AdMobAppIdAndroid').setType(
@@ -100,23 +100,120 @@ module.exports = {
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.setTestMode');
// Banner
// App Open
extension
.addCondition(
'BannerLoading',
_('Banner loading'),
_(
'Check if a banner is currently loading. It will be shown automatically when loaded.'
),
_('Banner is loading'),
'AppOpenLoading',
_('App open loading'),
_('Check if an app open is currently loading.'),
_('App open is loading'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isBannerLoading');
.setFunctionName('gdjs.adMob.isAppOpenLoading');
extension
.addCondition(
'AppOpenReady',
_('App open ready'),
_('Check if an app open is ready to be displayed.'),
_('App open is ready'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAppOpenReady');
extension
.addCondition(
'AppOpenShowing',
_('App open showing'),
_('Check if there is an app open being displayed.'),
_('App open is showing'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAppOpenShowing');
extension
.addCondition(
'AppOpenErrored',
_('App open errored'),
_('Check if there was a error while loading the app open.'),
_('App open had an error'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAppOpenErrored');
extension
.addAction(
'LoadAppOpen',
_('Load app open'),
_(
'Start loading an app open (that can be displayed automatically when the loading is finished).\nIf test mode is set, a test app open will be displayed.'
),
_(
'Load app open with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_ (landscape: _PARAM2_, display automatically when loaded: _PARAM3_)'
),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.addParameter('string', _('Android app open ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/3419835294"` for loading a test app open.'
)
.addParameter('string', _('iOS app open ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/5662855259"` for loading a test app open.'
)
.addParameter(
'yesorno',
_('Display in landscape? (portait otherwise)'),
'',
false
)
.setDefaultValue('false')
.addParameter(
'yesorno',
_('Displayed automatically when loading is finished?'),
'',
false
)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.loadAppOpen');
extension
.addAction(
'ShowAppOpen',
_('Show app open'),
_(
'Show the app open that was loaded. Will work only when the app open is fully loaded.'
),
_('Show the loaded app open'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.showAppOpen');
// Banner
extension
.addCondition(
'BannerShowing',
@@ -131,6 +228,34 @@ module.exports = {
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isBannerShowing');
extension
.addCondition(
'BannerConfigured',
_('Banner configured'),
_('Check if there is a banner correctly configured ready to be shown.'),
_('Banner is configured'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isBannerConfigured');
extension
.addCondition(
'BannerLoaded',
_('Banner loaded'),
_('Check if there is a banner correctly loaded ready to be shown.'),
_('Banner is loaded'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isBannerLoaded');
extension
.addCondition(
'BannerErrored',
@@ -149,6 +274,9 @@ module.exports = {
extension
.addDuplicatedCondition('BannerReady', 'BannerShowing')
.setHidden();
extension
.addDuplicatedCondition('Bannerloading', 'BannerShowing')
.setHidden();
extension
.addDuplicatedCondition('BannerExists', 'BannerShowing')
.setHidden();
@@ -158,7 +286,7 @@ module.exports = {
'SetupBanner',
_('Configure the banner'),
_(
"Configure a banner, which can then be displayed.\nIf test mode is set, a test banner will be displayed.\n\nOnce a banner is positioned (at the top or bottom of the game), it can't be moved anymore."
"Configure a banner, which can then be displayed.\nIf a banner is already displayed, it will be removed\nIf test mode is set, a test banner will be displayed.\n\nOnce a banner is positioned (at the top or bottom of the game), it can't be moved anymore."
),
_(
'Configure the banner with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_, display at top: _PARAM2_'
@@ -173,7 +301,7 @@ module.exports = {
)
.addParameter('string', _('iOS banner ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/6300978111"` for showing a test banner.'
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/2934735716"` for showing a test banner.'
)
.addParameter(
'yesorno',
@@ -298,7 +426,7 @@ module.exports = {
)
.addParameter('string', _('iOS interstitial ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/1033173712"` for loading a test interstitial.'
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/4411468910"` for loading a test interstitial.'
)
.addParameter(
'yesorno',
@@ -327,90 +455,266 @@ module.exports = {
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.showInterstitial');
// Reward video
// Rewarded Interstitial
extension
.addCondition(
'VideoLoading',
_('Video loading'),
_('Check if a reward video is currently loading.'),
_('Reward video is loading'),
'RewardedInterstitialLoading',
_('Rewarded interstitial loading'),
_('Check if a rewarded interstitial is currently loading.'),
_('Rewarded interstitial is loading'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isVideoLoading');
.setFunctionName('gdjs.adMob.isRewardedInterstitialLoading');
extension
.addCondition(
'VideoReady',
_('Video ready'),
_('Check if a reward video is ready to be displayed.'),
_('Reward video is ready'),
'RewardedInterstitialReady',
_('Rewarded interstitial ready'),
_('Check if a rewarded interstitial is ready to be displayed.'),
_('Rewarded interstitial is ready'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isVideoReady');
.setFunctionName('gdjs.adMob.isRewardedInterstitialReady');
extension
.addCondition(
'VideoShowing',
_('Video showing'),
_('Check if there is a reward video being displayed.'),
_('Reward video is showing'),
'RewardedInterstitialShowing',
_('Rewarded interstitial showing'),
_('Check if there is a rewarded interstitial being displayed.'),
_('Rewarded interstitial is showing'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isVideoShowing');
.setFunctionName('gdjs.adMob.isRewardedInterstitialShowing');
extension
.addCondition(
'VideoErrored',
_('Video had an error'),
_('Check if there was a error while loading the rewarded video.'),
_('Video ad had an error'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isVideoErrored');
extension
.addCondition(
'VideoReward',
_('Video reward received'),
'RewardedInterstitialErrored',
_('Rewarded interstitial had an error'),
_(
'Check if the reward of the video was given to the user.\nYou can mark this reward as cleared, so that the condition will be false and you can show later another reward video.'
'Check if there was a error while loading the rewarded interstitial.'
),
_('Rewarded Interstitial had an error'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isRewardedInterstitialErrored');
extension
.addCondition(
'RewardedInterstitialRewardReceived',
_('Rewarded Interstitial reward received'),
_(
'Check if the reward of the rewarded interstitial was given to the user.\nYou can mark this reward as cleared, so that the condition will be false and you can show later another rewarded interstitial.'
),
_(
'User got the reward of the rewarded interstitial (and clear this reward: _PARAM0_)'
),
_('User got the reward of the video (and clear this reward: _PARAM0_)'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.addParameter(
'yesorno',
_('Clear the reward (needed to show another video)'),
_('Clear the reward (needed to show another rewarded interstitial)'),
'',
false
)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.wasVideoRewardReceived');
.setFunctionName('gdjs.adMob.wasRewardedInterstitialRewardReceived');
extension
.addAction(
'LoadVideo',
_('Load video'),
'LoadRewardedInterstitial',
_('Load rewarded interstitial'),
_(
'Start loading a rewarded interstitial (that can be displayed automatically when the loading is finished).\nIf test mode is set, a test rewarded interstitial will be displayed.\nThis is similar to a rewarded video, but can be displayed at any time, and the user can close it.'
),
_(
'Load rewarded interstitial with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_ (display automatically when loaded: _PARAM2_)'
),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.addParameter('string', _('Android rewarded interstitial ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/5354046379"` for loading a test rewarded interstitial.'
)
.addParameter('string', _('iOS interstitial ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/6978759866"` for loading a test rewarded interstitial.'
)
.addParameter(
'yesorno',
_('Displayed automatically when loading is finished?'),
'',
false
)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.loadRewardedInterstitial');
extension
.addAction(
'ShowRewardedInterstitial',
_('Show rewarded interstitial'),
_(
'Show the rewarded interstitial that was loaded. Will work only when the rewarded interstitial is fully loaded.'
),
_('Show the loaded rewarded interstitial'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.showRewardedInterstitial');
extension
.addAction(
'ClaimRewardedInterstitialReward',
_('Mark the reward of the rewarded interstitial as claimed'),
_(
'Mark the rewarded interstitial reward as claimed. Useful if you used the condition to check if the reward was given to the user without clearing the reward.'
),
_('Mark the reward of the rewarded interstitial as claimed'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.markRewardedInterstitialRewardAsClaimed');
// Rewarded video
extension
.addCondition(
'RewardedVideoLoading',
_('Rewarded video loading'),
_('Check if a rewarded video is currently loading.'),
_('Rewarded video is loading'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isRewardedVideoLoading');
// Deprecated condition (was renamed):
extension
.addDuplicatedCondition('VideoLoading', 'RewardedVideoLoading')
.setHidden();
extension
.addCondition(
'RewardedVideoReady',
_('Rewarded video ready'),
_('Check if a rewarded video is ready to be displayed.'),
_('Rewarded video is ready'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isRewardedVideoReady');
// Deprecated condition (was renamed):
extension
.addDuplicatedCondition('VideoReady', 'RewardedVideoReady')
.setHidden();
extension
.addCondition(
'RewardedVideoShowing',
_('Rewarded video showing'),
_('Check if there is a rewarded video being displayed.'),
_('Rewarded video is showing'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isRewardedVideoShowing');
// Deprecated condition (was renamed):
extension
.addDuplicatedCondition('VideoShowing', 'RewardedVideoShowing')
.setHidden();
extension
.addCondition(
'RewardedVideoErrored',
_('Rewarded video had an error'),
_('Check if there was a error while loading the rewarded video.'),
_('Rewarded video ad had an error'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isRewardedVideoErrored');
// Deprecated condition (was renamed):
extension
.addDuplicatedCondition('VideoErrored', 'RewardedVideoErrored')
.setHidden();
extension
.addCondition(
'RewardedVideoRewardReceived',
_('Rewarded Video reward received'),
_(
'Check if the reward of the rewarded video was given to the user.\nYou can mark this reward as cleared, so that the condition will be false and you can show later another rewarded video.'
),
_(
'User got the reward of the rewarded video (and clear this reward: _PARAM0_)'
),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.addParameter(
'yesorno',
_('Clear the reward (needed to show another rewarded video)'),
'',
false
)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.wasRewardedVideoRewardReceived');
// Deprecated condition (was renamed):
extension
.addDuplicatedCondition('VideoReward', 'RewardedVideoRewardReceived')
.setHidden();
extension
.addAction(
'LoadRewardedVideo',
_('Load rewarded video'),
_(
'Start loading a reward video (that can be displayed automatically when the loading is finished).\nIf test mode is set, a test video will be displayed.'
),
@@ -427,7 +731,7 @@ module.exports = {
)
.addParameter('string', _('iOS reward video ID'), '', false)
.setParameterLongDescription(
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/5224354917"` for loading a test rewarded video.'
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/1712485313"` for loading a test rewarded video.'
)
.addParameter(
'yesorno',
@@ -438,12 +742,15 @@ module.exports = {
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.loadVideo');
.setFunctionName('gdjs.adMob.loadRewardedVideo');
// Deprecated action (was renamed):
extension.addDuplicatedAction('LoadVideo', 'LoadRewardedVideo').setHidden();
extension
.addAction(
'ShowVideo',
_('Show video'),
'ShowRewardedVideo',
_('Show rewarded video'),
_(
'Show the reward video that was loaded. Will work only when the video is fully loaded.'
),
@@ -454,23 +761,31 @@ module.exports = {
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.showVideo');
.setFunctionName('gdjs.adMob.showRewardedVideo');
// Deprecated action (was renamed):
extension.addDuplicatedAction('ShowVideo', 'ShowRewardedVideo').setHidden();
extension
.addAction(
'ClaimReward',
_('Mark the reward of the video as claimed'),
'ClaimRewardedVideoReward',
_('Mark the reward of the rewarded video as claimed'),
_(
'Mark the video reward as claimed. Useful if you used the condition to check if the reward was given to the user without clearing the reward.'
'Mark the rewarded video reward as claimed. Useful if you used the condition to check if the reward was given to the user without clearing the reward.'
),
_('Mark the reward of the video as claimed'),
_('Mark the reward of the rewarded video as claimed'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.markVideoRewardAsClaimed');
.setFunctionName('gdjs.adMob.markRewardedVideoRewardAsClaimed');
// Deprecated action (was renamed):
extension
.addDuplicatedAction('ClaimReward', 'ClaimRewardedVideoReward')
.setHidden();
return extension;
},

View File

@@ -1,10 +1,46 @@
namespace gdjs {
declare var admob: any;
declare var cordova: any;
export namespace adMob {
const logger = new gdjs.Logger('AdMob');
export enum AdSizeType {
const testAdIds = {
appOpen: {
android: 'ca-app-pub-3940256099942544/3419835294',
ios: 'ca-app-pub-3940256099942544/5662855259',
},
banner: {
android: 'ca-app-pub-3940256099942544/6300978111',
ios: 'ca-app-pub-3940256099942544/2934735716',
},
interstitial: {
android: 'ca-app-pub-3940256099942544/1033173712',
ios: 'ca-app-pub-3940256099942544/4411468910',
},
interstitialVideo: {
android: 'ca-app-pub-3940256099942544/8691691433',
ios: 'ca-app-pub-3940256099942544/5135589807',
},
rewarded: {
android: 'ca-app-pub-3940256099942544/5224354917',
ios: 'ca-app-pub-3940256099942544/1712485313',
},
rewardedInterstitial: {
android: 'ca-app-pub-3940256099942544/5354046379',
ios: 'ca-app-pub-3940256099942544/6978759866',
},
native: {
android: 'ca-app-pub-3940256099942544/2247696110',
ios: 'ca-app-pub-3940256099942544/3986624511',
},
nativeVideo: {
android: 'ca-app-pub-3940256099942544/1044960115',
ios: 'ca-app-pub-3940256099942544/2521693316',
},
};
enum AdSizeType {
BANNER,
LARGE_BANNER,
MEDIUM_RECTANGLE,
@@ -13,31 +49,113 @@ namespace gdjs {
SMART_BANNER,
}
// Banner
let bannerAndroidId = '';
let bannerIosId = '';
let bannerPosition: 'top' | 'bottom' = 'top';
let bannerRequestedAdSizeType: AdSizeType = AdSizeType.SMART_BANNER;
const adSizeTypes = {
BANNER: AdSizeType.BANNER,
LARGE_BANNER: AdSizeType.LARGE_BANNER,
MEDIUM_RECTANGLE: AdSizeType.MEDIUM_RECTANGLE,
FULL_BANNER: AdSizeType.FULL_BANNER,
LEADERBOARD: AdSizeType.LEADERBOARD,
SMART_BANNER: AdSizeType.SMART_BANNER,
};
let bannerLoading = false;
let bannerErrored = false;
let bannerShowing = false;
enum AppOpenAdOrientation {
Portrait = 1,
PortraitUpsideDown = 2,
LandscapeRight = 3,
LandscapeLeft = 4,
}
// Admob does not initialize automatically, so we store a flag to know if it's initialized.
let admobStarted = false;
let isUsingTestAds = false;
// Banner
let banner;
let bannerRequestedAdSizeType: AdSizeType = AdSizeType.SMART_BANNER;
let bannerConfigured = false; // Becomes true when the user configures the ad id and the position of the banner.
let bannerLoaded = false; // Becomes true when the banner is loaded by loaded.
let bannerShowing = false; // Becomes true when loaded or when the user shows/hides the banner.
let bannerErrored = false; // Becomes true when the banner fails to load.
// Interstitial
let interstitialLoading = false;
let interstitialReady = false;
let interstitialErrored = false;
let interstitialShowing = false;
let interstitial;
let interstitialLoading = false; // Becomes true when the interstitial is loading.
let interstitialReady = false; // Becomes true when the interstitial is loaded and ready to be shown.
let interstitialShowing = false; // Becomes true when the interstitial is showing.
let interstitialErrored = false; // Becomes true when the interstitial fails to load.
// Reward video
let videoLoading = false;
let videoReady = false;
let videoErrored = false;
let videoShowing = false;
let videoRewardReceived = false;
// App Open
let appOpen;
let appOpenLoading = false; // Becomes true when the appOpen is loading.
let appOpenReady = false; // Becomes true when the appOpen is loaded and ready to be shown.
let appOpenShowing = false; // Becomes true when the appOpen is showing.
let appOpenErrored = false; // Becomes true when the appOpen fails to load.
// Rewarded interstitial
let rewardedInterstitial;
let rewardedInterstitialLoading = false; // Becomes true when the interstitial is loading.
let rewardedInterstitialReady = false; // Becomes true when the interstitial is loaded and ready to be shown.
let rewardedInterstitialShowing = false; // Becomes true when the interstitial is showing.
let rewardedInterstitialRewardReceived = false; // Becomes true when the interstitial is closed and the reward is received.
let rewardedInterstitialErrored = false; // Becomes true when the interstitial fails to load.
// Rewarded video
let rewardedVideo;
let rewardedVideoLoading = false; // Becomes true when the video is loading.
let rewardedVideoReady = false; // Becomes true when the video is loaded and ready to be shown.
let rewardedVideoShowing = false; // Becomes true when the video is showing.
let rewardedVideoRewardReceived = false; // Becomes true when the video is closed and the reward is received.
let rewardedVideoErrored = false; // Becomes true when the video fails to load.
let npaValue = '0'; // TODO: expose an API to change this and also an automatic way using the consent SDK.
// Admob initialization listener
document.addEventListener(
'deviceready',
async () => {
// Obtain user consent ?
await admob.start();
logger.info('AdMob succesfully started.');
admobStarted = true;
},
false
);
/**
* Helper to know if we are on mobile and admob is correctly initialized.
*/
const checkIfAdMobIsAvailable = () => {
if (typeof cordova === 'undefined') {
logger.warn('We are not on mobile, AdMob will not be available.');
return false;
}
if (typeof admob === 'undefined' || !admobStarted) {
logger.warn('AdMob has not been configured or started properly.');
return false;
}
return true;
};
/**
* Helper to get the correct ad id depending on the platform. Android and iOS use different ids.
*/
const getAdUnitId = (androidAdUnitId, iosAdUnitId, type) => {
if (typeof cordova === 'undefined') {
logger.warn('Cordova is not available.');
return;
}
if (cordova.platformId === 'android') {
return isUsingTestAds ? testAdIds[type].android : androidAdUnitId;
} else if (cordova.platformId === 'ios') {
return isUsingTestAds ? testAdIds[type].ios : iosAdUnitId;
}
logger.error('Unsupported platform: ', cordova.platformId);
return null;
};
/**
* Activate or deactivate the test mode ("development" mode).
* When activated, tests ads will be served instead of real ones.
@@ -47,43 +165,116 @@ namespace gdjs {
* account being flagged for invalid activity.
*/
export const setTestMode = (enable: boolean) => {
if (typeof admob === 'undefined') {
if (!checkIfAdMobIsAvailable()) return;
isUsingTestAds = enable;
};
// -------------------
// ---- App Open -----
// -------------------
export const isAppOpenLoading = () => appOpenLoading;
export const isAppOpenReady = () => appOpenReady;
export const isAppOpenShowing = () => appOpenShowing;
export const isAppOpenErrored = () => appOpenErrored;
/** Load an AppOpen. */
export const loadAppOpen = async (
androidAdUnitId,
iosAdUnitId,
displayLandscape,
displayWhenLoaded
) => {
if (!checkIfAdMobIsAvailable()) return;
// If an appOpen is already loading or showing, we don't stop it.
if (appOpenLoading || appOpenShowing) {
return;
}
admob.setDevMode(enable);
const adUnitId = getAdUnitId(androidAdUnitId, iosAdUnitId, 'appOpen');
if (!adUnitId) return;
appOpenLoading = true;
appOpenReady = false;
appOpenErrored = false;
appOpen = new admob.AppOpenAd({
adUnitId,
orientation: displayLandscape
? AppOpenAdOrientation.LandscapeLeft
: AppOpenAdOrientation.Portrait,
});
appOpen.on('load', () => {
appOpenReady = true;
appOpenLoading = false;
});
appOpen.on('loadfail', () => {
appOpenLoading = false;
appOpenErrored = true;
});
appOpen.on('show', () => {
appOpenShowing = true;
appOpenReady = false;
});
appOpen.on('showfail', () => {
appOpenShowing = false;
appOpenErrored = true;
});
appOpen.on('dismiss', () => {
appOpenShowing = false;
});
try {
logger.info('Loading Admob App Open.');
await appOpen.load();
logger.info('AdMob App Open successfully loaded.');
appOpenLoading = false;
appOpenReady = true;
if (displayWhenLoaded) showAppOpen();
} catch (error) {
logger.error('Error while loading an App Open:', error);
appOpenLoading = false;
appOpenReady = false;
appOpenErrored = true;
}
};
// Banner
/** Check if a banner is loading. */
export const isBannerLoading = () => {
return bannerLoading;
};
/** Check if a banner is being shown on screen. */
export const isBannerShowing = () => {
return bannerShowing;
};
/** Check if the banner had an error while loading it. */
export const isBannerErrored = () => {
return bannerErrored;
};
/** Show the loaded appOpen. */
export const showAppOpen = async () => {
if (!checkIfAdMobIsAvailable()) return;
/**
* Set up a banner that can then be displayed by calling `showBanner`.
*/
export const setupBanner = function (androidID, iosID, atTop) {
if (typeof admob === 'undefined') {
if (!appOpen) {
logger.warn('App Open has not been set up, call loadAppOpen first.');
return;
}
if (!appOpenReady) {
logger.info('App Open not loaded yet, cannot display it.');
return;
}
appOpenErrored = false;
bannerAndroidId = androidID;
bannerIosId = iosID;
bannerPosition = atTop ? 'top' : 'bottom';
try {
logger.info('Showing AdMob App Open.');
await appOpen.show();
// AppOpen will be shown and
// `appOpenShowing` will be updated thanks to events
// (but it's too early to change it now).
} catch (error) {
logger.error('Error while showing an AdMob App Open:', error);
appOpenShowing = false;
appOpenErrored = true;
}
};
/**
* Set the size of the banner to be shown when `showBanner` is called.
* @param bannerAdSizeType The type of the banner to displayed
*/
// -----------------
// ---- Banner -----
// -----------------
export const isBannerConfigured = () => bannerConfigured;
export const isBannerLoaded = () => bannerLoaded;
export const isBannerShowing = () => bannerShowing;
export const isBannerErrored = () => bannerErrored;
export const setBannerAdSizeType = (
bannerAdSizeType:
| 'BANNER'
@@ -93,288 +284,421 @@ namespace gdjs {
| 'LEADERBOARD'
| 'SMART_BANNER'
) => {
const adSizeTypes = {
BANNER: AdSizeType.BANNER,
LARGE_BANNER: AdSizeType.LARGE_BANNER,
MEDIUM_RECTANGLE: AdSizeType.MEDIUM_RECTANGLE,
FULL_BANNER: AdSizeType.FULL_BANNER,
LEADERBOARD: AdSizeType.LEADERBOARD,
SMART_BANNER: AdSizeType.SMART_BANNER,
};
bannerRequestedAdSizeType =
adSizeTypes[bannerAdSizeType] || AdSizeType.SMART_BANNER;
};
/**
* Display the banner that was set up with `loadBanner` (and `setBannerAdSizeType`).
* Set up a banner that can then be displayed by calling `showBanner`.
* If a banner is already set up, it will be hidden and replaced by the new one.
*/
export const showBanner = () => {
if (typeof admob === 'undefined') {
return;
export const setupBanner = async (androidAdUnitId, iosAdUnitId, atTop) => {
if (!checkIfAdMobIsAvailable()) return;
const adUnitId = getAdUnitId(androidAdUnitId, iosAdUnitId, 'banner');
if (!adUnitId) return;
if (banner && bannerShowing) {
logger.info('Banner already visible, hiding it to display new one.');
await hideBanner();
}
bannerLoading = true;
bannerShowing = false;
bannerErrored = false;
admob.banner
.show({
id: {
android: bannerAndroidId,
ios: bannerIosId,
},
position: bannerPosition,
size: bannerRequestedAdSizeType,
})
.then(
() => {
bannerShowing = true;
bannerLoading = false;
logger.info('AdMob banner successfully shown.');
},
(error) => {
bannerShowing = false;
bannerLoading = false;
bannerErrored = true;
logger.error('Error while showing an AdMob banner:', error);
}
);
};
/** Hide the banner shown on screen. */
export const hideBanner = () => {
if (typeof admob === 'undefined') {
return;
}
bannerConfigured = false;
bannerLoaded = false;
bannerShowing = false;
admob.banner.hide({
android: bannerAndroidId,
ios: bannerIosId,
banner = new admob.BannerAd({
adUnitId,
position: atTop ? 'top' : 'bottom',
size: bannerRequestedAdSizeType,
});
banner.on('load', () => {
bannerShowing = true;
bannerLoaded = true;
});
banner.on('loadfail', () => {
bannerShowing = false;
bannerLoaded = false;
bannerErrored = true;
});
bannerConfigured = true;
};
// Interstitial
/** Check if the interstitial is loading. */
export const isInterstitialLoading = () => {
return interstitialLoading;
/**
* Display a banner that was set up with `setupBanner` (and `setBannerAdSizeType`).
*/
export const showBanner = async () => {
if (!banner) {
logger.info('Banner not configured, use setupBanner first.');
return;
}
if (bannerShowing) {
logger.info('Banner already visible. Ignoring.');
return;
}
bannerErrored = false;
try {
logger.info('Showing AdMob banner.');
await banner.show();
if (bannerLoaded) {
// Banner is already loaded, so it will be shown immediately.
bannerShowing = true;
}
} catch (error) {
bannerShowing = false;
bannerErrored = true;
logger.error('Error while showing an AdMob banner:', error);
}
};
/** Check if the interstitial is ready to display. */
export const isInterstitialReady = () => {
return interstitialReady;
};
/** Check if the interstitial is shown on screen. */
export const isInterstitialShowing = () => {
return interstitialShowing;
};
/** Check if the interstitial had an error while loading it. */
export const isInterstitialErrored = () => {
return interstitialErrored;
/** Hide the banner shown on screen. */
export const hideBanner = async () => {
if (!checkIfAdMobIsAvailable()) return;
if (!banner || !bannerShowing) {
logger.warn('No banner is being shown.');
return;
}
await banner.hide();
bannerShowing = false;
// Note that the banner is still loaded, which is why bannerLoaded is not set to false.
// We hide the banner, but keep it configured to display it again if needed.
};
// -----------------------
// ---- Interstitial -----
// -----------------------
export const isInterstitialLoading = () => interstitialLoading;
export const isInterstitialReady = () => interstitialReady;
export const isInterstitialShowing = () => interstitialShowing;
export const isInterstitialErrored = () => interstitialErrored;
/** Load an interstitial. */
export const loadInterstitial = (androidID, iosID, displayWhenLoaded) => {
if (typeof admob === 'undefined') {
return;
}
if (interstitialLoading || interstitialReady || interstitialShowing) {
export const loadInterstitial = async (
androidAdUnitId,
iosAdUnitId,
displayWhenLoaded
) => {
if (!checkIfAdMobIsAvailable()) return;
// If an interstitial is already loading or showing, we don't stop it.
if (interstitialLoading || interstitialShowing) {
return;
}
const adUnitId = getAdUnitId(
androidAdUnitId,
iosAdUnitId,
'interstitial'
);
if (!adUnitId) return;
interstitialLoading = true;
interstitialReady = false;
interstitialErrored = false;
admob.interstitial
.load({
id: {
android: androidID,
ios: iosID,
},
npa: npaValue,
})
.then(
() => {
logger.info('AdMob interstitial successfully loaded.');
if (displayWhenLoaded) showInterstitial();
},
(error) => {
interstitialLoading = false;
interstitialReady = false;
interstitialErrored = true;
logger.error('Error while loading a interstitial:', error);
}
);
interstitial = new admob.InterstitialAd({
adUnitId,
npa: npaValue,
});
interstitial.on('load', () => {
interstitialReady = true;
interstitialLoading = false;
});
interstitial.on('loadfail', () => {
interstitialLoading = false;
interstitialErrored = true;
});
interstitial.on('show', () => {
interstitialShowing = true;
interstitialReady = false;
});
interstitial.on('showfail', () => {
interstitialShowing = false;
interstitialErrored = true;
});
interstitial.on('dismiss', () => {
interstitialShowing = false;
});
try {
logger.info('Loading Admob interstitial.');
await interstitial.load();
logger.info('AdMob interstitial successfully loaded.');
interstitialLoading = false;
interstitialReady = true;
if (displayWhenLoaded) showInterstitial();
} catch (error) {
logger.error('Error while loading a interstitial:', error);
interstitialLoading = false;
interstitialReady = false;
interstitialErrored = true;
}
};
/** Show the loaded interstitial. */
export const showInterstitial = () => {
if (typeof admob === 'undefined') {
export const showInterstitial = async () => {
if (!checkIfAdMobIsAvailable()) return;
if (!interstitial) {
logger.warn(
'Interstitial has not been set up, call loadInterstitial first.'
);
return;
}
admob.interstitial.show().then(
() => {
// Interstitial will be shown and
// `interstitialShowing` will be updated thanks to events
// (but it's too early to change it now).
},
(error) => {
interstitialShowing = false;
interstitialErrored = true;
logger.error('Error while trying to show an interstitial:', error);
}
);
if (!interstitialReady) {
logger.info('Interstitial not loaded yet, cannot display it.');
return;
}
interstitialErrored = false;
try {
logger.info('Showing AdMob interstitial.');
await interstitial.show();
// Interstitial will be shown and
// `interstitialShowing` will be updated thanks to events
// (but it's too early to change it now).
} catch (error) {
logger.error('Error while showing an AdMob interstitial:', error);
interstitialShowing = false;
interstitialErrored = true;
}
};
// Reward video
/** Check if the video is loading. */
export const isVideoLoading = () => {
return videoLoading;
};
/** Check if the video is ready to display. */
export const isVideoReady = () => {
return videoReady;
};
/** Check if the video is shown on screen. */
export const isVideoShowing = () => {
return videoShowing;
};
/** Check if the video had an error while loading it. */
export const isVideoErrored = () => {
return videoErrored;
};
// --------------------------------
// ---- Rewarded Interstitial -----
// --------------------------------
export const isRewardedInterstitialLoading = () =>
rewardedInterstitialLoading;
export const isRewardedInterstitialReady = () => rewardedInterstitialReady;
export const isRewardedInterstitialShowing = () =>
rewardedInterstitialShowing;
export const isRewardedInterstitialErrored = () =>
rewardedInterstitialErrored;
/** Check if the reward of the video was received. */
export const wasVideoRewardReceived = function (markAsClaimed) {
const reward = videoRewardReceived;
/** Check if the reward of the rewarded interstitial was received. */
export const wasRewardedInterstitialRewardReceived = function (
markAsClaimed
) {
const reward = rewardedInterstitialRewardReceived;
if (markAsClaimed) {
videoRewardReceived = false;
rewardedInterstitialRewardReceived = false;
}
return reward;
};
/** Load a reward video. */
export const loadVideo = function (androidID, iosID, displayWhenLoaded) {
if (typeof admob === 'undefined') {
return;
}
if (videoLoading || videoReady || videoShowing) {
/** Load a rewarded interstitial. */
export const loadRewardedInterstitial = async (
androidAdUnitID,
iosAdUnitID,
displayWhenLoaded
) => {
if (!checkIfAdMobIsAvailable()) return;
if (rewardedInterstitialLoading || rewardedInterstitialShowing) {
return;
}
videoLoading = true;
videoReady = false;
videoErrored = false;
admob.rewardVideo
.load({
id: {
android: androidID,
ios: iosID,
},
npa: npaValue,
})
.then(
() => {
logger.info('AdMob reward video successfully loaded.');
const adUnitId = getAdUnitId(
androidAdUnitID,
iosAdUnitID,
'rewardedInterstitial'
);
if (!adUnitId) return;
if (displayWhenLoaded) showVideo();
},
(error) => {
videoLoading = false;
videoReady = false;
videoErrored = true;
logger.error('Error while loading a reward video:', error);
}
rewardedInterstitialLoading = true;
rewardedInterstitialReady = false;
rewardedInterstitialErrored = false;
rewardedInterstitial = new admob.RewardedInterstitialAd({
adUnitId,
npa: npaValue,
});
// Rewarded video event listeners
rewardedInterstitial.on('load', () => {
rewardedInterstitialReady = true;
rewardedInterstitialLoading = false;
});
rewardedInterstitial.on('loadfail', () => {
rewardedInterstitialLoading = false;
rewardedInterstitialErrored = true;
});
rewardedInterstitial.on('show', () => {
rewardedInterstitialShowing = true;
rewardedInterstitialReady = false;
});
rewardedInterstitial.on('showfail', () => {
rewardedInterstitialShowing = false;
rewardedInterstitialErrored = true;
});
rewardedInterstitial.on('dismiss', () => {
rewardedInterstitialShowing = false;
});
rewardedInterstitial.on('reward', () => {
rewardedInterstitialRewardReceived = true;
});
try {
logger.info('Loading AdMob rewarded interstitial.');
await rewardedInterstitial.load();
logger.info('AdMob rewarded interstitial successfully loaded.');
rewardedInterstitialLoading = false;
rewardedInterstitialReady = true;
if (displayWhenLoaded) showRewardedInterstitial();
} catch (error) {
rewardedInterstitialLoading = false;
rewardedInterstitialReady = false;
rewardedInterstitialErrored = true;
logger.error('Error while loading a rewarded interstitial:', error);
}
};
/** Show the loaded reward interstitial. */
export const showRewardedInterstitial = async () => {
if (!checkIfAdMobIsAvailable()) return;
if (!rewardedInterstitial) {
logger.warn(
'interstitial has not been set up, call loadRewardedInterstitial first.'
);
return;
}
if (!rewardedInterstitialReady) {
logger.info('Rewarded interstitial not loaded yet, cannot display it.');
}
rewardedInterstitialErrored = false;
try {
logger.info('Showing AdMob rewarded interstitial.');
await rewardedInterstitial.show();
// Rewarded interstitial will be shown and
// `rewardedInterstitialShowing` will be updated thanks to events
// (but it's too early to change it now).
} catch (error) {
logger.error(
'Error while showing an AdMob rewarded interstitial:',
error
);
rewardedInterstitialShowing = false;
rewardedInterstitialErrored = true;
}
};
/** Mark the reward of the interstitial as claimed. */
export const markRewardedInterstitialRewardAsClaimed = () => {
rewardedInterstitialRewardReceived = false;
};
// -------------------------
// ---- Rewarded Video -----
// -------------------------
export const isRewardedVideoLoading = () => rewardedVideoLoading;
export const isRewardedVideoReady = () => rewardedVideoReady;
export const isRewardedVideoShowing = () => rewardedVideoShowing;
export const isRewardedVideoErrored = () => rewardedVideoErrored;
/** Check if the reward of the rewarded video was received. */
export const wasRewardedVideoRewardReceived = function (markAsClaimed) {
const reward = rewardedVideoRewardReceived;
if (markAsClaimed) {
rewardedVideoRewardReceived = false;
}
return reward;
};
/** Load a rewarded video. */
export const loadRewardedVideo = async (
androidAdUnitID,
iosAdUnitID,
displayWhenLoaded
) => {
if (!checkIfAdMobIsAvailable()) return;
if (rewardedVideoLoading || rewardedVideoShowing) {
return;
}
const adUnitId = getAdUnitId(androidAdUnitID, iosAdUnitID, 'rewarded');
if (!adUnitId) return;
rewardedVideoLoading = true;
rewardedVideoReady = false;
rewardedVideoErrored = false;
rewardedVideo = new admob.RewardedAd({
adUnitId,
npa: npaValue,
});
// Rewarded video event listeners
rewardedVideo.on('load', () => {
rewardedVideoReady = true;
rewardedVideoLoading = false;
});
rewardedVideo.on('loadfail', () => {
rewardedVideoLoading = false;
rewardedVideoErrored = true;
});
rewardedVideo.on('show', () => {
rewardedVideoShowing = true;
rewardedVideoReady = false;
});
rewardedVideo.on('showfail', () => {
rewardedVideoShowing = false;
rewardedVideoErrored = true;
});
rewardedVideo.on('dismiss', () => {
rewardedVideoShowing = false;
});
rewardedVideo.on('reward', () => {
rewardedVideoRewardReceived = true;
});
try {
logger.info('Loading AdMob rewarded video.');
await rewardedVideo.load();
logger.info('AdMob rewarded video successfully loaded.');
rewardedVideoLoading = false;
rewardedVideoReady = true;
if (displayWhenLoaded) showRewardedVideo();
} catch (error) {
rewardedVideoLoading = false;
rewardedVideoReady = false;
rewardedVideoErrored = true;
logger.error('Error while loading a rewarded video:', error);
}
};
/** Show the loaded reward video. */
export const showVideo = () => {
if (typeof admob === 'undefined') {
export const showRewardedVideo = async () => {
if (!checkIfAdMobIsAvailable()) return;
if (!rewardedVideo) {
logger.warn('Video has not been set up, call loadRewardedVideo first.');
return;
}
if (!rewardedVideoReady) {
logger.info('Rewarded video not loaded yet, cannot display it.');
}
rewardedVideoErrored = false;
admob.rewardVideo.show().then(
() => {
// Video will be shown and
// `videoShowing` will be updated thanks to events
// (but it's too early to change it now).
},
(error) => {
videoShowing = false;
videoErrored = true;
logger.error('Error while trying to show a reward video:', error);
}
);
try {
logger.info('Showing AdMob rewarded video.');
await rewardedVideo.show();
// Rewarded video will be shown and
// `rewardedVideoShowing` will be updated thanks to events
// (but it's too early to change it now).
} catch (error) {
logger.error('Error while showing an AdMob rewarded video:', error);
rewardedVideoShowing = false;
rewardedVideoErrored = true;
}
};
/** Mark the reward of the video as claimed. */
export const markVideoRewardAsClaimed = () => {
videoRewardReceived = false;
export const markRewardedVideoRewardAsClaimed = () => {
rewardedVideoRewardReceived = false;
};
// Banner event listeners:
document.addEventListener('admob.banner.load', () => {
bannerShowing = true;
bannerLoading = false;
});
document.addEventListener('admob.banner.load_fail', () => {
bannerLoading = false;
});
document.addEventListener('admob.banner.open', () => {
// Not implemented.
});
document.addEventListener('admob.banner.exit_app', () => {
// Not implemented.
});
document.addEventListener('admob.banner.close', () => {
// Not implemented.
});
// Interstitial event listeners
document.addEventListener('admob.interstitial.load', () => {
interstitialReady = true;
interstitialLoading = false;
});
document.addEventListener('admob.interstitial.load_fail', () => {
interstitialLoading = false;
});
document.addEventListener('admob.interstitial.open', () => {
interstitialShowing = true;
interstitialReady = false;
});
document.addEventListener('admob.interstitial.close', () => {
interstitialShowing = false;
});
document.addEventListener('admob.interstitial.exit_app', () => {
// Not implemented.
});
// Reward video event listeners
document.addEventListener('admob.reward_video.load', () => {
videoReady = true;
videoLoading = false;
});
document.addEventListener('admob.reward_video.load_fail', () => {
videoLoading = false;
});
document.addEventListener('admob.reward_video.open', () => {
videoShowing = true;
videoReady = false;
});
document.addEventListener('admob.reward_video.close', () => {
videoShowing = false;
});
document.addEventListener('admob.reward_video.start', () => {
// Not implemented.
});
document.addEventListener('admob.reward_video.complete', () => {
// Not implemented.
});
document.addEventListener('admob.reward_video.reward', () => {
videoRewardReceived = true;
});
document.addEventListener('admob.reward_video.exit_app', () => {
// Not implemented.
});
}
}

View File

@@ -1,10 +1,7 @@
// @ts-check
describe('gdjs.AnchorRuntimeBehavior', function () {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
// @ts-ignore
properties: { windowWidth: 1000, windowHeight: 1000 },
const runtimeGame = gdjs.getPixiRuntimeGame({
propertiesOverrides: { windowHeight: 1000, windowWidth: 1000 },
});
const anchorBehaviorName = 'Anchor';
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);

View File

@@ -36,7 +36,7 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bbtext')
.setCategory('User interface');
.setCategory('Text');
extension
.addInstructionOrExpressionGroupMetadata(_('BBCode Text Object'))
.setIcon('JsPlatform/Extensions/bbcode32.png');
@@ -174,7 +174,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd.js'
)
.setCategoryFullName(_('User interface'));
.setCategoryFullName(_('Text'));
/**
* Utility function to add both a setter and a getter to a property from a list.

View File

@@ -36,9 +36,10 @@ module.exports = {
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bitmap_text')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Bitmap Text"))
.setIcon("JsPlatform/Extensions/bitmapfont32.png");
.setCategory('Text');
extension
.addInstructionOrExpressionGroupMetadata(_('Bitmap Text'))
.setIcon('JsPlatform/Extensions/bitmapfont32.png');
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
@@ -91,7 +92,7 @@ module.exports = {
.setType('resource')
.addExtraInfo('bitmapFont') //fnt or xml files
.setLabel(_('Bitmap Font'))
.setGroup(_("Font"));
.setGroup(_('Font'));
objectProperties
.getOrCreate('textureAtlasResourceName')
@@ -99,7 +100,7 @@ module.exports = {
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Bitmap atlas image'))
.setGroup(_("Font"));
.setGroup(_('Font'));
objectProperties
.getOrCreate('scale')
@@ -113,7 +114,7 @@ module.exports = {
.setValue(objectContent.tint)
.setType('color')
.setLabel(_('Font tint'))
.setGroup(_("Font"));
.setGroup(_('Font'));
objectProperties
.getOrCreate('wordWrap')
@@ -174,7 +175,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
)
.setCategoryFullName(_('User interface'));
.setCategoryFullName(_('Text'));
object
.addExpressionAndConditionAndAction(
@@ -652,8 +653,7 @@ module.exports = {
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObjectConfiguration
.getProperties();
const properties = this._associatedObjectConfiguration.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'DialogueTree',
_('Dialogue Tree (experimental)'),
_('Dialogue Tree'),
'Handle dialogue trees, made using Yarn Spinner. Useful to make complex dialogues with multiple choices. The Yarn Spinner editor is embedded in GDevelop so you can edit your dialogues without leaving GDevelop.',
'Todor Imreorov',
'Open source (MIT License)'
@@ -36,7 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/dialogue-tree')
.setCategory('Game mechanic');
extension
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree'))
.setIcon('JsPlatform/Extensions/yarn32.png');
extension

View File

@@ -57,35 +57,18 @@ namespace gdjs {
}
const inputManager = instanceContainer.getGame().getInputManager();
//Try mouse
const mouseDraggableManager = DraggableManager.getMouseManager(
instanceContainer
);
if (
inputManager.isMouseButtonPressed(0) &&
!mouseDraggableManager.isDragging(this)
) {
if (mouseDraggableManager.tryAndTakeDragging(instanceContainer, this)) {
this._draggedByDraggableManager = mouseDraggableManager;
return true;
const touchIds = inputManager.getStartedTouchIdentifiers();
for (let i = 0; i < touchIds.length; ++i) {
const touchDraggableManager = DraggableManager.getTouchManager(
instanceContainer,
touchIds[i]
);
if (touchDraggableManager.isDragging(this)) {
continue;
}
} else {
//Try touches
const touchIds = inputManager.getStartedTouchIdentifiers();
for (let i = 0; i < touchIds.length; ++i) {
const touchDraggableManager = DraggableManager.getTouchManager(
instanceContainer,
touchIds[i]
);
if (touchDraggableManager.isDragging(this)) {
continue;
}
if (
touchDraggableManager.tryAndTakeDragging(instanceContainer, this)
) {
this._draggedByDraggableManager = touchDraggableManager;
return true;
}
if (touchDraggableManager.tryAndTakeDragging(instanceContainer, this)) {
this._draggedByDraggableManager = touchDraggableManager;
return true;
}
}
return false;
@@ -121,14 +104,6 @@ namespace gdjs {
}
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
const mouseDraggableManager = DraggableManager.getMouseManager(
instanceContainer
);
mouseDraggableManager.leftPressedLastFrame = instanceContainer
.getGame()
.getInputManager()
.isMouseButtonPressed(0);
this._justDropped = false;
}
@@ -144,7 +119,8 @@ namespace gdjs {
/**
* Handle the dragging
*/
abstract class DraggableManager {
class DraggableManager {
private _touchId: integer;
/**
* The object has left its original position.
* When true, the search for the best object to drag has ended.
@@ -157,24 +133,11 @@ namespace gdjs {
protected _xOffset: number = 0;
protected _yOffset: number = 0;
constructor(instanceContainer: gdjs.RuntimeInstanceContainer) {}
/**
* Get the platforms manager of an instance container.
*/
static getMouseManager(
instanceContainer: gdjs.RuntimeInstanceContainer
): MouseDraggableManager {
// @ts-ignore
if (!instanceContainer.mouseDraggableManager) {
//Create the shared manager if necessary.
// @ts-ignore
instanceContainer.mouseDraggableManager = new MouseDraggableManager(
instanceContainer
);
}
// @ts-ignore
return instanceContainer.mouseDraggableManager;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
touchId: integer
) {
this._touchId = touchId;
}
/**
@@ -196,7 +159,7 @@ namespace gdjs {
// @ts-ignore
instanceContainer.touchDraggableManagers[
touchId
] = new TouchDraggableManager(instanceContainer, touchId);
] = new DraggableManager(instanceContainer, touchId);
}
// @ts-ignore
return instanceContainer.touchDraggableManagers[touchId];
@@ -263,75 +226,6 @@ namespace gdjs {
this._draggableBehavior = null;
}
abstract isDragging(
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean;
abstract shouldEndDrag(
instanceContainer: gdjs.RuntimeInstanceContainer,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean;
abstract getPosition(
instanceContainer: gdjs.RuntimeInstanceContainer,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint;
}
/**
* Handle the dragging by mouse
*/
class MouseDraggableManager extends DraggableManager {
/** Used to only start dragging when clicking. */
leftPressedLastFrame = false;
constructor(instanceContainer: gdjs.RuntimeInstanceContainer) {
super(instanceContainer);
}
isDragging(draggableRuntimeBehavior: DraggableRuntimeBehavior): boolean {
return this.leftPressedLastFrame || this._draggingSomething;
}
getPosition(
instanceContainer: gdjs.RuntimeInstanceContainer,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint {
const workingPoint: FloatPoint = gdjs.staticArray(
MouseDraggableManager.prototype.getPosition
) as FloatPoint;
const inputManager = instanceContainer.getGame().getInputManager();
return instanceContainer
.getLayer(draggableRuntimeBehavior.owner.getLayer())
.convertCoords(
inputManager.getMouseX(),
inputManager.getMouseY(),
0,
workingPoint
);
}
shouldEndDrag(
instanceContainer: gdjs.RuntimeInstanceContainer,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean {
const inputManager = instanceContainer.getGame().getInputManager();
return !inputManager.isMouseButtonPressed(0);
}
}
/**
* Handle the dragging by touch
*/
class TouchDraggableManager extends DraggableManager {
private _touchId: integer;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
touchId: integer
) {
super(instanceContainer);
this._touchId = touchId;
}
isDragging(draggableRuntimeBehavior: DraggableRuntimeBehavior): boolean {
return this._draggingSomething;
}
@@ -341,7 +235,7 @@ namespace gdjs {
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint {
const workingPoint: FloatPoint = gdjs.staticArray(
TouchDraggableManager.prototype.getPosition
DraggableManager.prototype.getPosition
) as FloatPoint;
const inputManager = instanceContainer.getGame().getInputManager();
return instanceContainer
@@ -359,9 +253,7 @@ namespace gdjs {
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean {
const inputManager = instanceContainer.getGame().getInputManager();
return (
inputManager.getAllTouchIdentifiers().indexOf(this._touchId) === -1
);
return inputManager.hasTouchEnded(this._touchId);
}
}

View File

@@ -1,11 +1,6 @@
// @ts-check
describe('gdjs.DraggableRuntimeBehavior', function () {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
// @ts-ignore
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
@@ -66,17 +61,21 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
@@ -84,6 +83,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Mouse move without dragging
runtimeGame.getInputManager().onMouseMove(600, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
@@ -94,12 +94,15 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
@@ -111,17 +114,21 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Dragged point is in the bounding box but not in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
@@ -135,34 +142,42 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Dragged point is in the bounding box but not in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object2.getX()).to.be(450);
expect(object2.getY()).to.be(500);
// Dragged point is in the bounding box and in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(455, 505);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(855, 705);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object2.getX()).to.be(850);
expect(object2.getY()).to.be(700);
@@ -185,17 +200,21 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
if (firstInFront) {
// The 1st object moved
@@ -220,6 +239,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
@@ -237,22 +257,25 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Move another unrelated touch
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
// Start drag'n'drop with another touch
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(1);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
@@ -264,12 +287,12 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Dragged point is in the bounding box but not in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
@@ -286,6 +309,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Dragged point is in the bounding box but not in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
@@ -301,6 +325,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Dragged point is in the bounding box but not in hitbox
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(0, 455, 505);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
@@ -323,6 +348,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
runtimeScene.renderAndStep(1000 / 60);
@@ -361,6 +387,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
// Drag'n'drop
runtimeGame.getInputManager().touchSimulateMouse(false);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);

View File

@@ -176,12 +176,12 @@ module.exports = {
.setType('number');
bevelProperties
.getOrCreate('lightColor')
.setValue('#ffffff')
.setValue('255;255;255')
.setLabel(_('Light color (color of the outline)'))
.setType('color');
bevelProperties
.getOrCreate('shadowColor')
.setValue('#000000')
.setValue('0;0;0')
.setLabel(_('Shadow color (color of the outline)'))
.setType('color');
bevelProperties
@@ -333,13 +333,13 @@ module.exports = {
const colorReplaceProperties = colorReplaceEffect.getProperties();
colorReplaceProperties
.getOrCreate('originalColor')
.setValue('#ff0000')
.setValue('252;3;65')
.setLabel(_('Original Color'))
.setType('color')
.setDescription('The color that will be changed');
colorReplaceProperties
.getOrCreate('newColor')
.setValue('#000000')
.setValue('255;255;255')
.setLabel(_('New Color'))
.setType('color')
.setDescription('The new color');
@@ -517,7 +517,7 @@ module.exports = {
.setType('number');
dropShadowProperties
.getOrCreate('color')
.setValue('#000000')
.setValue('255;255;255')
.setLabel(_('Color of the shadow'))
.setType('color');
dropShadowProperties
@@ -646,7 +646,7 @@ module.exports = {
.setType('number');
glowProperties
.getOrCreate('color')
.setValue('#ffffff')
.setValue('255;255;255')
.setLabel(_('Color (color of the outline)'))
.setType('color');
@@ -868,7 +868,7 @@ module.exports = {
.setType('number');
outlineProperties
.getOrCreate('color')
.setValue('1')
.setValue('255;255;255')
.setLabel(_('Color of the outline'))
.setType('color');
outlineProperties

View File

@@ -89,7 +89,7 @@ module.exports = {
.addExtraInfo('image');
dummyEffectProperties
.getOrCreate('someColor')
.setValue('#0022FF')
.setValue('255;3;62')
.setLabel(_("Color (won't be used, just for demonstration purpose)"))
.setType('color')
.setDescription(_('Another optional description.'));

View File

@@ -2037,7 +2037,7 @@ module.exports = {
_('Update a document in Database'),
_('Updates a variable on the database.'),
_(
'Update varable _PARAM0_ with _PARAM1_ (store result state in _PARAM2_)'
'Update variable _PARAM0_ with _PARAM1_ (store result state in _PARAM2_)'
),
_('Realtime Database'),
'JsPlatform/Extensions/firebase.png',

View File

@@ -1,17 +1,3 @@
/**
* A firebase configuaration of a project made only for those tests.
*/
const firebaseConfig = {
apiKey: 'AIzaSyBwPnGpfEBXDjwQrWfU0wqgp4m9qEt7YM8',
authDomain: 'gdtest-e11a5.firebaseapp.com',
databaseURL: 'https://gdtest-e11a5.firebaseio.com',
projectId: 'gdtest-e11a5',
storageBucket: 'gdtest-e11a5.appspot.com',
messagingSenderId: '254035412678',
appId: '1:254035412678:web:2ddd6b83019b7f259b79c7',
measurementId: 'G-4REML26L59',
};
/**
* Turns a callback variable into a promise.
* @param {(callbackVariable: {setString: (result: "ok" | string) => void}, result: gdjs.Variable) => any} executor
@@ -38,6 +24,20 @@ const variable = new gdjs.Variable().fromJSObject({
it: ['is', true],
});
/**
* A firebase configuration of a project made only for those tests.
*/
const firebaseConfig = {
apiKey: 'AIzaSyBwPnGpfEBXDjwQrWfU0wqgp4m9qEt7YM8',
authDomain: 'gdtest-e11a5.firebaseapp.com',
databaseURL: 'https://gdtest-e11a5.firebaseio.com',
projectId: 'gdtest-e11a5',
storageBucket: 'gdtest-e11a5.appspot.com',
messagingSenderId: '254035412678',
appId: '1:254035412678:web:2ddd6b83019b7f259b79c7',
measurementId: 'G-4REML26L59',
};
// The tests require an internet connection, as a real Firebase instance is used.
const describeIfOnline = navigator.onLine ? describe : describe.skip;
describeIfOnline('Firebase extension end-to-end tests', function () {
@@ -48,8 +48,8 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
.toString()
.replace('.', '-')}-${Date.now()}`;
before(function setupFirebase() {
gdjs.evtTools.firebaseTools._setupFirebase({
before(async function setupFirebase() {
await gdjs.evtTools.firebaseTools._setupFirebase({
getGame: () => ({
getExtensionProperty: () => JSON.stringify(firebaseConfig),
}),

View File

@@ -1,10 +1,6 @@
describe('Inventory', function () {
it('Inventories can be serialized then unserialized with no data loss', () => {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'sword');

View File

@@ -28,14 +28,14 @@ module.exports = {
extension
.setExtensionInformation(
'Leaderboards',
_('Leaderboards (experimental)'),
_('Leaderboards'),
_('Allow your game to send scores to your leaderboards.'),
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/leaderboards')
.setCategory('Players')
.addInstructionOrExpressionGroupMetadata(_('Leaderboards (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('Leaderboards'))
.setIcon('JsPlatform/Extensions/leaderboard.svg');
extension
@@ -154,6 +154,21 @@ module.exports = {
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.isSaving');
extension
.addCondition(
'HasPlayerJustClosedLeaderboardView',
_('Closed by player'),
_('Check if the player has just closed the leaderboard view.'),
_('Player has just closed the leaderboard view'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.hasPlayerJustClosedLeaderboardView');
extension
.addStrExpression(
'LastSaveError',

View File

@@ -5,6 +5,19 @@ namespace gdjs {
const logger = new gdjs.Logger('Leaderboards');
export namespace evtTools {
export namespace leaderboards {
let _hasPlayerJustClosedLeaderboardView = false;
gdjs.registerRuntimeScenePostEventsCallback(() => {
// Set it back to false for the next frame.
_hasPlayerJustClosedLeaderboardView = false;
});
/**
* Returns true if the player has just closed the leaderboard view.
*/
export const hasPlayerJustClosedLeaderboardView = () =>
_hasPlayerJustClosedLeaderboardView;
const computeDigest = (payload: string): string => {
const shaObj = new jsSHA('SHA-256', 'TEXT', { encoding: 'UTF8' });
shaObj.update(payload);
@@ -521,6 +534,7 @@ namespace gdjs {
) {
switch (event.data) {
case 'closeLeaderboardView':
_hasPlayerJustClosedLeaderboardView = true;
closeLeaderboardView(runtimeScene);
break;
case 'leaderboardViewLoaded':

View File

@@ -56,13 +56,7 @@ const addLightObstacle = (runtimeScene, width, height) => {
describe('gdjs.LightRuntimeObject', function () {
PIXI.settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],
@@ -96,13 +90,7 @@ describe('gdjs.LightRuntimeObject', function () {
describe('Light with obstacles around it', function () {
PIXI.settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],

View File

@@ -6,12 +6,7 @@ describe('gdjs.LinksManager', function () {
object3Names,
eventsFunctionContext
) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'P2P',
_('P2P (experimental)'),
_('P2P'),
'Allow game instances to communicate remotely using messages sent via WebRTC (P2P).',
'Arthur Pacaud (arthuro555)',
'MIT'
@@ -36,7 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/p2p')
.setCategory('Network');
extension
.addInstructionOrExpressionGroupMetadata(_('P2P (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('P2P'))
.setIcon('JsPlatform/Extensions/p2picon.svg');
extension

View File

@@ -54,7 +54,8 @@ void PanelSpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("tiled", tiled);
}
void PanelSpriteObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
void PanelSpriteObject::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
worker.ExposeImage(textureName);
}
#endif

View File

@@ -28,9 +28,7 @@ class GD_EXTENSION_API PanelSpriteObject : public gd::ObjectConfiguration {
new PanelSpriteObject(*this));
}
#if defined(GD_IDE_ONLY)
virtual void ExposeResources(gd::ArbitraryResourceWorker &worker);
#endif
double GetWidth() const { return width; };
double GetHeight() const { return height; };

View File

@@ -179,9 +179,8 @@ namespace gdjs {
movementAngleIsAround(degreeAngle: float, tolerance: float) {
return (
gdjs.evtTools.common.angleDifference(
this._movementAngle,
degreeAngle
Math.abs(
gdjs.evtTools.common.angleDifference(this._movementAngle, degreeAngle)
) <= tolerance
);
}

View File

@@ -6,12 +6,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
const pathFindingName = 'auto1';
const createScene = (framePerSecond = 60) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [

View File

@@ -9,12 +9,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
const pathFindingName = 'auto1';
const createScene = (framePerSecond = 60) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [

View File

@@ -11,12 +11,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
const pathFindingName = 'auto1';
let createScene = () => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [

View File

@@ -426,7 +426,9 @@ module.exports = {
)
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.addIncludeFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.js')
.addRequiredFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.wasm')
.addRequiredFile(
'Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.wasm'
);
// Global
aut
@@ -1361,6 +1363,40 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('getLinearVelocityLength');
aut
.addCondition(
'LinearVelocityAngle',
_('Linear velocity angle'),
_('Test an object linear velocity angle.'),
_('the linear velocity angle'),
_('Velocity'),
'res/physics32.png',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.useStandardRelationalOperatorParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle to compare to (in degrees)')
)
)
.getCodeExtraInformation()
.setFunctionName('getLinearVelocityAngle');
aut
.addExpression(
'LinearVelocityAngle',
_('Linear velocity angle'),
_('Get the linear velocity angle of an object.'),
_('Velocity'),
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getLinearVelocityAngle');
aut
.addCondition(
'AngularVelocity',
@@ -1498,8 +1534,14 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('X component (in Newton * seconds or kilogram * meter per second)'))
.addParameter('expression', _('Y component (in Newton * seconds or kilogram * meter per second)'))
.addParameter(
'expression',
_('X component (in Newton * seconds or kilogram * meter per second)')
)
.addParameter(
'expression',
_('Y component (in Newton * seconds or kilogram * meter per second)')
)
.addParameter('expression', _('Applying X position'))
.addParameter('expression', _('Applying Y position'))
.getCodeExtraInformation()
@@ -1522,7 +1564,10 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter('expression', _('Length (in Newton * seconds or kilogram * meter per second)'))
.addParameter(
'expression',
_('Length (in Newton * seconds or kilogram * meter per second)')
)
.addParameter('expression', _('Applying X position'))
.addParameter('expression', _('Applying Y position'))
.getCodeExtraInformation()
@@ -1544,7 +1589,10 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Length (in Newton * seconds or kilogram * meter per second)'))
.addParameter(
'expression',
_('Length (in Newton * seconds or kilogram * meter per second)')
)
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Applying X position'))
@@ -1601,31 +1649,33 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('getMass');
aut
.addExpression(
'Inertia',
_('Inertia'),
_('Return the rotational inertia of the object (in kilograms * meters * meters)'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getInertia');
aut
.addExpression(
'Inertia',
_('Inertia'),
_(
'Return the rotational inertia of the object (in kilograms * meters * meters)'
),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getInertia');
aut
.addExpression(
'MassCenterX',
_('Mass center X'),
_('Mass center X'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getMassCenterX');
aut
.addExpression(
'MassCenterX',
_('Mass center X'),
_('Mass center X'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getMassCenterX');
aut
.addExpression(

View File

@@ -1408,6 +1408,22 @@ namespace gdjs {
).Length();
}
getLinearVelocityAngle(): float {
// If there is no body, set a new one
if (this._body === null) {
if (!this.createBody()) return 0;
}
const body = this._body!;
// Get the linear velocity angle
return gdjs.toDegrees(
Math.atan2(
body.GetLinearVelocity().get_y() * this._sharedData.scaleY,
body.GetLinearVelocity().get_x() * this._sharedData.scaleX
)
);
}
getAngularVelocity(): float {
// If there is no body, set a new one
if (this._body === null) {

View File

@@ -59,11 +59,7 @@ describe('Physics2RuntimeBehavior', () => {
}
function createGameWithSceneWithPhysics2SharedData() {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
properties: { windowWidth: 1000, windowHeight: 1000 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [

View File

@@ -1,12 +1,6 @@
const makePlatformerTestRuntimeScene = (timeDelta = 1000 / 60) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],

View File

@@ -1,12 +1,6 @@
describe(`gdjs.PlatformerObjectRuntimeBehavior.findHighestFloorAndMoveOnTop`, function () {
const makeTestRuntimeScene = () => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'PlayerAuthentication',
_('Player Authentication (experimental)'),
_('Player Authentication'),
_('Allow your game to authenticate players.'),
'Florian Rival',
'Open source (MIT License)'
@@ -36,9 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/player-authentication')
.setCategory('Players');
extension
.addInstructionOrExpressionGroupMetadata(
_('Player Authentication (experimental)')
)
.addInstructionOrExpressionGroupMetadata(_('Player Authentication'))
.setIcon('JsPlatform/Extensions/authentication.svg');
extension
@@ -70,6 +68,29 @@ module.exports = {
)
.setFunctionName('gdjs.playerAuthentication.displayAuthenticationBanner');
extension
.addAction(
'HideAuthenticationBanner',
_('Hide authentication banner'),
_(
'Hide the authentication banner from the top of the game screen.'
),
_('Hide the 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.removeAuthenticationBanner');
extension
.addAction(
'OpenAuthenticationWindow',

View File

@@ -19,10 +19,11 @@ namespace gdjs {
'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!',
title: 'Publish your game!',
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.',
"GDevelop's player accounts are only available for published games.",
text2:
'Click the button below to learn how to publish your game then try again.',
};
/**
@@ -119,7 +120,8 @@ namespace gdjs {
export const addAuthenticationTextsToLoadingContainer = (
loaderContainer: HTMLDivElement,
platform,
isGameRegistered
isGameRegistered,
wikiOpenAction
) => {
const textContainer: HTMLDivElement = document.createElement('div');
textContainer.id = 'authentication-container-texts';
@@ -151,8 +153,20 @@ namespace gdjs {
textContainer.appendChild(text2);
if (!isGameRegistered) {
// Remove the loader.
// Remove the loader and add the wiki link.
loaderContainer.innerHTML = '';
if (wikiOpenAction) {
const link = document.createElement('a');
addTouchAndClickEventListeners(link, wikiOpenAction);
link.innerText = 'How to publish my game';
link.style.color = '#0078d4';
link.style.textDecoration = 'none';
link.style.textDecoration = 'underline';
link.style.cursor = 'pointer';
textContainer.appendChild(link);
}
}
loaderContainer.prepend(textContainer);
@@ -170,7 +184,7 @@ namespace gdjs {
) => {
const link = document.createElement('a');
addTouchAndClickEventListeners(link, onClick);
link.innerText = 'Click here to authenticate';
link.innerText = 'Try again';
link.style.color = '#0078d4';
link.style.textDecoration = 'none';
link.style.textDecoration = 'underline';
@@ -431,7 +445,7 @@ namespace gdjs {
* Helper to add event listeners on a pressable/clickable element
* to work on both desktop and mobile.
*/
export const addTouchAndClickEventListeners = function (
const addTouchAndClickEventListeners = function (
element: HTMLElement,
action: () => void
) {

View File

@@ -20,6 +20,7 @@ namespace gdjs {
let _authenticationLoaderContainer: HTMLDivElement | null = null;
let _authenticationTextContainer: HTMLDivElement | null = null;
let _authenticationBanner: HTMLDivElement | null = null;
let _initialAuthenticationTimeoutId: NodeJS.Timeout | null = null;
let _authenticationTimeoutId: NodeJS.Timeout | null = null;
// Communication methods.
@@ -36,6 +37,43 @@ namespace gdjs {
_justLoggedIn = false;
});
// If the extension is used, register an eventlistener to know if the user is
// logged in while playing the game on Liluo.io.
// Then send a message to the parent iframe to say that the player auth is ready.
gdjs.registerFirstRuntimeSceneLoadedCallback(
(runtimeScene: RuntimeScene) => {
if (getPlatform(runtimeScene) !== 'web') {
// Automatic authentication is only valid when the game is hosted in Liluo.io.
return;
}
removeAuthenticationCallbacks(); // Remove any callback that could have been registered before.
_authenticationMessageCallback = (event: MessageEvent) => {
receiveAuthenticationMessage(runtimeScene, event, {
checkOrigin: true,
});
};
window.addEventListener(
'message',
_authenticationMessageCallback,
true
);
logger.info(
'Notifying parent window that player authentication is ready.'
);
window.parent.postMessage(
{
id: 'playerAuthReady',
},
'*' // We could restrict to liluo.io but it's not necessary as the message is not sensitive, and it allows easy debugging.
);
// If no answer after 3 seconds, assume that the game is not embedded in Liluo.io, and remove the listener.
_initialAuthenticationTimeoutId = setTimeout(() => {
logger.info('Removing initial authentication listener.');
removeAuthenticationCallbacks();
}, 3000);
}
);
const getLocalStorageKey = (gameId: string) =>
`${gameId}_authenticatedUser`;
@@ -190,19 +228,26 @@ namespace gdjs {
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);
try {
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;
_username = authenticatedUser.username;
_userId = authenticatedUser.userId;
_userToken = authenticatedUser.userToken;
_checkedLocalStorage = true;
} catch (err) {
logger.warn(
'Unable to read authentication details from localStorage. Player authentication will not be available.',
err
);
}
};
/**
@@ -228,16 +273,15 @@ namespace gdjs {
}
};
/**
* 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
) {
const saveAuthKeyToStorage = ({
username,
userId,
userToken,
}: {
username: string | null;
userId: string;
userToken: string;
}) => {
if (!username) {
logger.warn('The authenticated player does not have a username');
}
@@ -251,14 +295,34 @@ namespace gdjs {
logger.error('Missing game id in project properties.');
return;
}
window.localStorage.setItem(
getLocalStorageKey(gameId),
JSON.stringify({
username: _username,
userId: _userId,
userToken: _userToken,
})
);
try {
window.localStorage.setItem(
getLocalStorageKey(gameId),
JSON.stringify({
username: _username,
userId: _userId,
userToken: _userToken,
})
);
} catch (err) {
logger.warn(
'Unable to save the authentication details to localStorage. Player authentication will not be available.',
err
);
}
};
/**
* When the game 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
) {
saveAuthKeyToStorage({ userId, username, userToken });
cleanUpAuthWindowAndCallbacks(runtimeScene);
removeAuthenticationBanner(runtimeScene);
@@ -284,15 +348,15 @@ namespace gdjs {
* Reads the event sent by the authentication window and
* display the appropriate banner.
*/
const receiveMessageFromAuthenticationWindow = function (
const receiveAuthenticationMessage = function (
runtimeScene: gdjs.RuntimeScene,
event: MessageEvent,
{ checkOrigin }: { checkOrigin: boolean }
) {
const allowedOrigin = 'https://liluo.io';
const allowedOrigins = ['https://liluo.io', 'https://gd.games'];
// Check origin of message.
if (checkOrigin && event.origin !== allowedOrigin) {
if (checkOrigin && !allowedOrigins.includes(event.origin)) {
throw new Error(`Unexpected origin: ${event.origin}`);
}
// Check that message is not malformed.
@@ -315,6 +379,20 @@ namespace gdjs {
);
break;
}
case 'alreadyAuthenticated': {
if (!(event.data.body && event.data.body.token)) {
throw new Error('Malformed message.');
}
saveAuthKeyToStorage({
userId: event.data.body.userId,
username: event.data.body.username,
userToken: event.data.body.token,
});
removeAuthenticationCallbacks();
refreshAuthenticationBannerIfAny(runtimeScene);
break;
}
}
};
@@ -362,15 +440,42 @@ namespace gdjs {
};
/**
* Clear the authentication window timeout.
* Clear all existing authentication timeouts.
* Useful when:
* - a new authentication starts
* - the authentication succeeded
* - the authentication window is closed
*/
const clearAuthenticationWindowTimeout = () => {
if (_initialAuthenticationTimeoutId)
clearTimeout(_initialAuthenticationTimeoutId);
if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);
};
/**
* Helper to create the authentication banner based on the authentication status.
*/
const createAuthenticationBanner = function (
runtimeScene: gdjs.RuntimeScene
): HTMLDivElement {
const onDismissBanner = () => {
removeAuthenticationBanner(runtimeScene);
};
const onOpenAuthenticationWindow = () => {
openAuthenticationWindow(runtimeScene);
};
return _userToken
? authComponents.computeAuthenticatedBanner(
onOpenAuthenticationWindow,
onDismissBanner,
_username
)
: authComponents.computeNotAuthenticatedBanner(
onOpenAuthenticationWindow,
onDismissBanner
);
};
/**
* Action to display the banner to the user, depending on their authentication status.
*/
@@ -397,26 +502,39 @@ namespace gdjs {
);
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
);
_authenticationBanner = createAuthenticationBanner(runtimeScene);
domElementContainer.appendChild(_authenticationBanner);
};
/**
* Helper to recompute the authentication banner.
* This is useful if the user is already logged in on Liluo.io
* and we want to display the banner with the username.
*/
const refreshAuthenticationBannerIfAny = function (
runtimeScene: gdjs.RuntimeScene
) {
if (!_authenticationBanner) return;
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 oldAuthenticationBanner = _authenticationBanner;
_authenticationBanner = createAuthenticationBanner(runtimeScene);
domElementContainer.replaceChild(
_authenticationBanner,
oldAuthenticationBanner
);
};
/**
* Helper to handle authentication window on Electron.
* We open a new window, and create a websocket to know when the user is logged in.
@@ -516,7 +634,7 @@ namespace gdjs {
// know when the user is authenticated.
if (_authenticationInAppWindow) {
_cordovaAuthenticationMessageCallback = (event: MessageEvent) => {
receiveMessageFromAuthenticationWindow(runtimeScene, event, {
receiveAuthenticationMessage(runtimeScene, event, {
checkOrigin: false, // For Cordova we don't check the origin, as the message is read from the InAppBrowser directly.
});
};
@@ -545,7 +663,7 @@ namespace gdjs {
// Listen to messages posted by the authentication window, so that we can
// know when the user is authenticated.
_authenticationMessageCallback = (event: MessageEvent) => {
receiveMessageFromAuthenticationWindow(runtimeScene, event, {
receiveAuthenticationMessage(runtimeScene, event, {
checkOrigin: true,
});
};
@@ -622,10 +740,19 @@ namespace gdjs {
checkIfGameIsRegistered(runtimeScene.getGame(), _gameId)
.then((isGameRegistered) => {
if (_authenticationLoaderContainer) {
const electron = runtimeScene.getGame().getRenderer().getElectron();
const wikiOpenAction = electron
? () =>
electron.shell.openExternal(
'https://wiki.gdevelop.io/gdevelop5/publishing/web'
)
: null; // Only show a link if we're on electron.
_authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(
_authenticationLoaderContainer,
platform,
isGameRegistered
isGameRegistered,
wikiOpenAction
);
}
if (isGameRegistered) {
@@ -652,7 +779,7 @@ namespace gdjs {
runtimeScene,
'Error while checking if the game is registered.'
);
console.error(error);
logger.error(error);
});
};
@@ -669,6 +796,7 @@ namespace gdjs {
export const removeAuthenticationContainer = function (
runtimeScene: gdjs.RuntimeScene
) {
removeAuthenticationCallbacks();
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
@@ -685,6 +813,15 @@ namespace gdjs {
domElementContainer.removeChild(_authenticationRootContainer);
}
_authenticationRootContainer = null;
_authenticationLoaderContainer = null;
_authenticationTextContainer = null;
};
/*
* Remove the authentication callbacks from web or cordova.
*/
const removeAuthenticationCallbacks = function () {
// Remove the authentication callbacks.
if (_authenticationMessageCallback) {
window.removeEventListener(
@@ -696,16 +833,12 @@ namespace gdjs {
// 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 (
export const removeAuthenticationBanner = function (
runtimeScene: gdjs.RuntimeScene
) {
if (!_authenticationBanner) {

View File

@@ -28,9 +28,9 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddObject<ShapePainterObject>(
"Drawer", //"Drawer" is kept for compatibility with GD<=3.6.76
_("Shape painter"),
_("Allows you to draw simple shapes on the screen"),
_("Allows you to draw simple shapes on the screen using the events."),
"CppPlatform/Extensions/primitivedrawingicon.png")
.SetCategoryFullName(_("General"));
.SetCategoryFullName(_("Advanced"));
#if defined(GD_IDE_ONLY)
obj.AddAction(

View File

@@ -9,27 +9,47 @@ This project is released under the MIT License.
#include "GDCore/Tools/Localization.h"
void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
extension.SetExtensionInformation(
"SystemInfo",
_("System information"),
_("Get information about the system and device running the game."),
"Florian Rival",
"Open source (MIT License)")
extension
.SetExtensionInformation(
"SystemInfo",
_("System information"),
_("Get information about the system and device running the game."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Advanced");
extension.AddInstructionOrExpressionGroupMetadata(_("System information"))
.SetIcon("CppPlatform/Extensions/systeminfoicon.png");
extension
.AddCondition(
"IsMobile",
_("Is a mobile device"),
_("Check if the device running the game is a mobile device"),
_("The device is a mobile device"),
"",
"CppPlatform/Extensions/systeminfoicon.png",
"CppPlatform/Extensions/systeminfoicon.png")
extension.AddCondition(
"IsMobile",
_("Is a mobile device"),
_("Check if the device running the game is a mobile device (phone or "
"tablet on iOS, Android or other mobile devices). The game itself "
"might be a web game or distributed as a native mobile app (to check "
"this precisely, use other conditions)."),
_("The device is a mobile device"),
"",
"CppPlatform/Extensions/systeminfoicon.png",
"CppPlatform/Extensions/systeminfoicon.png");
.SetFunctionName("SystemInfo::IsMobile");
extension.AddCondition("IsNativeMobileApp",
_("Is a native mobile app"),
_("Check if the game is running as a native mobile "
"app (iOS or Android app)."),
_("The game is running as a native mobile app"),
"",
"CppPlatform/Extensions/systeminfoicon.png",
"CppPlatform/Extensions/systeminfoicon.png");
extension
.AddCondition("IsNativeDesktopApp",
_("Is a native desktop app"),
_("Check if the game is running as a native desktop app."),
_("The game is running as a native desktop app"),
"",
"CppPlatform/Extensions/systeminfoicon.png",
"CppPlatform/Extensions/systeminfoicon.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddCondition("IsWebGLSupported",

View File

@@ -28,6 +28,14 @@ class SystemInfoJsExtension : public gd::PlatformExtension {
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
.SetFunctionName("gdjs.evtTools.systemInfo.isMobile");
GetAllConditions()["SystemInfo::IsNativeMobileApp"]
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
.SetFunctionName("gdjs.evtTools.systemInfo.isNativeMobileApp");
GetAllConditions()["SystemInfo::IsNativeDesktopApp"]
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
.SetFunctionName("gdjs.evtTools.systemInfo.isNativeDesktopApp");
GetAllConditions()["SystemInfo::IsWebGLSupported"]
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")

View File

@@ -59,6 +59,32 @@ namespace gdjs {
}
return (cachedIsMobile = checkIsMobile());
};
/**
* Check if the game is running as a native mobile app - which in the case
* of an exported GDevelop game means: running packaged inside Cordova/Capacitor.js.
*
* Note: this could be improved to also detect running inside an embedded webview.
*
* @returns true if running inside Cordova (or Capacitor.js).
*/
export const isNativeMobileApp = (): boolean => {
return typeof window !== 'undefined' && (window as any).cordova;
};
/**
* Check if the game is running as a native desktop app - which in the case
* of an exported GDevelop game means: running packaged inside Electron.
*
* @param instanceContainer The current scene.
* @returns true if running inside Electron.
*/
export const isNativeDesktopApp = (
instanceContainer: gdjs.RuntimeInstanceContainer
): boolean => {
return !!instanceContainer.getGame().getRenderer().getElectron();
};
const checkHasTouchScreen = (): boolean => {
// First check if the device is mobile, as all mobile devices have a touchscreen
// and some older browsers don't have support for `navigator.maxTouchPoints`

View File

@@ -9,13 +9,14 @@ This project is released under the MIT License.
#include "GDCore/Tools/Localization.h"
#include "TextEntryObject.h"
// Deprecated extension - so no translation markers and the object is hidden in the editor.
void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
extension
.SetExtensionInformation(
"TextEntryObject",
_("Text entry object"),
_("An object that can be used to capture the text "
"entered with a keyboard by a player."),
"Text entry object",
"Deprecated object that can be used to capture the text "
"entered with a keyboard by a player.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("User interface")
@@ -24,71 +25,72 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
gd::ObjectMetadata& obj =
extension
.AddObject<TextEntryObject>("TextEntry",
_("Text entry"),
_("Invisible object used to get the text "
"entered with the keyboard."),
"Text entry",
"Invisible object used to get the text "
"entered with the keyboard.",
"CppPlatform/Extensions/textentry.png")
.SetCategoryFullName(_("User interface"));
.SetCategoryFullName("User interface")
.SetHidden(); // Deprecated
obj.AddAction("String",
_("Text in memory"),
_("Modify text in memory of the object"),
_("the text in memory"),
"Text in memory",
"Modify text in memory of the object",
"the text in memory",
"",
"CppPlatform/Extensions/textentry24.png",
"CppPlatform/Extensions/textentryicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("object", "Object", "TextEntry")
.UseStandardOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(_("Text")))
gd::ParameterOptions::MakeNewOptions().SetDescription("Text"))
.SetFunctionName("SetString")
.SetGetter("GetString");
obj.AddCondition("String",
_("Text in memory"),
_("Test the text of a Text Entry object."),
_("the text"),
"Text in memory",
"Test the text of a Text Entry object.",
"the text",
"",
"CppPlatform/Extensions/textentry24.png",
"CppPlatform/Extensions/textentryicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("object", "Object", "TextEntry")
.UseStandardRelationalOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(_("Text to compare to")))
gd::ParameterOptions::MakeNewOptions().SetDescription("Text to compare to"))
.SetFunctionName("GetString");
obj.AddAction(
"Activate",
_("De/activate capturing text input"),
_("Activate or deactivate the capture of text entered with keyboard."),
_("Activate capture by _PARAM0_ of the text entered with keyboard: "
"_PARAM1_"),
_("Setup"),
"De/activate capturing text input",
"Activate or deactivate the capture of text entered with keyboard.",
"Activate capture by _PARAM0_ of the text entered with keyboard: "
"_PARAM1_",
"Setup",
"CppPlatform/Extensions/textentry24.png",
"CppPlatform/Extensions/textentryicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("yesorno", _("Activate"))
.AddParameter("object", "Object", "TextEntry")
.AddParameter("yesorno", "Activate")
.SetFunctionName("Activate");
obj.AddCondition("Activated",
_("Text input"),
_("Test if the object captured text entered with keyboard."),
_("_PARAM0_ capture the text entered with keyboard"),
_("Setup"),
"Text input",
"Test if the object captured text entered with keyboard.",
"_PARAM0_ capture the text entered with keyboard",
"Setup",
"CppPlatform/Extensions/textentry24.png",
"CppPlatform/Extensions/textentryicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("object", "Object", "TextEntry")
.SetFunctionName("IsActivated");
obj.AddStrExpression("String",
_("Text entered with keyboard"),
_("Text entered with keyboard"),
_("Text entered with keyboard"),
"Text entered with keyboard",
"Text entered with keyboard",
"Text entered with keyboard",
"res/texteicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("object", "Object", "TextEntry")
.SetFunctionName("GetString");
}

View File

@@ -25,16 +25,18 @@ module.exports = {
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'TextInput',
_('Text Input'),
_('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");
extension
.setExtensionInformation(
'TextInput',
_('Text Input'),
_('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
@@ -275,7 +277,7 @@ module.exports = {
const object = extension
.addObject(
'TextInputObject',
_('Text input (experimental)'),
_('Text input'),
_('A text field the player can type text into.'),
'JsPlatform/Extensions/text_input.svg',
textInputObject
@@ -500,7 +502,10 @@ module.exports = {
'res/conditions/text24_black.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.useStandardParameters('boolean', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'boolean',
gd.ParameterOptions.makeNewOptions().setDescription(_('Read-only?'))
)
.setFunctionName('setReadOnly')
.setGetter('isReadOnly');
@@ -645,8 +650,7 @@ module.exports = {
update() {
const instance = this._instance;
const properties = this._associatedObjectConfiguration
.getProperties();
const properties = this._associatedObjectConfiguration.getProperties();
const placeholder =
instance.getRawStringProperty('placeholder') ||

View File

@@ -23,7 +23,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"some indicators, menu buttons, dialogues..."),
"Florian Rival and Victor Levasseur",
"Open source (MIT License)")
.SetCategory("User interface")
.SetCategory("Text")
.SetExtensionHelpPath("/objects/text");
extension.AddInstructionOrExpressionGroupMetadata(_("Text object"))
.SetIcon("CppPlatform/Extensions/texticon.png");
@@ -34,7 +34,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Text"),
_("Displays a text on the screen."),
"CppPlatform/Extensions/texticon.png")
.SetCategoryFullName(_("User interface"));
.SetCategoryFullName(_("Text"));
obj.AddAction("String",
_("Modify the text"),
@@ -87,7 +87,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale to compare to (1 by default)")))
@@ -103,7 +103,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))

View File

@@ -71,7 +71,8 @@ void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("underlined", underlined);
}
void TextObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
void TextObject::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
worker.ExposeFont(fontName);
}
#endif

View File

@@ -25,9 +25,7 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
return gd::make_unique<TextObject>(*this);
}
#if defined(GD_IDE_ONLY)
virtual void ExposeResources(gd::ArbitraryResourceWorker& worker);
#endif
/** \brief Change the text.
*/

View File

@@ -25,8 +25,8 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
const defineTileMap = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */) {
gd /*: libGDevelop */
) {
var objectTileMap = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.updateProperty = function (
@@ -54,6 +54,10 @@ const defineTileMap = function (
objectContent.layerIndex = parseFloat(newValue);
return true;
}
if (propertyName === 'levelIndex') {
objectContent.levelIndex = parseFloat(newValue);
return true;
}
if (propertyName === 'animationSpeedScale') {
objectContent.animationSpeedScale = parseFloat(newValue);
return true;
@@ -73,25 +77,27 @@ const defineTileMap = function (
'tilemapJsonFile',
new gd.PropertyDescriptor(objectContent.tilemapJsonFile)
.setType('resource')
.addExtraInfo('tilemap')
.addExtraInfo('json')
.setLabel(_('Tilemap JSON file'))
.setLabel(_('Tilemap file (Tiled or LDtk)'))
.setDescription(
_('This is the JSON file that was saved or exported from Tiled.')
_('This is the file that was saved or exported from Tiled/LDtk.')
)
.setGroup(_('Tilemap and tileset'))
.setGroup(_('LDtk and Tiled: Tilemap'))
);
objectProperties.set(
'tilesetJsonFile',
new gd.PropertyDescriptor(objectContent.tilesetJsonFile || '')
.setType('resource')
.addExtraInfo('tileset')
.addExtraInfo('json')
.setLabel(_('Tileset JSON file (optional)'))
.setDescription(
_(
"Optional, don't specify it if you've not saved the tileset in a different file."
"Tiled only - not useful for LDtk files. Optional: specify this if you've saved the tileset in a different file as the Tiled tilemap."
)
)
.setGroup(_('Tilemap and tileset'))
.setGroup(_('Tiled only: Tileset and Atlas image'))
);
objectProperties.set(
'tilemapAtlasImage',
@@ -99,7 +105,12 @@ const defineTileMap = function (
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Atlas image'))
.setGroup(_('Tilemap and tileset'))
.setDescription(
_(
'Tiled only - not useful for LDtk files. The Atlas image containing the tileset.'
)
)
.setGroup(_('Tiled only: Tileset and Atlas image'))
);
objectProperties.set(
'displayMode',
@@ -123,6 +134,14 @@ const defineTileMap = function (
)
.setGroup(_('Appearance'))
);
objectProperties.set(
'levelIndex',
new gd.PropertyDescriptor((objectContent.levelIndex || 0).toString())
.setType('number')
.setLabel(_('Level index to display'))
.setDescription(_('Select which level to render via its index (LDtk)'))
.setGroup(_('Appearance'))
);
objectProperties.set(
'animationSpeedScale',
new gd.PropertyDescriptor(objectContent.animationSpeedScale.toString())
@@ -147,6 +166,7 @@ const defineTileMap = function (
tilemapAtlasImage: '',
displayMode: 'visible',
layerIndex: 0,
levelIndex: 0,
animationSpeedScale: 1,
animationFps: 4,
})
@@ -179,7 +199,7 @@ const defineTileMap = function (
'TileMap',
_('Tilemap'),
_(
'Displays a tiled-based map, made with the Tiled editor (download it separately on https://www.mapeditor.org/).'
'Displays a tiled-based map, made with the Tiled editor (https://www.mapeditor.org/) or the LDtk editor (https://ldtk.io/).'
),
'JsPlatform/Extensions/tile_map.svg',
objectTileMap
@@ -187,44 +207,50 @@ const defineTileMap = function (
.setCategoryFullName(_('Advanced'))
.setIncludeFile('Extensions/TileMap/tilemapruntimeobject.js')
.addIncludeFile('Extensions/TileMap/TileMapRuntimeManager.js')
.addIncludeFile(
'Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js'
)
.addIncludeFile(
'Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.umd.js'
)
.addIncludeFile('Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js')
.addIncludeFile('Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.umd.js')
.addIncludeFile('Extensions/TileMap/pako/dist/pako.min.js')
.addIncludeFile('Extensions/TileMap/helper/TileMapHelper.js');
object
.addCondition(
'TilemapJsonFile',
_('Tilemap JSON file'),
_('Check the Tilemap JSON file being used.'),
_('The Tilemap JSON file of _PARAM0_ is _PARAM1_'),
_('Tilemap file (Tiled or LDtk)'),
_('Check the tilemap file (Tiled or LDtk) being used.'),
_('The tilemap file of _PARAM0_ is _PARAM1_'),
'',
'JsPlatform/Extensions/tile_map.svg',
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
.addParameter(
'tilemapResource',
_('Tilemap file (Tiled or LDtk)'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('isTilemapJsonFile');
object
.addAction(
'SetTilemapJsonFile',
_('Tilemap JSON file'),
_('Tilemap file (Tiled or LDtk)'),
_(
'Set the JSON file containing the Tilemap data to display. This is usually the JSON file exported from Tiled.'
'Set the Tiled or LDtk file containing the Tilemap data to display. This is usually the main file exported from Tiled/LDtk.'
),
_('Set the Tilemap JSON file of _PARAM0_ to _PARAM1_'),
_('Set the tilemape file of _PARAM0_ to _PARAM1_'),
'',
'JsPlatform/Extensions/tile_map.svg',
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
.addParameter(
'tilemapResource',
_('Tilemap file (Tiled or LDtk)'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setTilemapJsonFile');
@@ -239,7 +265,7 @@ const defineTileMap = function (
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
.addParameter('tilesetResource', _('Tileset JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('isTilesetJsonFile');
@@ -256,7 +282,7 @@ const defineTileMap = function (
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
.addParameter('tilesetResource', _('Tileset JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('setTilesetJsonFile');
@@ -349,6 +375,20 @@ const defineTileMap = function (
.getCodeExtraInformation()
.setFunctionName('getLayerIndex');
object
.addExpressionAndCondition(
'number',
'LevelIndex',
_('Level index'),
_('the level index being displayed.'),
_('the level index'),
'',
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('getLevelndex');
object
.addCondition(
'AnimationSpeedScale',
@@ -508,22 +548,25 @@ const defineTileMap = function (
'res/actions/scaleHeight24_black.png'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions().setDescription(
_('Scale (1 by default)')
))
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Scale (1 by default)')
)
)
.markAsAdvanced()
.setFunctionName('setScaleY')
.setGetter('getScaleY');
object
.addAction(
"Width",
_("Width"),
_("Change the width of an object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png"
'Width',
_('Width'),
_('Change the width of an object.'),
_('the width'),
_('Size'),
'res/actions/scaleWidth24_black.png',
'res/actions/scaleWidth_black.png'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardOperatorParameters(
@@ -536,13 +579,13 @@ const defineTileMap = function (
object
.addAction(
"Height",
_("Height"),
_("Change the height of an object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png"
'Height',
_('Height'),
_('Change the height of an object.'),
_('the height'),
_('Size'),
'res/actions/scaleHeight24_black.png',
'res/actions/scaleHeight_black.png'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardOperatorParameters(
@@ -557,8 +600,8 @@ const defineTileMap = function (
const defineCollisionMask = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */) {
gd /*: libGDevelop */
) {
var collisionMaskObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.updateProperty = function (
@@ -616,7 +659,9 @@ const defineCollisionMask = function (
.addExtraInfo('json')
.setLabel(_('Tilemap JSON file'))
.setDescription(
_('This is the JSON file that was saved or exported from Tiled.')
_(
'This is the JSON file that was saved or exported from Tiled. LDtk is not supported yet for collisions.'
)
)
);
objectProperties.set(
@@ -659,13 +704,21 @@ const defineCollisionMask = function (
);
objectProperties.set(
'outlineOpacity',
new gd.PropertyDescriptor(objectContent.outlineOpacity === undefined ? '64' : objectContent.outlineOpacity.toString())
new gd.PropertyDescriptor(
objectContent.outlineOpacity === undefined
? '64'
: objectContent.outlineOpacity.toString()
)
.setType('number')
.setLabel(_('Outline opacity (0-255)'))
);
objectProperties.set(
'outlineSize',
new gd.PropertyDescriptor(objectContent.outlineSize === undefined ? '1' : objectContent.outlineSize.toString())
new gd.PropertyDescriptor(
objectContent.outlineSize === undefined
? '1'
: objectContent.outlineSize.toString()
)
.setType('number')
.setLabel(_('Outline size (in pixels)'))
);
@@ -677,7 +730,11 @@ const defineCollisionMask = function (
);
objectProperties.set(
'fillOpacity',
new gd.PropertyDescriptor(objectContent.fillOpacity === undefined ? '32' : objectContent.fillOpacity.toString())
new gd.PropertyDescriptor(
objectContent.fillOpacity === undefined
? '32'
: objectContent.fillOpacity.toString()
)
.setType('number')
.setLabel(_('Fill opacity (0-255)'))
);
@@ -723,7 +780,7 @@ const defineCollisionMask = function (
const object = extension
.addObject(
'CollisionMask',
_('Tilemap collision mask (experimental)'),
_('Tilemap collision mask'),
_('Invisible object handling collisions with parts of a tilemap.'),
'JsPlatform/Extensions/tile_map_collision_mask32.svg',
collisionMaskObject
@@ -748,7 +805,12 @@ const defineCollisionMask = function (
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.addParameter(
'object',
_('Tile map collision mask'),
'CollisionMask',
false
)
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('isTilemapJsonFile');
@@ -765,7 +827,12 @@ const defineCollisionMask = function (
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.addParameter(
'object',
_('Tile map collision mask'),
'CollisionMask',
false
)
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('setTilemapJsonFile');
@@ -780,7 +847,12 @@ const defineCollisionMask = function (
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.addParameter(
'object',
_('Tile map collision mask'),
'CollisionMask',
false
)
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('isTilesetJsonFile');
@@ -797,7 +869,12 @@ const defineCollisionMask = function (
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
)
.addParameter('object', _('Tile map collision mask'), 'CollisionMask', false)
.addParameter(
'object',
_('Tile map collision mask'),
'CollisionMask',
false
)
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
.getCodeExtraInformation()
.setFunctionName('setTilesetJsonFile');
@@ -935,6 +1012,7 @@ module.exports = {
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'TileMap',
@@ -945,11 +1023,13 @@ module.exports = {
)
.setCategory('Advanced')
.setExtensionHelpPath('/objects/tilemap');
extension.addInstructionOrExpressionGroupMetadata(_("Tilemap"))
.setIcon("JsPlatform/Extensions/tile_map.svg");
defineTileMap(extension, _, gd);
defineCollisionMask(extension, _, gd);
extension
.addInstructionOrExpressionGroupMetadata(_('Tilemap'))
.setIcon('JsPlatform/Extensions/tile_map.svg');
defineTileMap(extension, _, gd);
defineCollisionMask(extension, _, gd);
return extension;
},
@@ -1075,8 +1155,9 @@ module.exports = {
);
RenderedTileMapInstance.prototype.onLoadingError = function () {
this.errorPixiObject = this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this.errorPixiObject =
this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this._pixiContainer.addChild(this.errorPixiObject);
this._pixiObject = this.errorPixiObject;
};
@@ -1124,33 +1205,59 @@ module.exports = {
.getValue(),
10
);
const levelIndex = parseInt(
this._associatedObjectConfiguration
.getProperties(this.project)
.get('levelIndex')
.getValue(),
10
);
const displayMode = this._associatedObjectConfiguration
.getProperties(this.project)
.get('displayMode')
.getValue();
const tilemapResource = this._project
.getResourcesManager()
.getResource(tilemapJsonFile);
let metadata = {};
try {
const tilemapMetadataAsString = tilemapResource.getMetadata();
if (tilemapMetadataAsString)
metadata = JSON.parse(tilemapMetadataAsString);
} catch (error) {
console.warn('Malformed metadata in a tilemap object:', error);
}
const mapping = metadata.embeddedResourcesMapping || {};
/** @type {TileMapHelper.TileMapManager} */
const manager = TilemapHelper.TileMapManager.getManager(this._project);
manager.getOrLoadTileMap(
this._loadTiledMapWithCallback.bind(this),
this._loadTileMapWithCallback.bind(this),
tilemapJsonFile,
tilesetJsonFile,
levelIndex,
pako,
(tileMap) => {
if (!tileMap) {
this.onLoadingError();
// _loadTiledMapWithCallback already log errors
// _loadTileMapWithCallback already log errors
return;
}
/** @type {TileMapHelper.TileTextureCache} */
const textureCache = manager.getOrLoadTextureCache(
this._loadTiledMapWithCallback.bind(this),
this._loadTileMapWithCallback.bind(this),
(textureName) =>
this._pixiResourcesLoader.getPIXITexture(this._project, textureName),
this._pixiResourcesLoader.getPIXITexture(
this._project,
mapping[textureName] || textureName
),
tilemapAtlasImage,
tilemapJsonFile,
tilesetJsonFile,
levelIndex,
(textureCache) => {
if (!textureCache) {
this.onLoadingError();
@@ -1175,39 +1282,44 @@ module.exports = {
};
// GDJS doesn't use Promise to avoid allocation.
RenderedTileMapInstance.prototype._loadTiledMapWithCallback = function (
RenderedTileMapInstance.prototype._loadTileMapWithCallback = function (
tilemapJsonFile,
tilesetJsonFile,
callback
) {
this._loadTiledMap(tilemapJsonFile, tilesetJsonFile).then(callback);
this._loadTileMap(tilemapJsonFile, tilesetJsonFile).then(callback);
};
RenderedTileMapInstance.prototype._loadTiledMap = async function (
RenderedTileMapInstance.prototype._loadTileMap = async function (
tilemapJsonFile,
tilesetJsonFile) {
let tileMapJsonData = null;
tilesetJsonFile
) {
try {
tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tilesetJsonData = tilesetJsonFile
? await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilesetJsonFile
)
: null;
const tileMap = TilemapHelper.TileMapManager.identify(tileMapJsonData);
if (tilesetJsonData) {
tileMapJsonData.tilesets = [tilesetJsonData];
if (tileMap.kind === 'tiled') {
const tilesetJsonData = tilesetJsonFile
? await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilesetJsonFile
)
: null;
if (tilesetJsonData) {
tileMapJsonData.tilesets = [tilesetJsonData];
}
}
} catch (err) {
console.error('Unable to load a Tilemap JSON data: ', err);
}
return tileMapJsonData;
return tileMap;
} catch (err) {
console.error('Unable to load a Tilemap JSON data: ', err);
}
return null;
};
/**
@@ -1216,7 +1328,8 @@ module.exports = {
RenderedTileMapInstance.prototype.update = function () {
if (this._instance.hasCustomSize()) {
this._pixiObject.scale.x = this._instance.getCustomWidth() / this.width;
this._pixiObject.scale.y = this._instance.getCustomHeight() / this.height;
this._pixiObject.scale.y =
this._instance.getCustomHeight() / this.height;
} else {
this._pixiObject.scale.x = 1;
this._pixiObject.scale.y = 1;
@@ -1233,8 +1346,12 @@ module.exports = {
// Modifying the pivot position also has an impact on the transform. The instance (X,Y) position
// of this object refers to the top-left point, but now in Pixi, as we changed the pivot, the Pixi
// object (X,Y) position refers to the center. So we add an offset to convert from top-left to center.
this._pixiObject.x = this._instance.getX() + this._pixiObject.pivot.x * this._pixiObject.scale.x;
this._pixiObject.y = this._instance.getY() + this._pixiObject.pivot.y * this._pixiObject.scale.y;
this._pixiObject.x =
this._instance.getX() +
this._pixiObject.pivot.x * this._pixiObject.scale.x;
this._pixiObject.y =
this._instance.getY() +
this._pixiObject.pivot.y * this._pixiObject.scale.y;
// Rotation works as intended because we put the pivot in the center
this._pixiObject.rotation = RenderedInstance.toRad(
@@ -1268,7 +1385,7 @@ module.exports = {
* @class RenderedTileMapInstance
* @constructor
*/
function RenderedCollisionMaskInstance(
function RenderedCollisionMaskInstance(
project,
layout,
instance,
@@ -1317,8 +1434,9 @@ module.exports = {
);
RenderedCollisionMaskInstance.prototype.onLoadingError = function () {
this.errorPixiObject = this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this.errorPixiObject =
this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this._pixiContainer.addChild(this.errorPixiObject);
this._pixiObject = this.errorPixiObject;
};
@@ -1334,7 +1452,7 @@ module.exports = {
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedCollisionMaskInstance.getThumbnail = function (
RenderedCollisionMaskInstance.getThumbnail = function (
project,
resourcesLoader,
objectConfiguration
@@ -1345,45 +1463,47 @@ module.exports = {
/**
* This is used to reload the Tilemap
*/
RenderedCollisionMaskInstance.prototype.updateTileMap = function () {
RenderedCollisionMaskInstance.prototype.updateTileMap = function () {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const collisionMaskTag = this._associatedObjectConfiguration
.getProperties(this.project)
.get('collisionMaskTag')
.getValue();
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineColor')
.getValue()
);
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillColor')
.getValue()
);
const outlineOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineOpacity')
.getValue() / 255;
const fillOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillOpacity')
.getValue() / 255;
const outlineSize = 1;
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const collisionMaskTag = this._associatedObjectConfiguration
.getProperties(this.project)
.get('collisionMaskTag')
.getValue();
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineColor')
.getValue()
);
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillColor')
.getValue()
);
const outlineOpacity =
this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineOpacity')
.getValue() / 255;
const fillOpacity =
this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillOpacity')
.getValue() / 255;
const outlineSize = 1;
/** @type {TileMapHelper.TileMapManager} */
const manager = TilemapHelper.TileMapManager.getManager(this._project);
@@ -1391,6 +1511,7 @@ module.exports = {
this._loadTiledMapWithCallback.bind(this),
tilemapJsonFile,
tilesetJsonFile,
0, // leveIndex
pako,
(tileMap) => {
if (!tileMap) {
@@ -1412,78 +1533,55 @@ module.exports = {
fillColor,
fillOpacity
);
// const textureCache = manager.getOrLoadTextureCache(
// this._loadTiledMapWithCallback.bind(this),
// (textureName) =>
// this._pixiResourcesLoader.getPIXITexture(this._project, textureName),
// tilemapAtlasImage,
// tilemapJsonFile,
// tilesetJsonFile,
// (textureCache) => {
// if (!textureCache) {
// return;
// }
// let tileId = -1;
// for (const definition of tileMap.getTileDefinitions()) {
// if (definition.getTag() === collisionMaskTag) {
// tileId = definition.getTag();
// }
// }
// if (tileId >= 0) {
// const texture = textureCache.findTileTexture(tileId, false, false, false);
// // TODO set the thumbnail from this texture
// }
// }
// );
}
);
};
// GDJS doesn't use Promise to avoid allocation.
RenderedCollisionMaskInstance.prototype._loadTiledMapWithCallback = function (
RenderedCollisionMaskInstance.prototype._loadTiledMapWithCallback =
function (tilemapJsonFile, tilesetJsonFile, callback) {
this._loadTileMap(tilemapJsonFile, tilesetJsonFile).then(callback);
};
RenderedCollisionMaskInstance.prototype._loadTileMap = async function (
tilemapJsonFile,
tilesetJsonFile,
callback
tilesetJsonFile
) {
this._loadTiledMap(tilemapJsonFile, tilesetJsonFile).then(callback);
};
RenderedCollisionMaskInstance.prototype._loadTiledMap = async function (
tilemapJsonFile,
tilesetJsonFile) {
let tileMapJsonData = null;
try {
tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tilesetJsonData = tilesetJsonFile
? await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilesetJsonFile
)
: null;
const tileMap = TilemapHelper.TileMapManager.identify(tileMapJsonData);
if (tilesetJsonData) {
tileMapJsonData.tilesets = [tilesetJsonData];
if (tileMap.kind === 'tiled') {
const tilesetJsonData = tilesetJsonFile
? await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilesetJsonFile
)
: null;
if (tilesetJsonData) {
tileMapJsonData.tilesets = [tilesetJsonData];
}
}
} catch (err) {
console.error('Unable to load a Tilemap JSON data: ', err);
}
return tileMapJsonData;
return tileMap;
} catch (err) {
console.error('Unable to load a Tilemap JSON data: ', err);
}
return null;
};
/**
* This is called to update the PIXI object on the scene editor
*/
RenderedCollisionMaskInstance.prototype.update = function () {
RenderedCollisionMaskInstance.prototype.update = function () {
if (this._instance.hasCustomSize()) {
this._pixiObject.scale.x = this._instance.getCustomWidth() / this.width;
this._pixiObject.scale.y = this._instance.getCustomHeight() / this.height;
this._pixiObject.scale.y =
this._instance.getCustomHeight() / this.height;
} else {
this._pixiObject.scale.x = 1;
this._pixiObject.scale.y = 1;
@@ -1500,8 +1598,12 @@ module.exports = {
// Modifying the pivot position also has an impact on the transform. The instance (X,Y) position
// of this object refers to the top-left point, but now in Pixi, as we changed the pivot, the Pixi
// object (X,Y) position refers to the center. So we add an offset to convert from top-left to center.
this._pixiObject.x = this._instance.getX() + this._pixiObject.pivot.x * this._pixiObject.scale.x;
this._pixiObject.y = this._instance.getY() + this._pixiObject.pivot.y * this._pixiObject.scale.y;
this._pixiObject.x =
this._instance.getX() +
this._pixiObject.pivot.x * this._pixiObject.scale.x;
this._pixiObject.y =
this._instance.getY() +
this._pixiObject.pivot.y * this._pixiObject.scale.y;
// Rotation works as intended because we put the pivot in the center
this._pixiObject.rotation = RenderedInstance.toRad(
@@ -1512,14 +1614,14 @@ module.exports = {
/**
* Return the width of the instance, when it's not resized.
*/
RenderedCollisionMaskInstance.prototype.getDefaultWidth = function () {
RenderedCollisionMaskInstance.prototype.getDefaultWidth = function () {
return this.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedCollisionMaskInstance.prototype.getDefaultHeight = function () {
RenderedCollisionMaskInstance.prototype.getDefaultHeight = function () {
return this.height;
};

View File

@@ -60,17 +60,22 @@ namespace gdjs {
/**
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param levelIndex The level of the tile map.
* @param callback A function called when the tile map is parsed.
*/
getOrLoadTileMap(
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tileMap: TileMapHelper.EditableTileMap | null) => void
levelIndex: number,
callback: (
tileMapFileContent: TileMapHelper.EditableTileMap | null
) => void
): void {
this._manager.getOrLoadTileMap(
this._loadTiledMap.bind(this),
this._loadTileMap.bind(this),
tileMapJsonResourceName,
tileSetJsonResourceName,
levelIndex,
pako,
callback
);
@@ -81,6 +86,7 @@ namespace gdjs {
* @param atlasImageResourceName The resource name of the atlas image.
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param levelIndex The level of the tile map.
* @param callback A function called when the tiles textures are split.
*/
getOrLoadTextureCache(
@@ -88,14 +94,16 @@ namespace gdjs {
atlasImageResourceName: string,
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
levelIndex: number,
callback: (textureCache: TileMapHelper.TileTextureCache | null) => void
): void {
this._manager.getOrLoadTextureCache(
this._loadTiledMap.bind(this),
this._loadTileMap.bind(this),
getTexture,
atlasImageResourceName,
tileMapJsonResourceName,
tileSetJsonResourceName,
levelIndex,
callback
);
}
@@ -104,10 +112,12 @@ namespace gdjs {
* Parse both JSON and set the content of the tile set in the right
* attribute in the tile map to merge both parsed data.
*/
private _loadTiledMap(
private _loadTileMap(
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tiledMap: TileMapHelper.TiledMap | null) => void
callback: (
tileMapFileContent: TileMapHelper.TileMapFileContent | null
) => void
): void {
this._instanceContainer
.getGame()
@@ -121,8 +131,17 @@ namespace gdjs {
callback(null);
return;
}
const tiledMap = tileMapJsonData as TileMapHelper.TiledMap;
if (tileSetJsonResourceName) {
const tileMapFileContent = TileMapHelper.TileMapManager.identify(
tileMapJsonData
);
if (!tileMapFileContent) {
callback(null);
return;
}
if (
tileMapFileContent.kind === 'tiled' &&
tileSetJsonResourceName
) {
this._instanceContainer
.getGame()
.getJsonManager()
@@ -135,13 +154,14 @@ namespace gdjs {
callback(null);
return;
}
const tiledMap = tileMapFileContent.data;
const tileSet = tileSetJsonData as TileMapHelper.TiledTileset;
tileSet.firstgid = tiledMap.tilesets[0].firstgid;
tiledMap.tilesets = [tileSet];
callback(tiledMap);
callback(tileMapFileContent);
});
} else {
callback(tiledMap);
callback(tileMapFileContent);
}
});
}

View File

@@ -580,7 +580,7 @@ namespace gdjs {
if (!definition) {
continue;
}
if (definition.hasTag(this.tag)) {
if (definition.hasTaggedHitBox(this.tag)) {
polygonItr = tile.getHitboxes()[Symbol.iterator]();
listNext = polygonItr.next();
}
@@ -661,7 +661,7 @@ namespace gdjs {
*/
getDefinition(): TileMapHelper.TileDefinition {
return this.layer.tileMap.getTileDefinition(
this.layer._source.get(this.x, this.y)!
this.layer._source.getTileId(this.x, this.y)!
)!;
}

View File

@@ -1,23 +1,25 @@
import {
EditableTileMap,
EditableTileMapLayer,
PixiTileMapHelper,
TileDefinition,
TiledMap,
TiledTileset,
TileMapFileContent,
TileMapManager,
TileTextureCache,
PixiTileMapHelper,
TiledTileset,
} from './dts/index';
declare global {
namespace TileMapHelper {
export { EditableTileMap };
export { EditableTileMapLayer };
export { TileDefinition };
export { TiledMap };
export { TiledTileset };
export { TileMapManager };
export { TileTextureCache };
export { PixiTileMapHelper };
export {
EditableTileMap,
EditableTileMapLayer,
PixiTileMapHelper,
TileDefinition,
TileMapFileContent,
TileMapManager,
TileTextureCache,
TiledTileset,
};
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,22 +2,15 @@
* @packageDocumentation
* @module TileMapHelper
*/
import { TiledMap, TiledTileset } from './tiled/TiledFormat';
import {
export {
EditableTileMap,
EditableTileMapLayer,
TileDefinition,
} from './model/TileMapModel';
import { TileMapManager } from './render/TileMapManager';
import { TileTextureCache } from './render/TileTextureCache';
import { PixiTileMapHelper } from './render/TileMapPixiHelper';
export { TileMapManager } from './render/TileMapManager';
export { TileTextureCache } from './render/TileTextureCache';
export { PixiTileMapHelper } from './render/TileMapPixiHelper';
export * from './load/TileMapFileContent';
export * from './model/CommonTypes';
export { EditableTileMap };
export { EditableTileMapLayer };
export { TileDefinition };
export { TiledMap };
export { TiledTileset };
export { TileMapManager };
export { TileTextureCache };
export { PixiTileMapHelper };
export { TiledTileset } from './load/tiled/TiledFormat';
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC"}

View File

@@ -0,0 +1,12 @@
import { LDtkTileMap } from '../load/ldtk/LDtkFormat';
import { TiledTileMap } from '../load/tiled/TiledFormat';
export declare type TileMapFileContent =
| {
kind: 'tiled';
data: TiledTileMap;
}
| {
kind: 'ldtk';
data: LDtkTileMap;
};
//# sourceMappingURL=TileMapFileContent.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileMapFileContent.d.ts","sourceRoot":"","sources":["../../src/load/TileMapFileContent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,oBAAY,kBAAkB,GAC1B;IACE,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;CACpB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC"}

View File

@@ -0,0 +1,18 @@
import type { EditableTileMap } from '../model/TileMapModel';
import { TileMapFileContent } from './TileMapFileContent';
export declare namespace TileMapLoader {
/**
* Create a {@link EditableTileMap} from the raw data.
*
* @param tileMapFileContent The data exported from Tiled/LDtk.
* @param levelIndex The level of the tile map to load from.
* @param pako The zlib library.
* @returns A {@link EditableTileMap}
*/
function load(
tileMapFileContent: TileMapFileContent,
levelIndex: number,
pako: any
): EditableTileMap | null;
}
//# sourceMappingURL=TileMapLoader.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileMapLoader.d.ts","sourceRoot":"","sources":["../../src/load/TileMapLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI1D,yBAAiB,aAAa,CAAC;IAC7B;;;;;;;OAOG;IACH,SAAgB,IAAI,CAClB,kBAAkB,EAAE,kBAAkB,EACtC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,GACR,eAAe,GAAG,IAAI,CAaxB;CACF"}

View File

@@ -0,0 +1,589 @@
import { integer } from '../../model/CommonTypes';
/**
* version 1.1.3 - https://github.com/deepnight/ldtk/blob/66fff7199932357f3ab9b044c2fc2a856f527831/docs/JSON_SCHEMA.json
*/
export declare type LDtkTileMap = {
/** LDtk application build identifier.<br/> This is only used to identify the LDtk version that generated this particular project file, which can be useful for specific bug fixing. Note that the build identifier is just the date of the release, so it's not unique to each user (one single global ID per LDtk public release), and as a result, completely anonymous. */
appBuildId: number;
/** Number of backup files to keep, if the `backupOnSave` is TRUE */
backupLimit: integer;
/** If TRUE, an extra copy of the project will be created in a sub folder, when saving. */
backupOnSave: boolean;
/** Project background color */
bgColor: string;
/** Default grid size for new layers */
defaultGridSize: integer;
/** Default background color of levels */
defaultLevelBgColor: string;
/** **WARNING**: this field will move to the `worlds` array after the \"multi-worlds\" update. It will then be `null`. You can enable the Multi-worlds advanced project option to enable the change immediately.<br/><br/> Default new level height */
defaultLevelHeight: integer | null;
/** **WARNING**: this field will move to the `worlds` array after the \"multi-worlds\" update. It will then be `null`. You can enable the Multi-worlds advanced project option to enable the change immediately.<br/><br/> Default new level width */
defaultLevelWidth: integer | null;
/** Default X pivot (0 to 1) for new entities */
defaultPivotX: number;
/** Default Y pivot (0 to 1) for new entities */
defaultPivotY: number;
/** A structure containing all the definitions of this project */
defs: LDtkDefinition;
/** **WARNING**: this deprecated value is no longer exported since version 0.9.3 Replaced by: `imageExportMode` */
exportPng: boolean | null;
/** If TRUE, a Tiled compatible file will also be generated along with the LDtk JSON file (default is FALSE) */
exportTiled: boolean;
/** If TRUE, one file will be saved for the project (incl. all its definitions) and one file in a sub-folder for each level. */
externalLevels: boolean;
/** An array containing various advanced flags (ie. options or other states). Possible values: `DiscardPreCsvIntGrid`, `ExportPreCsvIntGridFormat`, `IgnoreBackupSuggest`, `PrependIndexToLevelFileNames`, `MultiWorlds`, `UseMultilinesType` */
flags: LDtkFlag[];
/** Naming convention for Identifiers (first-letter uppercase, full uppercase etc.) Possible values: `Capitalize`, `Uppercase`, `Lowercase`, `Free` */
identifierStyle: 'Capitalize' | 'Uppercase' | 'Lowercase' | 'Free';
/** \"Image export\" option when saving project. Possible values: `None`, `OneImagePerLayer`, `OneImagePerLevel`, `LayersAndLevels` */
imageExportMode:
| 'None'
| 'OneImagePerLayer'
| 'OneImagePerLevel'
| 'LayersAndLevels';
/** File format version */
jsonVersion: string;
/** The default naming convention for level identifiers. */
levelNamePattern: string;
/** All levels. The order of this array is only relevant in `LinearHorizontal` and `linearVertical` world layouts (see `worldLayout` value).<br/> Otherwise, you should refer to the `worldX`,`worldY` coordinates of each Level. */
levels: LDtkLevel[];
/** If TRUE, the Json is partially minified (no indentation, nor line breaks, default is FALSE) */
minifyJson: boolean;
/** Next Unique integer ID available */
nextUid: integer;
/** File naming pattern for exported PNGs */
pngFilePattern: string | null;
/** If TRUE, a very simplified will be generated on saving, for quicker & easier engine integration. */
simplifiedExport: boolean;
/** This optional description is used by LDtk Samples to show up some informations and instructions. */
tutorialDesc: string | null;
/** This array is not used yet in current LDtk version (so, for now, it's always empty).<br/><br/>In a later update, it will be possible to have multiple Worlds in a single project, each containing multiple Levels.<br/><br/>What will change when \"Multiple worlds\" support will be added to LDtk:<br/><br/> - in current version, a LDtk project file can only contain a single world with multiple levels in it. In this case, levels and world layout related settings are stored in the root of the JSON.<br/> - after the \"Multiple worlds\" update, there will be a `worlds` array in root, each world containing levels and layout settings. Basically, it's pretty much only about moving the `levels` array to the `worlds` array, along with world layout related values (eg. `worldGridWidth` etc).<br/><br/>If you want to start supporting this future update easily, please refer to this documentation: https://github.com/deepnight/ldtk/issues/231 */
worlds: LDtkWorld[];
/** **WARNING**: this field will move to the `worlds` array after the \"multi-worlds\" update. It will then be `null`. You can enable the Multi-worlds advanced project option to enable the change immediately.<br/><br/> Height of the world grid in pixels. */
worldGridHeight: integer | null;
/** **WARNING**: this field will move to the `worlds` array after the \"multi-worlds\" update. It will then be `null`. You can enable the Multi-worlds advanced project option to enable the change immediately.<br/><br/> Width of the world grid in pixels. */
worldGridWidth: integer | null;
/** **WARNING**: this field will move to the `worlds` array after the \"multi-worlds\" update. It will then be `null`. You can enable the Multi-worlds advanced project option to enable the change immediately.<br/><br/> An enum that describes how levels are organized in this project (ie. linearly or in a 2D space). Possible values: &lt;`null`&gt;, `Free`, `GridVania`, `LinearHorizontal`, `LinearVertical` */
worldLayout:
| 'Free'
| 'GridVania'
| 'LinearHorizontal'
| 'LinearVertical'
| null;
};
/** Auto-layer rule group */
declare type LDtkAutoLayerRuleGroup = {
/** */
active: boolean;
/** *This field was removed in 1.0.0 and should no longer be used.* */
collapsed: boolean | null;
/** */
isOptional: boolean;
/** */
name: string;
/** */
rules: LDtkAutoRuleDef[];
/** */
uid: integer;
};
/** This complex section isn't meant to be used by game devs at all, as these rules are completely resolved internally by the editor before any saving. You should just ignore this part. */
declare type LDtkAutoRuleDef = {};
/** If you're writing your own LDtk importer, you should probably just ignore *most* stuff in the `defs` section, as it contains data that are mostly important to the editor. To keep you away from the `defs` section and avoid some unnecessary JSON parsing, important data from definitions is often duplicated in fields prefixed with a double underscore (eg. `__identifier` or `__type`). The 2 only definition types you might need here are **Tilesets** and **Enums**. */
declare type LDtkDefinition = {
/** All entities definitions, including their custom fields */
entities: LDtkEntityDef[];
/** All internal enums */
enums: LDtkEnumDef[];
/** Note: external enums are exactly the same as `enums`, except they have a `relPath` to point to an external source file. */
externalEnums: LDtkEnumDef[];
/** All layer definitions */
layers: LDtkLayerDef[];
/** All custom fields available to all levels. */
levelFields: LDtkFieldDef[];
/** All tilesets */
tilesets: LDtkTilesetDef[];
};
/** Entity definition */
declare type LDtkEntityDef = {
/** Base entity color */
color: string;
/** Array of field definitions */
fieldDefs: LDtkFieldDef[];
/** */
fillOpacity: number;
/** Pixel height */
height: integer;
/** */
hollow: boolean;
/** User defined unique identifier */
identifier: string;
/** Only applies to entities resizable on both X/Y. If TRUE, the entity instance width/height will keep the same aspect ratio as the definition. */
keepAspectRatio: boolean;
/** Possible values: `DiscardOldOnes`, `PreventAdding`, `MoveLastOne` */
limitBehavior: 'DiscardOldOnes' | 'MoveLastOne' | 'PreventAdding';
/** If TRUE, the maxCount is a \"per world\" limit, if FALSE, it's a \"per level\". Possible values: `PerLayer`, `PerLevel`, `PerWorld` */
limitScope: 'PerLayer' | 'PerLevel' | 'PerWorld';
/** */
lineOpacity: number;
/** Max instances count */
maxCount: integer;
/** An array of 4 dimensions for the up/right/down/left borders (in this order) when using 9-slice mode for `tileRenderMode`.<br/> If the tileRenderMode is not NineSlice, then this array is empty.<br/> See: https://en.wikipedia.org/wiki/9-slice_scaling */
nineSliceBorders: integer[];
/** Pivot X coordinate (from 0 to 1.0) */
pivotX: number;
/** Pivot Y coordinate (from 0 to 1.0) */
pivotY: number;
/** Possible values: `Rectangle`, `Ellipse`, `Tile`, `Cross` */
renderMode: 'Cross' | 'Ellipse' | 'Rectangle' | 'Ellipse';
/** If TRUE, the entity instances will be resizable horizontally */
resizableX: boolean;
/** If TRUE, the entity instances will be resizable vertically */
resizableY: boolean;
/** Display entity name in editor */
showName: boolean;
/** An array of strings that classifies this entity */
tags: string[];
/** **WARNING**: this deprecated value will be *removed* completely on version 1.2.0+ Replaced by: `tileRect` */
tileId: integer | null;
/** */
tileOpacity: number;
/** An object representing a rectangle from an existing Tileset */
tileRect: LDtkTilesetRect | null;
/** An enum describing how the the Entity tile is rendered inside the Entity bounds. Possible values: `Cover`, `FitInside`, `Repeat`, `Stretch`, `FullSizeCropped`, `FullSizeUncropped`, `NineSlice` */
tileRenderMode:
| 'Cover'
| 'FitInside'
| 'FullSizeCropped'
| 'FullSizeUncropped'
| 'NineSlice'
| 'Repeat'
| 'Stretch';
/** Tileset ID used for optional tile display */
tilesetId: integer | null;
/** Unique Int identifier */
uid: integer;
/** Pixel width */
width: integer;
};
/** Entity instance */
declare type LDtkEntityInstance = {
/** Grid-based coordinates (`[x,y]` format) */
__grid: integer[];
/** Entity definition identifier */
__identifier: string;
/** Pivot coordinates (`[x,y]` format, values are from 0 to 1) of the Entity */
__pivot: number[];
/** The entity \"smart\" color, guessed from either Entity definition, or one its field instances. */
__smartColor: string;
/** Array of tags defined in this Entity definition */
__tags: string[];
/** Optional TilesetRect used to display this entity (it could either be the default Entity tile, or some tile provided by a field value, like an Enum). */
__tile: LDtkTilesetRect | null;
/** Reference of the **Entity definition** UID */
defUid: integer;
/** An array of all custom fields and their values. */
fieldInstances: LDtkFieldInstance[];
/** Entity height in pixels. For non-resizable entities, it will be the same as Entity definition. */
height: integer;
/** Unique instance identifier */
iid: string;
/** Pixel coordinates (`[x,y]` format) in current level coordinate space. Don't forget optional layer offsets, if they exist! */
px: integer[];
/** Entity width in pixels. For non-resizable entities, it will be the same as Entity definition. */
width: integer;
};
/** Enum definition */
declare type LDtkEnumDef = {
/** */
externalFileChecksum: string | null;
/** Relative path to the external file providing this Enum */
externalRelPath: string | null;
/** Tileset UID if provided */
iconTilesetUid: integer | null;
/** User defined unique identifier */
identifier: string;
/** An array of user-defined tags to organize the Enums */
tags: string[];
/** Unique Int identifier */
uid: integer;
/** All possible enum values, with their optional Tile infos. */
values: LDtkEnumDefValues[];
};
/** Enum value definition */
declare type LDtkEnumDefValues = {
/** An array of 4 Int values that refers to the tile in the tileset image: `[ x, y, width, height ]` */
__tileSrcRect: integer[] | null;
/** Optional color */
color: integer;
/** Enum value */
id: string;
/** The optional ID of the tile */
tileId: integer | null;
};
/** In a tileset definition, enum based tag infos */
declare type LDtkEnumTagValue = {
/** */
enumValueId: string;
/** */
tileIds: integer[];
};
/** This section is mostly only intended for the LDtk editor app itself. You can safely ignore it. */
declare type LDtkFieldDef = {
/** Human readable value type. Possible values: `Int, Float, String, Bool, Color, ExternEnum.XXX, LocalEnum.XXX, Point, FilePath`.<br/> If the field is an array, this field will look like `Array<...>` (eg. `Array<Int>`, `Array<Point>` etc.)<br/> NOTE: if you enable the advanced option **Use Multilines type**, you will have \"*Multilines*\" instead of \"*String*\" when relevant. */
__type: string;
/** Optional list of accepted file extensions for FilePath value type. Includes the dot: `.ext` */
acceptFileTypes: string[] | null;
/** Possible values: `Any`, `OnlySame`, `OnlyTags` */
allowedRefs: 'Any' | 'OnlySame' | 'OnlyTags';
/** */
allowedRefTags: string[];
/** */
allowOutOfLevelRef: boolean;
/** Array max length */
arrayMaxLength: integer | null;
/** Array min length */
arrayMinLength: integer | null;
/** */
autoChainRef: boolean;
/** TRUE if the value can be null. For arrays, TRUE means it can contain null values (exception: array of Points can't have null values). */
canBeNull: boolean;
/** Default value if selected value is null or invalid. */
defaultOverride: any | null;
/** */
editorAlwaysShow: boolean;
/** */
editorCutLongValues: boolean;
/** Possible values: `Hidden`, `ValueOnly`, `NameAndValue`, `EntityTile`, `Points`, `PointStar`, `PointPath`, `PointPathLoop`, `RadiusPx`, `RadiusGrid`, `ArrayCountWithLabel`, `ArrayCountNoLabel`, `RefLinkBetweenPivots`, `RefLinkBetweenCenters` */
editorDisplayMode:
| 'ArrayCountNoLabel'
| 'ArrayCountWithLabel'
| 'EntityTile'
| 'Hidden'
| 'NameAndValue'
| 'PointPath'
| 'PointPathLoop'
| 'PointStar'
| 'Points'
| 'RadiusGrid'
| 'RadiusPx'
| 'RefLinkBetweenCenters'
| 'RefLinkBetweenPivots'
| 'ValueOnly';
/** Possible values: `Above`, `Center`, `Beneath` */
editorDisplayPos: 'Above' | 'Beneath' | 'Center';
/** */
editorTextPrefix: string | null;
/** */
editorTextSuffix: string | null;
/** User defined unique identifier */
identifier: string;
/** TRUE if the value is an array of multiple values */
isArray: boolean;
/** Max limit for value, if applicable */
max: number | null;
/** Min limit for value, if applicable */
min: number | null;
/** Optional regular expression that needs to be matched to accept values. Expected format: `/some_reg_ex/g`, with optional \"i\" flag. */
regex: string | null;
/** */
symmetricalRef: boolean;
/** Possible values: &lt;`null`&gt;, `LangPython`, `LangRuby`, `LangJS`, `LangLua`, `LangC`, `LangHaxe`, `LangMarkdown`, `LangJson`, `LangXml`, `LangLog` */
textLanguageMode:
| 'LangC'
| 'LangHaxe'
| 'LangJS'
| 'LangJson'
| 'LangLog'
| 'LangLua'
| 'LangMarkdown'
| 'LangPython'
| 'LangRuby'
| 'LangXml'
| null;
/** UID of the tileset used for a Tile */
tilesetUid: integer | null;
/** Internal enum representing the possible field types. Possible values: F_Int, F_Float, F_String, F_Text, F_Bool, F_Color, F_Enum(...), F_Point, F_Path, F_EntityRef, F_Tile */
type: string;
/** Unique Int identifier */
uid: integer;
/** If TRUE, the color associated with this field will override the Entity or Level default color in the editor UI. For Enum fields, this would be the color associated to their values. */
useForSmartColor: boolean;
};
/** Field instance */
declare type LDtkFieldInstance = {
/** Field definition identifier */
__identifier: string;
/** Optional TilesetRect used to display this field (this can be the field own Tile, or some other Tile guessed from the value, like an Enum). */
__tile: LDtkTilesetRect | null;
/** Type of the field, such as `Int`, `Float`, `String`, `Enum(my_enum_name)`, `Bool`, etc.<br/> NOTE: if you enable the advanced option **Use Multilines type**, you will have \"*Multilines*\" instead of \"*String*\" when relevant. */
__type: string;
/** Actual value of the field instance. The value type varies, depending on `__type`:<br/> - For **classic types** (ie. Integer, Float, Boolean, String, Text and FilePath), you just get the actual value with the expected type.<br/> - For **Color**, the value is an hexadecimal string using \"#rrggbb\" format.<br/> - For **Enum**, the value is a String representing the selected enum value.<br/> - For **Point**, the value is a [GridPoint](#ldtk-GridPoint) object.<br/> - For **Tile**, the value is a [TilesetRect](#ldtk-TilesetRect) object.<br/> - For **EntityRef**, the value is an [EntityReferenceInfos](#ldtk-EntityReferenceInfos) object.<br/><br/> If the field is an array, then this `__value` will also be a JSON array. */
__value: any;
/** Reference of the **Field definition** UID */
defUid: integer;
/** Editor internal raw values */
realEditorValues: any[];
};
declare type LDtkFlag =
| 'DiscardPreCsvIntGrid'
| 'ExportPreCsvIntGridFormat'
| 'IgnoreBackupSuggest'
| 'PrependIndexToLevelFileNames'
| 'MultiWorlds'
| 'UseMultilinesType';
/** IntGrid value definition */
declare type LDtkIntGridValueDef = {
/** */
color: string;
/** User defined unique identifier */
identifier: string | null;
/** The IntGrid value itself */
value: integer;
};
/** IntGrid value instance */
declare type LDtkIntGridValueInstance = {
/** Coordinate ID in the layer grid */
coordId: integer;
/** IntGrid value */
v: integer;
};
/** Layer definition */
declare type LDtkLayerDef = {
/** Type of the layer (*IntGrid, Entities, Tiles or AutoLayer*) */
__type: string;
/** Contains all the auto-layer rule definitions. */
autoRuleGroups: LDtkAutoLayerRuleGroup[];
/** */
autoSourceLayerDefUid: integer | null;
/** **WARNING**: this deprecated value will be *removed* completely on version 1.2.0+ Replaced by: `tilesetDefUid` */
autoTilesetDefUid: integer | null;
/** Opacity of the layer (0 to 1.0) */
displayOpacity: number;
/** An array of tags to forbid some Entities in this layer */
excludedTags: string[];
/** Width and height of the grid in pixels*/
gridSize: integer;
/** Height of the optional \"guide\" grid in pixels */
guideGridHei: integer;
/** Width of the optional \"guide\" grid in pixels */
guideGridWid: integer;
/** */
hideFieldsWhenInactive: boolean;
/** Hide the layer from the list on the side of the editor view. */
hideInList: boolean;
/** User defined unique identifier */
identifier: string;
/** Alpha of this layer when it is not the active one. */
inactiveOpacity: number;
/** An array that defines extra optional info for each IntGrid value.<br/> WARNING: the array order is not related to actual IntGrid values! As user can re-order IntGrid values freely, you may value \"2\" before value \"1\" in this array. */
intGridValues: LDtkIntGridValueDef[];
/** Parallax horizontal factor (from -1 to 1, defaults to 0) which affects the scrolling speed of this layer, creating a fake 3D (parallax) effect. */
parallaxFactorX: number;
/** Parallax vertical factor (from -1 to 1, defaults to 0) which affects the scrolling speed of this layer, creating a fake 3D (parallax) effect. */
parallaxFactorY: number;
/** If true (default), a layer with a parallax factor will also be scaled up/down accordingly. */
parallaxScaling: boolean;
/** X offset of the layer, in pixels (IMPORTANT: this should be added to the `LayerInstance` optional offset) */
pxOffsetX: integer;
/** Y offset of the layer, in pixels (IMPORTANT: this should be added to the `LayerInstance` optional offset) */
pxOffsetY: integer;
/** An array of tags to filter Entities that can be added to this layer */
requiredTags: string[];
/** If the tiles are smaller or larger than the layer grid, the pivot value will be used to position the tile relatively its grid cell. */
tilePivotX: number;
/** If the tiles are smaller or larger than the layer grid, the pivot value will be used to position the tile relatively its grid cell.*/
tilePivotY: number;
/** Reference to the default Tileset UID being used by this layer definition.<br/> **WARNING**: some layer *instances* might use a different tileset. So most of the time, you should probably use the `__tilesetDefUid` value found in layer instances.<br/> Note: since version 1.0.0, the old `autoTilesetDefUid` was removed and merged into this value. */
tilesetDefUid: integer | null;
/** Type of the layer as Haxe Enum Possible values: `IntGrid`, `Entities`, `Tiles`, `AutoLayer` */
type: 'AutoLayer' | 'Entities' | 'IntGrid' | 'Tiles';
/** Unique Int identifier */
uid: integer;
};
/** Layer instance */
declare type LDtkLayerInstance = {
/** Grid-based height */
__cHei: integer;
/** Grid-based width */
__cWid: integer;
/** Grid size */
__gridSize: integer;
/** Layer definition identifier */
__identifier: string;
/** Layer opacity as Float [0-1] */
__opacity: number;
/** Total layer X pixel offset, including both instance and definition offsets. */
__pxTotalOffsetX: integer;
/** Total layer Y pixel offset, including both instance and definition offsets. */
__pxTotalOffsetY: integer;
/** The definition UID of corresponding Tileset, if any. */
__tilesetDefUid: integer | null;
/** The relative path to corresponding Tileset, if any. */
__tilesetRelPath: string | null;
/** Layer type (possible values: IntGrid, Entities, Tiles or AutoLayer) */
__type: string;
/** An array containing all tiles generated by Auto-layer rules. The array is already sorted in display order (ie. 1st tile is beneath 2nd, which is beneath 3rd etc.).<br/><br/> Note: if multiple tiles are stacked in the same cell as the result of different rules, all tiles behind opaque ones will be discarded. */
autoLayerTiles: LDtkTile[];
/** */
entityInstances: LDtkEntityInstance[];
/** */
gridTiles: LDtkTile[];
/** Unique layer instance identifier */
iid: string;
/** **WARNING**: this deprecated value is no longer exported since version 1.0.0 Replaced by: `intGridCsv` */
intGrid: LDtkIntGridValueInstance[] | null;
/** A list of all values in the IntGrid layer, stored in CSV format (Comma Separated Values).<br/> Order is from left to right, and top to bottom (ie. first row from left to right, followed by second row, etc).<br/> `0` means \"empty cell\" and IntGrid values start at 1.<br/> The array size is `__cWid` x `__cHei` cells. */
intGridCsv: integer[];
/** Reference the Layer definition UID */
layerDefUid: integer;
/** Reference to the UID of the level containing this layer instance */
levelId: integer;
/** An Array containing the UIDs of optional rules that were enabled in this specific layer instance. */
optionalRules: integer[];
/** This layer can use another tileset by overriding the tileset UID here. */
overrideTilesetUid: integer | null;
/** X offset in pixels to render this layer, usually 0 (IMPORTANT: this should be added to the `LayerDef` optional offset, see `__pxTotalOffsetX`) */
pxOffsetX: integer;
/** Y offset in pixels to render this layer, usually 0 (IMPORTANT: this should be added to the `LayerDef` optional offset, see `__pxTotalOffsetY`) */
pxOffsetY: integer;
/** Random seed used for Auto-Layers rendering */
seed: integer;
/** Layer instance visibility */
visible: boolean;
};
/** This section contains all the level data. It can be found in 2 distinct forms, depending on Project current settings: - If \"*Separate level files*\" is **disabled** (default): full level data is *embedded* inside the main Project JSON file, - If \"*Separate level files*\" is **enabled**: level data is stored in *separate* standalone `.ldtkl` files (one per level). In this case, the main Project JSON file will still contain most level data, except heavy sections, like the `layerInstances` array (which will be null). The `externalRelPath` string points to the `ldtkl` file. A `ldtkl` file is just a JSON file containing exactly what is described below. */
declare type LDtkLevel = {
/** Background color of the level (same as `bgColor`, except the default value is automatically used here if its value is `null`) */
__bgColor: string;
/** Position informations of the background image, if there is one. */
__bgPos: LDtkLevelBgPosInfos | null;
/** An array listing all other levels touching this one on the world map.<br/> Only relevant for world layouts where level spatial positioning is manual (ie. GridVania, Free). For Horizontal and Vertical layouts, this array is always empty. */
__neighbours: LDtkNeighbourLevel[];
/** The \"guessed\" color for this level in the editor, decided using either the background color or an existing custom field. */
__smartColor: string;
/** Background color of the level. If `null`, the project `defaultLevelBgColor` should be used. */
bgColor: string | null;
/** Background image Y pivot (0-1) */
bgPivotY: number;
/** Background image X pivot (0-1) */
bgPivotX: number;
/** An enum defining the way the background image (if any) is positioned on the level. See `__bgPos` for resulting position info. Possible values: &lt;`null`&gt;, `Unscaled`, `Contain`, `Cover`, `CoverDirty` */
bgPos: 'Unscaled' | 'Contain' | 'Cover' | 'CoverDirty' | null;
/** The *optional* relative path to the level background image. */
bgRelPath: string | null;
/** This value is not null if the project option \"*Save levels separately*\" is enabled. In this case, this **relative** path points to the level Json file. */
externalRelPath: string | null;
/** An array containing this level custom field values. */
fieldInstances: LDtkFieldInstance[];
/** User defined unique identifier */
identifier: string;
/** Unique instance identifier */
iid: string;
/** An array containing all Layer instances. **IMPORTANT**: if the project option \"*Save levels separately*\" is enabled, this field will be `null`.<br/> This array is **sorted in display order**: the 1st layer is the top-most and the last is behind. */
layerInstances: LDtkLayerInstance[] | null;
/** Height of the level in pixels */
pxHei: integer;
/** Width of the level in pixels */
pxWid: integer;
/** Unique Int identifier */
uid: integer;
/** If TRUE, the level identifier will always automatically use the naming pattern as defined in `Project.levelNamePattern`. Becomes FALSE if the identifier is manually modified by user. */
useAutoIdentifier: boolean;
/** Index that represents the \"depth\" of the level in the world. Default is 0, greater means \"above\", lower means \"below\".<br/> This value is mostly used for display only and is intended to make stacking of levels easier to manage. */
worldDepth: integer;
/** World X coordinate in pixels.<br/> Only relevant for world layouts where level spatial positioning is manual (ie. GridVania, Free). For Horizontal and Vertical layouts, the value is always -1 here. */
worldX: integer;
/** World Y coordinate in pixels.<br/> Only relevant for world layouts where level spatial positioning is manual (ie. GridVania, Free). For Horizontal and Vertical layouts, the value is always -1 here. */
worldY: integer;
};
/** Level background image position info */
declare type LDtkLevelBgPosInfos = {
/** An array of 4 float values describing the cropped sub-rectangle of the displayed background image. This cropping happens when original is larger than the level bounds. Array format: `[ cropX, cropY, cropWidth, cropHeight ]` */
cropRect: number[];
/** An array containing the `[scaleX,scaleY]` values of the **cropped** background image, depending on `bgPos` option. */
scale: number[];
/** An array containing the `[x,y]` pixel coordinates of the top-left corner of the **cropped** background image, depending on `bgPos` option. */
topLeftPx: integer[];
};
/** Nearby level info */
declare type LDtkNeighbourLevel = {
/** A single lowercase character tipping on the level location (`n`orth, `s`outh, `w`est, `e`ast). */
dir: string;
/** Neighbour Instance Identifier */
levelIid: string;
/** **WARNING**: this deprecated value will be *removed* completely on version 1.2.0+ Replaced by: `levelIid` */
levelUid: integer;
};
/** This structure represents a single tile from a given Tileset. */
declare type LDtkTile = {
/** Internal data used by the editor.<br/> For auto-layer tiles: `[ruleId, coordId]`.<br/> For tile-layer tiles: `[coordId]`. */
d: integer[];
/** \"Flip bits\", a 2-bits integer to represent the mirror transformations of the tile.<br/> - Bit 0 = X flip<br/> - Bit 1 = Y flip<br/> Examples: f=0 (no flip), f=1 (X flip only), f=2 (Y flip only), f=3 (both flips) */
f: integer;
/** Pixel coordinates of the tile in the **layer** (`[x,y]` format). Don't forget optional layer offsets, if they exist! */
px: integer[];
/** Pixel coordinates of the tile in the **tileset** (`[x,y]` format) */
src: integer[];
/** The *Tile ID* in the corresponding tileset. */
t: integer;
};
/** The `Tileset` definition is the most important part among project definitions. It contains some extra informations about each integrated tileset. If you only had to parse one definition section, that would be the one. */
export declare type LDtkTilesetDef = {
/** Grid-based height */
__cHei: integer;
/** Grid-based width */
__cWid: integer;
/** The following data is used internally for various optimizations. It's always synced with source image changes. */
cachedPixelData: object | null;
/** An array of custom tile metadata */
customData: LDtkTileCustomMetadata[];
/** If this value is set, then it means that this atlas uses an internal LDtk atlas image instead of a loaded one. Possible values: &lt;`null`&gt;, `LdtkIcons` */
embedAtlas: 'LdtkIcons' | null;
/** Tileset tags using Enum values specified by `tagsSourceEnumId`. This array contains 1 element per Enum value, which contains an array of all Tile IDs that are tagged with it. */
enumTags: LDtkEnumTagValue[];
/** User defined unique identifier */
identifier: string;
/** Distance in pixels from image borders */
padding: integer;
/** Image height in pixels */
pxHei: integer;
/** Image width in pixels */
pxWid: integer;
/** Path to the source file, relative to the current project JSON file<br/> It can be null if no image was provided, or when using an embed atlas. */
relPath: string | null;
/** Array of group of tiles selections, only meant to be used in the editor */
savedSelections: object[];
/** Space in pixels between all tiles */
spacing: integer;
/** An array of user-defined tags to organize the Tilesets */
tags: string[];
/** Optional Enum definition UID used for this tileset meta-data */
tagsSourceEnumUid: integer | null;
/** */
tileGridSize: integer;
/** Unique Intidentifier */
uid: integer;
};
/** In a tileset definition, user defined meta-data of a tile. */
declare type LDtkTileCustomMetadata = {
/** */
data: string;
/** */
tileId: integer;
};
/** This object represents a custom sub rectangle in a Tileset image. */
declare type LDtkTilesetRect = {
/** Height in pixels */
h: integer;
/** UID of the tileset */
tilesetUid: integer;
/** Width in pixels */
w: integer;
/** X pixels coordinate of the top-left corner in the Tileset image */
x: integer;
/** Y pixels coordinate of the top-left corner in the Tileset image */
y: integer;
};
declare type LDtkWorld = {};
export {};
//# sourceMappingURL=LDtkFormat.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import { EditableTileMap } from '../../model/TileMapModel';
import { LDtkTileMap } from './LDtkFormat';
export declare namespace LDtkTileMapLoader {
/**
* Create a {@link EditableTileMap} from the LDtk JSON.
*
* @param ldtkTileMap A tile map exported from LDtk.
* @param levelIndex The level of the tile map to load from.
* @returns A {@link EditableTileMap}
*/
function load(
ldtkTileMap: LDtkTileMap,
levelIndex: number
): EditableTileMap | null;
}
//# sourceMappingURL=LDtkTileMapLoader.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"LDtkTileMapLoader.d.ts","sourceRoot":"","sources":["../../../src/load/ldtk/LDtkTileMapLoader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,yBAAiB,iBAAiB,CAAC;IACjC;;;;;;OAMG;IACH,SAAgB,IAAI,CAClB,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,MAAM,GACjB,eAAe,GAAG,IAAI,CAwJxB;CACF"}

View File

@@ -0,0 +1,5 @@
export declare function getLDtkTileId(
tileSetId: number,
tileId: number
): number;
//# sourceMappingURL=LDtkTileMapLoaderHelper.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"LDtkTileMapLoaderHelper.d.ts","sourceRoot":"","sources":["../../../src/load/ldtk/LDtkTileMapLoaderHelper.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQvE"}

View File

@@ -1,8 +1,8 @@
import { float, integer } from '../model/CommonTypes';
import { float, integer } from '../../model/CommonTypes';
/**
* Tiled JSON format (https://www.mapeditor.org/).
* Tiled JSON format (https://github.com/mapeditor/tiled/blob/master/docs/reference/json-map-format.rst).
*/
export declare type TiledMap = {
export declare type TiledTileMap = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
backgroundcolor?: string;
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */

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