Compare commits

...

234 Commits

Author SHA1 Message Date
Clément Pasteau
7c0a7a4152 Game templates in changelog (#6458)
Do not show in changelog
2024-03-14 16:42:38 +01:00
Clément Pasteau
d4cdb3ff83 Bump to 5.3.195 (#6455) 2024-03-14 16:20:15 +01:00
AlexandreS
a7cac91e45 Fix warning about end of subscription (#6456)
Do not show in changelog
2024-03-14 15:49:09 +01:00
D8H
6a6d72cd9a Fix games from crashing when a scene contains a wrapped text object with a big font size (#6453) 2024-03-14 15:36:34 +01:00
github-actions[bot]
11d74b3ea5 Update translations [skip ci] (#6442)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-03-14 15:31:27 +01:00
Clément Pasteau
5b65cf84eb Fix survey mobile (#6454)
Do not show in changelog
2024-03-14 15:00:57 +01:00
Clément Pasteau
6b7af0474f Improve Guided lessons (#6446)
* Lessons have been slightly reworked overall
* Most languages are now supported
* A new option allows to fill a value automatically with a button, speeding up the typing part
2024-03-14 14:40:12 +01:00
D8H
99f7e55044 Fix the object effect color tween action (#6452) 2024-03-14 14:13:28 +01:00
AlexandreS
6b522b1c31 Add support for game sessions achievements notifications (#6449) 2024-03-14 12:18:42 +01:00
D8H
0f35e48690 Allow to change text of custom objects like any text object (#6450) 2024-03-14 11:50:55 +01:00
D8H
53d19dd628 Fix custom object capability changes to be applied right away (#6447) 2024-03-14 10:49:33 +01:00
Florian Rival
155dc1bec8 Display redesigned subscription plans (#6440)
Don't show in changelog
2024-03-14 10:45:29 +01:00
D8H
be26e39eae Fix 3D custom objects CenterZ expression and condition (#6448) 2024-03-13 14:07:24 +01:00
TRP
90fa5ea8e8 Add action to draw a fillet rectangle with Shape painter (rounded rectangle with inverted corners) (#6433) 2024-03-12 16:11:02 +01:00
Florian Rival
4845251f78 Fix broken editor panels drag'n'drop (#6444) 2024-03-11 11:34:27 +01:00
Arthur Pacaud (arthuro555)
31ef3fec58 Add TypeScript type checking to JsExtension.js files (#6321) 2024-03-09 17:22:12 +01:00
Clément Pasteau
292b23ea17 Fix usage displayer (#6441)
* Fix wrong logic for usage displayer

* Bump version too
2024-03-08 18:11:11 +01:00
github-actions[bot]
9c5a5db7a8 Update translations [skip ci] (#6439)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-03-08 16:40:48 +01:00
Clément Pasteau
d73ae4c56e Bump to 5.3.193 (#6436) 2024-03-08 14:30:29 +01:00
D8H
339929e021 Sort 3D effects to show light and fog effects first (#6437) 2024-03-08 14:17:56 +01:00
github-actions[bot]
cea1cf20f1 Update translations [skip ci] (#6438)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-03-08 14:16:04 +01:00
github-actions[bot]
4ec2705f75 Update translations [skip ci] (#6435)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-03-08 12:36:52 +01:00
AlexandreS
408f6f8134 Display notifications to the user (#6432) 2024-03-08 12:15:21 +01:00
github-actions[bot]
c64bac0010 Update translations [skip ci] (#6424)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-03-08 09:54:15 +01:00
Clément Pasteau
f8d5c89ebf Allow using credits to start builds when quota is exhausted (#6430) 2024-03-08 09:26:18 +01:00
Florian Rival
ca3bb40e96 Store an achievement when finishing a tutorial (#6431) 2024-03-07 16:18:50 +01:00
Florian Rival
713d437b70 Fix naming of license file for Electron
Don't show in changelog
2024-03-07 10:15:04 +01:00
Florian Rival
09381c3836 Fix warning
Don't show in changelog
2024-03-06 17:47:08 +01:00
Florian Rival
ea4c2e0827 Fix useless query param
Don't show in changelog
2024-03-06 17:38:31 +01:00
Florian Rival
a5d1149c21 Rework the Get Started page to directly show recommendations for all users (#6428) 2024-03-06 17:35:30 +01:00
Florian Rival
119b0fadce Avoid rendering all asset packs and loading 100+ images in the Shop/Asset Store tab (#6425) 2024-03-06 17:11:32 +01:00
D8H
7111859768 Remove useless import (#6427)
Don't show in changelog
2024-03-05 15:44:07 +01:00
Vladyslav Pohorielov
08dfb4d36b Include PixiJS Spine pre-built files as part of the extension, like other extensions (#6340)
Only show in developer changelog
2024-03-05 15:28:23 +01:00
Florian Rival
cacd482af9 Fix type check for GDJS and documentation generation 2024-03-05 14:34:34 +01:00
D8H
3568a58019 Allow custom objects to be used as 3D objects (#6369)
- Custom objects like the 3D particle emitter can be used as any other 3D object
  - with the same set of actions and conditions thanks to the 3D capability
  - in the scene editor, their Z position and 3D rotations can be changed
- Fix child creation in `doStepPostEvent` function
2024-03-05 13:51:41 +01:00
Florian Rival
a32d5fcd7e Add file to ease detection of GDevelop by tool like SteamDB or Itch (#6423) 2024-03-05 13:12:16 +01:00
github-actions[bot]
1a999fd2dd Update translations [skip ci] (#6413)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-03-04 18:13:44 +01:00
Florian Rival
76648764bb Refactor EditorMosaic, add some placeholder for potential screen adaptation (#6421)
Don't show in changelog
2024-03-04 16:47:36 +01:00
Vladyslav Pohorielov
c25803c122 Fix Spine resources not loaded properly in games in the web-app version of GDevelop (#6416) 2024-03-04 16:03:48 +01:00
D8H
ba8d7f4e38 Fix a crash that sometimes happens when deleting links of an object (#6411) 2024-03-01 17:18:01 +01:00
AlexandreS
e1c22f6994 Display QRCode at the end of the gd games export flow (#6410) 2024-03-01 16:36:29 +01:00
Clément Pasteau
7b70f9172f Bump to 5.3.192 (#6408) 2024-03-01 12:58:55 +01:00
github-actions[bot]
a871e0f2ec Update translations [skip ci] (#6406)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-03-01 12:51:58 +01:00
Clément Pasteau
f6499e1163 Fix export flow with gdgames and show warning when game is not owned (#6407) 2024-03-01 12:34:23 +01:00
github-actions[bot]
d03e58636d Update translations [skip ci] (#6404)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-03-01 10:17:35 +01:00
AlexandreS
a7be928f2f Fix icons generating utility not working on macOS (#6405) 2024-02-29 17:07:35 +01:00
AlexandreS
7c83610d28 Fix missing s in Reponsive (#6403)
Don't show in changelog
2024-02-29 10:03:44 +01:00
github-actions[bot]
1a1e92b072 Update translations [skip ci] (#6398)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-02-29 09:52:55 +01:00
Florian Rival
5118f13e0b Add support for usages with 30 days period (#6401) 2024-02-28 18:20:41 +01:00
Clément Pasteau
f3ee18cdc7 Fix share tab and other design improvements on mobile + info alert when build is running (#6400)
Do not show in changelog
2024-02-28 15:12:33 +01:00
AlexandreS
c5584f746e Upgrade automatic pull request action (#6399)
Don't show in changelog
2024-02-28 14:34:57 +01:00
AlexandreS
706a6de94c Bump newIDE version (#6389) 2024-02-28 13:54:16 +01:00
github-actions[bot]
8c750b54dd Update translations [skip ci] (#6397)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2024-02-28 13:54:01 +01:00
AlexandreS
649d744664 Remove tree view nav with left and right arrow keys in project manager and events function list (#6396) 2024-02-28 13:53:30 +01:00
Florian Rival
12a6fec18e Ensure special characters like parenthesis are removed from Cordova project name to allow proper iOS app store upload (#6395)
Don't show in changelog
2024-02-28 13:35:13 +01:00
github-actions[bot]
7fc4aa47f6 Update translations [skip ci] (#6394)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-28 11:44:06 +01:00
github-actions[bot]
483f78fa75 Update translations [skip ci] (#6393)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-28 11:25:10 +01:00
Clément Pasteau
35b80818dc Improve export flow's overall design (#6392) 2024-02-28 11:04:42 +01:00
github-actions[bot]
b3f19726dc Update translations [skip ci] (#6391)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-02-28 10:50:47 +01:00
D8H
dd332e3cce Fix boolean property instruction group (#6386) 2024-02-28 10:24:03 +01:00
D8H
dc95a7511f Use a tree view for the project manager (#6346)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-02-27 16:51:59 +01:00
github-actions[bot]
77130c7d2e Update translations [skip ci] (#6382)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-02-27 16:25:46 +01:00
AlexandreS
8d1c3e8290 Improve display performance on Project and Team members lists (#6388) 2024-02-27 15:24:10 +01:00
AlexandreS
63570fd3c8 Paginate list of games (#6387) 2024-02-27 12:39:06 +01:00
AlexandreS
6945409280 Fix: Update BBText hitboxes when text changes (#6385)
- Also: Deprecate current font family setting action that uses a string as input and create a new action that uses a selector instead.
2024-02-27 08:57:17 +01:00
AlexandreS
e345b2726f Fix URL opening action on iOS (#6384) 2024-02-26 12:04:52 +01:00
github-actions[bot]
3a2a662f62 Update translations [skip ci] (#6380)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-23 19:35:40 +01:00
Clément Pasteau
73d850f933 Fix missing dependencies (#6381) 2024-02-23 19:35:24 +01:00
github-actions[bot]
1f2293e7d6 Update translations [skip ci] (#6371)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-02-23 18:54:24 +01:00
Clément Pasteau
b038984097 Display promotions & announcements only on a few pages & make it scrollable (#6379)
Do not show in changelog
2024-02-23 18:53:56 +01:00
AlexandreS
a45a5bfe7e Remove shuffling of data from backend services (#6378) 2024-02-23 17:33:25 +01:00
Florian Rival
8cbefa0cde Add support for iOS Cloud builds and automated publishing to App Store Connect (#6373)
* This allows to build directly from GDevelop, without any additional development tool or access to a Mac, a game for iOS.
* Builds can be done for development (testing on iPhone/iPad) or for the App Store directly.
* An Apple Developer account is required, so that you can generate the required certificate files (and authentication keys for uploading to App Store Connect).
* Using these new "iOS cloud builds" requires a Gold or a Pro subscription.
2024-02-23 16:42:09 +01:00
AlexandreS
1d607d474a Fix: Display audio previews in premium asset pack previews (#6377) 2024-02-23 16:07:17 +01:00
Florian Rival
3cd6e36a73 Fix scrollbars not properly styled in latest Chrome (#6376) 2024-02-23 12:16:51 +01:00
Florian Rival
cfbec2df4a Fix naming 2024-02-23 09:30:26 +01:00
Clément Pasteau
2823fde86a Few game template improvements (#6375)
Do not show in changelog
2024-02-22 14:36:33 +01:00
D8H
f29ae50f2e Fix 3d model resource changes not being detected by the other fields of the object editor (#6374) 2024-02-22 11:40:36 +01:00
Clément Pasteau
ba40c67941 Improve visibility on small tablets and mobiles in landscape (#6372)
* Prevent the screen to be detected as mobile and some layout to shift unnecessarily
2024-02-22 10:23:20 +01:00
D8H
c8ca3c2931 Fix a crash when the camera Z is changed and there is no 3D object in the project (#6370) 2024-02-21 10:36:33 +01:00
github-actions[bot]
0bb0969ab8 Update translations [skip ci] (#6365)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-20 12:07:31 +01:00
Clément Pasteau
3b0a7c442f Hide slideshow arrows when not hovering (#6367)
* And handle keyboard navigation

Do not show in changelog
2024-02-20 12:00:56 +01:00
AlexandreS
df3c95c466 Bump newIDE version (#6366) 2024-02-19 16:26:05 +01:00
AlexandreS
482d52ff5a Open example store from all templates list of build section (#6364)
Don't show in changelog
2024-02-19 16:13:19 +01:00
github-actions[bot]
5a78c763ef Update translations [skip ci] (#6358)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-02-19 14:15:40 +01:00
AlexandreS
3393af4b3b Add button to see more templates on the build section (#6359) 2024-02-19 13:57:41 +01:00
D8H
c2b77c9df7 Fix platformer character position when its height changes (#6360) 2024-02-19 12:57:26 +01:00
Clément Pasteau
514e4692ab Display promotion items on the homepage (#6353) 2024-02-19 12:45:49 +01:00
AlexandreS
210e59f201 Fix: Auto scroll to instruction item when editing an instruction (#6361) 2024-02-19 12:22:38 +01:00
D8H
e2058052f2 Fix a crash that sometimes happens when several sounds are played at the same time (#6357) 2024-02-19 11:04:01 +01:00
github-actions[bot]
90dca41e20 Update translations [skip ci] (#6351)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-02-19 10:29:05 +01:00
AlexandreS
15b463dad1 Add possibility to claim a monthly asset pack free of charge (#6350)
For gold and startup subscribers only
2024-02-16 17:54:45 +01:00
github-actions[bot]
a97a1f8a86 Update translations [skip ci] (#6348)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-16 14:15:40 +01:00
D8H
901f84daaa Add bloom and color effects for 3D layers (#6228)
- Add 3D Effects for color adjustment and bloom.
- A lower quality anti-aliasing is used when post-processing is enable (SMAA instead of MSAA).
- Upgrade to Three.js 0.160.0.
2024-02-16 14:12:46 +01:00
Clément Pasteau
6af50ae5c0 Show all products on app stores, purchasable with GDevelop credits (#6349) 2024-02-16 11:19:10 +01:00
AlexandreS
f7bd4bee6e Fix conflict between opening and previewing a private game template from the store section (#6347) 2024-02-15 14:39:11 +01:00
github-actions[bot]
4a4b015242 Update translations [skip ci] (#6345)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-15 14:35:19 +01:00
Clément Pasteau
75ee0b68e3 Allow buying asset packs and game templates with credits (#6343) 2024-02-14 17:03:38 +01:00
github-actions[bot]
778104447c Update translations [skip ci] (#6336)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-02-13 18:52:45 +01:00
D8H
a4ddc0a6ef Fix parameters drop-down list for properties of type choices (#6342) 2024-02-13 14:32:30 +01:00
D8H
3e713029e2 Fix visibility of hidden extension functions starting with an object parameter from within the extension (#6341) 2024-02-13 11:27:57 +01:00
D8H
7eb5402ee2 Keep original object and resource names when exporting assets (#6329) 2024-02-12 19:16:53 +01:00
AlexandreS
25adb026b8 Fix typo (#6337)
Don't show in changelog
2024-02-09 12:14:48 +01:00
Clément Pasteau
54bd2960ac Fix doublons in example tags search (#6335) 2024-02-09 12:14:34 +01:00
AlexandreS
1fde65b5e3 Fix template opening (#6334)
Don't show in changelog
2024-02-09 12:06:28 +01:00
github-actions[bot]
b4295b4077 Update translations [skip ci] (#6332)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-02-09 11:42:05 +01:00
AlexandreS
c7a929374d Fix homepage navigation (#6333)
Don't show in changelog
2024-02-09 10:09:02 +01:00
AlexandreS
7571e64396 Bump newIDE version (#6331) 2024-02-09 10:08:38 +01:00
github-actions[bot]
198267d7cb Update translations [skip ci] (#6325)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-02-09 09:36:53 +01:00
Aurélien Vivet
0982424d0d Set a preference for the importation of resources for projects stored on the computer (#6318) 2024-02-09 09:33:58 +01:00
D8H
29747690c2 Remove unused method declaration (#6330)
* don't show in changelog
2024-02-08 19:14:41 +01:00
AlexandreS
ad74532752 Remove unused import (#6328)
Don't show in changelog
2024-02-08 15:56:15 +01:00
AlexandreS
80e5376f74 Make opening screen more stable and reliable (#6327) 2024-02-08 15:40:50 +01:00
D8H
cc24eab2aa Allow to set the text outline and shadow from the object editor (#6246)
* Avoid the shadow to be cut out.
* Fix the shadow angle action.
2024-02-08 13:11:18 +01:00
D8H
423c8165ad Collapse advanced behavior properties (#6326) 2024-02-08 13:08:52 +01:00
D8H
0af00818dc Allow to select instances with a selection rectangle starting from within another instance holding Shift (#6304) 2024-02-07 16:24:37 +01:00
D8H
9f5c783d73 Keep 3D model origin as part of the object when used as the object origin (#6303)
- In case you choose "Model origin" for the "Origin point" property and the model origin is outside of the model, the model size may be smaller than in previous releases. To solve the issue you can:
  - Choose one of the other options for the "Origin point" property
  - Reset the "Scaling factor" to its previous value to get back the same size as before
  - Modify the model to set the origin nearer to the geometry
- Also fix the scaling of flat 3D models
2024-02-07 15:26:07 +01:00
github-actions[bot]
ad65971c01 Update translations [skip ci] (#6320)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-07 14:43:44 +01:00
Clément Pasteau
85ef9a9561 Fix casing gdevelop (#6323)
Do not show in changelog
2024-02-07 12:40:24 +01:00
AlexandreS
455d77fcdf Add possibility to cancel edition with Piskel/Jfxr/Yarn from the editor (#6319) 2024-02-05 18:12:43 +01:00
github-actions[bot]
02093fec0f Update translations [skip ci] (#6300)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-02-05 10:51:30 +01:00
Clément Pasteau
8227ab9cad Fix add assets button from shop being always visible (#6316) 2024-02-05 10:43:57 +01:00
D8H
f8eb91f3d2 Fix a crash when exporting assets sharing the same resources (#6314) 2024-02-03 13:41:09 +01:00
AlexandreS
a260aa5e3e Hide play tab for student profiles (#6313) 2024-02-02 11:13:37 +01:00
AlexandreS
4d8d93a550 Put Instances list search bar at the same place as the other panels in Scene editor (#6312) 2024-02-02 10:23:22 +01:00
AlexandreS
cf160bcca1 Improve game templates and examples display on build page (#6309) 2024-02-01 15:25:49 +01:00
D8H
44a0e22f97 Add autocompletion for "input type" action of text input objects (#6308) 2024-02-01 11:54:09 +01:00
D8H
d47d3285b2 Fix particle emitters not deleting itself when all their particles die before being displayed (#6306) 2024-01-31 20:12:28 +01:00
D8H
2ee6590967 Avoid event-functions to be selected when their menu is shown (#6301) 2024-01-30 11:44:16 +01:00
AlexandreS
cd77951e1a Fix game template/example search when there are only game templates matching (#6302) 2024-01-30 11:37:24 +01:00
D8H
fa4efef857 Fix a missing parameter in the sentence of the tween progress condition (#6299) 2024-01-30 10:35:25 +01:00
AlexandreS
bc8204d696 Remove cancel button when not logging in with provider (#6298)
Don't show in changelog
2024-01-30 10:07:18 +01:00
github-actions[bot]
b2eab5a327 Update translations [skip ci] (#6285)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-30 09:38:18 +01:00
Clément Pasteau
dba65822dd Avoid showing a loader on profile and user chip on subsequent loads (#6296) 2024-01-30 09:23:09 +01:00
AlexandreS
8f155e4322 Fix version history displaying loader on locally-stored projects (#6295) 2024-01-29 17:34:30 +01:00
D8H
13ebe5c588 Select the highest 3D objects first (#6294) 2024-01-29 16:18:48 +01:00
AlexandreS
bcd8e5608a Fix action/condition selector not updating on object change (#6291) 2024-01-29 14:18:44 +01:00
D8H
14e444413d Use the object name as the default new image name (#6293) 2024-01-29 14:01:53 +01:00
D8H
a2f75bc0dc Show installed version in the extension detail dialog (#6292) 2024-01-29 12:37:30 +01:00
D8H
6914e01a15 Fix behavior function renaming (#6290) 2024-01-29 11:18:23 +01:00
D8H
90b5f7a322 Fix the orthographic camera zoom update and its default Z position (#6288) 2024-01-29 11:17:57 +01:00
AlexandreS
13cf9b1d0b Show error box when there's an error logging in with provider (#6284)
Do not show in changelog
2024-01-26 16:28:48 +01:00
Clément Pasteau
6fc0198298 Bump version to 5.3.188 (#6283) 2024-01-26 16:26:11 +01:00
github-actions[bot]
0999ba611d Update translations [skip ci] (#6265)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-26 16:25:56 +01:00
Clément Pasteau
119d1af76a Game template bundles can be displayed and bought (#6275)
Do not show in changelog
2024-01-26 16:17:58 +01:00
AlexandreS
d6b4dacb1e Add possibility to sign up using Google, GitHub and Apple (#6249) 2024-01-26 15:46:18 +01:00
D8H
9edb3cfe91 Fix missing collision masks on new Sprite objects (#6282) 2024-01-26 15:25:12 +01:00
D8H
d44a1c3537 Generate particle textures only once (#6280) 2024-01-26 14:33:52 +01:00
AlexandreS
8cc84e5728 Fix legacy plans not showing in the user profile
Also: adds back the possibility to cancel a legacy subscription
2024-01-26 14:17:59 +01:00
D8H
76130b43d4 Handle export of assets with animations sharing frames (#6274) 2024-01-26 13:46:42 +01:00
D8H
40b0823b91 Fix a memory leak in the 2D particle emitter (#6278) 2024-01-26 12:23:13 +01:00
D8H
9b447e08e2 Forbid life-cycle functions to be renamed (#6269) 2024-01-25 14:53:07 +01:00
Aurélien Vivet
10314a1911 Fix focus not set in deletion confirmation dialog (#6270) 2024-01-25 14:16:17 +01:00
D8H
d0195719c2 Fix the 2 columns layout that may show hidden properties (#6268) 2024-01-25 12:18:52 +01:00
D8H
dd8c040381 Fix a crash when displaying the contextual menu of titles in object group lists (#6263) 2024-01-25 11:45:17 +01:00
D8H
5e0c8a92aa Fix a crash in the sprite editor of a child-object (#6266) 2024-01-25 11:44:31 +01:00
github-actions[bot]
977c102ddc Update translations [skip ci] (#6264)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-01-25 10:58:54 +01:00
Clément Pasteau
849d79d5d7 Avoid creating multiple canvas for ThreeJS (#6259) 2024-01-25 10:58:24 +01:00
D8H
28f7c9ae0b Fix a crash when the scale tween action is used in a 2D game (#6260) 2024-01-25 10:49:34 +01:00
github-actions[bot]
38e58327fa Update translations [skip ci] (#6244)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-24 11:22:04 +01:00
Clément Pasteau
3cc29efaa2 Remove report button on editor crash to avoid cluttering Github issues (#6247)
Do not show in changelog
2024-01-24 11:14:38 +01:00
Clément Pasteau
65eb76fb57 use newer electron builder (#6227)
Co-authored-by: romw314 <106016361+romw314@users.noreply.github.com>
2024-01-24 11:14:19 +01:00
AlexandreS
9eb721662f Remove useless margins (#6242)
Don't show in changelog
2024-01-23 14:22:39 +01:00
github-actions[bot]
b10b131010 Update translations [skip ci] (#6238)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2024-01-23 14:13:37 +01:00
Tristan Rhodes
b5fd1bb351 Improved wording "Stop music and sounds at the beginning of this scene" (#6240) 2024-01-23 14:05:31 +01:00
Aurélien Vivet
f1c9521625 Fix sentence of the Draw a Torus action (#6239) 2024-01-23 11:00:28 +01:00
github-actions[bot]
6ceb3c2c10 Update translations [skip ci] (#6234)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2024-01-22 15:38:34 +01:00
AlexandreS
43827876cd Fix subscription changes (#6236)
Don't show in changelog
2024-01-22 15:37:15 +01:00
AlexandreS
62b746300a Adapt credit banner button color to each theme (#6237)
Don't show in changelog
2024-01-22 15:29:33 +01:00
AlexandreS
769ebcd91c Fix credit banner text color on light themes (#6235)
Don't show in changelog
2024-01-22 11:12:57 +01:00
Florian Rival
70d5de16bf Fix Windows code signing (#6233) 2024-01-21 22:03:44 +01:00
Clément Pasteau
7fbe1bd23d Bump version to 5.3.187 (#6214) 2024-01-18 17:58:12 +01:00
github-actions[bot]
de8a679e31 Update translations [skip ci] (#6217)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-18 17:57:31 +01:00
Clément Pasteau
a1a4029b35 Introduce GDevelop credits and Marketing Campaigns (#6209)
* Credits can now be purchased from your Profile
* They can be used to purchase marketing campaigns, in order to promote your game on gd.games, with GDevelop social networks, or even within GDevelop itself.
* In the upcoming months, credits will unlock more and more exclusive perks in the app
2024-01-18 17:20:28 +01:00
Clément Pasteau
a377467031 Revert "Type JsExtensions files with typescript" (#6220)
Don't show in changelog
2024-01-18 16:01:28 +01:00
AlexandreS
dd090fd1d7 Abort login with providers (#6215)
Don't show in changelog
2024-01-18 12:32:01 +01:00
D8H
26ee9b3891 Avoid to add useless attributes on serialized extensions (#6216)
Don't show in changelog
2024-01-18 09:53:53 +01:00
Arthur Pacaud (arthuro555)
ad18eab4ae Type JsExtension.js files with TypeScript (#6200)
* Also improve typing of the C++ Core classes in TypeScript.

Only show in developer changelog
2024-01-18 09:53:24 +01:00
github-actions[bot]
007fc36a2e Update translations [skip ci] (#6184)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-01-18 09:36:40 +01:00
D8H
c8ebfde85b Fix platformer characters turn back speed when the deceleration is greater than the acceleration (#6208) 2024-01-17 18:32:14 +01:00
Vladyslav Pohorielov
d0005ba2cb Add support for Spine objects (2D skeletal animation) (#5927)
* This allows to use animatable 2D objects created with Spine (purchase a licence on [their website](https://esotericsoftware.com/) if you're interested).
* 2D skeletal animation allows for very smooth animations, and keep resource usage low compared to animations made of frames like Sprite objects. It's perfect for 2D games and can be used for animated characters, avatars, UI elements.
* Many thanks to @f0nar and @LousyMolars for their work on this new feature and associated game examples!

---------

Co-authored-by: Vladyslav Pohorielov <vpohorielov@playtika.com>
Co-authored-by: Gleb Volkov <glebusheg@gmail.com>
Co-authored-by: Florian Rival <Florian.rival@gmail.com>
Co-authored-by: Davy Hélard <davy.helard@gmail.com>
2024-01-17 18:19:08 +01:00
D8H
f623b352ee Hide deprecated properties of the platformer character behind a button (#6199) 2024-01-17 10:53:55 +01:00
D8H
16e2d8a005 Move some properties of the platformer character behavior in 2 columns (#6197) 2024-01-16 12:22:13 +01:00
D8H
7654883cb1 Allow to use orthographic camera in 3D layers (#6187) 2024-01-15 23:05:23 +01:00
D8H
3ae5db2a49 Fix scale and camera zoom tweens from setting NaN when the initial value was 0 (#6178) 2024-01-15 14:44:10 +01:00
Arthur Pacaud (arthuro555)
9ed002c879 Add expression to read the authenticated user unique identifier (#6192)
* This is useful if you want to interface with a third party or in-house solution to store user data.
2024-01-15 14:00:14 +01:00
Florian Rival
d4283c2350 Add new icon for 3D model objects (#6186) 2024-01-12 18:24:43 +01:00
AlexandreS
5a176d21e7 Add SSO to login/sign up (#6167)
Feature hidden in live environment.

Don't show in changelog
2024-01-12 14:45:16 +01:00
D8H
bfdfd7f0fb Improve the new UI of the extension editor (#6165)
- Add icons for behavior and object in the function tree
- Move the indicators on the right.
- Remove the big button.
- Fix the effect icon.
- Add checkboxes for the visibility.
- Rename on double click.
- Fix indentation.
- Hide the 3 dots button.

- Don't show in changelog
2024-01-12 13:11:16 +01:00
AlexandreS
aae75f2232 Fix use of Warning icon in tooltips (#6183)
Don't show in changelog
2024-01-12 13:04:20 +01:00
github-actions[bot]
977092c0a3 Update translations [skip ci] (#6179)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-01-12 12:38:05 +01:00
D8H
556688cedb Add pagination to asset store results (#6174) 2024-01-11 17:52:46 +01:00
github-actions[bot]
ce93dc5310 Update translations [skip ci] (#6170)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-10 15:57:04 +01:00
Clément Pasteau
d6d425db4f Show premium game templates from the same author on a template's page (#6168) 2024-01-10 15:42:51 +01:00
AlexandreS
2737e75639 Fix merge issue (#6169)
Don't show in changelog
2024-01-10 15:35:31 +01:00
github-actions[bot]
36eab18133 Update translations [skip ci] (#6164)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2024-01-09 16:30:55 +01:00
Clément Pasteau
48acbb12ee Decode filenames from any URLs before using them (#6018)
Only show in developer changelog
2024-01-09 16:00:09 +01:00
Florian Rival
21904e46f1 Avoid an extra click to purchase an asset pack or game template (#6161) 2024-01-09 15:32:33 +01:00
AlexandreS
94753ac053 Get subscription plans from server (#6128)
Don't show in changelog
2024-01-09 15:28:28 +01:00
github-actions[bot]
b160ee9b27 Update translations [skip ci] (#6147)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-01-09 15:15:24 +01:00
D8H
f76e8a72b6 Make the function property editor better adapt to small size (#6158) 2024-01-08 18:11:18 +01:00
D8H
5bc342688d Fix the cameras position to keep them centered on the object they follow when the game resolution is changed (#3821)
- Also fix the camera position for pixel-art games when a zoom and a game resolution change is used to have big pixels
2024-01-08 12:43:50 +01:00
D8H
3e6204c0eb Remember the size of mosaic nodes when they are uncollapsed (#6156)
- Don't show in changelog
2024-01-08 12:42:31 +01:00
D8H
7b1c340ad0 Fix game crashing at loading when video files are missing (#6155) 2024-01-08 12:08:48 +01:00
D8H
fac724dc3f Fix the indentation of extensions exported from the web-app (#6148) 2024-01-05 11:47:54 +01:00
D8H
9b4151f64c Add a button to export all the objetcs of a scene to submit them to the asset store (#4848)
* The dialog can be reached from the hidden drop-down menu of "Scene objects".
2024-01-05 11:09:27 +01:00
github-actions[bot]
6b5ab6c811 Update translations [skip ci] (#6130)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2024-01-05 10:06:13 +01:00
Clément Pasteau
5943092b0c Fix correctly taking folders into account when adding assets from the Home Store (#6146) 2024-01-05 10:05:44 +01:00
D8H
deb0c5ffc3 Display events functions of extensions in a tree view (#6133) 2024-01-04 18:14:44 +01:00
Clément Pasteau
c56fa03bf6 Fix crash when trying to access a child of a non existing structure child in an expression (#6144) 2024-01-04 16:20:49 +01:00
D8H
0d49d449db Import several animations to a sprite object in one go (#6035)
- Sprite animations are automatically created when several image files are selected and they ends with an animation name and an optional frame index (most naming conversions should work).
2024-01-04 11:02:04 +01:00
Clément Pasteau
fdd702cd09 Fix renaming a layer with external layouts (#6140)
* Instances of external layouts and of the attached scene are now all moved properly to the renamed layer
2024-01-04 10:31:34 +01:00
Clément Pasteau
5a8e4a7ca9 Allow confirming and canceling confirmation dialogs with keyboard shortcuts (#6138) 2024-01-03 14:52:36 +01:00
D8H
2e4e91c21e Add tween actions (position, rotation and depth) for 3D objects (#6122) 2024-01-02 10:03:59 +01:00
AlexandreS
6ece930809 Use axios error extractor where possible (#6132)
Only show in developer changelog
2023-12-27 19:40:16 +01:00
AlexandreS
c5baa81977 Display Game template categories with chips (#6129)
Don't show in changelog
2023-12-27 16:10:19 +01:00
github-actions[bot]
d24be38874 Update translations [skip ci] (#6109)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-27 10:35:14 +01:00
AlexandreS
1efffbbb78 Fix: Avoid project commit error by retrying call to server (#6127) 2023-12-26 12:12:25 +01:00
D8H
090d76a368 Show a drop-down list for string with choices parameters (#6120) 2023-12-22 15:31:43 +01:00
D8H
d44a9375de Add variables autocompletion for groups in variable fields (#6110) 2023-12-21 12:20:14 +01:00
AlexandreS
5a2a3893f9 Remove dashboard from profile dialog (#6113) 2023-12-21 12:08:23 +01:00
AlexandreS
0a6b0dc785 Use cloud storage provider internal name (#6108)
Don't show in changelog
2023-12-20 12:15:04 +01:00
github-actions[bot]
02e99726dc Update translations [skip ci] (#6103)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-20 11:55:13 +01:00
AlexandreS
843055d8df Add error message when versions fail to load (#6105)
Don't show in changelog
2023-12-20 11:40:07 +01:00
AlexandreS
f7fda5cb5e Fix placeholder thumbnail in objects lists for prefabs (#6107) 2023-12-20 11:37:28 +01:00
AlexandreS
e529642aec Fix program opening count logic when user has never acknowledged the feature (#6106)
Don't show in changelog
2023-12-20 10:01:35 +01:00
AlexandreS
c8e10d7043 Bump newIDE version (#6104) 2023-12-19 12:25:29 +01:00
AlexandreS
5f0de0e9a7 Adapt extract changelog script to currently used format (#6102)
Only show in developer changelog
2023-12-19 12:18:25 +01:00
github-actions[bot]
e51638ce4b Update translations [skip ci] (#6101)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-19 12:17:54 +01:00
AlexandreS
0fbd6a606a Add possibility to restore a previous version of a cloud project (#6022)
- Display all the project versions (paginated)
- Allow to open any version and name it for better tracking
- Make it possible to restore a given version
2023-12-19 11:50:28 +01:00
github-actions[bot]
2fa543c3db Update translations [skip ci] (#6083)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-18 16:56:41 +01:00
AlexandreS
38e35c9695 Prevent crash on new object dialog (#6099) 2023-12-18 16:50:21 +01:00
Florian Rival
ad13a1a101 Fix changes done in an extension not applied when clicking on 'Share' immediately without a preview or navigation in the editor (#6098) 2023-12-18 16:02:27 +01:00
AlexandreS
cb9d98d027 Wait 10 program openings before displaying GamesDashboard info (#6097)
Don't show in changelog
2023-12-18 15:51:23 +01:00
D8H
064c3f1572 Fix warning in the object list (#6091)
- don't show in changelog
2023-12-15 18:46:40 +01:00
878 changed files with 57790 additions and 48136 deletions

View File

@@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 20
cache: "npm"
cache-dependency-path: "newIDE/app/package-lock.json"
@@ -58,7 +58,7 @@ jobs:
working-directory: newIDE/app
- name: Create a Pull Request with the changes
uses: peter-evans/create-pull-request@v5
uses: peter-evans/create-pull-request@v6
with:
commit-message: Update translations [skip ci]
branch: chore/update-translations

View File

@@ -56,6 +56,7 @@ blocks:
- name: GDJS typing and documentation generation
commands:
- checkout
- cache restore newIDE-app-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum newIDE/app/package-lock.json)
- cache restore GDJS-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/package-lock.json)
- cache restore GDJS-tests-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/tests/package-lock.json)
- cd GDJS

1
.vscode/tasks.json vendored
View File

@@ -8,6 +8,7 @@
"group": "build",
"label": "Start development server",
"detail": "Starts the GDevelop development server.",
"options": { "env": { "NODE_OPTIONS": "--max-old-space-size=8192" } },
"problemMatcher": [
{
"owner": "cra",

View File

@@ -714,6 +714,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
metadata.GetType() == "tilesetResource" ||
metadata.GetType() == "videoResource" ||
metadata.GetType() == "model3DResource" ||
metadata.GetType() == "atlasResource" ||
metadata.GetType() == "spineResource" ||
// Deprecated, old parameter names:
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {

View File

@@ -1281,8 +1281,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Enable an effect on the object"),
_("Enable effect _PARAM1_ on _PARAM0_: _PARAM2_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("yesorno", _("Enable?"))
@@ -1297,8 +1297,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"names) in the effects window."),
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Property name"))
@@ -1315,8 +1315,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"names) in the effects window."),
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Property name"))
@@ -1332,8 +1332,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"names) in the effects window."),
_("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Property name"))
@@ -1347,8 +1347,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Check if the effect on an object is enabled."),
_("Effect _PARAM1_ of _PARAM0_ is enabled"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.MarkAsSimple()

View File

@@ -27,7 +27,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension.AddInstructionOrExpressionGroupMetadata(_("Layers and cameras"))
.SetIcon("res/conditions/camera24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
.SetIcon("res/actions/effect24.png");
.SetIcon("res/actions/effect_black.svg");
extension
.AddExpressionAndConditionAndAction(
@@ -450,8 +450,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"), "", true)
.SetDefaultValue("\"\"")
@@ -469,8 +469,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"), "", true)
.SetDefaultValue("\"\"")
@@ -488,8 +488,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"names) in the effects window."),
_("Enable _PARAM3_ for effect _PARAM2_ of layer _PARAM1_: _PARAM4_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"), "", true)
.SetDefaultValue("\"\"")
@@ -504,8 +504,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("The effect on a layer is enabled"),
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
_(""),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"), "", true)
.SetDefaultValue("\"\"")
@@ -518,8 +518,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("Enable an effect on a layer"),
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"), "", true)
.SetDefaultValue("\"\"")

View File

@@ -24,7 +24,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
.SetIcon("res/actions/effect24.png");
.SetIcon("res/actions/effect_black.svg");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"EffectBehavior",
@@ -32,7 +32,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
"Effect",
_("Apply visual effects to objects."),
"",
"res/actions/effect24.png",
"res/actions/effect_black.svg",
"EffectBehavior",
std::make_shared<gd::Behavior>(),
std::make_shared<gd::BehaviorsSharedData>())
@@ -43,8 +43,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
_("Enable an effect on the object"),
_("Enable effect _PARAM2_ on _PARAM0_: _PARAM3_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
.AddParameter("objectEffectName", _("Effect name"))
@@ -58,8 +58,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
.AddParameter("objectEffectName", _("Effect name"))
@@ -75,8 +75,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
.AddParameter("objectEffectName", _("Effect name"))
@@ -91,8 +91,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
"names) in the effects window."),
_("Enable _PARAM3_ for effect _PARAM2_ of _PARAM0_: _PARAM4_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
.AddParameter("objectEffectName", _("Effect name"))
@@ -105,8 +105,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
_("Check if the effect on an object is enabled."),
_("Effect _PARAM2_ of _PARAM0_ is enabled"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
.AddParameter("objectEffectName", _("Effect name"))

View File

@@ -24,7 +24,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
.SetIcon("res/actions/effect24.png");
.SetIcon("res/actions/effect_black.svg");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"FlippableBehavior",

View File

@@ -114,6 +114,8 @@ public:
/**
* \brief Erase any existing include file and add the specified include.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
virtual AbstractFunctionMetadata &
SetIncludeFile(const gd::String &includeFile) = 0;

View File

@@ -13,12 +13,15 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Tools/Log.h"
namespace gd {
const std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::badProperties;
BehaviorMetadata::BehaviorMetadata(
const gd::String& extensionNamespace_,
const gd::String& nameWithNamespace,
@@ -47,8 +50,14 @@ BehaviorMetadata::BehaviorMetadata(
"BehaviorMetadata is valid for: " + nameWithNamespace);
}
if (instance) instance->SetTypeName(nameWithNamespace);
if (sharedDatasInstance) sharedDatasInstance->SetTypeName(nameWithNamespace);
if (instance) {
instance->SetTypeName(nameWithNamespace);
instance->InitializeContent();
}
if (sharedDatasInstance) {
sharedDatasInstance->SetTypeName(nameWithNamespace);
sharedDatasInstance->InitializeContent();
}
}
gd::InstructionMetadata& BehaviorMetadata::AddCondition(
@@ -405,10 +414,30 @@ gd::Behavior& BehaviorMetadata::Get() const {
return *instance;
}
std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::GetProperties() const {
if (!instance) {
return badProperties;
}
// TODO Properties should be declared on BehaviorMetadata directly.
// - Add 2 `properties` members (one for shared properties)
// - Add methods to declare new properties
return instance->GetProperties();
}
gd::BehaviorsSharedData* BehaviorMetadata::GetSharedDataInstance() const {
return sharedDatasInstance.get();
}
std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::GetSharedProperties() const {
if (!sharedDatasInstance) {
return badProperties;
}
// TODO Properties should be declared on BehaviorMetadata directly.
// - Add 2 `properties` members (one for shared properties)
// - Add methods to declare new properties
return sharedDatasInstance->GetProperties();
}
const std::vector<gd::String>& BehaviorMetadata::GetRequiredBehaviorTypes() const {
requiredBehaviors.clear();
for (auto& property : Get().GetProperties()) {

View File

@@ -18,6 +18,7 @@ class BehaviorsSharedData;
class MultipleInstructionMetadata;
class InstructionMetadata;
class ExpressionMetadata;
class PropertyDescriptor;
} // namespace gd
namespace gd {
@@ -204,6 +205,8 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
* \brief Erase any existing include file and add the specified include.
* \note The requirement may vary depending on the platform: Most of the time,
* the include file contains the declaration of the behavior.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
BehaviorMetadata& SetIncludeFile(const gd::String& includeFile) override;
@@ -302,6 +305,15 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
*/
gd::Behavior& Get() const;
/**
* \brief Called when the IDE wants to know about the custom properties of the
* behavior.
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Return the associated gd::BehaviorsSharedData, handling behavior
* shared data, if any (nullptr if none).
@@ -311,6 +323,15 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
*/
gd::BehaviorsSharedData* GetSharedDataInstance() const;
/**
* \brief Called when the IDE wants to know about the custom shared properties
* of the behavior.
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
std::map<gd::String, gd::PropertyDescriptor> GetSharedProperties() const;
/**
* \brief Return a reference to a map containing the names of the actions
* (as keys) and the metadata associated with (as values).
@@ -357,6 +378,8 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
std::shared_ptr<gd::Behavior> instance;
std::shared_ptr<gd::BehaviorsSharedData> sharedDatasInstance;
static const std::map<gd::String, gd::PropertyDescriptor> badProperties;
};
} // namespace gd

View File

@@ -65,6 +65,8 @@ class GD_CORE_API EffectMetadata {
/**
* \brief Clear any existing include file and add the specified include file.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
EffectMetadata& SetIncludeFile(const gd::String& includeFile);

View File

@@ -288,6 +288,8 @@ class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
/**
* \brief Erase any existing include file and add the specified include.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
ExpressionMetadata& SetIncludeFile(
const gd::String& includeFile) override {

View File

@@ -494,6 +494,8 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
/**
* \brief Erase any existing include file and add the specified include.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) override {
codeExtraInformation.includeFiles.clear();

View File

@@ -142,6 +142,8 @@ public:
* \brief Erase any existing include file and add the specified include.
* \note The requirement may vary depending on the platform: Most of the time,
* the include file contains the declaration of the behavior.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
virtual InstructionOrExpressionContainerMetadata &
SetIncludeFile(const gd::String &includeFile) = 0;

View File

@@ -150,6 +150,10 @@ class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata
return *this;
}
/**
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
MultipleInstructionMetadata &SetIncludeFile(const gd::String &includeFile) override {
if (expression)
expression->SetIncludeFile(includeFile);

View File

@@ -264,6 +264,8 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
* \brief Erase any existing include file and add the specified include.
* \note The requirement may vary depending on the platform: Most of the time,
* the include file contains the declaration of the object.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
ObjectMetadata& SetIncludeFile(const gd::String& includeFile) override;

View File

@@ -217,7 +217,9 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "jsonResource" ||
parameterType == "tilemapResource" ||
parameterType == "tilesetResource" ||
parameterType == "model3DResource";
parameterType == "model3DResource" ||
parameterType == "atlasResource" ||
parameterType == "spineResource";
}
return false;
}

View File

@@ -285,22 +285,6 @@ class GD_CORE_API PlatformExtension {
std::shared_ptr<gd::Behavior> instance,
std::shared_ptr<gd::BehaviorsSharedData> sharedDatasInstance);
/**
* \brief Declare a new events based behavior as being part of the extension.
*
* \param name The name of the behavior
* \param fullname The user friendly name of the behavior
* \param description The user friendly description of the behavior
* \param group The behavior category label
* \param icon The icon of the behavior.
*/
gd::BehaviorMetadata& AddEventsBasedBehavior(
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& group_,
const gd::String& icon_);
/**
* \brief Declare a new effect as being part of the extension.
* \param name The internal name of the effect (also called effect type).

View File

@@ -39,7 +39,8 @@ struct VariableAndItsParent {
};
/**
* \brief Find the last parent (i.e: the variables container) of a node representing a variable.
* \brief Find the last parent (i.e: the variables container) of a node
* representing a variable.
*
* Useful for completions, to know which variables can be entered in a node.
*
@@ -48,13 +49,12 @@ struct VariableAndItsParent {
class GD_CORE_API ExpressionVariableParentFinder
: public ExpressionParser2NodeWorker {
public:
static VariableAndItsParent GetLastParentOfNode(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node) {
gd::ExpressionVariableParentFinder typeFinder(
platform, projectScopedContainers);
gd::ExpressionVariableParentFinder typeFinder(platform,
projectScopedContainers);
node.Visit(typeFinder);
return typeFinder.variableAndItsParent;
}
@@ -70,7 +70,8 @@ class GD_CORE_API ExpressionVariableParentFinder
variableNode(nullptr),
thisIsALegacyPrescopedVariable(false),
bailOutBecauseEmptyVariableName(false),
legacyPrescopedVariablesContainer(nullptr){};
legacyPrescopedVariablesContainer(nullptr),
variableAndItsParent{} {};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
@@ -135,10 +136,10 @@ class GD_CORE_API ExpressionVariableParentFinder
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.name.empty() && node.child) {
// A variable accessor should always have a name if it has a child (i.e: another accessor).
// While the parser may have generated an empty name,
// flag this so we avoid finding a wrong parent (and so, run the risk of giving
// wrong autocompletions).
// A variable accessor should always have a name if it has a child (i.e:
// another accessor). While the parser may have generated an empty name,
// flag this so we avoid finding a wrong parent (and so, run the risk of
// giving wrong autocompletions).
bailOutBecauseEmptyVariableName = true;
}
childVariableNames.insert(childVariableNames.begin(), node.name);
@@ -300,7 +301,8 @@ class GD_CORE_API ExpressionVariableParentFinder
const std::vector<gd::String>& childVariableNames,
size_t startIndex = 0) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue when visiting nodes.
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
const gd::Variable* currentVariable = &variable;
@@ -332,8 +334,8 @@ class GD_CORE_API ExpressionVariableParentFinder
}
}
// Return the last parent of the chain of variables (so not the last variable
// but the one before it).
// Return the last parent of the chain of variables (so not the last
// variable but the one before it).
return {.parentVariable = currentVariable};
}
@@ -341,14 +343,16 @@ class GD_CORE_API ExpressionVariableParentFinder
const gd::VariablesContainer& variablesContainer,
const std::vector<gd::String>& childVariableNames) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue when visiting nodes.
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
if (childVariableNames.empty())
return {}; // There is no "parent" to the variables container itself.
const gd::String& firstChildName = *childVariableNames.begin();
const gd::Variable* variable = variablesContainer.Has(firstChildName) ?
&variablesContainer.Get(firstChildName) : nullptr;
const gd::Variable* variable = variablesContainer.Has(firstChildName)
? &variablesContainer.Get(firstChildName)
: nullptr;
if (childVariableNames.size() == 1 || !variable)
return {// Only one child: the parent is the variables container itself.
.parentVariablesContainer = &variablesContainer};

View File

@@ -0,0 +1,111 @@
/*
* GDevelop Core
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ObjectAssetSerializer.h"
#include <algorithm>
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Project/AssetResourcePathCleaner.h"
#include "GDCore/IDE/Project/ResourcesInUseHelper.h"
#include "GDCore/IDE/Project/ResourcesRenamer.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
namespace gd {
gd::String
ObjectAssetSerializer::GetObjectExtensionName(const gd::Object &object) {
const gd::String &type = object.GetType();
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
return separatorIndex != std::string::npos ? type.substr(0, separatorIndex)
: "";
}
void ObjectAssetSerializer::SerializeTo(
gd::Project &project, const gd::Object &object,
const gd::String &objectFullName, SerializerElement &element,
std::vector<gd::String> &usedResourceNames) {
auto cleanObject = object.Clone();
cleanObject->GetVariables().Clear();
cleanObject->GetEffects().Clear();
for (auto &&behaviorName : cleanObject->GetAllBehaviorNames()) {
cleanObject->RemoveBehavior(behaviorName);
}
gd::String extensionName = GetObjectExtensionName(*cleanObject);
element.SetAttribute("id", "");
element.SetAttribute("name", "");
element.SetAttribute("license", "");
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
auto &extension = project.GetEventsFunctionsExtension(extensionName);
element.SetAttribute("description", extension.GetShortDescription());
}
element.SetAttribute("gdevelopVersion", "");
element.SetAttribute("version", "");
element.SetIntAttribute("animationsCount", 1);
element.SetIntAttribute("maxFramesCount", 1);
// TODO Find the right object dimensions.
element.SetIntAttribute("width", 0);
element.SetIntAttribute("height", 0);
SerializerElement &authorsElement = element.AddChild("authors");
authorsElement.ConsiderAsArrayOf("author");
SerializerElement &tagsElement = element.AddChild("tags");
tagsElement.ConsiderAsArrayOf("tag");
SerializerElement &objectAssetsElement = element.AddChild("objectAssets");
objectAssetsElement.ConsiderAsArrayOf("objectAsset");
SerializerElement &objectAssetElement =
objectAssetsElement.AddChild("objectAsset");
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
SerializerElement &resourcesElement =
objectAssetElement.AddChild("resources");
resourcesElement.ConsiderAsArrayOf("resource");
auto &resourcesManager = project.GetResourcesManager();
gd::ResourcesInUseHelper resourcesInUse(resourcesManager);
cleanObject->GetConfiguration().ExposeResources(resourcesInUse);
for (auto &&resourceName : resourcesInUse.GetAllResources()) {
if (resourceName.length() == 0) {
continue;
}
usedResourceNames.push_back(resourceName);
auto &resource = resourcesManager.GetResource(resourceName);
SerializerElement &resourceElement = resourcesElement.AddChild("resource");
resource.SerializeTo(resourceElement);
resourceElement.SetAttribute("kind", resource.GetKind());
resourceElement.SetAttribute("name", resource.GetName());
}
SerializerElement &requiredExtensionsElement =
objectAssetElement.AddChild("requiredExtensions");
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
SerializerElement &requiredExtensionElement =
requiredExtensionsElement.AddChild("requiredExtension");
requiredExtensionElement.SetAttribute("extensionName", extensionName);
requiredExtensionElement.SetAttribute("extensionVersion", "1.0.0");
}
// TODO This can be removed when the asset script no longer require it.
SerializerElement &customizationElement =
objectAssetElement.AddChild("customization");
customizationElement.ConsiderAsArrayOf("empty");
}
} // namespace gd

View File

@@ -0,0 +1,57 @@
/*
* GDevelop Core
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Object;
class ExtensionDependency;
class PropertyDescriptor;
class Project;
class Layout;
class ArbitraryResourceWorker;
class InitialInstance;
class SerializerElement;
class EffectsContainer;
class AbstractFileSystem;
} // namespace gd
namespace gd {
/**
* \brief Serialize objects into an asset for the store.
*
* \ingroup IDE
*/
class GD_CORE_API ObjectAssetSerializer {
public:
/**
* \brief Serialize an object into an asset.
*
* \param project The project that contains the object and its resources.
* It's not actually modified.
* \param object The object to serialize as an asset.
* \param objectFullName The object name with spaces instead of PascalCase.
* \param element The element where the asset is serialize.
* \param usedResourceNames Return the names of the resources used by the asset.
*/
static void
SerializeTo(gd::Project &project, const gd::Object &object,
const gd::String &objectFullName, SerializerElement &element,
std::vector<gd::String> &usedResourceNames);
~ObjectAssetSerializer(){};
private:
ObjectAssetSerializer(){};
static gd::String GetObjectExtensionName(const gd::Object &object);
};
} // namespace gd

View File

@@ -52,6 +52,16 @@ void ArbitraryResourceWorker::ExposeModel3D(gd::String& resourceName){
// do.
};
void ArbitraryResourceWorker::ExposeAtlas(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
// 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.
@@ -120,6 +130,7 @@ void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
gd::String potentiallyUpdatedTargetResourceName = targetResourceName;
ExposeResourceWithType(targetResource.GetKind(), potentiallyUpdatedTargetResourceName);
ExposeEmbeddeds(potentiallyUpdatedTargetResourceName);
if (potentiallyUpdatedTargetResourceName != targetResourceName) {
// The resource name was renamed. Also update the mapping.
@@ -176,6 +187,14 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
ExposeVideo(resourceName);
return;
}
if (resourceType == "atlas") {
ExposeAtlas(resourceName);
return;
}
if (resourceType == "spine") {
ExposeSpine(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}
@@ -244,6 +263,14 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
gd::String updatedParameterValue = parameterValue;
worker.ExposeModel3D(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "atlasResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeAtlas(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "spineResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeSpine(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
}
});

View File

@@ -96,6 +96,16 @@ public:
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);
/**
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
*/
virtual void ExposeAtlas(gd::String &resourceName);
/**
* \brief Expose an spine, which is always a reference to a "spine" resource.
*/
virtual void ExposeSpine(gd::String &resourceName);
/**
* \brief Expose a video, which is always a reference to a "video" resource.
@@ -123,14 +133,15 @@ public:
*/
virtual void ExposeEmbeddeds(gd::String &resourceName);
protected:
gd::ResourcesManager * resourcesManager;
private:
/**
* \brief Expose a resource: resources that have a file are
* exposed as file (see ExposeFile).
*/
void ExposeResource(gd::Resource &resource);
gd::ResourcesManager * resourcesManager;
};
/**

View File

@@ -0,0 +1,74 @@
/*
* GDevelop Core
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "AssetResourcePathCleaner.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/String.h"
namespace gd {
void AssetResourcePathCleaner::ExposeImage(gd::String &imageName) {
ExposeResourceAsFile(imageName);
}
void AssetResourcePathCleaner::ExposeAudio(gd::String &audioName) {
ExposeResourceAsFile(audioName);
}
void AssetResourcePathCleaner::ExposeFont(gd::String &fontName) {
ExposeResourceAsFile(fontName);
}
void AssetResourcePathCleaner::ExposeJson(gd::String &jsonName) {
ExposeResourceAsFile(jsonName);
}
void AssetResourcePathCleaner::ExposeTilemap(gd::String &tilemapName) {
ExposeResourceAsFile(tilemapName);
}
void AssetResourcePathCleaner::ExposeTileset(gd::String &tilesetName) {
ExposeResourceAsFile(tilesetName);
}
void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) {
ExposeResourceAsFile(videoName);
}
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) {
ExposeResourceAsFile(bitmapFontName);
}
void AssetResourcePathCleaner::ExposeResourceAsFile(gd::String &resourceName) {
auto &resource = resourcesManager->GetResource(resourceName);
gd::String file = resource.GetFile();
ExposeFile(file);
resourcesNameReverseMap[file] = resourceName;
resourceName = file;
}
void AssetResourcePathCleaner::ExposeFile(gd::String &resourceFilePath) {
size_t slashPos = resourceFilePath.find_last_of("/");
size_t antiSlashPos = resourceFilePath.find_last_of("\\");
size_t baseNamePos =
slashPos == String::npos
? antiSlashPos == String::npos ? 0 : (antiSlashPos + 1)
: antiSlashPos == String::npos ? (slashPos + 1)
: slashPos > antiSlashPos ? (slashPos + 1)
: (antiSlashPos + 1);
gd::String baseName =
baseNamePos != 0
? resourceFilePath.substr(baseNamePos, resourceFilePath.length())
: resourceFilePath;
resourcesFileNameMap[resourceFilePath] = baseName;
resourceFilePath = baseName;
}
} // namespace gd

View File

@@ -0,0 +1,65 @@
/*
* GDevelop Core
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
#include "GDCore/String.h"
#include <map>
#include <memory>
#include <vector>
namespace gd {
class AbstractFileSystem;
class Project;
} // namespace gd
namespace gd {
/**
* \brief AssetResourcePathCleaner is used when exporting an object as an asset.
* It removes the folder from the path.
*
* \see ArbitraryResourceWorker
*
* \ingroup IDE
*/
class GD_CORE_API AssetResourcePathCleaner : public ArbitraryResourceWorker {
public:
AssetResourcePathCleaner(
gd::ResourcesManager &resourcesManager,
std::map<gd::String, gd::String> &resourcesFileNameMap_,
std::map<gd::String, gd::String> &resourcesNameReverseMap_)
: ArbitraryResourceWorker(resourcesManager),
resourcesFileNameMap(resourcesFileNameMap_),
resourcesNameReverseMap(resourcesNameReverseMap_){};
virtual ~AssetResourcePathCleaner(){};
void ExposeImage(gd::String &imageName) override;
void ExposeAudio(gd::String &audioName) override;
void ExposeFont(gd::String &fontName) override;
void ExposeJson(gd::String &jsonName) override;
void ExposeTilemap(gd::String &tilemapName) override;
void ExposeTileset(gd::String &tilesetName) override;
void ExposeVideo(gd::String &videoName) override;
void ExposeBitmapFont(gd::String &bitmapFontName) override;
void ExposeFile(gd::String &resource) override;
protected:
void ExposeResourceAsFile(gd::String &resourceName);
/**
* New file names that can be accessed by their original name.
*/
std::map<gd::String, gd::String> &resourcesFileNameMap;
/**
* Original resource names that can be accessed by their new name.
*/
std::map<gd::String, gd::String> &resourcesNameReverseMap;
};
} // namespace gd

View File

@@ -79,6 +79,12 @@ public:
virtual void ExposeModel3D(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeAtlas(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeSpine(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
void MatchResourceName(gd::String& otherResourceName) {
if (otherResourceName == resourceName) matchesResourceName = true;

View File

@@ -3,9 +3,10 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef PROJECTRESOURCESCOPIER_H
#define PROJECTRESOURCESCOPIER_H
#pragma once
#include "GDCore/String.h"
namespace gd {
class Project;
class AbstractFileSystem;
@@ -47,6 +48,7 @@ class GD_CORE_API ProjectResourcesCopier {
bool updateOriginalProject,
bool preserveAbsoluteFilenames = true,
bool preserveDirectoryStructure = true);
private:
static bool CopyAllResourcesTo(gd::Project& originalProject,
gd::Project& clonedProject,
@@ -57,5 +59,3 @@ private:
};
} // namespace gd
#endif // PROJECTRESOURCESCOPIER_H

View File

@@ -0,0 +1,25 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ResourcesInUseHelper.h"
namespace gd {
const std::vector<gd::String> ResourcesInUseHelper::resourceTypes = {
"image", "audio", "font", "json", "tilemap",
"tileset", "video", "bitmapFont", "model3D"};
const std::vector<gd::String> &ResourcesInUseHelper::GetAllResources() {
allResources.clear();
for (auto &&resourceType : gd::ResourcesInUseHelper::resourceTypes) {
for (auto &&resourceName : GetAll(resourceType)) {
allResources.push_back(resourceName);
}
}
return allResources;
}
} // namespace gd

View File

@@ -4,9 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef IMAGESUSEDINVENTORIZER_H
#define IMAGESUSEDINVENTORIZER_H
#pragma once
#include <set>
#include <vector>
@@ -38,6 +36,7 @@ public:
: gd::ArbitraryResourceWorker(resourcesManager){};
virtual ~ResourcesInUseHelper(){};
const std::vector<gd::String>& GetAllResources();
std::set<gd::String>& GetAllImages() { return GetAll("image"); };
std::set<gd::String>& GetAllAudios() { return GetAll("audio"); };
std::set<gd::String>& GetAllFonts() { return GetAll("font"); };
@@ -47,6 +46,8 @@ public:
std::set<gd::String>& GetAllVideos() { return GetAll("video"); };
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
std::set<gd::String>& GetAll3DModels() { return GetAll("model3D"); };
std::set<gd::String>& GetAllAtlases() { return GetAll("atlas"); };
std::set<gd::String>& GetAllSpines() { return GetAll("spine"); };
std::set<gd::String>& GetAll(const gd::String& resourceType) {
if (resourceType == "image") return allImages;
if (resourceType == "audio") return allAudios;
@@ -57,6 +58,8 @@ public:
if (resourceType == "video") return allVideos;
if (resourceType == "bitmapFont") return allBitmapFonts;
if (resourceType == "model3D") return allModel3Ds;
if (resourceType == "atlas") return allAtlases;
if (resourceType == "spine") return allSpines;
return emptyResources;
};
@@ -64,35 +67,42 @@ public:
virtual void ExposeFile(gd::String& resource) override{
/*Don't care, we just list resource names*/
};
virtual void ExposeImage(gd::String& imageResourceName) override {
allImages.insert(imageResourceName);
virtual void ExposeImage(gd::String& resourceName) override {
allImages.insert(resourceName);
};
virtual void ExposeAudio(gd::String& audioResourceName) override {
allAudios.insert(audioResourceName);
virtual void ExposeAudio(gd::String& resourceName) override {
allAudios.insert(resourceName);
};
virtual void ExposeFont(gd::String& fontResourceName) override {
allFonts.insert(fontResourceName);
virtual void ExposeFont(gd::String& resourceName) override {
allFonts.insert(resourceName);
};
virtual void ExposeJson(gd::String& jsonResourceName) override {
allJsons.insert(jsonResourceName);
virtual void ExposeJson(gd::String& resourceName) override {
allJsons.insert(resourceName);
};
virtual void ExposeTilemap(gd::String& tilemapResourceName) override {
allTilemaps.insert(tilemapResourceName);
virtual void ExposeTilemap(gd::String& resourceName) override {
allTilemaps.insert(resourceName);
};
virtual void ExposeTileset(gd::String& tilesetResourceName) override {
allTilesets.insert(tilesetResourceName);
virtual void ExposeTileset(gd::String& resourceName) override {
allTilesets.insert(resourceName);
};
virtual void ExposeVideo(gd::String& videoResourceName) override {
allVideos.insert(videoResourceName);
virtual void ExposeVideo(gd::String& resourceName) override {
allVideos.insert(resourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontResourceName) override {
allBitmapFonts.insert(bitmapFontResourceName);
virtual void ExposeBitmapFont(gd::String& resourceName) override {
allBitmapFonts.insert(resourceName);
};
virtual void ExposeModel3D(gd::String& resourceName) override {
allModel3Ds.insert(resourceName);
};
virtual void ExposeAtlas(gd::String& resourceName) override {
allAtlases.insert(resourceName);
};
virtual void ExposeSpine(gd::String& resourceName) override {
allSpines.insert(resourceName);
};
protected:
std::vector<gd::String> allResources;
std::set<gd::String> allImages;
std::set<gd::String> allAudios;
std::set<gd::String> allFonts;
@@ -102,10 +112,11 @@ public:
std::set<gd::String> allVideos;
std::set<gd::String> allBitmapFonts;
std::set<gd::String> allModel3Ds;
std::set<gd::String> allAtlases;
std::set<gd::String> allSpines;
std::set<gd::String> emptyResources;
static const std::vector<gd::String> resourceTypes;
};
} // namespace gd
#endif // IMAGESUSEDINVENTORIZER_H
#endif

View File

@@ -28,7 +28,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
auto stripToFilenameOnly = [&]() {
fs.MakeAbsolute(resourceFullFilename, baseDirectory);
SetNewFilename(resourceFullFilename, fs.FileNameFrom(resourceFullFilename));
resourceFilename = oldFilenames[resourceFullFilename];
resourceFilename = newFilenames[resourceFullFilename];
};
// if we do not want to preserve the folders at all,
@@ -45,7 +45,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
gd::String relativeFilename = resourceFullFilename;
if (fs.MakeRelative(relativeFilename, baseDirectory)) {
SetNewFilename(resourceFullFilename, relativeFilename);
resourceFilename = oldFilenames[resourceFullFilename];
resourceFilename = newFilenames[resourceFullFilename];
} else {
// The filename cannot be made relative. Consider that it is absolute.
// Just strip the filename to its file part
@@ -63,7 +63,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
void ResourcesMergingHelper::SetNewFilename(gd::String oldFilename,
gd::String newFilename) {
if (oldFilenames.find(oldFilename) != oldFilenames.end()) return;
if (newFilenames.find(oldFilename) != newFilenames.end()) return;
// Extract baseName and extension from the new filename
size_t extensionPos = newFilename.find_last_of(".");
@@ -80,13 +80,13 @@ void ResourcesMergingHelper::SetNewFilename(gd::String oldFilename,
gd::NewNameGenerator::Generate(
baseName,
[this, extension](const gd::String& newBaseName) {
return newFilenames.find(newBaseName + extension) !=
newFilenames.end();
return oldFilenames.find(newBaseName + extension) !=
oldFilenames.end();
}) +
extension;
oldFilenames[oldFilename] = finalFilename;
newFilenames[finalFilename] = oldFilename;
newFilenames[oldFilename] = finalFilename;
oldFilenames[finalFilename] = oldFilename;
}
void ResourcesMergingHelper::SetBaseDirectory(

View File

@@ -64,19 +64,25 @@ public:
* the Base Directory.
*/
std::map<gd::String, gd::String>& GetAllResourcesOldAndNewFilename() {
return oldFilenames;
return newFilenames;
};
/**
* Resources merging helper collects all resources filenames and update these
* filenames.
*/
virtual void ExposeFile(gd::String& resource) override;
void ExposeFile(gd::String& resource) override;
protected:
void SetNewFilename(gd::String oldFilename, gd::String newFilename);
/**
* Original file names that can be accessed by their new name.
*/
std::map<gd::String, gd::String> oldFilenames;
/**
* New file names that can be accessed by their original name.
*/
std::map<gd::String, gd::String> newFilenames;
gd::String baseDirectory;
bool preserveDirectoriesStructure; ///< If set to true, the directory

View File

@@ -65,6 +65,12 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeModel3D(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
virtual void ExposeAtlas(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
virtual void ExposeSpine(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
private:
void RenameIfNeeded(gd::String& resourceName) {

View File

@@ -80,6 +80,12 @@ private:
void ExposeModel3D(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
void ExposeAtlas(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
void ExposeSpine(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
std::set<gd::String> resourceNames;
};

View File

@@ -16,8 +16,8 @@
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
#include "GDCore/IDE/Events/CustomObjectTypeRenamer.h"
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsVariableReplacer.h"
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
@@ -138,7 +138,8 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
}
}
VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
VariablesChangeset
WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
gd::Project &project,
const gd::SerializerElement &oldSerializedVariablesContainer,
const gd::VariablesContainer &newVariablesContainer) {
@@ -149,9 +150,9 @@ VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer
if (oldVariablesContainer.GetPersistentUuid() !=
newVariablesContainer.GetPersistentUuid()) {
gd::LogWarning(
_("Called ComputeChangesetForVariablesContainer on variables containers "
"that are different - they can't be compared."));
gd::LogWarning(_(
"Called ComputeChangesetForVariablesContainer on variables containers "
"that are different - they can't be compared."));
return changeset;
}
@@ -192,14 +193,11 @@ VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer
}
void WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
gd::Project &project,
const gd::VariablesContainer &newVariablesContainer,
const gd::VariablesChangeset& changeset) {
gd::Project &project, const gd::VariablesContainer &newVariablesContainer,
const gd::VariablesChangeset &changeset) {
gd::EventsVariableReplacer eventsVariableReplacer(
project.GetCurrentPlatform(),
newVariablesContainer,
changeset.oldToNewVariableNames,
changeset.removedVariableNames);
project.GetCurrentPlatform(), newVariablesContainer,
changeset.oldToNewVariableNames, changeset.removedVariableNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsVariableReplacer);
}
@@ -743,14 +741,14 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(),
properties,
oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -813,14 +811,14 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(),
properties,
oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -870,14 +868,14 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(),
properties,
oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1351,7 +1349,6 @@ void WholeProjectRefactorer::DoRenameBehavior(
gd::Project &project, const gd::String &oldBehaviorType,
const gd::String &newBehaviorType,
const gd::ProjectBrowser &projectBrowser) {
// Rename behavior in required behavior properties
auto requiredBehaviorRenamer =
gd::RequiredBehaviorRenamer(oldBehaviorType, newBehaviorType);
@@ -1378,7 +1375,6 @@ void WholeProjectRefactorer::DoRenameBehavior(
void WholeProjectRefactorer::DoRenameObject(
gd::Project &project, const gd::String &oldObjectType,
const gd::String &newObjectType, const gd::ProjectBrowser &projectBrowser) {
// Rename object type in objects lists.
auto customObjectTypeRenamer =
gd::CustomObjectTypeRenamer(oldObjectType, newObjectType);
@@ -1398,7 +1394,8 @@ void WholeProjectRefactorer::DoRenameObject(
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
gd::Project &project, gd::Layout &layout, const gd::String &objectName,
bool isObjectGroup, bool removeEventsAndGroups) {
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
// Remove object in the current layout
if (removeEventsAndGroups) {
@@ -1447,7 +1444,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
if (oldName == newName || newName.empty() || oldName.empty())
return;
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(
@@ -1536,10 +1534,19 @@ void WholeProjectRefactorer::RenameLayer(gd::Project &project,
const gd::String &newName) {
if (oldName == newName || newName.empty() || oldName.empty())
return;
gd::ProjectElementRenamer projectElementRenamer(project.GetCurrentPlatform(),
"layer", oldName, newName);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, projectElementRenamer);
layout.GetInitialInstances().MoveInstancesToLayer(oldName, newName);
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto &externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().MoveInstancesToLayer(oldName, newName);
}
}
void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
@@ -1552,8 +1559,8 @@ void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "layerEffectName", oldName, newName);
projectElementRenamer.SetLayerConstraint(layer.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, projectElementRenamer);
}
void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
@@ -1566,8 +1573,8 @@ void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectAnimationName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, projectElementRenamer);
}
void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
@@ -1580,8 +1587,8 @@ void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectPointName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, projectElementRenamer);
}
void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
@@ -1594,8 +1601,8 @@ void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "objectEffectName", oldName, newName);
projectElementRenamer.SetObjectConstraint(object.GetName());
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
projectElementRenamer);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, projectElementRenamer);
}
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsBasedObject(
@@ -1617,9 +1624,12 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer, const gd::String &objectName,
bool isObjectGroup, bool removeEventsAndGroups) {
// In theory we should pass a ProjectScopedContainers to this function so it does not have to construct one.
// In practice, this is ok because we only deal with objects.
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsContainer, objectsContainer);
// In theory we should pass a ProjectScopedContainers to this function so it
// does not have to construct one. In practice, this is ok because we only
// deal with objects.
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(
globalObjectsContainer, objectsContainer);
if (removeEventsAndGroups) {
gd::EventsRefactorer::RemoveObjectInEvents(
@@ -1655,9 +1665,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) {
// In theory we should pass a ProjectScopedContainers to this function so it does not have to construct one.
// In practice, this is ok because we only deal with objects.
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsContainer, objectsContainer);
// In theory we should pass a ProjectScopedContainers to this function so it
// does not have to construct one. In practice, this is ok because we only
// deal with objects.
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(
globalObjectsContainer, objectsContainer);
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
@@ -1705,7 +1718,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
if (layout.HasObjectNamed(objectName))
continue;
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
removeEventsAndGroups);
}
}
@@ -1753,7 +1766,8 @@ size_t WholeProjectRefactorer::GetLayoutAndExternalLayoutLayerInstancesCount(
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto &externalLayout = project.GetExternalLayout(name);
count += externalLayout.GetInitialInstances().GetLayerInstancesCount(layerName);
count +=
externalLayout.GetInitialInstances().GetLayerInstancesCount(layerName);
}
return count;
}

View File

@@ -4,8 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/Behavior.h"
#include <iostream>
#include "GDCore/Project/PropertyDescriptor.h"
namespace gd {

View File

@@ -155,6 +155,10 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
worker.ExposeBitmapFont(newPropertyValue);
} else if (resourceType == "model3D") {
worker.ExposeModel3D(newPropertyValue);
} else if (resourceType == "atlas") {
worker.ExposeAtlas(newPropertyValue);
} else if (resourceType == "spine") {
worker.ExposeSpine(newPropertyValue);
}
if (newPropertyValue != oldPropertyValue) {

View File

@@ -13,7 +13,9 @@ EventsBasedObject::EventsBasedObject()
: AbstractEventsBasedEntity(
"MyObject",
gd::EventsFunctionsContainer::FunctionOwner::Object),
ObjectsContainer() {
ObjectsContainer(),
isRenderedIn3D(false),
isTextContainer(false) {
}
EventsBasedObject::~EventsBasedObject() {}
@@ -30,6 +32,9 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
if (isRenderedIn3D) {
element.SetBoolAttribute("is3D", true);
}
if (isTextContainer) {
element.SetBoolAttribute("isTextContainer", true);
}
AbstractEventsBasedEntity::SerializeTo(element);
SerializeObjectsTo(element.AddChild("objects"));
@@ -40,6 +45,7 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
defaultName = element.GetStringAttribute("defaultName");
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
AbstractEventsBasedEntity::UnserializeFrom(project, element);
UnserializeObjectsFrom(project, element.GetChild("objects"));

View File

@@ -85,6 +85,19 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
*/
bool IsRenderedIn3D() const { return isRenderedIn3D; }
/**
* \brief Declare a TextContainer capability.
*/
EventsBasedObject& MarkAsTextContainer(bool isTextContainer_) {
isTextContainer = isTextContainer_;
return *this;
}
/**
* \brief Return true if the object needs a TextContainer capability.
*/
bool IsTextContainer() const { return isTextContainer; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(gd::Project& project,
@@ -93,6 +106,7 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
private:
gd::String defaultName;
bool isRenderedIn3D;
bool isTextContainer;
};
} // namespace gd

View File

@@ -39,6 +39,7 @@ void Layer::SetCameraCount(std::size_t n) {
void Layer::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("renderingType", GetRenderingType());
element.SetAttribute("cameraType", GetCameraType());
element.SetAttribute("visibility", GetVisibility());
element.SetAttribute("isLocked", IsLocked());
element.SetAttribute("isLightingLayer", IsLightingLayer());
@@ -78,6 +79,7 @@ void Layer::SerializeTo(SerializerElement& element) const {
void Layer::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name", "", "Name"));
SetRenderingType(element.GetStringAttribute("renderingType", ""));
SetCameraType(element.GetStringAttribute("cameraType", "perspective"));
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
SetLocked(element.GetBoolAttribute("isLocked", false));
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));

View File

@@ -104,10 +104,17 @@ class GD_CORE_API Layer {
const gd::String& GetName() const { return name; }
const gd::String& GetRenderingType() const { return renderingType; }
void SetRenderingType(const gd::String& renderingType_) {
renderingType = renderingType_;
}
const gd::String& GetCameraType() const { return cameraType; }
void SetCameraType(const gd::String& cameraType_) {
cameraType = cameraType_;
}
/**
* \brief Change if layer is displayed or not
*/
@@ -268,6 +275,7 @@ class GD_CORE_API Layer {
gd::String name; ///< The name of the layer
gd::String renderingType; ///< The rendering type: "" (empty), "2d", "3d" or
///< "2d+3d".
gd::String cameraType;
bool isVisible; ///< True if the layer is visible
bool isLocked; ///< True if the layer is locked
bool isLightingLayer; ///< True if the layer is used to display lights and

View File

@@ -120,9 +120,6 @@ class GD_CORE_API Object {
*/
const gd::String& GetType() const { return configuration->GetType(); }
/** \brief Shortcut to check if the object is a 3D object.
*/
bool Is3DObject() const { return configuration->Is3DObject(); }
///@}
/** \name Behaviors management

View File

@@ -5,11 +5,8 @@
*/
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -20,7 +17,7 @@ namespace gd {
ObjectConfiguration::~ObjectConfiguration() {}
ObjectConfiguration::ObjectConfiguration(): is3DObject(false) {}
ObjectConfiguration::ObjectConfiguration() {}
std::map<gd::String, gd::PropertyDescriptor> ObjectConfiguration::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;

View File

@@ -63,20 +63,12 @@ class GD_CORE_API ObjectConfiguration {
*/
void SetType(const gd::String& type_) {
type = type_;
// For now, as a shortcut, consider only the objects from the built-in 3D extension
// to be 3D object.
is3DObject = type.find("Scene3D::") == 0;
}
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
/** \brief Shortcut to check if the object is a 3D object.
*/
bool Is3DObject() const { return is3DObject; }
/** \name Object properties
* Reading and updating object configuration properties
*/
@@ -180,7 +172,6 @@ class GD_CORE_API ObjectConfiguration {
protected:
gd::String type; ///< Which type of object is represented by this
///< configuration.
bool is3DObject;
/**
* \brief Derived object configuration can redefine this method to load

View File

@@ -102,21 +102,19 @@ std::unique_ptr<gd::Object> Project::CreateObject(
behavior->SetDefaultBehavior(true);
};
if (Project::HasEventsBasedObject(objectType)) {
addDefaultBehavior("EffectCapability::EffectBehavior");
addDefaultBehavior("ResizableCapability::ResizableBehavior");
addDefaultBehavior("ScalableCapability::ScalableBehavior");
addDefaultBehavior("FlippableCapability::FlippableBehavior");
} else {
auto& objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
for (auto& behaviorType : objectMetadata.GetDefaultBehaviors()) {
auto &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (!MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
addDefaultBehavior(behaviorType);
}
}
// During project deserialization, event-based object metadata are not yet
// generated. Default behaviors will be added by
// MetadataDeclarationHelper::UpdateCustomObjectDefaultBehaviors
else if (!project.HasEventsBasedObject(objectType)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
return std::move(object);
}

View File

@@ -29,7 +29,15 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
for (const gd::String& information : extraInformation) {
extraInformationElement.AddChild("").SetStringValue(information);
}
element.AddChild("hidden").SetBoolValue(hidden);
if (hidden) {
element.AddChild("hidden").SetBoolValue(hidden);
}
if (deprecated) {
element.AddChild("deprecated").SetBoolValue(deprecated);
}
if (advanced) {
element.AddChild("advanced").SetBoolValue(advanced);
}
}
void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
@@ -58,6 +66,12 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
hidden = element.HasChild("hidden")
? element.GetChild("hidden").GetBoolValue()
: false;
deprecated = element.HasChild("deprecated")
? element.GetChild("deprecated").GetBoolValue()
: false;
advanced = element.HasChild("advanced")
? element.GetChild("advanced").GetBoolValue()
: false;
}
void PropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {

View File

@@ -30,12 +30,16 @@ class GD_CORE_API PropertyDescriptor {
* \param propertyValue The value of the property.
*/
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string"), label(""), hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {}
: currentValue(propertyValue), type("string"), label(""), hidden(false),
deprecated(false), advanced(false),
measurementUnit(gd::MeasurementUnit::GetUndefined()) {}
/**
* \brief Empty constructor creating an empty property to be displayed.
*/
PropertyDescriptor() : hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {};
PropertyDescriptor()
: hidden(false), deprecated(false), advanced(false),
measurementUnit(gd::MeasurementUnit::GetUndefined()){};
/**
* \brief Destructor
@@ -142,6 +146,32 @@ class GD_CORE_API PropertyDescriptor {
*/
bool IsHidden() const { return hidden; }
/**
* \brief Set if the property is deprecated.
*/
PropertyDescriptor& SetDeprecated(bool enable = true) {
deprecated = enable;
return *this;
}
/**
* \brief Check if the property is deprecated.
*/
bool IsDeprecated() const { return deprecated; }
/**
* \brief Set if the property is marked as advanced.
*/
PropertyDescriptor& SetAdvanced(bool enable = true) {
advanced = enable;
return *this;
}
/**
* \brief Check if the property is marked as advanced.
*/
bool IsAdvanced() const { return advanced; }
/** \name Serialization
*/
///@{
@@ -179,6 +209,8 @@ class GD_CORE_API PropertyDescriptor {
///< choices, if a property is a displayed as a combo
///< box.
bool hidden;
bool deprecated;
bool advanced;
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
};

View File

@@ -93,6 +93,10 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<BitmapFontResource>();
else if (kind == "model3D")
return std::make_shared<Model3DResource>();
else if (kind == "atlas")
return std::make_shared<AtlasResource>();
else if (kind == "spine")
return std::make_shared<SpineResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -756,6 +760,20 @@ void Model3DResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}
void AtlasResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void AtlasResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void AtlasResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {

View File

@@ -373,6 +373,21 @@ class GD_CORE_API JsonResource : public Resource {
gd::String file;
};
/**
* \brief Describe a spine json file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API SpineResource : public JsonResource {
public:
SpineResource() : JsonResource() { SetKind("spine"); };
virtual ~SpineResource(){};
virtual SpineResource* Clone() const override {
return new SpineResource(*this);
}
};
/**
* \brief Describe a tilemap file used by a project.
*
@@ -507,6 +522,32 @@ class GD_CORE_API Model3DResource : public Resource {
gd::String file;
};
/**
* \brief Describe an atlas file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API AtlasResource : public Resource {
public:
AtlasResource() : Resource() { SetKind("atlas"); };
virtual ~AtlasResource(){};
virtual AtlasResource* Clone() const override {
return new AtlasResource(*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; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*

View File

@@ -489,7 +489,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"Effect",
_("Apply visual effects to objects."),
"",
"res/actions/effect24.png", "EffectBehavior",
"res/actions/effect_black.svg", "EffectBehavior",
std::make_shared<gd::Behavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetHidden();

View File

@@ -203,6 +203,23 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto node = parser.ParseExpression("abcd[0]");
REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"No object, variable or property with this name found.");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 0);
}
{
auto node = parser.ParseExpression("abcd[0].efg");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
@@ -214,6 +231,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto node = parser.ParseExpression("abcd.efg.hij");
REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
@@ -762,6 +785,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto node = parser.ParseExpression("abcd.efg.hij");
REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
@@ -1495,6 +1524,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto node =
parser.ParseExpression("MyNonExistingSceneVariable");
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
@@ -1516,6 +1551,25 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid scene variables (2 levels, variable and child do not exist)") {
{
auto node =
parser.ParseExpression("MyNonExistingSceneVariable.MyNonExistingChild");
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name.");
}
}
SECTION("Valid object variables (1 level)") {
{
auto node =
@@ -1586,6 +1640,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
auto node =
parser.ParseExpression("MyNonExistingSpriteObject.MyVariable");
// Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);

View File

@@ -0,0 +1,147 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/ObjectAssetSerializer.h"
#include "DummyPlatform.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/CustomObjectConfiguration.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
#include <string>
using namespace gd;
TEST_CASE("ObjectAssetSerializer", "[common]") {
SECTION("Can serialize custom objects as assets") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
eventsBasedObject.SetFullName("My events based object");
eventsBasedObject.SetDescription("An events based object for test");
eventsBasedObject.InsertNewObject(project, "MyExtension::Sprite", "MyChild",
0);
auto &resourceManager = project.GetResourcesManager();
gd::ImageResource imageResource;
imageResource.SetName("assets/Idle.png");
imageResource.SetFile("assets/Idle.png");
imageResource.SetSmooth(true);
resourceManager.AddResource(imageResource);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object = layout.InsertNewObject(
project, "MyEventsExtension::MyEventsBasedObject", "MyObject", 0);
auto &configuration = object.GetConfiguration();
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(&configuration);
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(
&customObjectConfiguration->GetChildObjectConfiguration("MyChild"));
REQUIRE(spriteConfiguration != nullptr);
{
gd::Animation animation;
animation.SetName("Idle");
animation.SetDirectionsCount(1);
auto &direction = animation.GetDirection(0);
gd::Sprite frame;
frame.SetImageName("assets/Idle.png");
direction.AddSprite(frame);
spriteConfiguration->AddAnimation(animation);
}
SerializerElement assetElement;
std::vector<gd::String> usedResourceNames;
ObjectAssetSerializer::SerializeTo(project, object, "My Object",
assetElement, usedResourceNames);
// This list is used to copy resource files.
REQUIRE(usedResourceNames.size() == 1);
REQUIRE(usedResourceNames[0] == "assets/Idle.png");
// Check that the project is left untouched.
REQUIRE(resourceManager.HasResource("assets/Idle.png"));
REQUIRE(resourceManager.GetResource("assets/Idle.png").GetFile() ==
"assets/Idle.png");
REQUIRE(!resourceManager.HasResource("Idle.png"));
REQUIRE(assetElement.HasChild("objectAssets"));
auto &objectAssetsElement = assetElement.GetChild("objectAssets");
objectAssetsElement.ConsiderAsArrayOf("objectAsset");
REQUIRE(objectAssetsElement.GetChildrenCount() == 1);
auto &objectAssetElement = objectAssetsElement.GetChild(0);
REQUIRE(objectAssetElement.HasChild("requiredExtensions"));
auto &requiredExtensionsElement =
objectAssetElement.GetChild("requiredExtensions");
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
REQUIRE(requiredExtensionsElement.GetChildrenCount() == 1);
auto &requiredExtensionElement = requiredExtensionsElement.GetChild(0);
REQUIRE(requiredExtensionElement.GetStringAttribute("extensionName") ==
"MyEventsExtension");
// Resources are renamed according to asset script naming conventions.
REQUIRE(objectAssetElement.HasChild("resources"));
auto &resourcesElement = objectAssetElement.GetChild("resources");
resourcesElement.ConsiderAsArrayOf("resource");
REQUIRE(resourcesElement.GetChildrenCount() == 1);
{
auto &resourceElement = resourcesElement.GetChild(0);
REQUIRE(resourceElement.GetStringAttribute("name") == "assets/Idle.png");
REQUIRE(resourceElement.GetStringAttribute("file") == "assets/Idle.png");
REQUIRE(resourceElement.GetStringAttribute("kind") == "image");
REQUIRE(resourceElement.GetBoolAttribute("smoothed") == true);
}
// Resources used in object configuration are updated.
REQUIRE(objectAssetElement.HasChild("object"));
auto &objectElement = objectAssetElement.GetChild("object");
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") ==
"MyEventsExtension::MyEventsBasedObject");
auto &childrenContentElement = objectElement.GetChild("childrenContent");
REQUIRE(childrenContentElement.HasChild("MyChild"));
auto &childElement = childrenContentElement.GetChild("MyChild");
REQUIRE(childElement.HasChild("animations"));
auto &animationsElement = childElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") == "Idle");
auto &directionsElement = animationElement.GetChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
REQUIRE(directionsElement.GetChildrenCount() == 1);
auto &directionElement = directionsElement.GetChild(0);
auto &spritesElement = directionElement.GetChild("sprites");
spritesElement.ConsiderAsArrayOf("sprite");
REQUIRE(spritesElement.GetChildrenCount() == 1);
auto &spriteElement = spritesElement.GetChild(0);
REQUIRE(spriteElement.GetStringAttribute("image") == "assets/Idle.png");
}
}

View File

@@ -3474,6 +3474,72 @@ TEST_CASE("RenameLayer", "[common]") {
"MyExtension::CameraCenterX(\"layerA\")");
}
SECTION("Renaming a layer also moves the instances on this layer and of the associated external layouts") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &layout = project.InsertNewLayout("My layout", 0);
auto &otherLayout = project.InsertNewLayout("My other layout", 1);
layout.InsertNewLayer("My layer", 0);
otherLayout.InsertNewLayer("My layer", 0);
auto &externalLayout =
project.InsertNewExternalLayout("My external layout", 0);
auto &otherExternalLayout =
project.InsertNewExternalLayout("My other external layout", 0);
externalLayout.SetAssociatedLayout("My layout");
otherExternalLayout.SetAssociatedLayout("My other layout");
auto &initialInstances = layout.GetInitialInstances();
auto &initialInstance1 = initialInstances.InsertNewInitialInstance();
initialInstance1.SetLayer("My layer");
auto &initialInstance2 = initialInstances.InsertNewInitialInstance();
initialInstance2.SetLayer("My layer");
auto &initialInstance3 = initialInstances.InsertNewInitialInstance();
initialInstance3.SetLayer("");
auto &externalInitialInstances = externalLayout.GetInitialInstances();
auto &externalInitialInstance1 = externalInitialInstances.InsertNewInitialInstance();
externalInitialInstance1.SetLayer("My layer");
auto &externalInitialInstance2 = externalInitialInstances.InsertNewInitialInstance();
externalInitialInstance2.SetLayer("My layer");
auto &externalInitialInstance3 = externalInitialInstances.InsertNewInitialInstance();
externalInitialInstance3.SetLayer("");
auto &otherInitialInstances = otherLayout.GetInitialInstances();
auto &otherInitialInstance1 = otherInitialInstances.InsertNewInitialInstance();
otherInitialInstance1.SetLayer("My layer");
auto &otherExternalInitialInstances = otherExternalLayout.GetInitialInstances();
auto &otherExternalInitialInstance1 = otherExternalInitialInstances.InsertNewInitialInstance();
otherExternalInitialInstance1.SetLayer("My layer");
REQUIRE(initialInstance1.GetLayer() == "My layer");
REQUIRE(initialInstance2.GetLayer() == "My layer");
REQUIRE(initialInstance3.GetLayer() == "");
REQUIRE(externalInitialInstance1.GetLayer() == "My layer");
REQUIRE(externalInitialInstance2.GetLayer() == "My layer");
REQUIRE(externalInitialInstance3.GetLayer() == "");
REQUIRE(otherInitialInstance1.GetLayer() == "My layer");
REQUIRE(otherExternalInitialInstance1.GetLayer() == "My layer");
gd::WholeProjectRefactorer::RenameLayer(project, layout, "My layer", "My new layer");
// Instances on the renamed layer are moved to the new layer.
REQUIRE(initialInstance1.GetLayer() == "My new layer");
REQUIRE(initialInstance2.GetLayer() == "My new layer");
REQUIRE(initialInstance3.GetLayer() == "");
// Instances on the renamed layer of external layouts are moved to the new layer.
REQUIRE(externalInitialInstance1.GetLayer() == "My new layer");
REQUIRE(externalInitialInstance2.GetLayer() == "My new layer");
REQUIRE(externalInitialInstance3.GetLayer() == "");
// Instances on the renamed layer of other layouts & external layouts are not moved.
REQUIRE(otherInitialInstance1.GetLayer() == "My layer");
REQUIRE(otherExternalInitialInstance1.GetLayer() == "My layer");
}
SECTION("Can rename a layer when a layer parameter is empty") {
gd::Project project;
gd::Platform platform;

View File

@@ -154,7 +154,23 @@ namespace gdjs {
* @return The Z position of the rendered object.
*/
getDrawableZ(): float {
return this.getZ();
return this._z;
}
/**
* Return the bottom Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMinZ(): number {
return this.getDrawableZ();
}
/**
* Return the top Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMaxZ(): number {
return this.getDrawableZ() + this.getDepth();
}
/**

View File

@@ -24,8 +24,8 @@ namespace gdjs {
updatePosition() {
this._threeObject3D.position.set(
this._object.x + this._object.getWidth() / 2,
this._object.y + this._object.getHeight() / 2,
this._object.getX() + this._object.getWidth() / 2,
this._object.getY() + this._object.getHeight() / 2,
this._object.getZ() + this._object.getDepth() / 2
);
}

View File

@@ -103,6 +103,27 @@ namespace gdjs {
flipZ(enable: boolean): void;
isFlippedZ(): boolean;
/**
* Return the bottom Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMinZ(): number;
/**
* Return the top Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMaxZ(): number;
}
export namespace Base3DHandler {
export const is3D = (
object: gdjs.RuntimeObject
): object is gdjs.RuntimeObject & gdjs.Base3DHandler => {
//@ts-ignore We are checking if the methods are present.
return object.getZ && object.setZ;
};
}
/**
@@ -202,6 +223,14 @@ namespace gdjs {
isFlippedZ(): boolean {
return this.object.isFlippedZ();
}
getUnrotatedAABBMinZ(): number {
return this.object.getUnrotatedAABBMinZ();
}
getUnrotatedAABBMaxZ(): number {
return this.object.getUnrotatedAABBMaxZ();
}
}
gdjs.registerBehavior('Scene3D::Base3DBehavior', gdjs.Base3DBehavior);

View File

@@ -0,0 +1,89 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::Bloom',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
if (typeof THREE === 'undefined') {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
shaderPass: THREE_ADDONS.UnrealBloomPass;
_isEnabled: boolean;
constructor() {
this.shaderPass = new THREE_ADDONS.UnrealBloomPass(
new THREE.Vector2(256, 256),
1,
0,
0
);
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().addPostProcessingPass(this.shaderPass);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().removePostProcessingPass(this.shaderPass);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'strength') {
this.shaderPass.strength = value;
}
if (parameterName === 'radius') {
this.shaderPass.radius = value;
}
if (parameterName === 'threshold') {
this.shaderPass.threshold = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'strength') {
return this.shaderPass.strength;
}
if (parameterName === 'radius') {
return this.shaderPass.radius;
}
if (parameterName === 'threshold') {
return this.shaderPass.threshold;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {}
updateColorParameter(parameterName: string, value: number): void {}
getColorParameter(parameterName: string): number {
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -0,0 +1,80 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::BrightnessAndContrast',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
if (typeof THREE === 'undefined') {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
shaderPass: THREE_ADDONS.ShaderPass;
_isEnabled: boolean;
constructor() {
this.shaderPass = new THREE_ADDONS.ShaderPass(
THREE_ADDONS.BrightnessContrastShader
);
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().addPostProcessingPass(this.shaderPass);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().removePostProcessingPass(this.shaderPass);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'brightness') {
this.shaderPass.uniforms[parameterName].value = value;
}
if (parameterName === 'contrast') {
this.shaderPass.uniforms[parameterName].value = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'brightness') {
return this.shaderPass.uniforms[parameterName].value;
}
if (parameterName === 'contrast') {
return this.shaderPass.uniforms[parameterName].value;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {}
updateColorParameter(parameterName: string, value: number): void {}
getColorParameter(parameterName: string): number {
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -0,0 +1,373 @@
namespace gdjs {
export interface Object3DDataContent {
width: float;
height: float;
depth: float;
}
/** Base parameters for {@link gdjs.RuntimeObject3D} */
export interface Object3DData extends ObjectData {
/** The base parameters of the RuntimeObject3D */
content: Object3DDataContent;
}
/**
* Base class for 3D custom objects.
*/
export class CustomRuntimeObject3D
extends gdjs.CustomRuntimeObject
implements gdjs.Base3DHandler {
/**
* Position on the Z axis.
*/
private _z: float = 0;
private _minZ: float = 0;
private _maxZ: float = 0;
private _scaleZ: float = 1;
private _flippedZ: boolean = false;
/**
* Euler angle with the `ZYX` order.
*
* Note that `_rotationZ` is `angle` from `gdjs.RuntimeObject`.
*/
private _rotationX: float = 0;
/**
* Euler angle with the `ZYX` order.
*
* Note that `_rotationZ` is `angle` from `gdjs.RuntimeObject`.
*/
private _rotationY: float = 0;
private _customCenterZ: float = 0;
private static _temporaryVector = new THREE.Vector3();
constructor(
parent: gdjs.RuntimeInstanceContainer,
objectData: Object3DData & CustomObjectConfiguration
) {
super(parent, objectData);
this._renderer.reinitialize(this, parent);
}
protected _createRender() {
const parent = this._runtimeScene;
return new gdjs.CustomRuntimeObject3DRenderer(
this,
this._instanceContainer,
parent
);
}
protected _reinitializeRenderer(): void {
this.getRenderer().reinitialize(this, this.getParent());
}
getRenderer(): gdjs.CustomRuntimeObject3DRenderer {
return super.getRenderer() as gdjs.CustomRuntimeObject3DRenderer;
}
get3DRendererObject() {
// It can't be null because Three.js is always loaded
// when a custom 3D object is used.
return this.getRenderer().get3DRendererObject()!;
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
super.extraInitializationFromInitialInstance(initialInstanceData);
if (initialInstanceData.depth !== undefined)
this.setDepth(initialInstanceData.depth);
}
/**
* Set the object position on the Z axis.
*/
setZ(z: float): void {
if (z === this._z) return;
this._z = z;
this.getRenderer().updatePosition();
}
/**
* Get the object position on the Z axis.
*/
getZ(): float {
return this._z;
}
/**
* Get the Z position of the rendered object.
*
* For most objects, this will returns the same value as getZ(). But if the
* object has an origin that is not the same as the point (0,0,0) of the
* object displayed, getDrawableZ will differ.
*
* @return The Z position of the rendered object.
*/
getDrawableZ(): float {
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return this._z + this._minZ;
}
/**
* Return the Z position of the object center, **relative to the object Z
* position** (`getDrawableX`).
*
* Use `getCenterZInScene` to get the position of the center in the scene.
*
* @return the Z position of the object center, relative to
* `getDrawableZ()`.
*/
getCenterZ(): float {
return this.getDepth() / 2;
}
getCenterZInScene(): float {
return this.getDrawableZ() + this.getCenterZ();
}
setCenterZInScene(z: float): void {
this.setZ(z + this._z - (this.getDrawableZ() + this.getCenterZ()));
}
/**
* Return the bottom Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMinZ(): number {
return this.getDrawableZ();
}
/**
* Return the top Z of the object.
* Rotations around X and Y are not taken into account.
*/
getUnrotatedAABBMaxZ(): number {
return this.getDrawableZ() + this.getDepth();
}
/**
* Set the object rotation on the X axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
setRotationX(angle: float): void {
this._rotationX = angle;
this.getRenderer().updateRotation();
}
/**
* Set the object rotation on the Y axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
setRotationY(angle: float): void {
this._rotationY = angle;
this.getRenderer().updateRotation();
}
/**
* Get the object rotation on the X axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
getRotationX(): float {
return this._rotationX;
}
/**
* Get the object rotation on the Y axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
getRotationY(): float {
return this._rotationY;
}
/**
* Turn the object around the scene x axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundX(deltaAngle: float): void {
const axisX = gdjs.CustomRuntimeObject3D._temporaryVector;
axisX.set(1, 0, 0);
const mesh = this.get3DRendererObject();
mesh.rotateOnWorldAxis(axisX, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* Turn the object around the scene y axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundY(deltaAngle: float): void {
const axisY = gdjs.CustomRuntimeObject3D._temporaryVector;
axisY.set(0, 1, 0);
const mesh = this.get3DRendererObject();
mesh.rotateOnWorldAxis(axisY, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* Turn the object around the scene z axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundZ(deltaAngle: float): void {
const axisZ = gdjs.CustomRuntimeObject3D._temporaryVector;
axisZ.set(0, 0, 1);
const mesh = this.get3DRendererObject();
mesh.rotateOnWorldAxis(axisZ, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* @return the internal width of the object according to its children.
*/
getUnscaledDepth(): float {
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return this._maxZ - this._minZ;
}
_updateUntransformedHitBoxes(): void {
super._updateUntransformedHitBoxes();
let minZ = Number.MAX_VALUE;
let maxZ = -Number.MAX_VALUE;
for (const childInstance of this._instanceContainer.getAdhocListOfAllInstances()) {
if (!childInstance.isIncludedInParentCollisionMask()) {
continue;
}
if (!gdjs.Base3DHandler.is3D(childInstance)) {
continue;
}
minZ = Math.min(minZ, childInstance.getUnrotatedAABBMinZ());
maxZ = Math.max(maxZ, childInstance.getUnrotatedAABBMaxZ());
}
if (minZ === Number.MAX_VALUE) {
// The unscaled size can't be 0 because setWidth and setHeight wouldn't
// have any effect.
minZ = 0;
maxZ = 1;
}
this._minZ = minZ;
this._maxZ = maxZ;
}
/**
* @returns the center Z from the local origin (0;0).
*/
getUnscaledCenterZ(): float {
if (this.hasCustomRotationCenter()) {
return this._customCenterZ;
}
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return (this._minZ + this._maxZ) / 2;
}
/**
* The center of rotation is defined relatively to the origin (the object
* position).
* This avoids the center to move when children push the bounds.
*
* When no custom center is defined, it will move
* to stay at the center of the children bounds.
*
* @param x coordinate of the custom center
* @param y coordinate of the custom center
*/
setRotationCenter3D(x: float, y: float, z: float) {
this._customCenterZ = z;
this.setRotationCenter(x, y);
}
/**
* Get the object size on the Z axis (called "depth").
*/
getDepth(): float {
return this.getUnscaledDepth() * this.getScaleZ();
}
/**
* Set the object size on the Z axis (called "depth").
*/
setDepth(depth: float): void {
const unscaledDepth = this.getUnscaledDepth();
if (unscaledDepth !== 0) {
this.setScaleZ(depth / unscaledDepth);
}
}
/**
* Change the scale on X, Y and Z axis of the object.
*
* @param newScale The new scale (must be greater than 0).
*/
setScale(newScale: number): void {
super.setScale(newScale);
this.setScaleZ(newScale);
}
/**
* Change the scale on Z axis of the object (changing its height).
*
* @param newScale The new scale (must be greater than 0).
*/
setScaleZ(newScale: number): void {
if (newScale < 0) {
newScale = 0;
}
if (newScale === Math.abs(this._scaleZ)) {
return;
}
this._scaleZ = newScale * (this._flippedZ ? -1 : 1);
this.getRenderer().updateSize();
}
/**
* Get the scale of the object (or the geometric average of X, Y and Z scale in case they are different).
*
* @return the scale of the object (or the geometric average of X, Y and Z scale in case they are different).
*/
getScale(): number {
const scaleX = this.getScaleX();
const scaleY = this.getScaleY();
const scaleZ = this.getScaleZ();
return scaleX === scaleY && scaleX === scaleZ
? scaleX
: Math.pow(scaleX * scaleY * scaleZ, 1 / 3);
}
/**
* Get the scale of the object on Z axis.
*
* @return the scale of the object on Z axis
*/
getScaleZ(): float {
return Math.abs(this._scaleZ);
}
flipZ(enable: boolean) {
if (enable === this._flippedZ) {
return;
}
this._flippedZ = enable;
this.getRenderer().updateSize();
}
isFlippedZ(): boolean {
return this._flippedZ;
}
}
}

View File

@@ -0,0 +1,135 @@
namespace gdjs {
/**
* The renderer for a {@link gdjs.CustomRuntimeObject3D} using Three.js.
*/
export class CustomRuntimeObject3DRenderer
implements gdjs.RuntimeInstanceContainerRenderer {
_object: gdjs.CustomRuntimeObject3D;
_instanceContainer: gdjs.CustomRuntimeObjectInstanceContainer;
_isContainerDirty: boolean = true;
_threeGroup: THREE.Group;
constructor(
object: gdjs.CustomRuntimeObject3D,
instanceContainer: gdjs.CustomRuntimeObjectInstanceContainer,
parent: gdjs.RuntimeInstanceContainer
) {
this._object = object;
this._instanceContainer = instanceContainer;
this._threeGroup = new THREE.Group();
this._threeGroup.rotation.order = 'ZYX';
const layer = parent.getLayer('');
if (layer) {
layer.getRenderer().add3DRendererObject(this._threeGroup);
}
}
get3DRendererObject(): THREE.Object3D {
return this._threeGroup;
}
getRendererObject() {
return null;
}
reinitialize(
object: gdjs.CustomRuntimeObject3D,
parent: gdjs.RuntimeInstanceContainer
) {
this._object = object;
this._isContainerDirty = true;
const layer = parent.getLayer('');
if (layer) {
layer.getRenderer().add3DRendererObject(this._threeGroup);
}
}
_updateThreeGroup() {
const threeObject3D = this.get3DRendererObject();
const scaleX = this._object.getScaleX();
const scaleY = this._object.getScaleY();
const scaleZ = this._object.getScaleZ();
const pivotX = this._object.getUnscaledCenterX() * scaleX;
const pivotY = this._object.getUnscaledCenterY() * scaleY;
const pivotZ = this._object.getUnscaledCenterZ() * scaleZ;
threeObject3D.rotation.set(
gdjs.toRad(this._object.getRotationX()),
gdjs.toRad(this._object.getRotationY()),
gdjs.toRad(this._object.angle)
);
threeObject3D.position.set(
this._object.isFlippedX() ? pivotX : -pivotX,
this._object.isFlippedY() ? pivotY : -pivotY,
this._object.isFlippedZ() ? pivotZ : -pivotZ
);
threeObject3D.position.applyEuler(threeObject3D.rotation);
threeObject3D.position.x += this._object.getX() + pivotX;
threeObject3D.position.y += this._object.getY() + pivotY;
threeObject3D.position.z += this._object.getZ() + pivotZ;
threeObject3D.scale.set(
this._object.isFlippedX() ? -scaleX : scaleX,
this._object.isFlippedY() ? -scaleY : scaleY,
this._object.isFlippedZ() ? -scaleZ : scaleZ
);
threeObject3D.visible = !this._object.hidden;
this._isContainerDirty = false;
}
/**
* Call this to make sure the object is ready to be rendered.
*/
ensureUpToDate() {
if (this._isContainerDirty) {
this._updateThreeGroup();
}
}
update(): void {
this._isContainerDirty = true;
}
updateX(): void {
this._isContainerDirty = true;
}
updateY(): void {
this._isContainerDirty = true;
}
updateAngle(): void {
this._isContainerDirty = true;
}
updatePosition() {
this._isContainerDirty = true;
}
updateRotation() {
this._isContainerDirty = true;
}
updateSize() {
this._isContainerDirty = true;
}
updateVisibility(): void {
this._threeGroup.visible = !this._object.hidden;
}
updateOpacity(): void {
// Opacity is not handled by 3D custom objects.
}
setLayerIndex(layer: gdjs.RuntimeLayer, index: float): void {
// Layers are not handled for 3D custom objects.
}
}
}

View File

@@ -0,0 +1,74 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::Exposure',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
if (typeof THREE === 'undefined') {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
shaderPass: THREE_ADDONS.ShaderPass;
_isEnabled: boolean;
constructor() {
this.shaderPass = new THREE_ADDONS.ShaderPass(
THREE_ADDONS.ExposureShader
);
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().addPostProcessingPass(this.shaderPass);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().removePostProcessingPass(this.shaderPass);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'exposure') {
this.shaderPass.uniforms[parameterName].value = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'exposure') {
return this.shaderPass.uniforms[parameterName].value;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {}
updateColorParameter(parameterName: string, value: number): void {}
getColorParameter(parameterName: string): number {
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -0,0 +1,80 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::HueAndSaturation',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
if (typeof THREE === 'undefined') {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
shaderPass: THREE_ADDONS.ShaderPass;
_isEnabled: boolean;
constructor() {
this.shaderPass = new THREE_ADDONS.ShaderPass(
THREE_ADDONS.HueSaturationShader
);
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().addPostProcessingPass(this.shaderPass);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().removePostProcessingPass(this.shaderPass);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'hue') {
this.shaderPass.uniforms[parameterName].value = value / 180;
}
if (parameterName === 'saturation') {
this.shaderPass.uniforms[parameterName].value = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'hue') {
return this.shaderPass.uniforms[parameterName].value * 180;
}
if (parameterName === 'saturation') {
return this.shaderPass.uniforms[parameterName].value;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {}
updateColorParameter(parameterName: string, value: number): void {}
getColorParameter(parameterName: string): number {
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -247,7 +239,7 @@ module.exports = {
'Model3DObject',
_('3D Model'),
_('An animated 3D model.'),
'JsPlatform/Extensions/3d_box.svg',
'JsPlatform/Extensions/3d_model.svg',
new gd.Model3DObjectConfiguration()
)
.setCategoryFullName(_('General'))
@@ -811,7 +803,6 @@ module.exports = {
}
const Cube3DObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
Cube3DObject.updateProperty = function (
objectContent,
propertyName,
@@ -860,7 +851,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
Cube3DObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -1100,7 +1090,6 @@ module.exports = {
})
);
// $FlowExpectedError
Cube3DObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -1112,7 +1101,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
Cube3DObject.getInitialInstanceProperties = function (
content,
instance,
@@ -1665,7 +1653,7 @@ module.exports = {
'Change the camera rotation to look at an object. The camera top always face the screen.'
),
_('Change the camera rotation of _PARAM2_ to look at _PARAM1_'),
_("Layers and cameras"),
_('Layers and cameras'),
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
@@ -1939,6 +1927,86 @@ module.exports = {
.setType('number')
.setGroup(_('Orientation'));
}
{
const effect = extension
.addEffect('HueAndSaturation')
.setFullName(_('Hue and saturation'))
.setDescription(_('Adjust hue and saturation.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/HueAndSaturationEffect.js');
const properties = effect.getProperties();
properties
.getOrCreate('hue')
.setValue('0')
.setLabel(_('Hue in degrees (between -180 and 180)'))
.setType('number');
properties
.getOrCreate('saturation')
.setValue('0')
.setLabel(_('Saturation (between -1 and 1)'))
.setType('number');
}
{
const effect = extension
.addEffect('Exposure')
.setFullName(_('Exposure'))
.setDescription(_('Adjust exposure.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/ExposureEffect.js');
const properties = effect.getProperties();
properties
.getOrCreate('exposure')
.setValue('1')
.setLabel(_('Exposure (positive value)'))
.setType('number');
}
{
const effect = extension
.addEffect('Bloom')
.setFullName(_('Bloom'))
.setDescription(_('Apply a bloom effect.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/BloomEffect.js');
const properties = effect.getProperties();
properties
.getOrCreate('strength')
.setValue('1')
.setLabel(_('Strength (between 0 and 3)'))
.setType('number');
properties
.getOrCreate('radius')
.setValue('0')
.setLabel(_('Radius (between 0 and 1)'))
.setType('number');
properties
.getOrCreate('threshold')
.setValue('0')
.setLabel(_('Threshold (between 0 and 1)'))
.setType('number');
}
{
const effect = extension
.addEffect('BrightnessAndContrast')
.setFullName(_('Brightness and contrast.'))
.setDescription(_('Adjust brightness and contrast.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/BrightnessAndContrastEffect.js');
const properties = effect.getProperties();
properties
.getOrCreate('brightness')
.setValue('0')
.setLabel(_('Brightness (between -1 and 1)'))
.setType('number');
properties
.getOrCreate('contrast')
.setValue('0')
.setLabel(_('Contrast (between -1 and 1)'))
.setType('number');
}
// Don't forget to update the alert condition in Model3DEditor.js when
// adding a new light.
@@ -1954,10 +2022,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -1965,17 +2030,13 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {},
registerEditorConfigurations: function (objectsEditorService) {},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const Rendered3DInstance = objectsRenderingService.Rendered3DInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -1983,39 +2044,25 @@ module.exports = {
const THREE_ADDONS = objectsRenderingService.THREE_ADDONS;
const materialIndexToFaceIndex = {
// $FlowFixMe
0: 3,
// $FlowFixMe
1: 2,
// $FlowFixMe
2: 5,
// $FlowFixMe
3: 4,
// $FlowFixMe
4: 0,
// $FlowFixMe
5: 1,
};
const noRepeatTextureVertexIndexToUvMapping = {
// $FlowFixMe
0: [0, 0],
// $FlowFixMe
1: [1, 0],
// $FlowFixMe
2: [0, 1],
// $FlowFixMe
3: [1, 1],
};
const noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ = {
// $FlowFixMe
0: [0, 1],
// $FlowFixMe
1: [0, 0],
// $FlowFixMe
2: [1, 1],
// $FlowFixMe
3: [1, 0],
};
@@ -2061,6 +2108,11 @@ module.exports = {
};
class RenderedCube3DObject2DInstance extends RenderedInstance {
/** @type {number} */
_centerX = 0;
/** @type {number} */
_centerY = 0;
constructor(
project,
layout,
@@ -2077,10 +2129,9 @@ module.exports = {
pixiContainer,
pixiResourcesLoader
);
/**
* Name of the resource that is rendered.
* If no face is visible, this will be null.
*/
// Name of the resource that is rendered.
// If no face is visible, this will be null.
this._renderedResourceName = undefined;
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
@@ -2110,12 +2161,9 @@ module.exports = {
}
static getThumbnail(project, resourcesLoader, objectConfiguration) {
const instance = this._instance;
const textureResourceName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
const textureResourceName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
if (textureResourceName) {
return resourcesLoader.getResourceFullUrl(
project,
@@ -2127,20 +2175,18 @@ module.exports = {
}
updateTextureIfNeeded() {
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (textureName === this._renderedResourceName) return;
this.updateTexture();
}
updateTexture() {
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (!textureName) {
this._renderFallbackObject = true;
@@ -2398,10 +2444,9 @@ module.exports = {
continue;
}
const shouldRepeatTexture =
this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldRepeatTexture = this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldOrientateFacesTowardsY = this._facesOrientation === 'Y';
@@ -2436,13 +2481,16 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
} else {
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
}
}
break;
@@ -2472,13 +2520,16 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
} else {
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
x = -x;
y = -y;
}
@@ -2624,6 +2675,8 @@ module.exports = {
RenderedCube3DObject3DInstance
);
const epsilon = 1 / (1 << 16);
class Model3DRendered2DInstance extends RenderedInstance {
_modelOriginPoint = [0, 0, 0];
@@ -2695,7 +2748,7 @@ module.exports = {
}
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/3d_box.svg';
return 'JsPlatform/Extensions/3d_model.svg';
}
getOriginX() {
@@ -2746,13 +2799,24 @@ module.exports = {
);
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const shouldKeepModelOrigin = !this._originPoint;
if (shouldKeepModelOrigin) {
// Keep the origin as part of the model.
// For instance, a model can be 1 face of a cube and we want to keep the
// inside as part of the object even if it's just void.
// It also avoids to have the origin outside of the object box.
boundingBox.expandByPoint(new THREE.Vector3(0, 0, 0));
}
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
this._modelOriginPoint[0] = -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] = -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] = -boundingBox.min.z / modelDepth;
this._modelOriginPoint[0] =
modelWidth < epsilon ? 0 : -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] =
modelHeight < epsilon ? 0 : -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] =
modelDepth < epsilon ? 0 : -boundingBox.min.z / modelDepth;
// The model is flipped on Y axis.
this._modelOriginPoint[1] = 1 - this._modelOriginPoint[1];
@@ -2761,19 +2825,10 @@ module.exports = {
const centerPoint = this._centerPoint;
if (centerPoint) {
threeObject.position.set(
-(
boundingBox.min.x +
(boundingBox.max.x - boundingBox.min.x) * centerPoint[0]
),
-(boundingBox.min.x + modelWidth * centerPoint[0]),
// The model is flipped on Y axis.
-(
boundingBox.min.y +
(boundingBox.max.y - boundingBox.min.y) * (1 - centerPoint[1])
),
-(
boundingBox.min.z +
(boundingBox.max.z - boundingBox.min.z) * centerPoint[2]
)
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
-(boundingBox.min.z + modelDepth * centerPoint[2])
);
}
@@ -2786,9 +2841,9 @@ module.exports = {
);
// Stretch the model in a 1x1x1 cube.
const scaleX = 1 / modelWidth;
const scaleY = 1 / modelHeight;
const scaleZ = 1 / modelDepth;
const scaleX = modelWidth < epsilon ? 1 : 1 / modelWidth;
const scaleY = modelHeight < epsilon ? 1 : 1 / modelHeight;
const scaleZ = modelDepth < epsilon ? 1 : 1 / modelDepth;
const scaleMatrix = new THREE.Matrix4();
// Flip on Y because the Y axis is on the opposite side of direct basis.
@@ -2799,10 +2854,22 @@ module.exports = {
if (keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
const widthRatio = originalWidth / modelWidth;
const heightRatio = originalHeight / modelHeight;
const depthRatio = originalDepth / modelDepth;
const scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
const widthRatio =
modelWidth < epsilon
? Number.POSITIVE_INFINITY
: originalWidth / modelWidth;
const heightRatio =
modelHeight < epsilon
? Number.POSITIVE_INFINITY
: originalHeight / modelHeight;
const depthRatio =
modelDepth < epsilon
? Number.POSITIVE_INFINITY
: originalDepth / modelDepth;
let scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(scaleRatio)) {
scaleRatio = 1;
}
this._defaultWidth = scaleRatio * modelWidth;
this._defaultHeight = scaleRatio * modelHeight;
@@ -2954,6 +3021,11 @@ module.exports = {
return this.getHeight() * originPoint[1];
}
getOriginZ() {
const originPoint = this.getOriginPoint();
return this.getDepth() * originPoint[2];
}
getCenterX() {
const centerPoint = this.getCenterPoint();
return this.getWidth() * centerPoint[0];
@@ -2964,6 +3036,11 @@ module.exports = {
return this.getHeight() * centerPoint[1];
}
getCenterZ() {
const centerPoint = this.getCenterPoint();
return this.getDepth() * centerPoint[2];
}
getOriginPoint() {
return this._originPoint || this._modelOriginPoint;
}
@@ -2990,12 +3067,24 @@ module.exports = {
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const shouldKeepModelOrigin = !this._originPoint;
if (shouldKeepModelOrigin) {
// Keep the origin as part of the model.
// For instance, a model can be 1 face of a cube and we want to keep the
// inside as part of the object even if it's just void.
// It also avoids to have the origin outside of the object box.
boundingBox.expandByPoint(new THREE.Vector3(0, 0, 0));
}
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
this._modelOriginPoint[0] = -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] = -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] = -boundingBox.min.z / modelDepth;
this._modelOriginPoint[0] =
modelWidth < epsilon ? 0 : -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] =
modelHeight < epsilon ? 0 : -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] =
modelDepth < epsilon ? 0 : -boundingBox.min.z / modelDepth;
// The model is flipped on Y axis.
this._modelOriginPoint[1] = 1 - this._modelOriginPoint[1];
@@ -3004,19 +3093,10 @@ module.exports = {
const centerPoint = this._centerPoint;
if (centerPoint) {
threeObject.position.set(
-(
boundingBox.min.x +
(boundingBox.max.x - boundingBox.min.x) * centerPoint[0]
),
-(boundingBox.min.x + modelWidth * centerPoint[0]),
// The model is flipped on Y axis.
-(
boundingBox.min.y +
(boundingBox.max.y - boundingBox.min.y) * (1 - centerPoint[1])
),
-(
boundingBox.min.z +
(boundingBox.max.z - boundingBox.min.z) * centerPoint[2]
)
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
-(boundingBox.min.z + modelDepth * centerPoint[2])
);
}
@@ -3029,9 +3109,9 @@ module.exports = {
);
// Stretch the model in a 1x1x1 cube.
const scaleX = 1 / modelWidth;
const scaleY = 1 / modelHeight;
const scaleZ = 1 / modelDepth;
const scaleX = modelWidth < epsilon ? 1 : 1 / modelWidth;
const scaleY = modelHeight < epsilon ? 1 : 1 / modelHeight;
const scaleZ = modelDepth < epsilon ? 1 : 1 / modelDepth;
const scaleMatrix = new THREE.Matrix4();
// Flip on Y because the Y axis is on the opposite side of direct basis.
@@ -3042,10 +3122,22 @@ module.exports = {
if (keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
const widthRatio = originalWidth / modelWidth;
const heightRatio = originalHeight / modelHeight;
const depthRatio = originalDepth / modelDepth;
const scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
const widthRatio =
modelWidth < epsilon
? Number.POSITIVE_INFINITY
: originalWidth / modelWidth;
const heightRatio =
modelHeight < epsilon
? Number.POSITIVE_INFINITY
: originalHeight / modelHeight;
const depthRatio =
modelDepth < epsilon
? Number.POSITIVE_INFINITY
: originalDepth / modelDepth;
let scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(scaleRatio)) {
scaleRatio = 1;
}
this._defaultWidth = scaleRatio * modelWidth;
this._defaultHeight = scaleRatio * modelHeight;

View File

@@ -1,6 +1,8 @@
namespace gdjs {
type FloatPoint3D = [float, float, float];
const epsilon = 1 / (1 << 16);
const removeMetalness = (material: THREE.Material): void => {
//@ts-ignore
if (material.metalness) {
@@ -9,7 +11,7 @@ namespace gdjs {
}
};
const removeMetalnessFromMesh = (node: THREE.Object3D<THREE.Event>) => {
const removeMetalnessFromMesh = (node: THREE.Object3D) => {
const mesh = node as THREE.Mesh;
if (!mesh.material) {
return;
@@ -23,9 +25,8 @@ namespace gdjs {
}
};
const traverseToRemoveMetalnessFromMeshes = (
node: THREE.Object3D<THREE.Event>
) => node.traverse(removeMetalnessFromMesh);
const traverseToRemoveMetalnessFromMeshes = (node: THREE.Object3D) =>
node.traverse(removeMetalnessFromMesh);
const convertToBasicMaterial = (
material: THREE.Material
@@ -44,7 +45,7 @@ namespace gdjs {
return basicMaterial;
};
const setBasicMaterialTo = (node: THREE.Object3D<THREE.Event>): void => {
const setBasicMaterialTo = (node: THREE.Object3D): void => {
const mesh = node as THREE.Mesh;
if (!mesh.material) {
return;
@@ -59,9 +60,8 @@ namespace gdjs {
}
};
const traverseToSetBasicMaterialFromMeshes = (
node: THREE.Object3D<THREE.Event>
) => node.traverse(setBasicMaterialTo);
const traverseToSetBasicMaterialFromMeshes = (node: THREE.Object3D) =>
node.traverse(setBasicMaterialTo);
class Model3DRuntimeObject3DRenderer extends gdjs.RuntimeObject3DRenderer {
private _model3DRuntimeObject: gdjs.Model3DRuntimeObject;
@@ -158,12 +158,24 @@ namespace gdjs {
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const shouldKeepModelOrigin = !this._model3DRuntimeObject._originPoint;
if (shouldKeepModelOrigin) {
// Keep the origin as part of the model.
// For instance, a model can be 1 face of a cube and we want to keep the
// inside as part of the object even if it's just void.
// It also avoids to have the origin outside of the object box.
boundingBox.expandByPoint(new THREE.Vector3(0, 0, 0));
}
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
this._modelOriginPoint[0] = -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] = -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] = -boundingBox.min.z / modelDepth;
this._modelOriginPoint[0] =
modelWidth < epsilon ? 0 : -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] =
modelHeight < epsilon ? 0 : -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] =
modelDepth < epsilon ? 0 : -boundingBox.min.z / modelDepth;
// The model is flipped on Y axis.
this._modelOriginPoint[1] = 1 - this._modelOriginPoint[1];
@@ -172,19 +184,10 @@ namespace gdjs {
const centerPoint = this._model3DRuntimeObject._centerPoint;
if (centerPoint) {
threeObject.position.set(
-(
boundingBox.min.x +
(boundingBox.max.x - boundingBox.min.x) * centerPoint[0]
),
-(boundingBox.min.x + modelWidth * centerPoint[0]),
// The model is flipped on Y axis.
-(
boundingBox.min.y +
(boundingBox.max.y - boundingBox.min.y) * (1 - centerPoint[1])
),
-(
boundingBox.min.z +
(boundingBox.max.z - boundingBox.min.z) * centerPoint[2]
)
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
-(boundingBox.min.z + modelDepth * centerPoint[2])
);
}
@@ -197,9 +200,9 @@ namespace gdjs {
);
// Stretch the model in a 1x1x1 cube.
const scaleX = 1 / modelWidth;
const scaleY = 1 / modelHeight;
const scaleZ = 1 / modelDepth;
const scaleX = modelWidth < epsilon ? 1 : 1 / modelWidth;
const scaleY = modelHeight < epsilon ? 1 : 1 / modelHeight;
const scaleZ = modelDepth < epsilon ? 1 : 1 / modelDepth;
const scaleMatrix = new THREE.Matrix4();
// Flip on Y because the Y axis is on the opposite side of direct basis.
@@ -210,10 +213,22 @@ namespace gdjs {
if (keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
const widthRatio = originalWidth / modelWidth;
const heightRatio = originalHeight / modelHeight;
const depthRatio = originalDepth / modelDepth;
const scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
const widthRatio =
modelWidth < epsilon
? Number.POSITIVE_INFINITY
: originalWidth / modelWidth;
const heightRatio =
modelHeight < epsilon
? Number.POSITIVE_INFINITY
: originalHeight / modelHeight;
const depthRatio =
modelDepth < epsilon
? Number.POSITIVE_INFINITY
: originalDepth / modelDepth;
let scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(scaleRatio)) {
scaleRatio = 1;
}
this._object._setOriginalWidth(scaleRatio * modelWidth);
this._object._setOriginalHeight(scaleRatio * modelHeight);

View File

@@ -11,7 +11,11 @@ namespace gdjs {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
const fov = threeCamera ? threeCamera.fov : assumedFovIn2D;
const fov = threeCamera
? threeCamera instanceof THREE.OrthographicCamera
? null
: threeCamera.fov
: assumedFovIn2D;
return layer.getCameraZ(fov, cameraIndex);
};
@@ -24,7 +28,11 @@ namespace gdjs {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
const fov = threeCamera ? threeCamera.fov : assumedFovIn2D;
const fov = threeCamera
? threeCamera instanceof THREE.OrthographicCamera
? null
: threeCamera.fov
: assumedFovIn2D;
layer.setCameraZ(z, fov, cameraIndex);
};
@@ -213,8 +221,11 @@ namespace gdjs {
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 45;
return threeCamera.fov;
return threeCamera
? threeCamera instanceof THREE.OrthographicCamera
? 0
: threeCamera.fov
: assumedFovIn2D;
};
export const setFov = (
@@ -227,7 +238,8 @@ namespace gdjs {
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
if (!threeCamera || threeCamera instanceof THREE.OrthographicCamera)
return;
threeCamera.fov = Math.min(Math.max(angle, 0), 180);
layerRenderer.setThreeCameraDirty(true);

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -789,10 +781,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,11 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -710,10 +709,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,20 +13,11 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
const stringifyOptions = (options) => '["' + options.join('","') + '"]';
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -42,7 +34,6 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bbcode32.png');
var objectBBText = new gd.ObjectJsImplementation();
// $FlowExpectedError
objectBBText.updateProperty = function (
objectContent,
propertyName,
@@ -59,7 +50,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
objectBBText.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -126,7 +116,8 @@ module.exports = {
};
objectBBText.setRawJSONContent(
JSON.stringify({
text: '[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
text:
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
visible: true,
@@ -137,7 +128,6 @@ module.exports = {
})
);
// $FlowExpectedError
objectBBText.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -148,7 +138,6 @@ module.exports = {
) {
return false;
};
// $FlowExpectedError
objectBBText.getInitialInstanceProperties = function (
content,
instance,
@@ -223,10 +212,9 @@ module.exports = {
parameterType === 'string' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)
@@ -276,10 +264,9 @@ module.exports = {
parameterType === 'number' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)
@@ -436,6 +423,21 @@ module.exports = {
addSettersAndGettersToObject(object, setterAndGetterProperties, 'BBText');
object
.addAction(
`SetFontFamily2`,
_('Font family'),
_('Set font family'),
_('Set the font of _PARAM0_ to _PARAM1_'),
'',
'res/actions/font24.png',
'res/actions/font24.png'
)
.addParameter('object', 'BBText', 'BBText', false)
.addParameter('fontResource', _('Font family'), '', false)
.getCodeExtraInformation()
.setFunctionName(`setFontFamily`);
const actions = object.getAllActions();
const conditions = object.getAllConditions();
const expressions = object.getAllExpressions();
@@ -443,6 +445,9 @@ module.exports = {
actions.get('BBText::SetOpacity').setHidden();
conditions.get('BBText::IsOpacity').setHidden();
expressions.get('GetOpacity').setHidden();
// Action deprecated because it's using the `string` type instead of the more
// user-friendly `fontResource` type.
actions.get('BBText::SetFontFamily').setHidden();
return extension;
},
@@ -457,10 +462,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -468,9 +470,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'BBText::BBText',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -483,11 +483,8 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
const MultiStyleText = objectsRenderingService.requireModule(
__dirname,
'pixi-multistyle-text/dist/pixi-multistyle-text.umd'
@@ -496,150 +493,145 @@ module.exports = {
/**
* Renderer for instances of BBText inside the IDE.
*
* @extends RenderedBBTextInstance
* @extends RenderedInstance
* @class RenderedBBTextInstance
* @constructor
*/
function RenderedBBTextInstance(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
class RenderedBBTextInstance extends RenderedInstance {
constructor(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
) {
super(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
const bbTextStyles = {
default: {
// Use a default font family the time for the resource font to be loaded.
fontFamily: 'Arial',
fontSize: '24px',
fill: '#cccccc',
tagStyle: 'bbcode',
wordWrap: true,
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
},
};
const bbTextStyles = {
default: {
// Use a default font family the time for the resource font to be loaded.
fontFamily: 'Arial',
fontSize: '24px',
fill: '#cccccc',
tagStyle: 'bbcode',
wordWrap: true,
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
},
};
this._pixiObject = new MultiStyleText('', bbTextStyles);
this._pixiObject = new MultiStyleText('', bbTextStyles);
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBBTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBBTextInstance.getThumbnail = function (
project,
resourcesLoader,
objectConfiguration
) {
return 'JsPlatform/Extensions/bbcode24.png';
};
/**
* This is called to update the PIXI object on the scene editor
*/
RenderedBBTextInstance.prototype.update = function () {
const properties = this._associatedObjectConfiguration.getProperties();
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
const opacity = properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const color = properties.get('color').getValue();
this._pixiObject.textStyles.default.fill =
objectsRenderingService.rgbOrHexToHexNumber(color);
const fontSize = properties.get('fontSize').getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontResourceName = properties.get('fontFamily').getValue();
if (this._fontResourceName !== fontResourceName) {
this._fontResourceName = fontResourceName;
this._pixiResourcesLoader
.loadFontFamily(this._project, fontResourceName)
.then((fontFamily) => {
// Once the font is loaded, we can use the given fontFamily.
this._pixiObject.textStyles.default.fontFamily = fontFamily;
this._pixiObject.dirty = true;
})
.catch((err) => {
// Ignore errors
console.warn(
'Unable to load font family for RenderedBBTextInstance',
err
);
});
/**
* Return the path to the thumbnail of the specified object.
*/
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/bbcode24.png';
}
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap;
this._pixiObject.dirty = true;
}
/**
* This is called to update the PIXI object on the scene editor
*/
update() {
const properties = this._associatedObjectConfiguration.getProperties();
const align = properties.get('align').getValue();
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;
}
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
}
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
const opacity = +properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject._style.wordWrapWidth !== customWidth
) {
this._pixiObject._style.wordWrapWidth = customWidth;
const color = properties.get('color').getValue();
this._pixiObject.textStyles.default.fill = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const fontSize = properties.get('fontSize').getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontResourceName = properties.get('fontFamily').getValue();
if (this._fontResourceName !== fontResourceName) {
this._fontResourceName = fontResourceName;
this._pixiResourcesLoader
.loadFontFamily(this._project, fontResourceName)
.then((fontFamily) => {
// Once the font is loaded, we can use the given fontFamily.
this._pixiObject.textStyles.default.fontFamily = fontFamily;
this._pixiObject.dirty = true;
})
.catch((err) => {
// Ignore errors
console.warn(
'Unable to load font family for RenderedBBTextInstance',
err
);
});
}
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap;
this._pixiObject.dirty = true;
}
const align = properties.get('align').getValue();
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject._style.wordWrapWidth !== customWidth
) {
this._pixiObject._style.wordWrapWidth = customWidth;
this._pixiObject.dirty = true;
}
}
}
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the width of the instance, when it's not resized.
*/
getDefaultWidth() {
return this._pixiObject.width;
}
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
/**
* Return the height of the instance, when it's not resized.
*/
getDefaultHeight() {
return this._pixiObject.height;
}
}
objectsRenderingService.registerInstanceRenderer(
'BBText::BBText',

View File

@@ -141,6 +141,7 @@ namespace gdjs {
setBBText(text): void {
this._text = text;
this._renderer.updateText();
this.invalidateHitboxes();
}
/**

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -42,7 +34,6 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bitmapfont32.png');
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
bitmapTextObject.updateProperty = function (
objectContent,
propertyName,
@@ -59,7 +50,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
bitmapTextObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -127,7 +117,8 @@ module.exports = {
};
bitmapTextObject.setRawJSONContent(
JSON.stringify({
text: 'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
@@ -139,7 +130,6 @@ module.exports = {
})
);
// $FlowExpectedError
bitmapTextObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -150,7 +140,6 @@ module.exports = {
) {
return false;
};
// $FlowExpectedError
bitmapTextObject.getInitialInstanceProperties = function (
content,
instance,
@@ -176,7 +165,7 @@ module.exports = {
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
)
.setCategoryFullName(_('Text'))
.addDefaultBehavior("TextContainerCapability::TextContainerBehavior")
.addDefaultBehavior('TextContainerCapability::TextContainerBehavior')
.addDefaultBehavior('EffectCapability::EffectBehavior')
.addDefaultBehavior('OpacityCapability::OpacityBehavior')
.addDefaultBehavior('ScalableCapability::ScalableBehavior');
@@ -327,33 +316,33 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addAction(
'SetBitmapFontAndTextureAtlasResourceName2',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.'),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
'',
'res/actions/font24.png',
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter(
'bitmapFontResource',
_('Bitmap font resource name'),
'',
false
)
.addParameter(
'imageResource',
_('Texture atlas resource name'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addAction(
'SetBitmapFontAndTextureAtlasResourceName2',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.'),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
'',
'res/actions/font24.png',
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter(
'bitmapFontResource',
_('Bitmap font resource name'),
'',
false
)
.addParameter(
'imageResource',
_('Texture atlas resource name'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addExpressionAndCondition(
@@ -451,10 +440,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -462,9 +448,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'BitmapText::BitmapTextObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -477,9 +461,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -649,156 +631,144 @@ module.exports = {
};
/**
* Renderer for instances of BitmapText inside the IDE.
*
* @extends RenderedBitmapTextInstance
* @class RenderedBitmapTextInstance
* @constructor
* Return the path to the thumbnail of the specified object.
* This is called to update the PIXI object on the scene editor
*/
function RenderedBitmapTextInstance(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
class RenderedBitmapTextInstance extends RenderedInstance {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/bitmapfont24.png';
}
constructor(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
// We'll track changes of the font to trigger the loading of the new font.
this._currentBitmapFontResourceName = '';
this._currentTextureAtlasResourceName = '';
this._pixiObject = new PIXI.BitmapText('', {
// Use a default font. The proper font will be loaded in `update` method.
fontName: getDefaultBitmapFont().font,
});
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBitmapTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBitmapTextInstance.getThumbnail = function (
project,
resourcesLoader,
objectConfiguration
) {
return 'JsPlatform/Extensions/bitmapfont24.png';
};
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObjectConfiguration.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
this._pixiObject.text = rawText;
const opacity = properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const align = properties.get('align').getValue();
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
this._pixiObject.tint =
objectsRenderingService.rgbOrHexToHexNumber(color);
const scale = properties.get('scale').getValue() || 1;
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
this._currentTextureAtlasResourceName !== textureAtlasResourceName
) {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
super(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
// Temporarily go back to the default font, as the PIXI.BitmapText
// object does not support being displayed with a font not installed at all.
// It will be replaced as soon as the proper font is loaded.
this._pixiObject.fontName = getDefaultBitmapFont().font;
// We'll track changes of the font to trigger the loading of the new font.
this._currentBitmapFontResourceName = '';
this._currentTextureAtlasResourceName = '';
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(
this._pixiResourcesLoader,
this._project,
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
this._pixiObject = new PIXI.BitmapText('', {
// Use a default font. The proper font will be loaded in `update` method.
fontName: getDefaultBitmapFont().font,
});
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
update() {
const properties = this._associatedObjectConfiguration.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
this._pixiObject.text = rawText;
const opacity = +properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const align = properties.get('align').getValue();
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
this._pixiObject.tint = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const scale = +(properties.get('scale').getValue() || 1);
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
this._currentTextureAtlasResourceName !== textureAtlasResourceName
) {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
// Temporarily go back to the default font, as the PIXI.BitmapText
// object does not support being displayed with a font not installed at all.
// It will be replaced as soon as the proper font is loaded.
this._pixiObject.fontName = getDefaultBitmapFont().font;
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(
this._pixiResourcesLoader,
this._project,
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
});
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + (this._pixiObject.textWidth * scale) / 2;
this._pixiObject.position.y =
this._instance.getY() + (this._pixiObject.textHeight * scale) / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
}
this._pixiObject.position.x =
this._instance.getX() + (this._pixiObject.textWidth * scale) / 2;
this._pixiObject.position.y =
this._instance.getY() + (this._pixiObject.textHeight * scale) / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
};
onRemovedFromScene() {
RenderedInstance.prototype.onRemovedFromScene.call(this);
RenderedBitmapTextInstance.prototype.onRemovedFromScene = function () {
RenderedInstance.prototype.onRemovedFromScene.call(this);
const fontName = this._pixiObject.fontName;
this._pixiObject.destroy();
releaseBitmapFont(fontName);
}
const fontName = this._pixiObject.fontName;
this._pixiObject.destroy();
releaseBitmapFont(fontName);
};
/**
* Return the width of the instance, when it's not resized.
*/
getDefaultWidth() {
return this._pixiObject.width;
}
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
/**
* Return the height of the instance, when it's not resized.
*/
getDefaultHeight() {
return this._pixiObject.height;
}
}
objectsRenderingService.registerInstanceRenderer(
'BitmapText::BitmapTextObject',

View File

@@ -26,6 +26,7 @@ set(
TextEntryObject
TextObject
TiledSpriteObject
Spine
TopDownMovementBehavior)
# Automatically add all listed extensions

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -114,10 +106,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,450 +13,389 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
"DeviceSensors",
_("Device sensors"),
_(
"Allow the game to access the sensors of a mobile device."
),
"Matthias Meike",
"Open source (MIT License)"
).setExtensionHelpPath("/all-features/device-sensors")
.setCategory('Input');
extension.addInstructionOrExpressionGroupMetadata(_("Device sensors"))
.setIcon("JsPlatform/Extensions/orientation_active32.png");
extension
.setExtensionInformation(
'DeviceSensors',
_('Device sensors'),
_('Allow the game to access the sensors of a mobile device.'),
'Matthias Meike',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/device-sensors')
.setCategory('Input');
extension
.addInstructionOrExpressionGroupMetadata(_('Device sensors'))
.setIcon('JsPlatform/Extensions/orientation_active32.png');
extension
.addCondition(
"OrientationSensorActive",
_("Sensor active"),
'OrientationSensorActive',
_('Sensor active'),
_(
"The condition is true if the device orientation sensor is currently active"
'The condition is true if the device orientation sensor is currently active'
),
_("Orientation sensor is active"),
_("Orientation"),
"JsPlatform/Extensions/orientation_active32.png",
"JsPlatform/Extensions/orientation_active32.png"
_('Orientation sensor is active'),
_('Orientation'),
'JsPlatform/Extensions/orientation_active32.png',
'JsPlatform/Extensions/orientation_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.isActive");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.isActive');
extension
extension
.addCondition(
"OrientationAlpha",
_("Compare the value of orientation alpha"),
_(
"Compare the value of orientation alpha. (Range: 0 to 360°)"
),
_("the orientation alpha"),
_("Orientation"),
"JsPlatform/Extensions/orientation_alpha32.png",
"JsPlatform/Extensions/orientation_alpha32.png"
'OrientationAlpha',
_('Compare the value of orientation alpha'),
_('Compare the value of orientation alpha. (Range: 0 to 360°)'),
_('the orientation alpha'),
_('Orientation'),
'JsPlatform/Extensions/orientation_alpha32.png',
'JsPlatform/Extensions/orientation_alpha32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAlpha');
extension
extension
.addCondition(
"OrientationBeta",
_("Compare the value of orientation beta"),
_(
"Compare the value of orientation beta. (Range: -180 to 180°)"
),
_("the orientation beta"),
_("Orientation"),
"JsPlatform/Extensions/orientation_beta32.png",
"JsPlatform/Extensions/orientation_beta32.png"
'OrientationBeta',
_('Compare the value of orientation beta'),
_('Compare the value of orientation beta. (Range: -180 to 180°)'),
_('the orientation beta'),
_('Orientation'),
'JsPlatform/Extensions/orientation_beta32.png',
'JsPlatform/Extensions/orientation_beta32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationBeta');
extension
extension
.addCondition(
"OrientationGamma",
_("Compare the value of orientation gamma"),
_(
"Compare the value of orientation gamma. (Range: -90 to 90°)"
),
_("the orientation gamma"),
_("Orientation"),
"JsPlatform/Extensions/orientation_gamma32.png",
"JsPlatform/Extensions/orientation_gamma32.png"
'OrientationGamma',
_('Compare the value of orientation gamma'),
_('Compare the value of orientation gamma. (Range: -90 to 90°)'),
_('the orientation gamma'),
_('Orientation'),
'JsPlatform/Extensions/orientation_gamma32.png',
'JsPlatform/Extensions/orientation_gamma32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationGamma');
extension
.addAction(
"ActivateOrientationListener",
_("Activate orientation sensor"),
_("Activate the orientation sensor. (remember to turn it off again)"),
_("Activate the orientation sensor."),
_("Orientation"),
"JsPlatform/Extensions/orientation_active32.png",
"JsPlatform/Extensions/orientation_active32.png"
'ActivateOrientationListener',
_('Activate orientation sensor'),
_('Activate the orientation sensor. (remember to turn it off again)'),
_('Activate the orientation sensor.'),
_('Orientation'),
'JsPlatform/Extensions/orientation_active32.png',
'JsPlatform/Extensions/orientation_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.activateOrientationSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName(
'gdjs.deviceSensors.orientation.activateOrientationSensor'
);
extension
.addAction(
"DeactivateOrientationListener",
_("Deactivate orientation sensor"),
_("Deactivate the orientation sensor."),
_("Deactivate the orientation sensor."),
_("Orientation"),
"JsPlatform/Extensions/orientation_inactive32.png",
"JsPlatform/Extensions/orientation_inactive32.png"
'DeactivateOrientationListener',
_('Deactivate orientation sensor'),
_('Deactivate the orientation sensor.'),
_('Deactivate the orientation sensor.'),
_('Orientation'),
'JsPlatform/Extensions/orientation_inactive32.png',
'JsPlatform/Extensions/orientation_inactive32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.deactivateOrientationSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName(
'gdjs.deviceSensors.orientation.deactivateOrientationSensor'
);
extension
.addExpression(
"OrientationAbsolute",
_("Is Absolute"),
_("Get if the devices orientation is absolute and not relative"),
_("Orientation"),
"JsPlatform/Extensions/orientation_absolute16.png"
'OrientationAbsolute',
_('Is Absolute'),
_('Get if the devices orientation is absolute and not relative'),
_('Orientation'),
'JsPlatform/Extensions/orientation_absolute16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAbsolute");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAbsolute');
extension
.addExpression(
"OrientationAlpha",
_("Alpha value"),
_("Get the devices orientation Alpha (compass)"),
_("Orientation"),
"JsPlatform/Extensions/orientation_alpha16.png"
'OrientationAlpha',
_('Alpha value'),
_('Get the devices orientation Alpha (compass)'),
_('Orientation'),
'JsPlatform/Extensions/orientation_alpha16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAlpha');
extension
.addExpression(
"OrientationBeta",
_("Beta value"),
_("Get the devices orientation Beta"),
_("Orientation"),
"JsPlatform/Extensions/orientation_beta16.png"
'OrientationBeta',
_('Beta value'),
_('Get the devices orientation Beta'),
_('Orientation'),
'JsPlatform/Extensions/orientation_beta16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationBeta');
extension
.addExpression(
"OrientationGamma",
_("Gamma value"),
_("Get the devices orientation Gamma value"),
_("Orientation"),
"JsPlatform/Extensions/orientation_gamma16.png"
'OrientationGamma',
_('Gamma value'),
_('Get the devices orientation Gamma value'),
_('Orientation'),
'JsPlatform/Extensions/orientation_gamma16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationGamma');
extension
extension
.addCondition(
"MotionSensorActive",
_("Sensor active"),
'MotionSensorActive',
_('Sensor active'),
_(
"The condition is true if the device motion sensor is currently active"
'The condition is true if the device motion sensor is currently active'
),
_("Motion sensor is active"),
_("Motion"),
"JsPlatform/Extensions/motion_active32.png",
"JsPlatform/Extensions/motion_active32.png"
_('Motion sensor is active'),
_('Motion'),
'JsPlatform/Extensions/motion_active32.png',
'JsPlatform/Extensions/motion_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.isActive");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.isActive');
extension
extension
.addCondition(
"RotationAlpha",
_("Compare the value of rotation alpha"),
'RotationAlpha',
_('Compare the value of rotation alpha'),
_(
"Compare the value of rotation alpha. (Note: few devices support this sensor)"
'Compare the value of rotation alpha. (Note: few devices support this sensor)'
),
_("the rotation alpha"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_alpha32.png",
"JsPlatform/Extensions/motion_rotation_alpha32.png"
_('the rotation alpha'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_alpha32.png',
'JsPlatform/Extensions/motion_rotation_alpha32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationAlpha');
extension
extension
.addCondition(
"RotationBeta",
_("Compare the value of rotation beta"),
'RotationBeta',
_('Compare the value of rotation beta'),
_(
"Compare the value of rotation beta. (Note: few devices support this sensor)"
'Compare the value of rotation beta. (Note: few devices support this sensor)'
),
_("the rotation beta"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_beta32.png",
"JsPlatform/Extensions/motion_rotation_beta32.png"
_('the rotation beta'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_beta32.png',
'JsPlatform/Extensions/motion_rotation_beta32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationBeta');
extension
extension
.addCondition(
"RotationGamma",
_("Compare the value of rotation gamma"),
'RotationGamma',
_('Compare the value of rotation gamma'),
_(
"Compare the value of rotation gamma. (Note: few devices support this sensor)"
'Compare the value of rotation gamma. (Note: few devices support this sensor)'
),
_("the rotation gamma"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_gamma32.png",
"JsPlatform/Extensions/motion_rotation_gamma32.png"
_('the rotation gamma'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_gamma32.png',
'JsPlatform/Extensions/motion_rotation_gamma32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationGamma');
extension
extension
.addCondition(
"AccelerationX",
_("Compare the value of acceleration on X-axis"),
_(
"Compare the value of acceleration on the X-axis (m/s²)."
),
_("the acceleration X"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_x32.png",
"JsPlatform/Extensions/motion_acceleration_x32.png"
'AccelerationX',
_('Compare the value of acceleration on X-axis'),
_('Compare the value of acceleration on the X-axis (m/s²).'),
_('the acceleration X'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_x32.png',
'JsPlatform/Extensions/motion_acceleration_x32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationX");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationX');
extension
extension
.addCondition(
"AccelerationY",
_("Compare the value of acceleration on Y-axis"),
_(
"Compare the value of acceleration on the Y-axis (m/s²)."
),
_("the acceleration Y"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_y32.png",
"JsPlatform/Extensions/motion_acceleration_y32.png"
'AccelerationY',
_('Compare the value of acceleration on Y-axis'),
_('Compare the value of acceleration on the Y-axis (m/s²).'),
_('the acceleration Y'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_y32.png',
'JsPlatform/Extensions/motion_acceleration_y32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationY");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationY');
extension
extension
.addCondition(
"AccelerationZ",
_("Compare the value of acceleration on Z-axis"),
_(
"Compare the value of acceleration on the Z-axis (m/s²)."
),
_("the acceleration Z"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_z32.png",
"JsPlatform/Extensions/motion_acceleration_z32.png"
'AccelerationZ',
_('Compare the value of acceleration on Z-axis'),
_('Compare the value of acceleration on the Z-axis (m/s²).'),
_('the acceleration Z'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_z32.png',
'JsPlatform/Extensions/motion_acceleration_z32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationZ");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationZ');
extension
.addAction(
"ActivateMotionListener",
_("Activate motion sensor"),
_("Activate the motion sensor. (remember to turn it off again)"),
_("Activate the motion sensor."),
_("Motion"),
"JsPlatform/Extensions/motion_active32.png",
"JsPlatform/Extensions/motion_active32.png"
'ActivateMotionListener',
_('Activate motion sensor'),
_('Activate the motion sensor. (remember to turn it off again)'),
_('Activate the motion sensor.'),
_('Motion'),
'JsPlatform/Extensions/motion_active32.png',
'JsPlatform/Extensions/motion_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.activateMotionSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.activateMotionSensor');
extension
.addAction(
"DeactivateMotionListener",
_("Deactivate motion sensor"),
_("Deactivate the motion sensor."),
_("Deactivate the motion sensor."),
_("Motion"),
"JsPlatform/Extensions/motion_inactive32.png",
"JsPlatform/Extensions/motion_inactive32.png"
'DeactivateMotionListener',
_('Deactivate motion sensor'),
_('Deactivate the motion sensor.'),
_('Deactivate the motion sensor.'),
_('Motion'),
'JsPlatform/Extensions/motion_inactive32.png',
'JsPlatform/Extensions/motion_inactive32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.deactivateMotionSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.deactivateMotionSensor');
extension
.addExpression(
"RotationAlpha",
_("Alpha value"),
_("Get the devices rotation Alpha"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_alpha16.png"
'RotationAlpha',
_('Alpha value'),
_('Get the devices rotation Alpha'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_alpha16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationAlpha');
extension
.addExpression(
"RotationBeta",
_("Beta value"),
_("Get the devices rotation Beta"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_beta16.png"
'RotationBeta',
_('Beta value'),
_('Get the devices rotation Beta'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_beta16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationBeta');
extension
.addExpression(
"RotationGamma",
_("Gamma value"),
_("Get the devices rotation Gamma"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_gamma16.png"
'RotationGamma',
_('Gamma value'),
_('Get the devices rotation Gamma'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_gamma16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationGamma');
extension
extension
.addExpression(
"AccelerationX",
_("Acceleration X value"),
_("Get the devices acceleration on the X-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_x16.png"
'AccelerationX',
_('Acceleration X value'),
_('Get the devices acceleration on the X-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_x16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationX");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationX');
extension
extension
.addExpression(
"AccelerationY",
_("Acceleration Y value"),
_("Get the devices acceleration on the Y-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_y16.png"
'AccelerationY',
_('Acceleration Y value'),
_('Get the devices acceleration on the Y-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_y16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationY");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationY');
extension
extension
.addExpression(
"AccelerationZ",
_("Acceleration Z value"),
_("Get the devices acceleration on the Z-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_z16.png"
'AccelerationZ',
_('Acceleration Z value'),
_('Get the devices acceleration on the Z-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_z16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationZ");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationZ');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) { return []; },
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -36,8 +28,9 @@ module.exports = {
)
.setExtensionHelpPath('/all-features/device-vibration')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Device vibration"))
.setIcon("JsPlatform/Extensions/vibration_start32.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Device vibration'))
.setIcon('JsPlatform/Extensions/vibration_start32.png');
extension
.addDependency()
@@ -99,10 +92,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -721,10 +713,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,28 +13,20 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'Effects',
'Effects',
'Lots of different effects to be used in your game.',
'Various contributors from PixiJS, PixiJS filters and GDevelop',
'MIT'
)
.setCategory('Visual effect')
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
extension
.setExtensionInformation(
'Effects',
'Effects',
'Lots of different effects to be used in your game.',
'Various contributors from PixiJS, PixiJS filters and GDevelop',
'MIT'
)
.setCategory('Visual effect')
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
// You can declare an effect here. Please order the effects by alphabetical order.
// This file is for common effects that are well-known/"battle-tested". If you have an
@@ -230,7 +223,11 @@ module.exports = {
const blurEffect = extension
.addEffect('Blur')
.setFullName(_('Blur (Gaussian, slow - prefer to use Kawase blur)'))
.setDescription(_('Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'))
.setDescription(
_(
'Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/blur-pixi-filter.js');
const blurProperties = blurEffect.getProperties();
@@ -728,13 +725,11 @@ module.exports = {
const hslAdjustmentEffect = extension
.addEffect('HslAdjustment')
.setFullName(_('HSL Adjustment'))
.setDescription(
_(
'Adjust hue, saturation and lightness.'
)
)
.setDescription(_('Adjust hue, saturation and lightness.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-hsl-adjustment.js')
.addIncludeFile(
'Extensions/Effects/pixi-filters/filter-hsl-adjustment.js'
)
.addIncludeFile('Extensions/Effects/hsl-adjustment-pixi-filter.js');
const hslAdjustmentProperties = hslAdjustmentEffect.getProperties();
hslAdjustmentProperties
@@ -767,7 +762,9 @@ module.exports = {
.addEffect('KawaseBlur')
.setFullName(_('Blur (Kawase, fast)'))
.setDescription(
_('Blur the rendered image, with much better performance than Gaussian blur.')
_(
'Blur the rendered image, with much better performance than Gaussian blur.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
@@ -816,9 +813,7 @@ module.exports = {
const motionBlurEffect = extension
.addEffect('MotionBlur')
.setFullName(_('Motion Blur'))
.setDescription(
_('Blur the rendered image to give a feeling of speed.')
)
.setDescription(_('Blur the rendered image to give a feeling of speed.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-motion-blur.js')
.addIncludeFile('Extensions/Effects/motion-blur-pixi-filter.js');
@@ -1174,7 +1169,9 @@ module.exports = {
.setValue('0')
.setLabel(_('Elapsed time'))
.setType('number')
.setDescription('It can be set back to 0 to play the shockwave animation again.');
.setDescription(
'It can be set back to 0 to play the shockwave animation again.'
);
shockwaveEffectProperties
.getOrCreate('speed')
.setValue('500')
@@ -1311,10 +1308,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'MyDummyExtension',
@@ -153,7 +145,6 @@ module.exports = {
// Everything that is stored inside the behavior is in "behaviorContent" and is automatically
// saved/loaded to JSON.
var dummyBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -170,7 +161,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -187,7 +177,6 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
behaviorContent.setBoolAttribute('property2', true);
@@ -201,6 +190,7 @@ module.exports = {
'',
'CppPlatform/Extensions/topdownmovementicon.png',
'DummyBehavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
dummyBehavior,
new gd.BehaviorsSharedData()
)
@@ -215,7 +205,6 @@ module.exports = {
// Create a new gd.BehaviorSharedDataJsImplementation object and implement the methods
// that are called to get and set the properties of the shared data.
var dummyBehaviorWithSharedData = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.updateProperty = function (
behaviorContent,
propertyName,
@@ -228,7 +217,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -238,13 +226,11 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
};
var sharedData = new gd.BehaviorSharedDataJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.updateProperty = function (
sharedContent,
propertyName,
@@ -257,7 +243,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.getProperties = function (sharedContent) {
var sharedProperties = new gd.MapStringPropertyDescriptor();
@@ -267,7 +252,6 @@ module.exports = {
return sharedProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute(
'sharedProperty1',
@@ -284,6 +268,7 @@ module.exports = {
'',
'CppPlatform/Extensions/topdownmovementicon.png',
'DummyBehaviorWithSharedData',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
dummyBehaviorWithSharedData,
sharedData
)
@@ -302,7 +287,6 @@ module.exports = {
// Everything that is stored inside the object is in "content" and is automatically
// saved/loaded to JSON.
var dummyObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.updateProperty = function (
objectContent,
propertyName,
@@ -327,7 +311,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.getProperties = function (objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
@@ -362,7 +345,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -382,7 +364,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.getInitialInstanceProperties = function (
content,
instance,
@@ -446,10 +427,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
const dummyBehavior = extension
.getBehaviorMetadata('MyDummyExtension::DummyBehavior')
.get();
@@ -474,9 +452,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'MyDummyExtension::DummyObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -489,9 +465,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,368 +13,414 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
"FacebookInstantGames",
_("Facebook Instant Games"),
'FacebookInstantGames',
_('Facebook Instant Games'),
_(
"Allow your game to send scores and interact with the Facebook Instant Games platform."
'Allow your game to send scores and interact with the Facebook Instant Games platform.'
),
"Florian Rival",
"Open source (MIT License)"
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath("/publishing/publishing-to-facebook-instant-games")
.setExtensionHelpPath('/publishing/publishing-to-facebook-instant-games')
.setCategory('Third-party');
extension.addInstructionOrExpressionGroupMetadata(_("Facebook Instant Games"))
.setIcon("JsPlatform/Extensions/facebookicon32.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Facebook Instant Games'))
.setIcon('JsPlatform/Extensions/facebookicon32.png');
extension
.addAction(
"SavePlayerData",
_("Save player data"),
'SavePlayerData',
_('Save player data'),
_(
"Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers"
'Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers'
),
_(
"Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)"
'Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)'
),
_("Player data"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
_('Player data'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter("string", 'Data key name (e.g: "Lives")', "", false)
.addParameter("scenevar", "Scene variable with the content to save", "", false)
.addParameter('string', 'Data key name (e.g: "Lives")', '', false)
.addParameter(
"scenevar",
_("Variable where to store the success message (optional)"),
"",
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.setPlayerData");
extension
.addAction(
"LoadPlayerData",
_("Load player data"),
_("Load the player data with the given key in a variable"),
_(
"Load player data with key _PARAM0_ in _PARAM1_ (or error in _PARAM2_)"
),
_("Player data"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
)
.addParameter("string", _('Data key name (e.g: "Lives")'), "", false)
.addParameter(
"scenevar",
_("Variable where to store loaded data"),
"",
'scenevar',
'Scene variable with the content to save',
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_('Variable where to store the success message (optional)'),
'',
true
)
.addParameter(
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadPlayerData");
.setFunctionName('gdjs.evtTools.facebookInstantGames.setPlayerData');
extension
.addAction(
"SavePlayerScore",
_("Save player score"),
'LoadPlayerData',
_('Load player data'),
_('Load the player data with the given key in a variable'),
_(
"Save the score, and optionally the content of the given variable in the player score, for the given metadata."
'Load player data with key _PARAM0_ in _PARAM1_ (or error in _PARAM2_)'
),
_(
"In leaderboard _PARAM0_, save score _PARAM1_ for the player and extra data from _PARAM2_ (store success message in _PARAM3_ or error in _PARAM4_)"
),
_("Leaderboards"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
_('Player data'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter('string', _('Data key name (e.g: "Lives")'), '', false)
.addParameter(
'scenevar',
_('Variable where to store loaded data'),
'',
false
)
.addParameter(
"string",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadPlayerData');
extension
.addAction(
'SavePlayerScore',
_('Save player score'),
_(
'Save the score, and optionally the content of the given variable in the player score, for the given metadata.'
),
_(
'In leaderboard _PARAM0_, save score _PARAM1_ for the player and extra data from _PARAM2_ (store success message in _PARAM3_ or error in _PARAM4_)'
),
_('Leaderboards'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
'string',
'Leaderboard name (e.g: "PlayersBestTimes")',
"",
'',
false
)
.addParameter("expression", "Score to register for the player", "", false)
.addParameter('expression', 'Score to register for the player', '', false)
.addParameter(
"scenevar",
_("Optional variable with metadata to save"),
"",
'scenevar',
_('Optional variable with metadata to save'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the success message (optional)"),
"",
'scenevar',
_('Variable where to store the success message (optional)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.setPlayerScore");
extension
.addAction(
"LoadPlayerEntry",
_("Load player entry"),
_("Load the player entry in the given leaderboard"),
'scenevar',
_(
"Load player entry from leaderboard _PARAM0_. Set rank in _PARAM1_, score in _PARAM2_ (extra data if any in _PARAM3_ and error in _PARAM4_)"
'Variable where to store the error message (optional, if an error occurs)'
),
_("Leaderboards"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName('gdjs.evtTools.facebookInstantGames.setPlayerScore');
extension
.addAction(
'LoadPlayerEntry',
_('Load player entry'),
_('Load the player entry in the given leaderboard'),
_(
'Load player entry from leaderboard _PARAM0_. Set rank in _PARAM1_, score in _PARAM2_ (extra data if any in _PARAM3_ and error in _PARAM4_)'
),
_('Leaderboards'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
'string',
_('Leaderboard name (e.g: "PlayersBestTimes")'),
"",
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the player rank (of -1 if not ranked)"),
"",
'scenevar',
_('Variable where to store the player rank (of -1 if not ranked)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the player score (of -1 if no score)"),
"",
'scenevar',
_('Variable where to store the player score (of -1 if no score)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store extra data (if any)"),
"",
'scenevar',
_('Variable where to store extra data (if any)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerEntry");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerEntry');
extension
.addCondition(
"AreAdsSupported",
_("Check if ads are supported"),
_("Check if showing ads is supported on this device (only mobile phones can show ads)"),
_("Ads can be shown on this device"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'AreAdsSupported',
_('Check if ads are supported'),
_(
'Check if showing ads is supported on this device (only mobile phones can show ads)'
),
_('Ads can be shown on this device'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.areAdsSupported");
.setFunctionName('gdjs.evtTools.facebookInstantGames.areAdsSupported');
extension
.addCondition(
"IsInterstitialAdReady",
_("Is the interstitial ad ready"),
_("Check if the interstitial ad requested from Facebook is loaded and ready to be shown."),
_("The interstitial ad is loaded and ready to be shown"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'IsInterstitialAdReady',
_('Is the interstitial ad ready'),
_(
'Check if the interstitial ad requested from Facebook is loaded and ready to be shown.'
),
_('The interstitial ad is loaded and ready to be shown'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.isInterstitialAdReady");
.setFunctionName(
'gdjs.evtTools.facebookInstantGames.isInterstitialAdReady'
);
extension
.addAction(
"LoadInterstitialAd",
_("Load and prepare an interstitial ad"),
_("Request and load an interstitial ad from Facebook, so that it is ready to be shown."),
_("Request and load an interstitial ad from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'LoadInterstitialAd',
_('Load and prepare an interstitial ad'),
_(
'Request and load an interstitial ad from Facebook, so that it is ready to be shown.'
),
_(
'Request and load an interstitial ad from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
_("The Ad Placement id (can be found while setting up the ad on Facebook)"),
"",
'string',
_(
'The Ad Placement id (can be found while setting up the ad on Facebook)'
),
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadInterstitialAd");
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadInterstitialAd');
extension
.addAction(
"ShowInterstitialAd",
_("Show the loaded interstitial ad"),
_("Show the interstitial ad previously loaded in memory. This won't work if you did not load the interstitial before."),
_("Show the interstitial ad previously loaded in memory (if any error, store it in _PARAM0_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'ShowInterstitialAd',
_('Show the loaded interstitial ad'),
_(
"Show the interstitial ad previously loaded in memory. This won't work if you did not load the interstitial before."
),
_(
'Show the interstitial ad previously loaded in memory (if any error, store it in _PARAM0_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.showInterstitialAd");
.setFunctionName('gdjs.evtTools.facebookInstantGames.showInterstitialAd');
extension
.addCondition(
"IsRewardedVideoReady",
_("Is the rewarded video ready"),
_("Check if the rewarded video requested from Facebook is loaded and ready to be shown."),
_("The rewarded video is loaded and ready to be shown"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'IsRewardedVideoReady',
_('Is the rewarded video ready'),
_(
'Check if the rewarded video requested from Facebook is loaded and ready to be shown.'
),
_('The rewarded video is loaded and ready to be shown'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.isRewardedVideoReady");
.setFunctionName(
'gdjs.evtTools.facebookInstantGames.isRewardedVideoReady'
);
extension
.addAction(
"LoadRewardedVideo",
_("Load and prepare a rewarded video"),
_("Request and load a rewarded video from Facebook, so that it is ready to be shown."),
_("Request and load a rewarded video from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'LoadRewardedVideo',
_('Load and prepare a rewarded video'),
_(
'Request and load a rewarded video from Facebook, so that it is ready to be shown.'
),
_(
'Request and load a rewarded video from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
_("The Ad Placement id (can be found while setting up the ad on Facebook)"),
"",
'string',
_(
'The Ad Placement id (can be found while setting up the ad on Facebook)'
),
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadRewardedVideo");
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadRewardedVideo');
extension
.addAction(
"ShowRewardedVideo",
_("Show the loaded rewarded video"),
_("Show the rewarded video previously loaded in memory. This won't work if you did not load the video before."),
_("Show the rewarded video previously loaded in memory (if any error, store it in _PARAM0_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'ShowRewardedVideo',
_('Show the loaded rewarded video'),
_(
"Show the rewarded video previously loaded in memory. This won't work if you did not load the video before."
),
_(
'Show the rewarded video previously loaded in memory (if any error, store it in _PARAM0_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.showRewardedVideo");
.setFunctionName('gdjs.evtTools.facebookInstantGames.showRewardedVideo');
extension
.addStrExpression(
"PlayerId",
_("Player identifier"),
_("Get the player unique identifier"),
'PlayerId',
_('Player identifier'),
_('Get the player unique identifier'),
'',
"JsPlatform/Extensions/facebookicon32.png"
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerId");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerId');
extension
.addStrExpression(
"PlayerName",
_("Player name"),
_("Get the player name"),
'PlayerName',
_('Player name'),
_('Get the player name'),
'',
"JsPlatform/Extensions/facebookicon32.png"
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerName");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerName');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
runExtensionSanityTests: function (gd, extension) {
return [];
}
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -11,11 +12,10 @@
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
@@ -2314,10 +2314,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension */
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

225
Extensions/JsExtensionTypes.d.ts vendored Normal file
View File

@@ -0,0 +1,225 @@
type GDNamespace = typeof import('../GDevelop.js/types');
// This is necessary for typescript to interpret the identifier PIXI as a namespace
// in this file and merge it with the other namespace declarations.
declare namespace PIXI {}
/**
* RenderedInstance is the base class used for creating 2D renderers of instances,
* which display on the scene editor, using Pixi.js, the instance of an object (see InstancesEditor).
*/
class RenderedInstance {
_project: gd.Project;
_layout: gd.Layout;
_instance: gd.InitialInstance;
_associatedObjectConfiguration: gd.ObjectConfiguration;
_pixiContainer: PIXI.Container;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
wasUsed: boolean;
constructor(
project: gdProject,
layout: gdLayout,
instance: gdInitialInstance,
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
pixiResourcesLoader: Class<PixiResourcesLoader>
);
/**
* Convert an angle from degrees to radians.
*/
static toRad(angleInDegrees: number): number;
/**
* Called when the scene editor is rendered.
*/
update(): void;
getPixiObject(): PIXI.DisplayObject | null;
getInstance(): gd.InitialInstance;
/**
* Called to notify the instance renderer that its associated instance was removed from
* the scene. The PIXI object should probably be removed from the container: This is what
* the default implementation of the method does.
*/
onRemovedFromScene(): void;
getOriginX(): number;
getOriginY(): number;
getCenterX(): number;
getCenterY(): number;
getCustomWidth(): number;
getCustomHeight(): number;
getWidth(): number;
getHeight(): number;
getDepth(): number;
/**
* Return the width of the instance when the instance doesn't have a custom size.
*/
getDefaultWidth(): number;
/**
* Return the height of the instance when the instance doesn't have a custom size.
*/
getDefaultHeight(): number;
getDefaultDepth(): number;
}
/**
* Rendered3DInstance is the base class used for creating 3D renderers of instances,
* which display on the scene editor, using Three.js, the instance of an object (see InstancesEditor).
* It can also display 2D artifacts on Pixi 2D plane (3D object shadow projected on the plane for instance).
*/
class Rendered3DInstance {
_project: gdProject;
_layout: gdLayout;
_instance: gdInitialInstance;
_associatedObjectConfiguration: gdObjectConfiguration;
_pixiContainer: PIXI.Container;
_threeGroup: THREE.Group;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
_threeObject: THREE.Object3D | null;
wasUsed: boolean;
constructor(
project: gdProject,
layout: gdLayout,
instance: gdInitialInstance,
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
threeGroup: THREE.Group,
pixiResourcesLoader: Class<PixiResourcesLoader>
);
/**
* Convert an angle from degrees to radians.
*/
static toRad(angleInDegrees: number): number;
/**
* Called when the scene editor is rendered.
*/
update(): void;
getPixiObject(): PIXI.DisplayObject;
getThreeObject(): THREE.Object3D;
getInstance(): gd.InitialInstance;
/**
* Called to notify the instance renderer that its associated instance was removed from
* the scene. The PIXI object should probably be removed from the container: This is what
* the default implementation of the method does.
*/
onRemovedFromScene(): void;
getOriginX(): number;
getOriginY(): number;
getCenterX(): number;
getCenterY(): number;
getWidth(): number;
getHeight(): number;
getDepth(): number;
/**
* Return the width of the instance when the instance doesn't have a custom size.
*/
getDefaultWidth(): number;
/**
* Return the height of the instance when the instance doesn't have a custom size.
*/
getDefaultHeight(): number;
/**
* Return the depth of the instance when the instance doesn't have a custom size.
*/
getDefaultDepth(): number;
}
declare type ObjectsRenderingService = {
gd: GDNamespace;
PIXI: PIXI;
THREE: typeof import('../newIDE/app/node_modules/three');
THREE_ADDONS: { SkeletonUtils: any };
RenderedInstance: typeof RenderedInstance;
Rendered3DInstance: typeof Rendered3DInstance;
registerInstanceRenderer: (objectType: string, renderer: any) => void;
registerInstance3DRenderer: (objectType: string, renderer: any) => void;
requireModule: (dirname: string, moduleName: string) => any;
getThumbnail: (
project: gd.Project,
objectConfiguration: gd.ObjectConfiguration
) => string;
rgbOrHexToHexNumber: (value: string) => number;
registerClearCache: (clearCache: (_: any) => void) => void;
};
declare type ObjectsEditorService = {
registerEditorConfiguration: (
objectType: string,
editorConfiguration: any
) => void;
getDefaultObjectJsImplementationPropertiesEditor: ({
helpPagePath: string,
}) => any;
};
declare type ExtensionModule = {
createExtension: (
_: (string) => string,
gd: GDNamespace
) => gd.PlatformExtension;
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instantiating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array.
*
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: (
gd: GDNamespace,
extension: gd.PlatformExtension
) => string[];
/**
* Register editors for objects.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations?: (
objectsEditorService: ObjectsEditorService
) => void;
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers?: (
objectsRenderingService: ObjectsRenderingService
) => void;
};

View File

@@ -1,35 +0,0 @@
// @flow
/**
* @file This file contains the (Flow) types that are used in the JavaScript
* extensions declaration (i.e: JsExtension.js files).
* Extension runtime files are in TypeScript (ts files) and not using Flow.
*
* If you do changes here, run `node import-GDJS-Runtime.js` (in newIDE/app/scripts),
* and be sure that the types declared here are reflecting the types exposed by the editor.
*
* Note that Flow comments are used to avoid having to preprocess this file and the
* JsExtension.js files through Babel. This allows to keep plain JS files, while allowing
* Flow static type checking to be run on them when integrated in the editor.
*/
/*::
export type ObjectsRenderingService = {
gd: libGDevelop,
PIXI: any,
THREE: any,
THREE_ADDONS: {SkeletonUtils: any},
RenderedInstance: any,
Rendered3DInstance: any,
registerInstanceRenderer: (objectType: string, renderer: any) => void,
registerInstance3DRenderer: (objectType: string, renderer: any) => void,
requireModule: (dirname: string, moduleName: string) => any,
getThumbnail: (project: gdProject, objectConfiguration: gdObjectConfiguration) => string,
rgbOrHexToHexNumber: (value: string) => number,
registerClearCache: (clearCache: any => void) => void,
};
export type ObjectsEditorService = {
registerEditorConfiguration: (objectType: string, editorConfiguration: any) => void,
getDefaultObjectJsImplementationPropertiesEditor: ({| helpPagePath: string |}) => any,
};
*/

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -96,7 +88,9 @@ module.exports = {
.setIncludeFile('Extensions/Leaderboards/sha256.js')
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore')
.setAsyncFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore');
.setAsyncFunctionName(
'gdjs.evtTools.leaderboards.saveConnectedPlayerScore'
);
extension
.addCondition(
@@ -299,10 +293,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,32 +13,23 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'Lighting',
_('Lights'),
extension
.setExtensionInformation(
'Lighting',
_('Lights'),
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
'Harsimran Virk',
'MIT'
)
.setCategory('Visual effect')
.setTags("light");
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
'Harsimran Virk',
'MIT'
)
.setCategory('Visual effect')
.setTags('light');
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -46,14 +38,12 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.getProperties = function (behaviorContent) {
const behaviorProperties = new gd.MapStringPropertyDescriptor();
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.initializeContent = function (behaviorContent) {};
extension
.addBehavior(
@@ -66,6 +56,7 @@ module.exports = {
'',
'CppPlatform/Extensions/lightObstacleIcon32.png',
'LightObstacleBehavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
lightObstacleBehavior,
new gd.BehaviorsSharedData()
)
@@ -77,7 +68,6 @@ module.exports = {
const lightObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateProperty = function (
objectContent,
propertyName,
@@ -106,7 +96,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -160,7 +149,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -172,7 +160,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getInitialInstanceProperties = function (
content,
instance,
@@ -233,16 +220,11 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'Lighting::LightObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -255,9 +237,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -283,32 +263,34 @@ module.exports = {
);
this._radius = parseFloat(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const color = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('color')
.getValue()
);
// The icon in the middle.
const lightIconSprite = new PIXI.Sprite(PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png'));
const lightIconSprite = new PIXI.Sprite(
PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png')
);
lightIconSprite.anchor.x = 0.5;
lightIconSprite.anchor.y = 0.5;
// The circle to show the radius of the light.
const radiusBorderWidth = 2;
const radiusGraphics = new PIXI.Graphics();
radiusGraphics.lineStyle(
radiusBorderWidth,
color,
0.8
radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
radiusGraphics.drawCircle(
0,
0,
Math.max(1, this._radius - radiusBorderWidth)
);
radiusGraphics.drawCircle(0, 0, Math.max(1, this._radius - radiusBorderWidth));
this._pixiObject = new PIXI.Container();
this._pixiObject.addChild(lightIconSprite);
@@ -326,11 +308,7 @@ module.exports = {
/**
* Return the path to the thumbnail of the specified object.
*/
static getThumbnail(
project,
resourcesLoader,
objectConfiguration
) {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'CppPlatform/Extensions/lightIcon32.png';
}

View File

@@ -3,6 +3,7 @@ GDevelop - LinkedObjects Extension
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
*/
namespace gdjs {
const logger = new gdjs.Logger('LinkedObjects');
/**
* Manages the links between objects.
*/
@@ -114,7 +115,14 @@ namespace gdjs {
if (this._links.has(linkedObject.id)) {
const otherObjList = this._links
.get(linkedObject.id)!
.linkedObjectMap.get(removedObject.getName())!;
.linkedObjectMap.get(removedObject.getName());
if (!otherObjList) {
logger.error(
`Can't find link from ${linkedObject.id} (${linkedObject.name}) to ${removedObject.id} (${removedObject.name})`
);
return;
}
const index = otherObjList.indexOf(removedObject);
if (index !== -1) {

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension /*: gdPlatformExtension */ = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -474,10 +466,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -24,58 +24,41 @@ namespace gdjs {
runtimeObject: gdjs.RuntimeObject,
objectData: any
) {
let texture = null;
const graphics = new PIXI.Graphics();
graphics.lineStyle(0, 0, 0);
graphics.beginFill(gdjs.rgbToHexNumber(255, 255, 255), 1);
if (objectData.rendererType === 'Point') {
graphics.drawCircle(0, 0, objectData.rendererParam1);
} else if (objectData.rendererType === 'Line') {
graphics.drawRect(
0,
0,
objectData.rendererParam1,
objectData.rendererParam2
);
// Draw an almost-invisible rectangle in the left hand to force PIXI to take a full texture with our line at the right hand
graphics.beginFill(gdjs.rgbToHexNumber(255, 255, 255), 0.001);
graphics.drawRect(
0,
0,
objectData.rendererParam1,
objectData.rendererParam2
);
} else if (objectData.textureParticleName) {
const sprite = new PIXI.Sprite(
(instanceContainer
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
objectData.textureParticleName
)
);
sprite.width = objectData.rendererParam1;
sprite.height = objectData.rendererParam2;
graphics.addChild(sprite);
} else {
graphics.drawRect(
0,
0,
objectData.rendererParam1,
objectData.rendererParam2
);
}
graphics.endFill();
// Render the texture from graphics using the PIXI Renderer.
// TODO: could be optimized by generating the texture only once per object type,
// instead of at each object creation.
const pixiRenderer = instanceContainer
.getGame()
.getRenderer()
.getPIXIRenderer();
//@ts-expect-error Pixi has wrong type definitions for this method
texture = pixiRenderer.generateTexture(graphics);
const imageManager = instanceContainer
.getGame()
.getImageManager() as gdjs.PixiImageManager;
let particleTexture: PIXI.Texture = PIXI.Texture.WHITE;
if (pixiRenderer) {
if (objectData.rendererType === 'Point') {
particleTexture = imageManager.getOrCreateDiskTexture(
objectData.rendererParam1,
pixiRenderer
);
} else if (objectData.rendererType === 'Line') {
particleTexture = imageManager.getOrCreateRectangleTexture(
objectData.rendererParam1,
objectData.rendererParam2,
pixiRenderer
);
} else if (objectData.textureParticleName) {
particleTexture = imageManager.getOrCreateScaledTexture(
objectData.textureParticleName,
objectData.rendererParam1,
objectData.rendererParam2,
pixiRenderer
);
} else {
particleTexture = imageManager.getOrCreateRectangleTexture(
objectData.rendererParam1,
objectData.rendererParam2,
pixiRenderer
);
}
}
const configuration = {
ease: undefined,
@@ -198,7 +181,7 @@ namespace gdjs {
{
type: 'textureSingle',
config: {
texture: texture,
texture: particleTexture,
},
},
{
@@ -236,8 +219,9 @@ namespace gdjs {
}
update(delta: float): void {
const wasEmitting = this.emitter.emit;
this.emitter.update(delta);
if (!this.started && this.getParticleCount() > 0) {
if (!this.started && wasEmitting) {
this.started = true;
}
}

View File

@@ -37,6 +37,7 @@ std::map<gd::String, gd::PropertyDescriptor> PathfindingBehavior::GetProperties(
.SetValue(behaviorContent.GetBoolAttribute("allowDiagonals") ? "true"
: "false")
.SetGroup(_("Path smoothing"))
.SetAdvanced()
.SetType("Boolean");
properties["Acceleration"]
.SetLabel(_("Acceleration"))
@@ -99,6 +100,7 @@ std::map<gd::String, gd::PropertyDescriptor> PathfindingBehavior::GetProperties(
properties["ExtraBorder"]
.SetDescription(_("Extra border size"))
.SetGroup(_("Collision"))
.SetAdvanced()
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetValue(
@@ -108,6 +110,7 @@ std::map<gd::String, gd::PropertyDescriptor> PathfindingBehavior::GetProperties(
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("smoothingMaxCellGap")))
.SetGroup(_("Path smoothing"))
.SetAdvanced()
.SetDescription(_("It's recommended to leave a max gap of 1 cell. "
"Setting it to 0 disable the smoothing."));

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -35,13 +27,12 @@ module.exports = {
)
.setExtensionHelpPath('/behaviors/physics2')
.setCategory('Movement')
.setTags("physics, gravity, obstacle, collision");
.setTags('physics, gravity, obstacle, collision');
extension
.addInstructionOrExpressionGroupMetadata(_('Physics Engine 2.0'))
.setIcon('res/physics32.png');
var physics2Behavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -51,104 +42,138 @@ module.exports = {
behaviorContent.getChild('bodyType').setStringValue(newValue);
return true;
}
if (propertyName === 'bullet') {
behaviorContent.getChild('bullet').setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'fixedRotation') {
behaviorContent
.getChild('fixedRotation')
.setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'canSleep') {
behaviorContent.getChild('canSleep').setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'shape') {
behaviorContent.getChild('shape').setStringValue(newValue);
return true;
}
if (propertyName === 'shapeDimensionA') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeDimensionA').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeDimensionA')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeDimensionB') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeDimensionB').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeDimensionB')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeOffsetX') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeOffsetX').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeOffsetX')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeOffsetY') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeOffsetY').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeOffsetY')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'polygonOrigin') {
behaviorContent.addChild('polygonOrigin').setStringValue(newValue);
return true;
}
if (propertyName === 'vertices') {
behaviorContent.addChild('vertices');
// $FlowFixMe
behaviorContent.setChild('vertices', gd.Serializer.fromJSON(newValue));
return true;
}
if (propertyName === 'density') {
behaviorContent
.getChild('density')
.setDoubleValue(parseFloat(newValue));
return true;
}
if (propertyName === 'friction') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('friction').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent.getChild('friction').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'restitution') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('restitution').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('restitution')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'linearDamping') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('linearDamping').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('linearDamping')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'angularDamping') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('angularDamping').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('angularDamping')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'gravityScale') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('gravityScale').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('gravityScale')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'layers') {
behaviorContent.getChild('layers').setIntValue(parseInt(newValue, 10));
return true;
}
if (propertyName === 'masks') {
behaviorContent.getChild('masks').setIntValue(parseInt(newValue, 10));
return true;
}
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -312,7 +337,6 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.initializeContent = function (behaviorContent) {
behaviorContent.addChild('bodyType').setStringValue('Dynamic');
behaviorContent.addChild('bullet').setBoolValue(false);
@@ -336,40 +360,41 @@ module.exports = {
};
var sharedData = new gd.BehaviorSharedDataJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.updateProperty = function (
sharedContent,
propertyName,
newValue
) {
if (propertyName === 'gravityX') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
sharedContent.getChild('gravityX').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('gravityX').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'gravityY') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
sharedContent.getChild('gravityY').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('gravityY').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'scaleX') {
newValue = parseInt(newValue, 10);
if (newValue !== newValue) return false;
sharedContent.getChild('scaleX').setDoubleValue(newValue);
const newValueAsNumber = parseInt(newValue, 10);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('scaleX').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'scaleY') {
newValue = parseInt(newValue, 10);
if (newValue !== newValue) return false;
sharedContent.getChild('scaleY').setDoubleValue(newValue);
const newValueAsNumber = parseInt(newValue, 10);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('scaleY').setDoubleValue(newValueAsNumber);
return true;
}
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.getProperties = function (sharedContent) {
var sharedProperties = new gd.MapStringPropertyDescriptor();
@@ -402,7 +427,6 @@ module.exports = {
return sharedProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.initializeContent = function (behaviorContent) {
behaviorContent.addChild('gravityX').setDoubleValue(0);
behaviorContent.addChild('gravityY').setDoubleValue(9.8);
@@ -422,6 +446,7 @@ module.exports = {
'',
'res/physics32.png',
'Physics2Behavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
physics2Behavior,
sharedData
)
@@ -775,10 +800,10 @@ module.exports = {
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('setSleepingAllowed');
// Deprecated action (fixed typo):
aut
.addDuplicatedAction("SetSleepingaAllowed", "SetSleepingAllowed")
.addDuplicatedAction('SetSleepingaAllowed', 'SetSleepingAllowed')
.setHidden();
aut
@@ -1476,10 +1501,16 @@ module.exports = {
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('X component (N)'))
.addParameter('expression', _('Y component (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyForce');
@@ -1499,10 +1530,16 @@ module.exports = {
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter('expression', _('Length (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyPolarForce');
@@ -1523,12 +1560,18 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Length (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyForceTowardPosition');
@@ -1546,18 +1589,18 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter(
'expression',
_('X component (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('X component (N·s or kg·m·s⁻¹)'))
.addParameter('expression', _('Y component (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.addParameter(
'expression',
_('Y component (N·s or kg·m·s⁻¹)')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyImpulse');
@@ -1578,14 +1621,17 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter(
'expression',
_('Length (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('Length (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyPolarImpulse');
@@ -1605,16 +1651,19 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter(
'expression',
_('Length (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('Length (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyImpulseTowardPosition');
@@ -1633,7 +1682,9 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Torque (N·m)'))
.setParameterLongDescription(_('A torque is like a rotation acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A torque is like a rotation acceleration but depends on the mass.')
)
.getCodeExtraInformation()
.setFunctionName('applyTorque');
@@ -1652,7 +1703,11 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angular impulse (N·m·s'))
.setParameterLongDescription(_('An impulse is like a rotation speed addition but depends on the mass.'))
.setParameterLongDescription(
_(
'An impulse is like a rotation speed addition but depends on the mass.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyAngularImpulse');
@@ -4061,10 +4116,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
const dummyBehavior = extension
.getBehaviorMetadata('Physics2::Physics2Behavior')
.get();

View File

@@ -150,7 +150,8 @@ PlatformerObjectBehavior::GetProperties(
properties["UseLegacyTrajectory"]
.SetLabel(_("Use frame rate dependent trajectories (deprecated, it's "
"recommended to leave this unchecked)"))
.SetGroup(_("Deprecated options (advanced)"))
.SetGroup(_("Deprecated options"))
.SetDeprecated()
.SetValue(behaviorContent.GetBoolAttribute("useLegacyTrajectory", true)
? "true"
: "false")

View File

@@ -62,7 +62,7 @@ namespace gdjs {
private _yGrabOffset: any;
private _xGrabTolerance: any;
_useLegacyTrajectory: boolean = true;
_useLegacyTrajectory: boolean;
_canGoDownFromJumpthru: boolean = false;
@@ -355,13 +355,25 @@ namespace gdjs {
private _updateSpeed(timeDelta: float): float {
const previousSpeed = this._currentSpeed;
//Change the speed according to the player's input.
// @ts-ignore
if (this._leftKey) {
this._currentSpeed -= this._acceleration * timeDelta;
}
if (this._rightKey) {
this._currentSpeed += this._acceleration * timeDelta;
// Change the speed according to the player's input.
// TODO Give priority to the last key for faster reaction time.
if (this._leftKey !== this._rightKey) {
if (this._leftKey) {
if (this._currentSpeed <= 0) {
this._currentSpeed -= this._acceleration * timeDelta;
} else {
// Turn back at least as fast as it would stop.
this._currentSpeed -=
Math.max(this._acceleration, this._deceleration) * timeDelta;
}
} else if (this._rightKey) {
if (this._currentSpeed >= 0) {
this._currentSpeed += this._acceleration * timeDelta;
} else {
this._currentSpeed +=
Math.max(this._acceleration, this._deceleration) * timeDelta;
}
}
}
//Take deceleration into account only if no key is pressed.
@@ -1680,13 +1692,15 @@ namespace gdjs {
beforeUpdatingObstacles(timeDelta: float) {
const object = this._behavior.owner;
//Stick the object to the floor if its height has changed.
// Stick the object to the floor if its height has changed.
if (this._oldHeight !== object.getHeight()) {
object.setY(
this._floorLastY -
object.getHeight() +
(object.getY() - object.getDrawableY())
);
// TODO This should probably be done after the events because
// the character stays at the wrong place during 1 frame.
const deltaY =
((this._oldHeight - object.getHeight()) *
(object.getHeight() + object.getDrawableY() - object.getY())) /
object.getHeight();
object.setY(object.getY() + deltaY);
}
// Directly follow the floor movement on the Y axis by moving the character.
// For the X axis, we follow the floor movement using `_requestedDeltaX`

View File

@@ -501,6 +501,22 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
);
});
});
it('can stay on a rotated platform when its height changes', function () {
const platform = addPlatformObject(runtimeScene);
platform.setPosition(0, -10);
platform.setAngle(-45);
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
const oldY = object.getY();
expect(object.getY()).to.be.within(-40, -39);
object.setHeight(object.getHeight() - 8);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getY()).to.be(oldY + 8);
});
});
[0, 25].forEach((slopeMaxAngle) => {

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -72,9 +64,7 @@ module.exports = {
.addAction(
'HideAuthenticationBanner',
_('Hide authentication banner'),
_(
'Hide the authentication banner from the top of the game screen.'
),
_('Hide the authentication banner from the top of the game screen.'),
_('Hide the authentication banner'),
'',
'JsPlatform/Extensions/authentication.svg',
@@ -169,6 +159,23 @@ module.exports = {
)
.setFunctionName('gdjs.playerAuthentication.getUsername');
extension
.addStrExpression(
'UserID',
_('User ID'),
_('Get the unique user ID of the authenticated player.'),
'',
'JsPlatform/Extensions/authentication.svg'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
)
.addIncludeFile(
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
)
.setFunctionName('gdjs.playerAuthentication.getUserId');
extension
.addCondition(
'IsPlayerAuthenticated',
@@ -209,10 +216,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -171,7 +171,7 @@ namespace gdjs {
if (!_checkedLocalStorage) {
readAuthenticatedUserFromLocalStorage();
}
return _userId || null;
return _userId || '';
};
/**

View File

@@ -124,6 +124,25 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("The height of the ellipse"))
.SetFunctionName("DrawEllipse");
obj.AddAction("FilletRectangle",
_("Fillet Rectangle"),
_("Draw a fillet rectangle on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a fillet "
"rectangle (fillet: _PARAM5_)"
"with _PARAM0_"),
_("Drawing"),
"res/actions/filletRectangle24.png",
"res/actions/filletRectangle.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Left X position"))
.AddParameter("expression", _("Top Y position"))
.AddParameter("expression", _("Right X position"))
.AddParameter("expression", _("Bottom Y position"))
.AddParameter("expression", _("Fillet (in pixels)"))
.SetFunctionName("DrawFilletRectangle");
obj.AddAction("RoundedRectangle",
_("Rounded rectangle"),
_("Draw a rounded rectangle on screen"),
@@ -163,9 +182,9 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
obj.AddAction("Torus",
_("Torus"),
_("Draw a torus on screen"),
_("Draw at _PARAM1_;_PARAM2_ a torus with inner radius"
"_PARAM3_ and outer radius _PARAM4_ and "
"with start arc _PARAM5_° and end arc _PARAM6_°"
_("Draw at _PARAM1_;_PARAM2_ a torus with "
"inner radius: _PARAM3_, outer radius: _PARAM4_ and "
"with start arc angle: _PARAM5_°, end angle: _PARAM6_° "
"with _PARAM0_"),
_("Drawing"),
"res/actions/torus24.png",

View File

@@ -47,6 +47,11 @@ class PrimitiveDrawingJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
.SetFunctionName("drawEllipse");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::FilletRectangle"]
.SetFunctionName("drawFilletRectangle");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
.SetFunctionName("drawRoundedRectangle");

View File

@@ -136,6 +136,25 @@ namespace gdjs {
this.invalidateBounds();
}
drawFilletRectangle(
x1: float,
y1: float,
x2: float,
y2: float,
fillet: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255
);
//@ts-ignore from @pixi/graphics-extras
this._graphics.drawFilletRect(x1, y1, x2 - x1, y2 - y1, fillet);
this._graphics.closePath();
this._graphics.endFill();
this.invalidateBounds();
}
drawChamferRectangle(
x1: float,
y1: float,

View File

@@ -210,6 +210,22 @@ namespace gdjs {
this._renderer.drawEllipse(centerX, centerY, width, height);
}
drawFilletRectangle(
startX1: float,
startY1: float,
endX2: float,
endY2: float,
fillet: float
) {
this._renderer.drawFilletRectangle(
startX1,
startY1,
endX2,
endY2,
fillet
);
}
drawRoundedRectangle(
startX1: float,
startY1: float,

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -59,10 +51,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -83,10 +75,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

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