Compare commits

...

365 Commits

Author SHA1 Message Date
Clément Pasteau
cc8115f95a Remove unused flow declaration 2024-01-18 15:44:11 +01:00
Clément Pasteau
5ba52c5ecb Remove all flow ignores and add ts to Spine 2024-01-18 15:41:49 +01:00
Clément Pasteau
7f2240fd63 Fix parameters being correctly set as optional 2024-01-18 15:24:50 +01:00
Clément Pasteau
5cceae603f Include JSExtensions? 2024-01-18 12:13:37 +01:00
Clément Pasteau
2303decdef Fix missing function 2024-01-18 11:54:05 +01:00
Clément Pasteau
a44810f71e Fix typo 2024-01-18 10:31:35 +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
D8H
9e5320f9d4 Add an item menu to add sub-folders in object folders (#6090) 2023-12-15 17:56:19 +01:00
Clément Pasteau
a1826d355d Rework subscriptions display in Profile (#6089) 2023-12-15 17:27:48 +01:00
D8H
32a3a094d1 Add new objects under the selected one in the list (#6087) 2023-12-15 16:51:50 +01:00
D8H
ee7dc2654b Fix parameter value choices autocompletion (#6086) 2023-12-15 12:02:24 +01:00
Clément Pasteau
64ffad3c0a Bump to 5.3.185 (#6082) 2023-12-14 14:06:01 +01:00
github-actions[bot]
dcc62f078f Update translations [skip ci] (#6076)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-14 13:54:43 +01:00
D8H
d920f05dbc Fix animation scanning in the 3D model object editor (#6080) 2023-12-14 13:28:38 +01:00
AlexandreS
ac82be800b Fix: update file metadata when saving cloud project triggers new commit (#6074) 2023-12-13 16:50:50 +01:00
Aurélien Vivet
2c92ae4042 Fix typo (#6075) 2023-12-13 16:35:36 +01:00
Clément Pasteau
465e934605 Bump version to 5.3.184 (#6072) 2023-12-13 14:34:04 +01:00
github-actions[bot]
248ba7675e Update translations [skip ci] (#6070)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-13 14:19:15 +01:00
AlexandreS
fd2b59ba45 Revert "Use python3 to update bindings (#6068)" (#6071)
This reverts commit 6edf63e98f.

Only show in developer changelog
2023-12-13 12:01:14 +01:00
Clément Pasteau
be54236ece Fix using which from key event when not defined (#6069)
Do not show in changelog
2023-12-13 11:59:57 +01:00
AlexandreS
6edf63e98f Use python3 to update bindings (#6068)
Only show in developer changelog
2023-12-13 11:36:44 +01:00
github-actions[bot]
034f1ad9cc Update translations [skip ci] (#6057)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-12-12 15:42:47 +01:00
Florian Rival
94b8c31ac2 Allow to increment position of points of sprites by 0.5 in the editor 2023-12-12 15:11:21 +01:00
Florian Rival
57d1241e2d Allow to increment position of vertices of collision masks by 0.5 2023-12-12 15:08:50 +01:00
github-actions[bot]
eb4708ca87 Update translations [skip ci] (#6051)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-11 14:47:52 +01:00
D8H
fac710780b Fix a flash of 1 black frame the first time a scene is started (#6048) 2023-12-11 14:41:27 +01:00
github-actions[bot]
d28aac325a Update translations [skip ci] (#6033)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-11 14:18:26 +01:00
Clément Pasteau
306b341ee5 Education premium users now also have a dedicated Discord channel (#6047) 2023-12-11 14:17:09 +01:00
D8H
1d8e04cb78 Allow to set 3D models dimensions with a scaling factor (#6044)
- It allows to keep consistent dimensions for a set of 3D models
2023-12-11 12:13:17 +01:00
Clément Pasteau
3b2855de59 Fix a possible crash when closing an app window (#6046) 2023-12-11 10:29:10 +01:00
AlexandreS
d5c2982b2d Increase home page mobile menu icon sizes (#6034) 2023-12-08 10:58:05 +01:00
Clément Pasteau
315b7387b3 Bump version to 5.3.183 (#6032) 2023-12-07 16:21:12 +01:00
Clément Pasteau
f4f92566f4 Remove showing GDevelop templates first + increase visibility (#6031)
Do not show in changelog
2023-12-07 15:56:22 +01:00
github-actions[bot]
7b72d4e080 Update translations [skip ci] (#6030)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-07 15:56:06 +01:00
D8H
ae96ebf79c Fix heavy assets failing to download on slow connections (#6024) 2023-12-07 15:36:27 +01:00
D8H
be813a0271 Start games without any loading screen if assets are ready before half the fade-in (#6025)
- This only works if the watermark is enabled.
- It allows players to start a game a 2nd time almost instantly.
2023-12-07 15:21:30 +01:00
github-actions[bot]
b306d80915 Update translations [skip ci] (#6020)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-12-07 11:56:18 +01:00
AlexandreS
9baed02aa1 Improve project list refreshing UI/UX (#6026) 2023-12-07 11:43:29 +01:00
Clément Pasteau
1b4c5c1b1c Improve export copy & logic (#6023)
Do not show in changelog
2023-12-06 15:13:30 +01:00
Arthur Pacaud (arthuro555)
4f04190614 Add automatic generation of TypeScript types for Core (in addition to existing Flow types) (#5429)
Only show in developer changelog
2023-12-05 18:47:20 +01:00
AlexandreS
d3134ecde9 Bump newIDE version (#6019) 2023-12-05 17:13:32 +01:00
github-actions[bot]
61ed7ffa16 Update translations [skip ci] (#6015)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-12-05 16:43:36 +01:00
Clément Pasteau
8be1961d3f Throw if wrong response from project api (#6014)
Do not show in changelog
2023-12-05 09:59:00 +01:00
github-actions[bot]
112c306610 Update translations [skip ci] (#6001)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-05 09:44:27 +01:00
D8H
423f15b513 Add actions to tween effect properties (#5993) 2023-12-04 15:25:40 +01:00
D8H
a9c89b14c3 Use a drop-down list for object effect parameters (#6005) 2023-12-04 14:39:12 +01:00
TRP
bd898463f5 Add an action to draw a torus with the shape painter object (#5981)
- Thanks @trp02
2023-12-04 14:11:41 +01:00
D8H
ed4635664c Fix 3D light rotation angle calculus when Y is the top (#6004)
- In order to get back the same light result as before, 27° should be subtracted to the rotation angle.
2023-12-04 12:48:56 +01:00
Clément Pasteau
b6f25db40c Throw if badges or achievements not loading properly (#6002)
Do not show in changelog
2023-12-04 12:21:11 +01:00
Florian Rival
f78662be5f Reduce the number of tutorial progress analytics events (#5992)
Don't show in changelog
2023-12-04 11:24:37 +01:00
github-actions[bot]
7aae35a029 Update translations [skip ci] (#5980)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-12-04 11:18:46 +01:00
D8H
6c323614ef Fix object type declaration for 3D capabilities actions (#6000)
- Don't show in changelog
2023-12-04 11:03:35 +01:00
AlexandreS
eb4170df20 Fix suggestion to add missing object variable that is used in events (#5999) 2023-12-04 10:40:29 +01:00
D8H
8830bb93ae Force sounds to download even when "preload in cache" is unchecked (#5984)
- Don't show in changelog
2023-12-01 16:30:11 +01:00
Florian Rival
87fa0a39ac Fix warning 2023-12-01 15:29:19 +01:00
Florian Rival
a9cc911ca8 Use JSX for rendering in game debugger message (#5983)
Only show in developer changelog
2023-12-01 09:57:21 +01:00
AlexandreS
d3fe6cf532 Remove unused project version get api call (#5982)
Don't show in changelog
2023-12-01 08:16:43 +01:00
Florian Rival
e2c40ff205 Improve structure variables completion (#5978)
* When a structure variable name is entered in an expression, completions will also be provided for children variables.
2023-11-30 13:47:19 +01:00
github-actions[bot]
970d04b0df Update translations [skip ci] (#5972)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-11-30 09:26:31 +01:00
D8H
8c5076443c Fix property name case in action sentences (#5977) 2023-11-29 15:29:04 +01:00
D8H
3744e98065 Use the new syntax when generating expressions and actions for properties (#5976) 2023-11-29 15:20:38 +01:00
AlexandreS
b6a1332124 Add game dashboard on home page and project manager (#5963) 2023-11-29 11:12:29 +01:00
Clément Pasteau
0251997703 Prevent loading announcements in state if response is not an array (#5975)
Do not show in changelog
2023-11-29 10:34:50 +01:00
D8H
57faa9fb4a Move community tier extensions in their own index section (#5973)
- Don't show in changelog.
2023-11-28 20:34:06 +01:00
D8H
ba95f66ccd Fix LDtk tile map resources export with the fast loading (#5951)
- Don't show in changelog
2023-11-28 14:31:47 +01:00
github-actions[bot]
9465873dbd Update translations [skip ci] (#5943)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-11-28 14:17:13 +01:00
Clément Pasteau
b5a9fe4fe1 Update fling game with new trigger (#5971)
Do not show in changelog
2023-11-28 14:16:43 +01:00
D8H
6531e2e970 Use relative links in extensions reference pages (#5970)
Don't show in changelog
2023-11-28 12:59:39 +01:00
Florian Rival
4f900c9451 Enable auto-completion for structure and array variables when writing an expression (#5960) 2023-11-28 11:30:26 +01:00
Clément Pasteau
f97f267a96 Put the Fling Game tutorial back (#5967)
* It was unintentionally removed!
2023-11-28 11:19:07 +01:00
Clément Pasteau
6cd8f54869 Fix possible crash in the New Object Dialog (#5968) 2023-11-28 09:51:07 +01:00
Clément Pasteau
9718fb788e Better wording for Discord role perks (#5966)
Do not show in changelog
2023-11-27 18:40:32 +01:00
Clément Pasteau
38651edf3e Discord username can now be added to one's Profile, allowing to claim a role on GDevelop's server (#5962)
* If you have a Gold or Startup subscription, head down to your profile, to claim access to a premium channel on Discord, where you can discuss together and ask for support.
2023-11-27 16:54:27 +01:00
Florian Rival
d34f1a654f Improve navigation for extensions in the documentation (#5950) 2023-11-24 11:43:47 +01:00
D8H
5abc74b66b Remove only (#5946)
Don't show in changelog
2023-11-23 19:05:08 +01:00
Clément Pasteau
a848764318 Fix libGD.wasm not properly loaded on Electron local + build warnings (#5942)
Don't show in changelog
2023-11-23 15:55:31 +01:00
github-actions[bot]
c0c6fddcbb Update translations [skip ci] (#5937)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-11-23 09:49:51 +01:00
AlexandreS
95ac26f05d Add placeholder in menu when no recent project file (#5940) 2023-11-23 09:28:33 +01:00
D8H
1f852648ef Make games launch faster by loading resources in the background (#5572)
* Only the first scene and global objects resources (images, sounds, 3D models etc...) will be downloaded during launch of the game. This usually allows for a very fast loading time.
* Other scenes resources will continue to load in the background. It has no impact on the game performance as this is done on other threads by the browser or the engine running the game.
* Scenes are loaded in the order they are listed in the project manager.
* You can also use actions and expressions to prioritize a scene (if it's known that a level will be needed soon for example) or read the current loading progress. This allows to create lightweight scenes that can act as custom loading screens. Otherwise, the launch loading screen will be shown if a scene is still loading when launched.
* Read more about this on https://wiki.gdevelop.io/gdevelop5/all-features/resources-loading/.
2023-11-22 22:51:24 +01:00
Florian Rival
b7da4361c3 Fix some C++ warnings and improve GDevelop.js README
Don't show in changelog
2023-11-22 19:03:21 +01:00
Arthur Pacaud (arthuro555)
71b369d40e Update Emscripten version to 3.1.21 (#5636)
* Any developer working on the C++ codebase should follow again the [README](https://github.com/4ian/GDevelop/tree/master/GDevelop.js) to install latest Emscripten version and re-compile C++.

Only show in developer changelog
2023-11-22 17:19:13 +01:00
AlexandreS
4d8cf56922 Do not change homepage tab at opening if an item from the asset store is requested (#5936) 2023-11-22 16:29:44 +01:00
github-actions[bot]
1a6e0ba5a1 Update translations [skip ci] (#5918)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-11-22 14:04:18 +01:00
AlexandreS
ec1ebcbf5b Fix desktop app not opening on Mac (#5934)
Don't show in changelog
2023-11-22 11:48:07 +01:00
Arthur Pacaud (arthuro555)
4ee9ccd7a9 Allow using resources as behavior properties (#5256)
Only show in developer changelog
2023-11-22 11:17:35 +01:00
AlexandreS
1ac248bfa4 Replace parcel watcher with chokidar (#5932) 2023-11-22 09:35:29 +01:00
D8H
65b78d4db7 Fix duplication of the "create" action in the search results (#5930) 2023-11-21 18:14:35 +01:00
D8H
639d90d743 Move deprecated physics actions at the bottom of search results (#5925) 2023-11-21 18:13:38 +01:00
D8H
45d0a78656 Simplify the wording of some actions names (#5929) 2023-11-21 18:12:09 +01:00
Florian Rival
0a0811e355 Fix crash with 'Put the object around another' action when target object was not existing (#5931) 2023-11-21 17:11:37 +01:00
D8H
a8f9df3dac Use descriptions to search actions and conditions (#5928) 2023-11-21 10:36:54 +01:00
Clément Pasteau
35db56d778 Fix Debugger sometimes crashing (#5926)
* Remove pixi effects renderer from the debugger payload, which was causing it to fail.
2023-11-20 16:06:41 +01:00
D8H
27efe8e3dd Improving the grouping of some behaviors actions and conditions (#5923) 2023-11-19 16:13:19 +01:00
github-actions[bot]
3584ee2aaf Update translations (#5916) 2023-11-17 14:49:31 +01:00
D8H
19a762fb60 Fix a memory and CPU leak when a finished tween is replaced (#5917) 2023-11-17 11:44:12 +01:00
AlexandreS
8a6dd8b940 Bump newIDE version (#5914) 2023-11-17 09:55:10 +01:00
AlexandreS
2173b49b19 New get started flow and customize content based on user survey (#5781) 2023-11-17 09:54:49 +01:00
github-actions[bot]
caf2752acb Update translations [skip ci] (#5912)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-11-16 17:35:40 +01:00
Clément Pasteau
e3291b515d Check existence of elements before destroying (#5913)
Do not show in changelog
2023-11-16 15:55:58 +01:00
Clément Pasteau
c1627b5ab2 Add error id to issue to fully track its trace (#5909)
Do not show in changelog
2023-11-16 11:55:47 +01:00
github-actions[bot]
7db5b97c45 Update translations [skip ci] (#5905)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-11-16 10:03:50 +01:00
D8H
425a0e92ca Fix camera flickering when "pixel rounding" is enabled (#5907) 2023-11-16 15:48:47 +09:00
AlexandreS
c2dfe579af Exclude project files (when configured to save scenes and events in different files) from watcher (#5885)
Also, exclude git files when using git versioning.
2023-11-15 16:15:15 +01:00
AlexandreS
4ea6fb78bd Improve objects list (#5895)
- Remove spell check on inputs
- Unselect item when a parent is closed (and the item becomes not visible)
- Allow to remove folder with all the objects contained in it (recursively)
- Improve selected row color 
- Add hover effects on rows
2023-11-15 09:53:15 +01:00
github-actions[bot]
8e668db6ea Update translations [skip ci] (#5900)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-11-14 17:59:53 +01:00
D8H
88063a4cad Show instructions about objects with the other object instructions (#5828) 2023-11-13 17:05:49 +01:00
AlexandreS
b180f5032e Fix getThumbanil static methods (#5902)
Don't show in changelog
2023-11-13 13:59:04 +01:00
AlexandreS
29e7f7d7a4 Remove build section ref and effect (#5901)
Don't show in changelog
2023-11-13 10:07:22 +01:00
AlexandreS
547f1e4bce Prevent dragging of treeview row placeholder item (#5899) 2023-11-13 09:42:18 +01:00
github-actions[bot]
9e176d91ec Update translations [skip ci] (#5879)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-11-13 09:41:27 +01:00
Mehrab
cac59d4727 Fix the grid/mask checkbox not updated when toggled on the web-app (#5897) 2023-11-12 17:51:34 +01:00
D8H
4f98ffa9ab Fix memory some memory leaks in the scene editor (#5853) 2023-11-10 16:14:54 +01:00
Clément Pasteau
7229406cfb Add errorBoundaries on all main components to prevent crashing the whole app (#5889)
Do not show in changelog
2023-11-10 10:04:01 +01:00
Clément Pasteau
d39cfd16a2 Prevent possible crashes of the editor in multiple part of the app (#5883)
Do not show in changelog
2023-11-09 17:02:11 +01:00
AlexandreS
2965f374bb Improve resources watching performance (#5882)
- Try at reducing Windows devices resources use
- Remove any `.git` folder from being watched (for project using versioning with git)
2023-11-08 09:17:11 +01:00
Clément Pasteau
2d5b2a49e7 Fix a possible crash of the instance editor when updating a sprite (#5881)
* Fix calling methods on Texture frame which can be null when an image is updated
2023-11-07 11:24:57 +01:00
Clément Pasteau
05df9cb599 Show back preview of list item on drag instead of full row for objects and groups (#5880) 2023-11-06 17:23:36 +01:00
D8H
7b28d2525e Fix a broken link in the extension list page (#5878) 2023-11-06 10:06:54 +01:00
github-actions[bot]
533e61cdbc Update translations [skip ci] (#5869)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-11-06 09:29:39 +01:00
Clément Pasteau
3a823113f1 Improve issue template (#5872)
Do not show in changelog
2023-11-03 17:17:15 +01:00
Clément Pasteau
17bc8b584d Show error in error boundary for easier debugging (#5855)
Do not show in changelog
2023-11-03 17:14:25 +01:00
AlexandreS
dbaf8a6569 Clear selection before updating list (#5873)
Don't show in changelog
2023-11-03 17:12:32 +01:00
Clément Pasteau
fcbc538c30 Reduce logs for web previews (#5854)
Do not show in changelog
2023-11-03 16:29:37 +01:00
Clément Pasteau
decd96c52f Only allow opening project folder in Resources tab if local project (#5867) 2023-11-02 18:04:20 +01:00
AlexandreS
8dbf9c99ce Bump newIDE version (#5868) 2023-11-02 14:35:50 +01:00
D8H
1318523804 Fix a crash when objects are used after being deleted (#5865) 2023-11-02 13:09:54 +01:00
github-actions[bot]
8ef5e2c1ed Update translations [skip ci] (#5850)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-11-02 11:50:33 +01:00
AlexandreS
d945426068 Fix custom loading screen not displaying (#5864) 2023-11-02 11:34:45 +01:00
AlexandreS
8eb07a492f Fix treeview search crash when there is no global object (#5866) 2023-11-02 11:14:39 +01:00
Florian Rival
37c9dd95ce Allow installing up to 200 assets at the same time (#5851) 2023-11-02 11:02:40 +01:00
AlexandreS
a6d837cbe6 Use new electron notarize tool (#5857)
Only show in developer changelog
2023-10-31 13:36:10 +01:00
AlexandreS
f6539626d3 Fix cloud projects preview (#5852) 2023-10-30 16:53:22 +01:00
AlexandreS
e0df9b0ff3 Bump newIDE version (#5849) 2023-10-30 15:21:43 +01:00
Jake
0b50231027 Update Solarized Dark theme mosaic toolbar color (#5809) 2023-10-30 15:11:15 +01:00
github-actions[bot]
94c9924a88 Update translations [skip ci] (#5848) 2023-10-30 14:54:33 +01:00
D8H
581cc5837a Fix some memory leaks in 2D object renderers (#5843) 2023-10-30 14:42:15 +01:00
TRP
ff77d107b2 Add new shape in shape painter: polygon (#5836) 2023-10-30 14:28:38 +01:00
AlexandreS
3a5198cb34 Remove possibility to rename item on click a short delay after item has been selected (#5847)
Users noticed that they unwillingly edited object names when they only wanted to click/double-click/drag the object
2023-10-30 13:53:27 +01:00
github-actions[bot]
c92025549d Update translations [skip ci] (#5811)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-10-30 09:53:06 +01:00
Florian Rival
600b307e7e Fix AppVeyor build (Python 3.12 not having distutils anymore) (#5846)
Don't show in changelog
2023-10-29 18:26:43 +01:00
D8H
297fade0bd Remove unused imports (#5844)
Don't show in changelog
2023-10-28 14:35:46 +02:00
D8H
84d7500eab Add the 3D particle emitter in the object list (#5840) 2023-10-27 11:51:05 +02:00
D8H
f99c4ab948 Move the Z-order action and condition to the "Layer" group (#5834) 2023-10-26 11:02:47 +02:00
D8H
5fe4f23c83 Improve the search to give as many results as possible (#5839) 2023-10-26 11:01:57 +02:00
Florian Rival
8820bd45e4 Fix typo 2023-10-26 09:09:55 +02:00
D8H
5e16968f37 Fix "Scale Z" group in actions/conditions editor (#5835) 2023-10-25 18:09:39 +02:00
TRP
6b6179ff22 Add new shape in Shape Painter: Chamfer rectangle (#5817) 2023-10-25 17:55:42 +02:00
AlexandreS
94bcd87a9f Add stop method on video and use it when object is destroyed (#5833)
Fixes bug where video are not restarted when switching scenes
2023-10-25 13:15:10 +02:00
D8H
356a1974ef Improve descriptions for impulse and force actions of the Physics behavior (#5832) 2023-10-25 12:38:09 +02:00
AlexandreS
345bad9876 Clean forgotten import (#5831)
Don't show in changelog
2023-10-25 11:17:54 +02:00
AlexandreS
0b8d843a73 Mobile version optimizations (#5830)
Fixes a bug where scene toolbar does not update on instance selection.
Don't show in changelog
2023-10-25 09:35:45 +02:00
D8H
9f0c987ec7 Fix scale actions group (#5825) 2023-10-23 17:05:11 +02:00
D8H
0e79209d2b Fix a regression on the object selection in the instruction editor (#5824)
- don't show in changelog
2023-10-23 12:26:57 +02:00
D8H
f991c09c39 Fix a crash when only the Ease expression is used from Tween extension (#5821) 2023-10-23 09:57:22 +02:00
D8H
6e24dfa9b8 Fix object selection for deprecated instructions (#5816) 2023-10-20 17:13:20 +02:00
D8H
a3cd00dc94 Fix a typo in the scale tween action (#5815) 2023-10-20 15:47:29 +02:00
Florian Rival
300c011151 Bump newIDE version (#5813) 2023-10-20 15:24:18 +02:00
D8H
230d410469 Fix missing exported files for scene tweens (#5812) 2023-10-20 15:20:16 +02:00
D8H
402e54f706 Fix the Ease expression of the Tween extension (#5810)
Don't show in changelog
2023-10-20 11:07:19 +02:00
github-actions[bot]
dc29c27272 Update translations [skip ci] (#5803) 2023-10-20 09:01:47 +02:00
Florian Rival
462bb84c51 Fix exporting not shown on iOS (#5808) 2023-10-19 17:26:25 +02:00
AlexandreS
a775e79bae Catch instance rendering process in scene editor (#5806)
Don't show in changelog
2023-10-19 16:32:57 +02:00
Florian Rival
bc8c867f23 Fix parameters not accessible in expressions for 'Action With Operator' functions (#5805) 2023-10-19 15:55:55 +02:00
AlexandreS
f34bf2b7b4 Focus confirm delete button (#5804)
Don't show in changelog
2023-10-19 15:52:18 +02:00
Florian Rival
e2d482e1aa Fix long formulas/expressions going out of the screen in the events sheet 2023-10-19 14:51:21 +02:00
AlexandreS
0724ae34d9 Use electron remote shell to open project folder (#5801)
Don't show in changelog
2023-10-19 14:47:31 +02:00
github-actions[bot]
2fc1c2fd86 Update translations [skip ci] (#5774) 2023-10-19 12:19:58 +02:00
D8H
9a42ae11ad Tween extension rework (#5710)
Fix bugs and change implementation for a better maintainability.
2023-10-19 11:24:32 +02:00
Florian Rival
32773e06f6 Improve error message when exception at runtime (#5800) 2023-10-19 10:20:08 +02:00
Florian Rival
00508df014 Don't show an undeclared variable as an error in action/condition editor 2023-10-18 17:22:07 +02:00
Florian Rival
c3cf5b7002 Display an error message in game when an exception/crash is detected (#5799) 2023-10-18 17:19:11 +02:00
AlexandreS
df7fab84b8 Bump newIDE version (#5798) 2023-10-18 16:39:32 +02:00
AlexandreS
95a0012c5e Load texture in runtime even if filename does not have an extension (#5797)
Don't show in changelog
2023-10-18 16:38:25 +02:00
AlexandreS
4bf644f9f2 Add "Help > About GDevelop" menu option for non-Mac users (#5796)
---------

Co-authored-by: Tristan Rhodes <tristan.rhodes@gmail.com>
2023-10-18 13:01:02 +02:00
AlexandreS
79cad1da3a Fix error message not cleared after choosing a color in a color picker (#5794) 2023-10-18 12:47:57 +02:00
AlexandreS
ffc6f5786b Do not clear selection when panning with middle mouse button (#5795) 2023-10-18 12:24:08 +02:00
Florian Rival
07df65f8c2 Fix a potential crash by ensuring selection of instances does not contain deleted global objects (#5791)
- Also keep the selection of objects/folders when changing between scene tabs (unless they have been deleted)
2023-10-18 11:12:19 +02:00
D8H
61f8f0ed66 Fix Bitmap text resource action parameter type (#5793) 2023-10-18 10:55:20 +02:00
D8H
c24e2e5ace Use similar wording for angle conditions (#5792)
* Don't show in changelog
2023-10-17 18:45:17 +02:00
D8H
25c72a6d1f Optimize evaluation of several forces on 1 object (#5789) 2023-10-17 17:09:27 +02:00
AlexandreS
6153b23e7d Improve data sent to automatic error report (#5790)
Also, attempt to fix a crash at GDevelop editor opening.
2023-10-17 16:48:47 +02:00
Florian Rival
1b7ccfded3 Fix warning 2023-10-17 14:00:37 +02:00
Florian Rival
ec57a0a80d Improve the Share/Publish dialog: larger window, clearer sections and wording (#5785) 2023-10-17 11:28:29 +02:00
D8H
95a9e37aba Add animation time control for sprites and 3D models (#5777) 2023-10-17 10:02:49 +02:00
D8H
787274f7fa Fix 3D distance sorting issues by using a higher default near plane (#5784) 2023-10-17 09:55:23 +02:00
D8H
ceb24f0edb Unify text expressions conditions and actions between objects (#5778) 2023-10-16 18:17:50 +02:00
D8H
028bf7a70d Fix Physics behavior forces effect under 60 fps (#5783) 2023-10-16 17:50:52 +02:00
AlexandreS
08471d0356 Use componentWillReceiveProps rather than componentWillUpdate (#5782)
Don't show in changelog
2023-10-16 17:15:27 +02:00
AlexandreS
921add6665 Organize your objects into folders (#5655)
You can now create folders as a means to organize your objects when editing a scene or an external layout.

Thanks to @Silver-Streak, Matt Pin, @arthuro555, SnailMail, @TheGemDev, Peeble, Rasterisko for all the help designing and testing the feature.

Note that object tags have been dropped in this version since they were not practical to use thus not widespread used.
2023-10-16 17:04:20 +02:00
AlexandreS
dabff06891 Add error message when trying to login without connection to internet (#5780) 2023-10-16 11:37:43 +02:00
AlexandreS
967033c407 Fix scene loading for video resources (#5772)
Listen error event on base texture loading
Also makes file watcher listen on addition and removal of files.
2023-10-13 15:03:57 +02:00
Florian Rival
0809749ddc Hide expressions GetArgumentAsString and GetArgumentAsNumber as they are now replaced by just being able to write the parameter name in an expression (#5773) 2023-10-13 10:14:28 +02:00
github-actions[bot]
8a27801355 Update translations [skip ci] (#5766)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-10-12 18:30:18 +02:00
AlexandreS
20cba9d7ed Fix linter and types (#5770)
Don't show in changelog
2023-10-12 18:00:31 +02:00
AlexandreS
3878b93c94 Fix: Prevent loading of forbidden GIF file from crashing a game loading screen (#5769) 2023-10-12 17:01:10 +02:00
D8H
645d5331cb Add a setting to show warnings about deprecated instructions (#5755) 2023-10-12 16:55:02 +02:00
AlexandreS
91db750632 Reload resources in the editor when the resource changes (#5631)
GDevelop now refreshes resources (images, videos, 3D models, fonts, etc.) when a change is detected on the filesystem so that you can immediately see the changes.

This allows to efficiently use GDevelop alongside other tools (Tiled or LDtk for instance).

Notes:
- This logically only affects the desktop version for projects saved on the filesystem;
- You can deactivate it in the preferences.
2023-10-12 16:01:01 +02:00
Clément Pasteau
740485b54a Activate support for unicode characters (including emojis) for all names (objects, groups, variables, functions, parameters) on all projects (#5703) 2023-10-12 15:25:37 +02:00
Florian Rival
e960fe7c5b Fix unicode/emojis not supported in function parameter names (#5768) 2023-10-12 15:20:12 +02:00
AlexandreS
0633c7b474 Fix: Prevent root variable from having white spaces (#5767) 2023-10-12 15:13:18 +02:00
AlexandreS
704bfd40cb Fix example opening wrongfully opening "Create from scratch" dialog (#5765) 2023-10-12 14:34:24 +02:00
github-actions[bot]
310abe7a13 Update translations [skip ci] (#5759)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-10-12 14:11:12 +02:00
Daniel R
b0f9bed273 Add string condition operators: starts with, ends with and contains (#4970)
- Thanks @danired
2023-10-12 13:33:32 +02:00
Clément Pasteau
766a62ad9b Invalidate cache when updating Panel Sprite opacity (#5764)
* This fixes a bug where creating a panel sprite with low opacity would prevent changing its opacity afterwards
2023-10-12 13:27:08 +02:00
D8H
ec6f669d94 Add HSL adjustment, motion blur and shockwave effects (#5760) 2023-10-12 10:49:29 +02:00
D8H
36a2408be0 Fix particles angle when the speed is negative (#5762) 2023-10-12 10:48:23 +02:00
Clément Pasteau
1343210a2b Fix "Locate file" for resource to open the folder in front of the app (#5758) 2023-10-11 15:52:27 +02:00
github-actions[bot]
1e76fedd7b Update translations [skip ci] (#5757)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-11 15:15:25 +02:00
Clément Pasteau
3923641988 Bump to 5.2.176 (#5756) 2023-10-11 10:42:18 +02:00
github-actions[bot]
a7d488626c Update translations [skip ci] (#5751)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-11 10:42:04 +02:00
Clément Pasteau
33f20b9bde Fix adding multiple assets after navigating into folder, correctly taking the assets into account (#5752) 2023-10-11 10:23:03 +02:00
AlexandreS
6c4cd6343f Fix Ctrl+Z closing project on Azerty keyboards (#5753)
Use event which property rather than code property when possible
2023-10-11 10:03:56 +02:00
Clément Pasteau
f1c04df0ee Do not sort animations by name in the Animations Dropdown for consistency (#5754) 2023-10-10 18:49:44 +02:00
D8H
9b466ff574 Fix particle emitter rotation speed (#5747) 2023-10-10 18:42:28 +02:00
Clément Pasteau
2b0969600e Deactivate accessibility in the editor too (#5750)
Do not show in changelog
2023-10-10 17:19:32 +02:00
github-actions[bot]
7ed1eee8f9 Update translations [skip ci] (#5748)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-10 17:11:17 +02:00
Clément Pasteau
9b9b729b42 Improve error message and component when crash (#5749)
Do not show in changelog
2023-10-10 15:27:23 +02:00
github-actions[bot]
530969d4b0 Update translations [skip ci] (#5735)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-10-10 14:19:42 +02:00
Clément Pasteau
11ef87b9ee Force window events to not be passive, so they can be prevented on mobile. (#5745)
This fixes a bug where mouse events where triggered on mobile, introducing unwanted interactions.
2023-10-10 14:17:16 +02:00
Clément Pasteau
e1857e2e1e Deactivate PixiJS accessibility plugin (#5746)
* For developer changelog
* Better remove it, as this isn't used and adds unnecessary elements in the Dom.
2023-10-10 14:16:57 +02:00
Florian Rival
d1385e5fc2 Fix completions in expressions not being case insensitive and limited for objects/variables/properties/parameters (#5742) 2023-10-10 10:48:33 +02:00
AlexandreS
9298026179 Add possibility to set Z offset when creating instances from external layout (#5704) 2023-10-10 09:40:10 +02:00
Florian Rival
913e367f6f Allow to properly use variables/properties/parameters in brackets to access a structure variable (#5738)
* This means expressions like `MyStructure[SomeIndexVariable]` will work properly for a structure if `SomeIndexVariable` is a string variable. Gdevelop now properly uses the type of the variable/property/parameter (to avoid considering what's inside the brackets as a number if it's a string). Remember to declare your variables in the variables editor so that you can use them in expressions.
2023-10-09 18:21:49 +02:00
AlexandreS
1bc27bdd8b Fix shape painter drawing stars crashing games 2023-10-09 10:55:04 +02:00
Florian Rival
4d8b39df95 Add more tests for new variable syntax
Don't show in changelog
2023-10-08 19:02:55 +02:00
Florian Rival
e6970319b9 Fix usage of string properties and parameters using the new simplified syntax (#5737)
* Properties or parameters holding a string where wrongly interpreted as a number using the new syntax.
2023-10-08 17:41:34 +02:00
Florian Rival
cdf21ebd4c Allow to use the new variable syntax for groups (#5730)
* You can write `MyObjectGroup.SomeVariable` in the expressions as long as all the objects of the groups have this variable declared in the objects variable editor.
2023-10-08 11:59:35 +02:00
Clément Pasteau
dc014e2ab0 Fix missing ID in simple text fields (#5728)
Do not show in changelog
2023-10-06 17:17:36 +02:00
Clément Pasteau
25509e1c7b Bump to 5.2.175 (#5731) 2023-10-06 17:11:16 +02:00
AlexandreS
516621e989 Fix infinite loop due to 2 items havinf the same unique id (#5729)
Don't show in changelog
2023-10-06 15:48:06 +02:00
github-actions[bot]
9b2435b210 Update translations [skip ci] (#5726)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-06 15:27:52 +02:00
Clément Pasteau
f894e1962a Split the loading of videos to ensure they are loaded without autoplay (#5723) 2023-10-06 15:06:30 +02:00
Florian Rival
cc35f51158 Fix "Add or edit variables" shown when not applicable (#5725) 2023-10-06 09:41:49 +02:00
Clément Pasteau
a7ef02f1c1 Bump to 5.2.174 (#5721) 2023-10-05 12:35:38 +02:00
github-actions[bot]
2cc7506ad7 Update translations [skip ci] (#5714)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-05 12:28:00 +02:00
D8H
608b460da7 Fix particle emitter spray cone angle being properly updated (#5720) 2023-10-05 12:19:39 +02:00
Clément Pasteau
6b082731c2 Fix dom errors because using a component inside a listItemText (#5717)
Do not show in changelog
2023-10-04 16:15:12 +02:00
Clément Pasteau
e040dd5f28 Deprecate "Always" condition (#5716)
* It was considered misleading as using it or not didn't change the condition's result
2023-10-04 15:07:45 +02:00
Clément Pasteau
ae64cfb3bb Fix crash when using Tilemaps on the web-app (#5715) 2023-10-04 14:55:29 +02:00
Clément Pasteau
02b96eb298 Fix loading video resources in Runtime (#5713) 2023-10-04 10:55:57 +02:00
Clément Pasteau
6f59a3ba03 Improve autocomplete options height on mobile (#5709) 2023-10-03 16:27:12 +02:00
github-actions[bot]
6180016b51 Update translations [skip ci] (#5706)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-10-03 16:10:54 +02:00
Clément Pasteau
b15e72410e Fix DOM and Console errors (#5708)
Do not show in changelog
2023-10-03 15:56:34 +02:00
Clément Pasteau
d6fbb0e78b Show a link to try premium game templates (#5707) 2023-10-03 13:51:04 +02:00
Clément Pasteau
8eb9136847 Fix wrong jsdoc typing (#5705)
Do not show in changelog
2023-10-02 16:28:59 +02:00
Clément Pasteau
46c02ad9b7 Improvements indicator wording (#5702)
Do not show in changelog
2023-10-02 11:50:42 +02:00
github-actions[bot]
9846a9b45e Update translations [skip ci] (#5701)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-10-02 11:41:41 +02:00
AlexandreS
3d86820581 Fix: Do not highlight instance on hover if moving an instance around (#5700) 2023-10-02 10:50:09 +02:00
D8H
b636eeb859 Fix a regression on the negation of compare number/string conditions (#5698) 2023-09-29 20:04:33 +02:00
github-actions[bot]
189bcc4ceb Update translations [skip ci] (#5697)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-09-29 16:09:38 +02:00
Clément Pasteau
3fe031063c Bump to 5.2.173 (#5696) 2023-09-29 15:30:51 +02:00
github-actions[bot]
d10712d584 Update translations [skip ci] (#5691)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-09-29 14:48:29 +02:00
Clément Pasteau
1721fb6211 Cloud projects can now be shared with collaborators (#5687)
* Ability to add 1 guest per project or multiple collaborators with a startup subscription
* Projects shared appear on the projects list of all collaborators and all are able to open it
* A warning appears if you are trying to save a project that someone else modified, to avoid losing changes, so ensure you bring changes one at a time!
2023-09-29 14:42:18 +02:00
D8H
e31ea07902 Fix a 0-size frame buffer exception (#5695)
* Don't show in changelog
2023-09-29 11:05:42 +02:00
Arthur Pacaud (arthuro555)
85929b6e76 Make it possible to render a GDevelop scene in VR (#5681)
Only show in developer changelog
2023-09-27 23:15:38 +02:00
Clément Pasteau
0662607409 Don't show premium features if GDevelop is not the seller (#5692)
Do not show in changelog
2023-09-27 17:51:26 +02:00
Arthur Pacaud (arthuro555)
52f10d73ab Make the Debugger more resilient to errors (#5680) 2023-09-27 16:47:01 +02:00
Florian Rival
d4cb4fafd3 Rework the variables editor so that it's more performant and comfortable to use (#5690)
- The editor is now way faster to open when lots of variables are present. It's also way faster when editing and when lots of variables are present.
- Variable names that have forbidden characters are automatically fixed, not breaking your workflow.
- Deletion of multiple variables in an array was fixed. 
- Also fix adding a new variable sometimes overriding a variable inherited from the object
- Allow editing text variables in a separate window (more comfortable and faster)
2023-09-27 16:30:46 +02:00
D8H
9f4b12ce8a Add missing text parameter types for event-based extensions (#5689)
* Replace "effect parameter" with "effect property" in the UI and documentation.
* Inline property fields for effect name and property.
2023-09-27 11:05:19 +02:00
github-actions[bot]
b5b7edafb1 Update translations [skip ci] (#5640)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-09-27 10:06:31 +02:00
D8H
1335819ea7 Add an expression, a condition and an action for objects center on Z axis (#5682) 2023-09-25 10:12:03 +02:00
Florian Rival
aa7ae758f7 Avoid some useless rendering of toolbars (#5679)
Don't show in changelog
2023-09-23 15:35:22 +02:00
Tristan Rhodes
7da88662fb Fix action sentence for BBText objects alignment (#5678) 2023-09-23 13:37:05 +02:00
Florian Rival
dbc1385787 Fix error when loading resources in editor (#5675)
Don't show in changelog
2023-09-23 13:35:00 +02:00
Florian Rival
8944df1831 Add support for writing parameter names directly in expressions (#5676) 2023-09-22 22:41:13 +02:00
Florian Rival
a0925c79c3 Add a new simplified way to use variables in expressions, and automate renaming of variables in events (#5580)
* You can now simply write the name of the scene or global variable in an expression to use it: `1 + MyVariable` (instead of `1 + Variable(MyVariable)`).
* Objects can also have their variables accessed like this: `MyObject.MyVariable` (instead of `MyObject.Variable(MyVariable)`.
* This also works for properties inside functions of behaviors or custom objects. For example, you can write `Speed` instead of `Object.Behavior::PropertySpeed()`.
* This syntax will also handle all types of variables without the need to write ToString. For example, you can now write "Score: " + CoinsEarned instead of "Score: " + ToString(Variable(CoinsEarned)).
* This syntax will only work (and autocompletions will be shown) if you add the variable in the variables editor of the scene, the project or in the variables of the object. It's a good practice to always declare your variables here and give them a default value - do it to benefit from this new simplified syntax, which will make your formulas and expressions much more readable.
* When you rename a variable in an editor, it will now rename the variables everywhere in the events of the project. This makes it much easier to change the name of a variable if you find a better one. Note that this works for "rootæ variables, but not variables inside structures or arrays.
2023-09-21 18:56:12 +02:00
D8H
73642092bb Upgrade games 2D rendering engine (PixiJS) to version 7.3.0 (#5633)
* This opens the path to multiple performance improvements (faster WebGL rendering, and then WebGPU rendering) as well as optimised resources loading in the future.
2023-09-21 17:14:05 +02:00
D8H
dfe84fbe2b Fix an issue in the frame rate synchronization of the Physics behavior (#5649)
* It should avoid jittery cameras that was happening from time to time.
2023-09-19 15:11:57 +02:00
Aurélien Vivet
cd896c399d Fix typo (#5668)
- Fix the width word used in the getHeight() method for the objects.
- Fix the purial of the window in an action.
2023-09-19 13:32:34 +01:00
Clément Pasteau
d8126ead34 Prevent triggering layout change when the virtual keyboard appears. (#5666)
* It fixes a bug on mobile where starting typing would show the virtual keyboard, triggering a resize of the window, which would change the layout and make the input disappear.
2023-09-18 18:15:22 +02:00
AlexandreS
bdbbe1f409 Fix Health bar and Joystick guided lessons in web app (#5665) 2023-09-18 10:38:36 +02:00
D8H
3a241d03f1 Allow sprite animations to play backward with a negative speed scale (#5662) 2023-09-18 09:24:51 +02:00
Florian Rival
e4cf92e949 Improve documentation of expressions by showing parameter in a human readable way 2023-09-16 16:35:24 +02:00
Florian Rival
0dc513317c Revert "Update createObjectsFrom and createObjectsFromExternalLayout to return a list of objects that it creates. (#5658)" (#5660)
This reverts commit 3e85242165.
2023-09-15 14:46:33 +02:00
Dawid Fatyga
3e85242165 Update createObjectsFrom and createObjectsFromExternalLayout to return a list of objects that it creates. (#5658)
Only show in developer changelog
2023-09-15 14:45:25 +02:00
D8H
ca9fe51d99 Fix scaling actions icons (#5647) 2023-09-13 12:54:22 +02:00
Florian Rival
2e9a5ee287 Fix ExpressionParser2 tests (#5654)
Don't show in changelog
2023-09-13 12:54:09 +02:00
Clément Pasteau
b57e944da5 Fix opening a custom object after having opened a pack in the store (#5651) 2023-09-11 14:30:36 +02:00
Florian Rival
06cdeb763d Fix user questionnaire sometimes shown even when already filled (#5648) 2023-09-08 14:15:00 +02:00
Clément Pasteau
5809a02a26 Add more events about premium game templates opening (#5644)
Do not show in changelog
2023-09-07 19:24:23 +02:00
Florian Rival
3a2878c737 Add more restricted characters in internationalized names (#5643)
Only show in developer changelog
2023-09-07 14:54:56 +02:00
Clément Pasteau
2cbfb806e2 Fix speed of adding an asset (#5642)
Do not show in changelog
2023-09-07 14:36:26 +02:00
AlexandreS
18211d197a Remember collapsed state of object behaviors configuration panels (#5641) 2023-09-07 14:30:23 +02:00
Clément Pasteau
128657c876 Prevent throwing when leaderboards cannot be fetched (#5639)
Do not show in changelog
2023-09-07 11:26:42 +02:00
github-actions[bot]
930d4d394f Update translations [skip ci] (#5629)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-09-07 09:53:55 +02:00
D8H
cc460657e5 Upgrade Storybook to 7.4.0 (#5625)
Only show in developer changelog 

Co-authored-by: Clément Pasteau <4895034+ClementPasteau@users.noreply.github.com>
Co-authored-by: Florian Rival <Florian.Rival@gmail.com>
2023-09-06 20:04:01 +02:00
AlexandreS
d9f2cb51a3 Add view of team and its groups (classrooms) for education plan holders (#5603) 2023-09-06 17:00:19 +02:00
AlexandreS
d736afe4a0 Fix events search crashing the events sheet when a parameter contains more than 5 spaces (#5628) 2023-09-06 16:53:35 +02:00
Clément Pasteau
10825c0a8c Allow using Premium game templates, high quality games (#5611)
* Those templates are ready-made games, which can be used for as many games as you want
* They directly work on mobile, web and desktop for a seamless experience
* They contain everything for a full game, start menu, options, leaderboards, and even character selection
* They can easily be modified to integrate your assets, or your custom logic to make this game yours
2023-09-06 15:26:45 +02:00
Clément Pasteau
a75a8e1bfe Add steamworks extension (#5440) 2023-09-06 09:31:27 +02:00
D8H
9a02905fb6 Refactor resources loading in Runtime (#5632)
* Only for dev
2023-09-06 00:12:07 +02:00
AlexandreS
414292812f Fix context menu actions disappearing when opening a project and Resources tab is restored (#5630) 2023-09-05 16:29:28 +02:00
AlexandreS
4d9035ad07 Display the navigation bar of the homepage at the bottom on mobile (#5627) 2023-09-05 14:10:01 +02:00
github-actions[bot]
bcf0184fdd Update translations [skip ci] (#5622)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-09-05 10:28:24 +02:00
Florian Rival
817f2cf758 Display a message if a subscription is valid but will expire in the future (#5626) 2023-09-04 12:35:00 +02:00
Aurélien Vivet
3c5e10f5c3 Fix typo in theme template (theme.json) (#5623)
Thanks to @Entr0py404 for finding this!
Only show in developer changelog

Co-authored-by: Tristan Rhodes <tristan.rhodes@gmail.com>
2023-09-02 18:38:34 +02:00
Clément Pasteau
7d19b811e4 Fix creating a local project in the right folder (#5621) 2023-09-01 17:46:47 +02:00
Clément Pasteau
17b3579e5b Bump to 5.2.172 (#5620) 2023-09-01 14:32:32 +02:00
github-actions[bot]
afa8d4f7f6 Update translations [skip ci] (#5614)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-09-01 14:30:07 +02:00
Florian Rival
b262cb4c3e Fix link in disabled events still processed internally (#5619)
* This could generate infinite loop crashs even when a link is included as
a sub event of a disabled event.
2023-09-01 13:58:51 +02:00
Clément Pasteau
1c69302c02 Fix using correct parameter for size capability behavior (#5618)
Do not show in changelog
2023-09-01 09:57:51 +02:00
AlexandreS
5ba36916c6 Display a nicer refresh icon in the UI and fields (#5617) 2023-09-01 09:15:21 +02:00
D8H
7f3e2b210a Fix missing effect tab on some objects (#5616) 2023-08-31 20:41:06 +02:00
D8H
f83df15817 Fix a performance regression on scenes with a lot of sprites (#5615) 2023-08-31 18:19:50 +02:00
Gleb Volkov
40639f5578 Add clearCanvas flag to RuntimeScene for multi-scene rendering support (#5525)
* Add `clearCanvas` field to `RuntimeScene` class and pass it to `PIXI.Renderer.render` as `clear` option to support multi-scene rendering.

* Show in developer changelog
2023-08-31 10:10:15 +02:00
Florian Rival
f07e3bfe19 Reduce "layout shifts" in Events Sheet when adding events or modifying them (#5612)
* Browsing the events sheet and editing events should be a bit more comfortable now.
2023-08-31 10:09:47 +02:00
D8H
ef9a82ee33 Fix scene editor window rectangle color (#5613) 2023-08-30 21:44:01 +02:00
1423 changed files with 137245 additions and 89553 deletions

View File

@@ -30,7 +30,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
@@ -62,9 +62,10 @@ jobs:
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
# To test signing the code in the CI, add "export CSC_FOR_PULL_REQUEST=true && " before the command.
- run:
name: Build GDevelop IDE
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && CI=false npm run build -- --mac --publish=never
command: export CSC_FOR_PULL_REQUEST=true && export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && CI=false npm run build -- --mac --publish=never
- run:
name: Clean dist folder to keep only installers/binaries.
@@ -106,7 +107,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
- run:
name: Install system dependencies for Electron builder
@@ -126,7 +127,8 @@ jobs:
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
- run:
@@ -183,7 +185,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
@@ -199,7 +201,8 @@ jobs:
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
- save_cache:
paths:

View File

@@ -0,0 +1,37 @@
name: 💥 Automatic crash report
description: Do not use this template for bug reports. This template is only for automatic crash reports.
body:
- type: textarea
id: description
attributes:
label: Describe what you were doing when the crash happened
description: If applicable, add screenshots to help explain your problem.
placeholder: |
1. Went to '...'
2. Clicked on '...'
3. Scrolled down to '...'
4. Saw error
- type: input
id: gdevelop_version
attributes:
label: GDevelop version
description: |
The version of GDevelop used. Leave the prefilled value.
validations:
required: true
- type: textarea
id: platform_info
attributes:
label: Platform info
description: |
The platform you are using GDevelop on. Leave the prefilled value.
- type: textarea
id: error_stack
attributes:
label: Additional error context
description: Additonal context about the problem. Leave the prefilled value.
- type: textarea
id: component_stack
attributes:
label: Additional component context
description: Additonal context about the problem. Leave the prefilled value.

View File

@@ -2,18 +2,21 @@ name: 🐛Bug report
description: Create a bug report about GDevelop or the game engine
body:
- type: checkboxes
id: searched_issues
attributes:
label: Is there an existing issue for this?
options:
- label: I have searched the [existing issues](https://github.com/4ian/GDevelop/issues)
required: true
- label: I have searched the [existing issues](https://github.com/4ian/GDevelop/issues)
required: true
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: reproduction_steps
attributes:
label: Steps to reproduce
description: |
@@ -27,6 +30,7 @@ body:
validations:
required: true
- type: dropdown
id: platform
attributes:
label: GDevelop platform
description: Which platform of GDevelop are you using?
@@ -38,6 +42,7 @@ body:
validations:
required: true
- type: input
id: gdevelop_version
attributes:
label: GDevelop version
description: |
@@ -47,6 +52,7 @@ body:
validations:
required: true
- type: textarea
id: platform_info
attributes:
label: Platform info
value: |
@@ -66,6 +72,7 @@ body:
</details>
- type: textarea
id: additional_context
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -26,7 +26,7 @@ jobs:
run: sudo apt update && sudo apt install gettext -y
- name: Install newIDE dependencies
run: npm install
run: npm ci
working-directory: newIDE/app
- name: Extract translations

View File

@@ -14,7 +14,7 @@ tasks:
init: |
sudo apt-get update
sudo apt install cmake python-is-python3 python3-distutils -y
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
cd GDevelop.js
npm install
source ../emsdk/emsdk_env.sh && npm run build -- --dev

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

View File

@@ -39,7 +39,7 @@ install:
- cd ..
# Install Emscripten (for GDevelop.js)
- git clone https://github.com/juj/emsdk.git
- cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
- cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# Install GDevelop.js dependencies
- cd GDevelop.js && npm install && cd ..
# Build GDevelop.js

View File

@@ -113,7 +113,8 @@
"memory_resource": "cpp",
"__bits": "cpp",
"__verbose_abort": "cpp",
"variant": "cpp"
"variant": "cpp",
"charconv": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,

View File

@@ -60,7 +60,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_
endif()
#Activate C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 would need to remove usage of bind2nd (should be easy).
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Mark some warnings as errors
@@ -77,7 +77,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-Wno-unused-private-field
# Make as much warnings considered as errors as possible (only one for now).
-Werror=return-stack-address)
-Werror=return-stack-address
-Werror=return-type)
endif()
# Define common directories:

View File

@@ -45,10 +45,39 @@ ForEachChildVariableEvent::GetAllActionsVectors() const {
return allActions;
}
vector<pair<gd::Expression*, gd::ParameterMetadata> >
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() {
vector<pair<gd::Expression*, gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("scenevar");
allExpressionsWithMetadata.push_back(
std::make_pair(&iterableVariableName, metadata));
allExpressionsWithMetadata.push_back(
std::make_pair(&valueIteratorVariableName, metadata));
allExpressionsWithMetadata.push_back(
std::make_pair(&keyIteratorVariableName, metadata));
return allExpressionsWithMetadata;
}
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() const {
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("scenevar");
allExpressionsWithMetadata.push_back(
std::make_pair(&iterableVariableName, metadata));
allExpressionsWithMetadata.push_back(
std::make_pair(&valueIteratorVariableName, metadata));
allExpressionsWithMetadata.push_back(
std::make_pair(&keyIteratorVariableName, metadata));
return allExpressionsWithMetadata;
}
void ForEachChildVariableEvent::SerializeTo(SerializerElement& element) const {
element.AddChild("iterableVariableName").SetValue(iterableVariableName);
element.AddChild("valueIteratorVariableName").SetValue(valueIteratorVariableName);
element.AddChild("keyIteratorVariableName").SetValue(keyIteratorVariableName);
element.AddChild("iterableVariableName").SetValue(iterableVariableName.GetPlainString());
element.AddChild("valueIteratorVariableName").SetValue(valueIteratorVariableName.GetPlainString());
element.AddChild("keyIteratorVariableName").SetValue(keyIteratorVariableName.GetPlainString());
gd::EventsListSerialization::SerializeInstructionsTo(
conditions, element.AddChild("conditions"));
gd::EventsListSerialization::SerializeInstructionsTo(

View File

@@ -8,6 +8,7 @@
#define FOREACHCHILDVARIABLEEVENT_H
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Expression.h"
namespace gd {
class Instruction;
class Project;
@@ -44,7 +45,7 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
*
* It is the structure variable that will be iterated on.
*/
const gd::String& GetIterableVariableName() const { return iterableVariableName; };
const gd::String& GetIterableVariableName() const { return iterableVariableName.GetPlainString(); };
/**
* \brief Set the iterable variable name attached to the event.
@@ -56,15 +57,15 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
/**
* \brief Get the value iterator variable attached to the event.
*
* It is the variable that will contain the value of the
* It is the variable that will contain the value of the
* iterable's child being iterated on.
*/
const gd::String& GetValueIteratorVariableName() const { return valueIteratorVariableName; };
const gd::String& GetValueIteratorVariableName() const { return valueIteratorVariableName.GetPlainString(); };
/**
* \brief Set the value iterator variable attached to the event.
*
* It is the variable that will contain the value of the
* It is the variable that will contain the value of the
* iterable's child being iterated on.
*/
void SetValueIteratorVariableName(gd::String newName) { valueIteratorVariableName = newName; };
@@ -72,15 +73,15 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
/**
* \brief Get the key iterator variable attached to the event.
*
* It is the variable that will contain the name of the
* It is the variable that will contain the name of the
* iterable's child being iterated on.
*/
const gd::String& GetKeyIteratorVariableName() const { return keyIteratorVariableName; };
const gd::String& GetKeyIteratorVariableName() const { return keyIteratorVariableName.GetPlainString(); };
/**
* \brief Set the key iterator variable attached to the event.
*
* It is the variable that will contain the name of the
* It is the variable that will contain the name of the
* iterable's child being iterated on.
*/
void SetKeyIteratorVariableName(gd::String newName) { keyIteratorVariableName = newName; };
@@ -92,14 +93,19 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
GetAllExpressionsWithMetadata() const;
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
GetAllExpressionsWithMetadata();
virtual void SerializeTo(SerializerElement& element) const;
virtual void UnserializeFrom(gd::Project& project,
const SerializerElement& element);
private:
gd::String valueIteratorVariableName;
gd::String keyIteratorVariableName;
gd::String iterableVariableName;
gd::Expression valueIteratorVariableName;
gd::Expression keyIteratorVariableName;
gd::Expression iterableVariableName;
gd::InstructionsList conditions;
gd::InstructionsList actions;
gd::EventsList events;

View File

@@ -64,12 +64,6 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
virtual void UnserializeFrom(gd::Project& project,
const SerializerElement& element);
std::vector<gd::Expression*> GetAllObjectExpressions() {
std::vector<gd::Expression*> allObjectExpressions;
allObjectExpressions.push_back(&objectsToPick);
return allObjectExpressions;
}
private:
gd::Expression objectsToPick;
gd::InstructionsList conditions;

View File

@@ -131,34 +131,40 @@ void LinkEvent::SerializeTo(SerializerElement& element) const {
void LinkEvent::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
SerializerElement& includeElement = element.GetChild("include", 0, "Limites");
SetTarget(element.GetChild("target", 0, "Scene").GetValue().GetString());
if (includeElement.HasAttribute("includeAll")) {
// Compatibility with GDevelop <= 4.0.92
if (includeElement.GetBoolAttribute("includeAll", true)) {
SetIncludeAllEvents();
// Compatibility with GD <= 5
if (element.HasChild("include", "Limites")) {
SerializerElement& includeElement = element.GetChild("include", 0, "Limites");
if (includeElement.HasAttribute("includeAll")) {
// Compatibility with GDevelop <= 4.0.92
if (includeElement.GetBoolAttribute("includeAll", true)) {
SetIncludeAllEvents();
} else {
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
includeElement.GetIntAttribute("end"));
}
} else {
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
includeElement.GetIntAttribute("end"));
// GDevelop > 4.0.92
IncludeConfig config = static_cast<IncludeConfig>(
includeElement.GetIntAttribute("includeConfig", 0));
if (config == INCLUDE_ALL)
SetIncludeAllEvents();
else if (config == INCLUDE_EVENTS_GROUP)
SetIncludeEventsGroup(includeElement.GetStringAttribute("eventsGroup"));
else if (config == INCLUDE_BY_INDEX)
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
includeElement.GetIntAttribute("end"));
}
} else {
// GDevelop > 4.0.92
IncludeConfig config = static_cast<IncludeConfig>(
includeElement.GetIntAttribute("includeConfig", 0));
if (config == INCLUDE_ALL)
SetIncludeAllEvents();
else if (config == INCLUDE_EVENTS_GROUP)
SetIncludeEventsGroup(includeElement.GetStringAttribute("eventsGroup"));
else if (config == INCLUDE_BY_INDEX)
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
includeElement.GetIntAttribute("end"));
// Since GDevelop 5, links always include all events.
SetIncludeAllEvents();
}
// end of compatibility code
}
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
return BaseEvent::AcceptVisitor(eventVisitor) |
return BaseEvent::AcceptVisitor(eventVisitor) ||
eventVisitor.VisitLinkEvent(*this);
}

View File

@@ -35,7 +35,7 @@ vector<pair<gd::Expression*, gd::ParameterMetadata> >
RepeatEvent::GetAllExpressionsWithMetadata() {
vector<pair<gd::Expression*, gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("expression");
auto metadata = gd::ParameterMetadata().SetType("number");
allExpressionsWithMetadata.push_back(
std::make_pair(&repeatNumberExpression, metadata));
@@ -61,7 +61,7 @@ vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
RepeatEvent::GetAllExpressionsWithMetadata() const {
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("expression");
auto metadata = gd::ParameterMetadata().SetType("number");
allExpressionsWithMetadata.push_back(
std::make_pair(&repeatNumberExpression, metadata));

View File

@@ -46,10 +46,6 @@ void EffectsCodeGenerator::GenerateEffectsIncludeFiles(
// TODO Add unit tests on this function.
// TODO Merge with UsedExtensionsFinder.
// Default lights rely on the fact that UsedExtensionsFinder doesn't find
// extension usages for effects. This has the happy side effect of not
// including Three.js when no 3D object are in the scenes.
// We need to make something explicit to avoid future bugs.
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and

View File

@@ -16,6 +16,7 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/Project.h"
using namespace std;
@@ -68,8 +69,36 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
}
}
return callStartString + "(" + argumentsStr + ") " + relationalOperator +
" " + rhs;
auto lhs = callStartString + "(" + argumentsStr + ")";
return GenerateRelationalOperation(relationalOperator, lhs, rhs);
}
/**
* @brief Generate a relational operation
*
* @param relationalOperator the operator
* @param lhs the left hand operand
* @param rhs the right hand operand
* @return gd::String
*/
gd::String EventsCodeGenerator::GenerateRelationalOperation(
const gd::String& relationalOperator,
const gd::String& lhs,
const gd::String& rhs) {
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " + rhs;
}
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
if (operatorString == "=") {
return "==";
}
if (operatorString != "<" && operatorString != ">" &&
operatorString != "<=" && operatorString != ">=" && operatorString != "!=" &&
operatorString != "startsWith" && operatorString != "endsWith" && operatorString != "contains") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
return "==";
}
return operatorString;
}
/**
@@ -295,16 +324,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::String objectInParameter =
condition.GetParameter(pNb).GetPlainString();
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
GetObjectsContainersList().GetTypeOfObject(objectInParameter) !=
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
@@ -315,11 +338,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::String objectName = condition.GetParameter(0).GetPlainString();
if (!objectName.empty() && !instrInfos.parameters.empty()) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Set up the context
gd::String objectType = gd::GetTypeOfObject(
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), realObjects[i]);
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
const ObjectMetadata& objInfo =
MetadataProvider::GetObjectMetadata(platform, objectType);
@@ -343,13 +365,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
}
} else if (instrInfos.IsBehaviorInstruction()) {
gd::String objectName = condition.GetParameter(0).GetPlainString();
gd::String behaviorType =
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
condition.GetParameter(1).GetPlainString());
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(condition.GetParameter(1).GetPlainString());
if (instrInfos.parameters.size() >= 2) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
const BehaviorMetadata& autoInfo =
@@ -479,16 +498,10 @@ gd::String EventsCodeGenerator::GenerateActionCode(
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
GetObjectsContainersList().GetTypeOfObject(objectInParameter) !=
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
@@ -501,11 +514,10 @@ gd::String EventsCodeGenerator::GenerateActionCode(
if (!instrInfos.parameters.empty()) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
gd::String objectType = gd::GetTypeOfObject(
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), realObjects[i]);
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
const ObjectMetadata& objInfo =
MetadataProvider::GetObjectMetadata(platform, objectType);
@@ -529,14 +541,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
}
} else if (instrInfos.IsBehaviorInstruction()) {
gd::String objectName = action.GetParameter(0).GetPlainString();
gd::String behaviorType =
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
action.GetParameter(1).GetPlainString());
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(action.GetParameter(1).GetPlainString());
if (instrInfos.parameters.size() >= 2) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
const BehaviorMetadata& autoInfo =
@@ -657,18 +666,6 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
return outputCode;
}
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
if (operatorString == "=") {
return "==";
}
if (operatorString != "<" && operatorString != ">" &&
operatorString != "<=" && operatorString != ">=" && operatorString != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
return "==";
}
return operatorString;
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
@@ -693,7 +690,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput =
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
} else if (metadata.GetType() == "relationalOperator") {
argOutput += GenerateRelationalOperatorCodes(parameter.GetPlainString());
argOutput += parameter.GetPlainString();
argOutput = "\"" + argOutput + "\"";
} else if (metadata.GetType() == "operator") {
argOutput += parameter.GetPlainString();
@@ -717,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") {
@@ -912,41 +911,6 @@ gd::String EventsCodeGenerator::ConvertToStringExplicit(
return "\"" + ConvertToString(plainString) + "\"";
}
std::vector<gd::String> EventsCodeGenerator::ExpandObjectsName(
const gd::String& objectName,
const EventsCodeGenerationContext& context) const {
// Note: this logic is duplicated in EventsContextAnalyzer::ExpandObjectsName
std::vector<gd::String> realObjects;
if (globalObjectsAndGroups.GetObjectGroups().Has(objectName))
realObjects = globalObjectsAndGroups.GetObjectGroups()
.Get(objectName)
.GetAllObjectsNames();
else if (objectsAndGroups.GetObjectGroups().Has(objectName))
realObjects =
objectsAndGroups.GetObjectGroups().Get(objectName).GetAllObjectsNames();
else
realObjects.push_back(objectName);
// If current object is present, use it and only it.
if (find(realObjects.begin(),
realObjects.end(),
context.GetCurrentObject()) != realObjects.end()) {
realObjects.clear();
realObjects.push_back(context.GetCurrentObject());
}
// Ensure that all returned objects actually exists.
for (std::size_t i = 0; i < realObjects.size();) {
if (!objectsAndGroups.HasObjectNamed(realObjects[i]) &&
!globalObjectsAndGroups.HasObjectNamed(realObjects[i]))
realObjects.erase(realObjects.begin() + i);
else
++i;
}
return realObjects;
}
void EventsCodeGenerator::DeleteUselessEvents(gd::EventsList& events) {
for (std::size_t eId = events.size() - 1; eId < events.size(); --eId) {
if (events[eId].CanHaveSubEvents()) // Process sub events, if any
@@ -963,6 +927,8 @@ void EventsCodeGenerator::DeleteUselessEvents(gd::EventsList& events) {
*/
void EventsCodeGenerator::PreprocessEventList(gd::EventsList& listEvent) {
for (std::size_t i = 0; i < listEvent.GetEventsCount(); ++i) {
if (listEvent[i].IsDisabled()) continue;
listEvent[i].Preprocess(*this, listEvent, i);
if (i <
listEvent.GetEventsCount()) { // Be sure that that there is still an
@@ -1260,12 +1226,24 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
return argumentsStr;
}
gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
const gd::NamedPropertyDescriptor& property,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
return "getProperty" + property.GetName() + "As" + type + "()";
}
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
return "getParameter" + parameter.GetName() + "As" + type + "()";
}
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_)
: platform(platform_),
globalObjectsAndGroups(project_),
objectsAndGroups(layout),
projectScopedContainers(gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project_, layout)),
hasProjectAndLayout(true),
project(&project_),
scene(&layout),
@@ -1277,11 +1255,9 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
EventsCodeGenerator::EventsCodeGenerator(
const gd::Platform& platform_,
const gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_)
const gd::ProjectScopedContainers& projectScopedContainers_)
: platform(platform_),
globalObjectsAndGroups(globalObjectsAndGroups_),
objectsAndGroups(objectsAndGroups_),
projectScopedContainers(projectScopedContainers_),
hasProjectAndLayout(false),
project(nullptr),
scene(nullptr),

View File

@@ -12,6 +12,7 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h"
namespace gd {
class EventsList;
@@ -19,6 +20,7 @@ class Expression;
class Project;
class Layout;
class ObjectsContainer;
class ObjectsContainersList;
class ExternalEvents;
class ParameterMetadata;
class ObjectMetadata;
@@ -56,9 +58,9 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Construct a code generator for the specified
* objects/groups and platform
*/
EventsCodeGenerator(const gd::Platform& platform,
const gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_);
EventsCodeGenerator(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers_);
virtual ~EventsCodeGenerator(){};
/**
@@ -326,18 +328,12 @@ class GD_CORE_API EventsCodeGenerator {
*/
bool ErrorOccurred() const { return errorOccurred; };
/**
* \brief Get the global objects/groups used for code generation.
*/
const gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
return globalObjectsAndGroups;
}
const gd::ObjectsContainersList& GetObjectsContainersList() const {
return projectScopedContainers.GetObjectsContainersList();
};
/**
* \brief Get the objects/groups used for code generation.
*/
const gd::ObjectsContainer& GetObjectsAndGroups() const {
return objectsAndGroups;
const gd::ProjectScopedContainers& GetProjectScopedContainers() const {
return projectScopedContainers;
}
/**
@@ -363,22 +359,6 @@ class GD_CORE_API EventsCodeGenerator {
*/
const gd::Platform& GetPlatform() const { return platform; }
/**
* \brief Convert a group name to the full list of objects contained in the
* group.
*
* Get a list containing the "real" objects name when the events refers to \a
* objectName :<br> If \a objectName is really an object, the list will only
* contains \a objectName unchanged.<br> If \a objectName is a group, the list
* will contains all the objects of the group.<br> If \a objectName is the
* "current" object in the context ( i.e: The object being used for launching
* an action... ), none of the two rules below apply, and the list will only
* contains the context "current" object name.
*/
std::vector<gd::String> ExpandObjectsName(
const gd::String& objectName,
const EventsCodeGenerationContext& context) const;
/**
* \brief Get the maximum depth of custom conditions reached during code
* generation.
@@ -493,10 +473,15 @@ class GD_CORE_API EventsCodeGenerator {
*/
size_t GenerateSingleUsageUniqueIdForEventsList();
virtual gd::String GenerateRelationalOperation(
const gd::String& relationalOperator,
const gd::String& lhs,
const gd::String& rhs);
protected:
virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString);
protected:
/**
* \brief Generate the code for a single parameter.
*
@@ -566,6 +551,12 @@ class GD_CORE_API EventsCodeGenerator {
return ".getChild(" + ConvertToStringExplicit(childName) + ")";
};
virtual gd::String GenerateVariableValueAs(const gd::String& type) {
return type == "number|string" ? ".getAsNumberOrString()"
: type == "string" ? ".getAsString()"
: ".getAsNumber()";
}
/**
* \brief Generate the code to get the child of a variable,
* using generated the expression.
@@ -594,6 +585,17 @@ class GD_CORE_API EventsCodeGenerator {
return "fakeObjectListOf_" + objectName;
}
virtual gd::String GeneratePropertyGetter(
const gd::PropertiesContainer& propertiesContainer,
const gd::NamedPropertyDescriptor& property,
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateParameterGetter(
const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context);
/**
* \brief Generate the code to reference an object which is
* in an empty/null state.
@@ -673,7 +675,8 @@ class GD_CORE_API EventsCodeGenerator {
* The default implementation generates C-style code : It wraps the predicate
* inside parenthesis and add a !.
*/
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
virtual gd::String GenerateNegatedPredicate(
const gd::String& predicate) const {
return "!(" + predicate + ")";
};
@@ -734,6 +737,7 @@ class GD_CORE_API EventsCodeGenerator {
const std::vector<gd::String>& arguments,
const gd::String& callStartString,
std::size_t startFromArgument = 0);
gd::String GenerateOperatorCall(const gd::InstructionMetadata& instrInfos,
const std::vector<gd::String>& arguments,
const gd::String& callStartString,
@@ -777,8 +781,7 @@ class GD_CORE_API EventsCodeGenerator {
const gd::Platform& platform; ///< The platform being used.
const gd::ObjectsContainer& globalObjectsAndGroups;
const gd::ObjectsContainer& objectsAndGroups;
gd::ProjectScopedContainers projectScopedContainers;
bool hasProjectAndLayout; ///< true only if project and layout are valid
///< references. If false, they should not be used.

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
@@ -25,6 +26,9 @@
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/VariablesContainersList.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
@@ -47,8 +51,7 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
}
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType);
node->Visit(validator);
if (!validator.GetFatalErrors().empty()) {
@@ -100,35 +103,95 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
// This "translation" from the type to an enum could be avoided
// if all types were moved to an enum.
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType,
node);
EventsCodeGenerator::VariableScope scope =
type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar")
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.name, scope, context, objectName);
if (node.child) node.child->Visit(*this);
if (gd::ParameterMetadata::IsExpression("variable", type)) {
// The node is a variable inside an expression waiting for a *variable* to be returned, not its value.
EventsCodeGenerator::VariableScope scope =
type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar")
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetObjectsContainersList(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.name, scope, context, objectName);
if (node.child) node.child->Visit(*this);
} else {
// The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
codeGenerator.GetProjectScopedContainers().MatchIdentifierWithName<void>(node.name, [&](){
// Generate the code to access the object variables.
// Defer generation of the access to the object and variable to the child,
// once we know the name of the variable.
objectNameToUseForVariableAccessor = node.name;
if (node.child) node.child->Visit(*this);
objectNameToUseForVariableAccessor = "";
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
if (!codeGenerator.HasProjectAndLayout()) {
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now.");
output += GenerateDefaultValue(type);
return;
}
// This could be adapted in the future if more scopes are supported.
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
if (codeGenerator.GetProjectScopedContainers().GetVariablesContainersList().GetBottomMostVariablesContainer()->Has(node.name)) {
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
}
output += codeGenerator.GenerateGetVariable(node.name, scope, context, "");
if (node.child) node.child->Visit(*this);
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
// Properties are not supported.
output += GenerateDefaultValue(type);
}, [&]() {
// Parameters are not supported.
output += GenerateDefaultValue(type);
}, [&]() {
// The identifier does not represents a variable (or a child variable), or not at least an existing
// one, nor an object variable. It's invalid.
output += GenerateDefaultValue(type);
});
}
}
void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
output += codeGenerator.GenerateVariableAccessor(node.name);
if (!objectNameToUseForVariableAccessor.empty()) {
// Use the name of the object passed by the parent, as we need both to access an object variable.
output += codeGenerator.GenerateGetVariable(node.name,
gd::EventsCodeGenerator::OBJECT_VARIABLE, context, objectNameToUseForVariableAccessor);
// We have accessed an object variable, from now we can continue accessing the child variables
// (including using the bracket notation).
objectNameToUseForVariableAccessor = "";
} else {
output += codeGenerator.GenerateVariableAccessor(node.name);
}
if (node.child) node.child->Visit(*this);
}
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
if (!objectNameToUseForVariableAccessor.empty()) {
// Bracket notation can't be used to directly access a variable of an object (`MyObject["MyVariable"]`).
// This would be rejected by the ExpressionValidator.
output += codeGenerator.GenerateBadVariable();
return;
}
ExpressionCodeGenerator generator("number|string", "", codeGenerator, context);
node.expression->Visit(generator);
output +=
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
@@ -137,10 +200,10 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType,
node);
if (gd::ParameterMetadata::IsObject(type)) {
output +=
codeGenerator.GenerateObject(node.identifierName, type, context);
@@ -153,8 +216,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetObjectsContainersList(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
@@ -162,30 +224,60 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
} else if (node.childIdentifierName.empty()) {
output += "/* Error during generation, unrecognized identifier type: " +
codeGenerator.ConvertToString(type) + " with value " +
codeGenerator.ConvertToString(node.identifierName) + " */ " +
codeGenerator.ConvertToStringExplicit(node.identifierName);
}
else {
// This is for function names that are put in IdentifierNode
// because the type is needed to tell them apart from variables.
output += GenerateDefaultValue(type);
} else {
const auto& variablesContainersList = codeGenerator.GetProjectScopedContainers().GetVariablesContainersList();
const auto& propertiesContainersList = codeGenerator.GetProjectScopedContainers().GetPropertiesContainersList();
const auto& parametersVectorsList = codeGenerator.GetProjectScopedContainers().GetParametersVectorsList();
// The node represents a variable, property, parameter or an object.
codeGenerator.GetProjectScopedContainers().MatchIdentifierWithName<void>(node.identifierName, [&]() {
// Generate the code to access the object variable.
output += codeGenerator.GenerateGetVariable(
node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE, context, node.identifierName);
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
if (!codeGenerator.HasProjectAndLayout()) {
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now.");
output += GenerateDefaultValue(type);
return;
}
// This could be adapted in the future if more scopes are supported at runtime.
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
if (variablesContainersList.GetBottomMostVariablesContainer()->Has(node.identifierName)) {
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
}
output += codeGenerator.GenerateGetVariable(node.identifierName, scope, context, "");
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
const auto& propertiesContainerAndProperty = propertiesContainersList.Get(node.identifierName);
output += codeGenerator.GeneratePropertyGetter(
propertiesContainerAndProperty.first, propertiesContainerAndProperty.second, type, context);
}, [&]() {
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
output += codeGenerator.GenerateParameterGetter(parameter, type, context);
}, [&]() {
// The identifier does not represents a variable (or a child variable), or not at least an existing
// one, nor an object variable. It's invalid.
output += GenerateDefaultValue(type);
});
}
}
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType,
node);
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetObjectsContainersList(),
node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
@@ -236,11 +328,6 @@ gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
const gd::String& objectName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata) {
const gd::ObjectsContainer& globalObjectsAndGroups =
codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups =
codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionMetadata.GetIncludeFiles());
@@ -261,12 +348,11 @@ gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(objectName, context);
codeGenerator.GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
for (std::size_t i = 0; i < realObjects.size(); ++i) {
context.ObjectsListNeeded(realObjects[i]);
gd::String objectType = gd::GetTypeOfObject(
globalObjectsAndGroups, objectsAndGroups, realObjects[i]);
gd::String objectType = codeGenerator.GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
const ObjectMetadata& objInfo = MetadataProvider::GetObjectMetadata(
codeGenerator.GetPlatform(), objectType);
@@ -288,11 +374,6 @@ gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
const gd::String& behaviorName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata) {
const gd::ObjectsContainer& globalObjectsAndGroups =
codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups =
codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionMetadata.GetIncludeFiles());
@@ -311,12 +392,11 @@ gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(objectName, context);
codeGenerator.GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
gd::String functionOutput = GenerateDefaultValue(type);
gd::String behaviorType = gd::GetTypeOfBehavior(
globalObjectsAndGroups, objectsAndGroups, behaviorName);
gd::String behaviorType = codeGenerator.GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
const BehaviorMetadata& autoInfo = MetadataProvider::GetBehaviorMetadata(
codeGenerator.GetPlatform(), behaviorType);
@@ -352,8 +432,7 @@ gd::String ExpressionCodeGenerator::GenerateParametersCodes(
if (!parameterMetadata.IsCodeOnly()) {
if (nonCodeOnlyParameterIndex < parameters.size()) {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetObjectsContainersList(),
rootObjectName,
*parameters[nonCodeOnlyParameterIndex].get());
ExpressionCodeGenerator generator(parameterMetadata.GetType(), objectName, codeGenerator, context);
@@ -423,8 +502,7 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType,
node);
output += GenerateDefaultValue(type);
@@ -433,8 +511,7 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
ObjectFunctionNameNode& node) {
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
codeGenerator.GetProjectScopedContainers(),
rootType,
node);
output += GenerateDefaultValue(type);

View File

@@ -102,6 +102,7 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
const std::vector<std::unique_ptr<ExpressionNode>>& parameters);
gd::String output;
gd::String objectNameToUseForVariableAccessor;
EventsCodeGenerator& codeGenerator;
EventsCodeGenerationContext& context;
const gd::String rootType;

View File

@@ -240,7 +240,7 @@ class GD_CORE_API BaseEvent {
*/
virtual void UnserializeFrom(gd::Project& project,
const SerializerElement& element){};
virtual bool AcceptVisitor(gd::EventVisitor& eventVisitor);
virtual void AcceptVisitor(gd::ReadOnlyEventVisitor& eventVisitor) const;
///@}
@@ -281,15 +281,6 @@ class GD_CORE_API BaseEvent {
* \brief True if the event should be folded in the events editor.
*/
bool IsFolded() const { return folded; }
/**
* \brief Return a list of all objects linked to the event.
*/
virtual std::vector<gd::Expression*> GetAllObjectExpressions() {
std::vector<gd::Expression*> allObjectExpressions;
return allObjectExpressions;
}
///@}
std::weak_ptr<gd::BaseEvent>

View File

@@ -47,14 +47,9 @@ class GD_CORE_API ExpressionParser2 {
virtual ~ExpressionParser2(){};
/**
* Parse the given expression with the specified type.
* Parse the given expression into a tree of nodes.
*
* \param type Type of the expression: "string", "number",
* type supported by gd::ParameterMetadata::IsObject, types supported by
* gd::ParameterMetadata::IsExpression or "unknown".
* \param expression The expression to parse
* \param objectName Specify the object name, only for the
* case of "objectvar" type.
* \param expression The expression to parse.
*
* \return The node representing the expression as a parsed tree.
*/
@@ -267,12 +262,11 @@ class GD_CORE_API ExpressionParser2 {
} else if (CheckIfChar(IsDot)) {
ExpressionParserLocation dotLocation = SkipChar();
SkipAllWhitespaces();
return ObjectFunctionOrBehaviorFunction(
return ObjectFunctionOrBehaviorFunctionOrVariable(
name, nameLocation, dotLocation);
} else if (CheckIfChar(IsOpeningSquareBracket)) {
return Variable(name, nameLocation);
}
else {
} else {
auto identifier = gd::make_unique<IdentifierNode>(name);
identifier->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
@@ -283,8 +277,11 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
auto variable = gd::make_unique<VariableNode>(name);
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->child->parent = variable.get();
}
variable->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
@@ -308,8 +305,12 @@ class GD_CORE_API ExpressionParser2 {
"bracket for each opening bracket."));
}
SkipIfChar(IsClosingSquareBracket);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
@@ -318,11 +319,18 @@ class GD_CORE_API ExpressionParser2 {
auto dotLocation = SkipChar();
SkipAllWhitespaces();
auto identifierAndLocation = ReadIdentifierName();
auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
auto child =
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
if (identifierAndLocation.name.empty()) {
child->diagnostic = RaiseSyntaxError(_("A name should be entered after the dot."));
}
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
child->child = VariableAccessorOrVariableBracketAccessor();
child->child->parent = child.get();
}
child->nameLocation = identifierAndLocation.location;
child->dotLocation = dotLocation;
child->location =
@@ -331,7 +339,11 @@ class GD_CORE_API ExpressionParser2 {
return std::move(child);
}
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
// Should never happen, unless a node called this function without checking if the current character
// was a dot or an opening bracket - this means there is an error in the grammar.
auto unrecognisedNode = gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>();
unrecognisedNode->diagnostic = RaiseSyntaxError(_("A dot or bracket was expected here."));
return std::move(unrecognisedNode);
}
std::unique_ptr<FunctionCallNode> FreeFunction(
@@ -358,27 +370,33 @@ class GD_CORE_API ExpressionParser2 {
}
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
ObjectFunctionOrBehaviorFunction(
ObjectFunctionOrBehaviorFunctionOrVariable(
const gd::String &parentIdentifier,
const ExpressionParserLocation &parentIdentifierLocation,
const ExpressionParserLocation &parentIdentifierDotLocation) {
auto childIdentifierAndLocation = ReadIdentifierName();
auto childIdentifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
const auto &childIdentifierNameLocation =
childIdentifierAndLocation.location;
std::unique_ptr<gd::ExpressionParserError> emptyNameError = childIdentifierName.empty() ?
RaiseSyntaxError(_("A name should be entered after the dot.")) : nullptr;
SkipAllWhitespaces();
if (IsNamespaceSeparator()) {
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
return BehaviorFunction(parentIdentifier,
auto behaviorFunction = BehaviorFunction(parentIdentifier,
childIdentifierName,
parentIdentifierLocation,
parentIdentifierDotLocation,
childIdentifierNameLocation,
namespaceSeparatorLocation);
if (emptyNameError) behaviorFunction->diagnostic = std::move(emptyNameError);
return std::move(behaviorFunction);
} else if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
@@ -387,7 +405,7 @@ class GD_CORE_API ExpressionParser2 {
childIdentifierName);
auto parametersNode = Parameters(function.get(), parentIdentifier);
function->parameters = std::move(parametersNode.parameters),
function->diagnostic = std::move(parametersNode.diagnostic);
function->diagnostic = emptyNameError ? std::move(emptyNameError) : std::move(parametersNode.diagnostic);
function->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
@@ -400,6 +418,8 @@ class GD_CORE_API ExpressionParser2 {
return std::move(function);
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
variable->diagnostic = std::move(emptyNameError);
auto child =
gd::make_unique<VariableAccessorNode>(childIdentifierName);
child->child = VariableAccessorOrVariableBracketAccessor();
@@ -420,17 +440,12 @@ class GD_CORE_API ExpressionParser2 {
auto node = gd::make_unique<IdentifierNode>(
parentIdentifier, childIdentifierName);
if (!CheckIfChar(IsParameterSeparator) && !CheckIfChar(IsClosingParenthesis) && !IsEndReached()) {
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis (for an object expression), a double colon "
"(:: for a behavior expression), a dot or an opening bracket (for "
"a child variable) where expected."));
}
node->location = ExpressionParserLocation(
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
node->identifierNameLocation = parentIdentifierLocation;
node->identifierNameDotLocation = parentIdentifierDotLocation;
node->childIdentifierNameLocation = childIdentifierNameLocation;
node->diagnostic = std::move(emptyNameError);
return std::move(node);
}
@@ -503,11 +518,6 @@ class GD_CORE_API ExpressionParser2 {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
gd::String lastObjectName = "";
// By convention, object is always the first parameter, and behavior the
// second one.
size_t parameterIndex =
WrittenParametersFirstIndex(objectName, behaviorName);
bool previousCharacterIsParameterSeparator = false;
while (!IsEndReached()) {
SkipAllWhitespaces();
@@ -526,7 +536,6 @@ class GD_CORE_API ExpressionParser2 {
SkipAllWhitespaces();
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
SkipIfChar(IsParameterSeparator);
parameterIndex++;
}
ExpressionParserLocation invalidClosingParenthesisLocation;
@@ -625,13 +634,13 @@ class GD_CORE_API ExpressionParser2 {
ExpressionParserLocation location;
};
IdentifierAndLocation ReadIdentifierName() {
IdentifierAndLocation ReadIdentifierName(bool allowDeprecatedSpacesInName = true) {
gd::String name;
size_t startPosition = currentPosition;
while (currentPosition < expression.size() &&
(CheckIfChar(IsAllowedInIdentifier)
// Allow whitespace in identifier name for compatibility
|| expression[currentPosition] == ' ')) {
|| (allowDeprecatedSpacesInName && expression[currentPosition] == ' '))) {
name += expression[currentPosition];
currentPosition++;
}

View File

@@ -189,7 +189,7 @@ struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
*
* The name of a function to call on an object or the behavior,
* for example: "MyObject.Function" or "MyObject.Physics".
*
*
* A variable, potentially with accessor to its child,
* for example: MyVariable or MyVariable.MyChild
*/
@@ -239,13 +239,14 @@ struct GD_CORE_API VariableAccessorOrVariableBracketAccessorNode : public Expres
};
/**
* \brief A variable with bracket accessor or at least 2 "dot" accessors.
* \brief A variable, or object variable, with bracket accessor or at least 2 "dot" accessors.
*
* Example: `MyVariable["MyChildren"]` or `MyVariable.MyChildren.MyGrandChildren`.
* Example: `MyObject["MyVariable"]` or `MyObject.MyVariable.MyChildren`.
*
* Example: MyVariable[MyChildren] or MyVariable.MyChildren.MyGranChildren.
*
* Other cases like "MyVariable" or "MyVariable.MyChildren" are IdentifierNode
* to allow handling ambiguities.
*
*
* \see gd::IdentifierNode
* \see gd::VariableAccessorNode
* \see gd::VariableBracketAccessorNode
@@ -267,7 +268,7 @@ struct GD_CORE_API VariableNode : public FunctionCallOrObjectFunctionNameOrEmpty
};
/**
* \brief A bracket accessor of a variable. Example: MyChild
* \brief A direct accessor to a child variable. Example: MyChild
* in MyVariable.MyChild
*/
struct GD_CORE_API VariableAccessorNode
@@ -285,7 +286,7 @@ struct GD_CORE_API VariableAccessorNode
};
/**
* \brief A bracket accessor of a variable. Example: ["MyChild"]
* \brief A bracket accessor to a child variable. Example: ["MyChild"]
* (in MyVariable["MyChild"]).
*/
struct GD_CORE_API VariableBracketAccessorNode
@@ -303,10 +304,10 @@ struct GD_CORE_API VariableBracketAccessorNode
/**
* \brief The name of a function to call on an object or the behavior
* For example: "MyObject.Physics::LinearVelocity".
*
*
* Other cases like "MyObject.Function" or "MyObject.Physics" are IdentifierNode
* to allow handling ambiguities.
*
*
* \see gd::IdentifierNode
*/
struct GD_CORE_API ObjectFunctionNameNode

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
@@ -43,6 +44,11 @@ class GD_CORE_API ExpressionParser2NodePrinter
*/
const gd::String& GetOutput() { return output; };
static gd::String PrintStringLiteral(const gd::String& str) {
return "\"" +
str.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") + "\"";
}
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
output += "(";
@@ -69,10 +75,7 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitNumberNode(NumberNode& node) override { output += node.number; }
void OnVisitTextNode(TextNode& node) override {
output +=
"\"" +
node.text.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") +
"\"";
output += PrintStringLiteral(node.text);
}
void OnVisitVariableNode(VariableNode& node) override {
output += node.name;
@@ -97,8 +100,8 @@ class GD_CORE_API ExpressionParser2NodePrinter
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
output +=
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
output += node.objectName + "." + node.objectFunctionOrBehaviorName +
"::" + node.behaviorFunctionName;
} else {
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
}

View File

@@ -75,10 +75,21 @@ inline bool IsZeroDigit(gd::String::value_type character) {
return character == '0';
}
inline bool IsAdditionalReservedCharacter(gd::String::value_type character) {
// These characters are not part of the grammar - but are often used in programming language
// and could become operators or part of the grammar one day.
return character == '~' || character == '\'' || character == '%' ||
character == '#' || character == '@' || character == '|' ||
character == '&' || character == '`' || character == '$' ||
character == ';';
}
/**
* Check if the given character can be used in an identifier. This is
* any unicode character, except for:
* `, . " () [] {} + - < > ? ^ = \ : ! / *` and whitespaces (space, line break, carriage return).
* `, . " () [] {} + - < > ? ^ = \ : ! / * ~ ' % # @ | & $ ;`
* and backtick and whitespaces (space, line break, carriage return).
*
* This is loosely based on what is allowed in languages like JavaScript
* (see https://mathiasbynens.be/notes/javascript-properties), without support
@@ -96,7 +107,7 @@ inline bool IsAllowedInIdentifier(gd::String::value_type character) {
if (!IsParameterSeparator(character) && !IsDot(character) &&
!IsQuote(character) && !IsBracket(character) &&
!IsExpressionOperator(character) && !IsTermOperator(character) &&
!IsWhitespace(character)) {
!IsWhitespace(character) && !IsAdditionalReservedCharacter(character)) {
return true;
}

View File

@@ -115,21 +115,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddExpression(
"GetArgumentAsNumber",
_("Get function parameter value"),
_("Get function parameter (also called \"argument\") value."),
_("Get function parameter (also called \"argument\") value. You don't need this most of the time as you can simply write the parameter name in an expression."),
"",
"res/function16.png")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddStrExpression(
"GetArgumentAsString",
_("Get function parameter text"),
_("Get function parameter (also called \"argument\") text."),
_("Get function parameter (also called \"argument\") text. You don't need this most of the time as you can simply write the parameter name in an expression."),
"",
"res/function16.png")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly();
.SetRelevantForFunctionEventsOnly()
.SetHidden();
extension
.AddCondition(

View File

@@ -49,6 +49,7 @@ class GD_CORE_API BuiltinExtensionsImplementer {
static void ImplementsAnimatableExtension(gd::PlatformExtension& extension);
static void ImplementsEffectExtension(gd::PlatformExtension& extension);
static void ImplementsOpacityExtension(gd::PlatformExtension& extension);
static void ImplementsTextContainerExtension(gd::PlatformExtension& extension);
};
} // namespace gd

View File

@@ -396,7 +396,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Z order"),
_("Modify the Z-order of an object"),
_("the z-order"),
_("Z order"),
_("Layers and cameras"),
"res/actions/planicon24.png",
"res/actions/planicon.png")
@@ -550,7 +550,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Z-order"),
_("Compare the Z-order of the specified object."),
_("the Z-order"),
_("Z-order"),
_("Layer"),
"res/conditions/planicon24.png",
"res/conditions/planicon.png")
@@ -617,6 +617,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
// Deprecated
obj.AddCondition("AngleOfDisplacement",
_("Angle of movement (using forces)"),
_("Compare the angle of movement of an object according to "
@@ -626,7 +627,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Movement using forces"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
.SetHidden()
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Tolerance, in degrees"))
.MarkAsAdvanced();
obj.AddCondition("IsTotalForceAngleAround",
_("Angle of movement (using forces)"),
_("Compare the angle of movement of an object according to "
"the forces applied on it."),
_("Angle of movement of _PARAM0_ is _PARAM1_ ± _PARAM2_°"),
_("Movement using forces"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Tolerance, in degrees"))
@@ -1137,7 +1151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("ZOrder",
_("Z-order"),
_("Z-order of an object"),
_("Visibility"),
"",
"res/actions/planicon.png")
.AddParameter("object", _("Object"));
@@ -1267,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?"))
@@ -1277,53 +1291,53 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
// Deprecated
obj.AddAction("SetEffectDoubleParameter",
_("Effect parameter (number)"),
_("Change the value of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (number)"),
_("Change the value of a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"names) in the effects window."),
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("expression", _("New value"))
.MarkAsSimple()
.SetHidden();
// Deprecated
obj.AddAction("SetEffectStringParameter",
_("Effect parameter (string)"),
_("Change the value (string) of a parameter of an effect.") +
_("Effect property (string)"),
_("Change the value (string) of a property of an effect.") +
"\n" +
_("You can find the parameter names (and change the effect "
_("You can find the property names (and change the effect "
"names) in the effects window."),
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("string", _("New value"))
.MarkAsSimple()
.SetHidden();
// Deprecated
obj.AddAction("SetEffectBooleanParameter",
_("Effect parameter (enable or disable)"),
_("Enable or disable a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (enable or disable)"),
_("Enable or disable a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"names) in the effects window."),
_("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"),
_("Effects"),
"res/actions/effect24.png",
"res/actions/effect.png")
"res/actions/effect_black.svg",
"res/actions/effect_black.svg")
.AddParameter("object", _("Object"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("yesorno", _("Enable?"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("yesorno", _("Enable this property"))
.MarkAsSimple()
.SetHidden();
@@ -1333,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()
@@ -1610,7 +1624,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Cast a ray from _PARAM1_;_PARAM2_, angle: _PARAM3_ and max "
"distance: _PARAM4_px, against _PARAM0_, and save the "
"result in _PARAM5_, _PARAM6_"),
"",
_("Collision"),
"res/conditions/raycast24.png",
"res/conditions/raycast.png")
.AddParameter("objectList", _("Objects to test against the ray"))
@@ -1641,7 +1655,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Cast a ray from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ "
"against _PARAM0_, and save the "
"result in _PARAM5_, _PARAM6_"),
"",
_("Collision"),
"res/conditions/raycast24.png",
"res/conditions/raycast.png")
.AddParameter("objectList", _("Objects to test against the ray"))

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(
@@ -327,7 +327,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0");
// TODO Deprecated: hide this action in a future release.
extension
.AddAction(
"FixCamera",
@@ -339,6 +338,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"",
"res/actions/camera24.png",
"res/actions/camera.png")
.SetHidden()
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectPtr", _("Object"))
.AddParameter("expression",
@@ -386,7 +386,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
_("Center the camera on an object"),
_("Center the camera on the specified object."),
_("Center camera on _PARAM1_ (layer: _PARAM3_)"),
"",
_("Layers and cameras"),
"res/actions/camera24.png",
"res/actions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -444,58 +444,58 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"SetLayerEffectParameter",
_("Effect parameter (number)"),
_("Change the value of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (number)"),
_("Change the value of a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"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("\"\"")
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("layerEffectParameterName", _("Property name"))
.AddParameter("expression", _("New value"))
.MarkAsAdvanced();
extension
.AddAction(
"SetLayerEffectStringParameter",
_("Effect parameter (string)"),
_("Change the value (string) of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (string)"),
_("Change the value (string) of a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"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("\"\"")
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("layerEffectParameterName", _("Property name"))
.AddParameter("string", _("New value"))
.MarkAsAdvanced();
extension
.AddAction(
"SetLayerEffectBooleanParameter",
_("Effect parameter (enable or disable)"),
_("Enable or disable a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (enable or disable)"),
_("Enable or disable a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"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("\"\"")
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("yesorno", _("Enable this parameter"))
.AddParameter("layerEffectParameterName", _("Property name"))
.AddParameter("yesorno", _("Enable this property"))
.MarkAsAdvanced();
extension
@@ -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("\"\"")
@@ -548,7 +548,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"ChangeLayerTimeScale",
_("Change layer time scale"),
_("Layer time scale"),
_("Change the time scale applied to the objects of the layer."),
_("Set the time scale of layer _PARAM1_ to _PARAM2_"),
"",
@@ -594,7 +594,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"SetLayerAmbientLightColor",
_("Set the ambient light color"),
_("Ambient light color"),
_("Set the ambient light color of the lighting layer in format "
"\"R;G;B\" string."),
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),

View File

@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
.SetIcon("res/actions/animation24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
.SetIcon("res/actions/animation24.png");
@@ -42,7 +44,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
"number",
"Index",
_("Animation (by number)"),
_("the number of the animation played by the object (the number from "
_("the animation played by the object using the animation number (from "
"the animations list)"),
_("the number of the animation"),
_("Animations and images"),
@@ -53,13 +55,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Animation index")))
.MarkAsSimple();
aut.GetAllExpressions()["Index"].SetGroup("");
aut.AddExpressionAndConditionAndAction(
"string",
"Name",
_("Animation (by name)"),
_("the animation played by the object using the name of the "
"animation."),
"animation"),
_("the animation"),
_("Animations and images"),
"res/actions/animation24.png")
@@ -69,10 +72,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
"objectAnimationName", gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Animation name")))
.MarkAsSimple();
aut.GetAllStrExpressions()["Name"].SetGroup("");
aut.AddScopedAction("PauseAnimation",
_("Pause the animation"),
_("Pause the animation of the object"),
_("Pause the animation of the object."),
_("Pause the animation of _PARAM0_"),
_("Animations and images"),
"res/actions/animation24.png",
@@ -83,7 +87,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
aut.AddScopedAction("PlayAnimation",
_("Resume the animation"),
_("Resume the animation of the object"),
_("Resume the animation of the object."),
_("Resume the animation of _PARAM0_"),
_("Animations and images"),
"res/actions/animation24.png",
@@ -107,6 +111,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Speed scale")))
.MarkAsSimple();
aut.GetAllExpressions()["SpeedScale"].SetGroup("");
aut.AddScopedCondition("IsAnimationPaused",
_("Animation paused"),
@@ -130,6 +135,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
.MarkAsSimple();
aut.AddExpressionAndConditionAndAction(
"number",
"ElapsedTime",
_("Animation elapsed time"),
_("the elapsed time from the beginning of the animation (in seconds)"),
_("the animation elapsed time"),
_("Animations and images"),
"res/actions/animation24.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
.UseStandardParameters(
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Elapsed time (in seconds)")))
.MarkAsAdvanced();
aut.GetAllExpressions()["ElapsedTime"].SetGroup("");
aut.AddExpression(
"Duration",
_("Animation duration"),
_("Return the current animation duration (in seconds)."),
_("Animations and images"),
"res/actions/animation24.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior");
}
} // namespace gd

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"))
@@ -52,52 +52,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
.MarkAsSimple();
aut.AddScopedAction("SetEffectDoubleParameter",
_("Effect parameter (number)"),
_("Change the value of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (number)"),
_("Change the value of a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"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"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("expression", _("New value"))
.MarkAsSimple();
aut.AddScopedAction("SetEffectStringParameter",
_("Effect parameter (string)"),
_("Change the value (string) of a parameter of an effect.") +
_("Effect property (string)"),
_("Change the value (string) of a property of an effect.") +
"\n" +
_("You can find the parameter names (and change the effect "
_("You can find the property names (and change the effect "
"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"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("string", _("New value"))
.MarkAsSimple();
aut.AddScopedAction("SetEffectBooleanParameter",
_("Effect parameter (enable or disable)"),
_("Enable or disable a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
_("Effect property (enable or disable)"),
_("Enable or disable a property of an effect.") + "\n" +
_("You can find the property names (and change the effect "
"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"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("yesorno", _("Enable?"))
.AddParameter("objectEffectParameterName", _("Property name"))
.AddParameter("yesorno", _("Enable this property"))
.MarkAsSimple();
aut.AddScopedCondition("IsEffectEnabled",
@@ -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

@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
.SetIcon("res/actions/opacity24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
.SetIcon("res/actions/opacity24.png");
@@ -54,6 +56,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
_("Opacity (0-255)")))
.SetFunctionName("setOpacity")
.SetGetter("getOpacity");
aut.GetAllExpressions()["Value"].SetGroup("");
}
} // namespace gd

View File

@@ -14,7 +14,7 @@ using namespace std;
namespace gd {
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
gd::PlatformExtension& extension) {
gd::PlatformExtension &extension) {
extension
.SetExtensionInformation("ResizableCapability",
_("Resizable capability"),
@@ -22,84 +22,86 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
.SetIcon("res/actions/scale24_black.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
"res/actions/scale24_black.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"ResizableBehavior",
_("Resizable capability"),
"Resizable",
_("Change the object dimensions."),
"",
"res/actions/scale24_black.png",
"ResizableBehavior",
std::make_shared<gd::Behavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetHidden();
gd::BehaviorMetadata &aut =
extension
.AddBehavior("ResizableBehavior",
_("Resizable capability"),
"Resizable",
_("Change the object dimensions."),
"",
"res/actions/scale24_black.png",
"ResizableBehavior",
std::make_shared<gd::Behavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetHidden();
aut.AddScopedAction("SetWidth",
_("Width"),
_("Change the width of the object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
_("Width"),
_("Change the width of the object."),
_("the width"),
_("Size"),
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions().SetDescription(
_("Width")))
.UseStandardOperatorParameters(
"number",
ParameterOptions::MakeNewOptions().SetDescription(_("Width")))
.MarkAsAdvanced();
aut.AddScopedCondition("Width",
_("Width"),
_("Compare the width of the object."),
_("the width"),
_("Size"),
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
_("Width"),
_("Compare the width of the object."),
_("the width"),
_("Size"),
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions().SetDescription(
_("Width")))
"number",
ParameterOptions::MakeNewOptions().SetDescription(_("Width")))
.MarkAsAdvanced();
aut.AddScopedAction("SetHeight",
_("Height"),
_("Change the height of the object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
_("Height"),
_("Change the height of the object."),
_("the height"),
_("Size"),
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions().SetDescription(
_("Height")))
.UseStandardOperatorParameters(
"number",
ParameterOptions::MakeNewOptions().SetDescription(_("Height")))
.MarkAsAdvanced();
aut.AddScopedCondition("Height",
_("Height"),
_("Compare the height of the object."),
_("the height"),
_("Size"),
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
_("Height"),
_("Compare the height of the object."),
_("the height"),
_("Size"),
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions().SetDescription(
_("Height")))
"number",
ParameterOptions::MakeNewOptions().SetDescription(_("Height")))
.MarkAsAdvanced();
aut.AddScopedAction("SetSize",
_("Size"),
_("Change the size of an object."),
_("Change the size of _PARAM0_: set to _PARAM1_ x _PARAM2_"),
_("Size"),
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
aut.AddScopedAction(
"SetSize",
_("Size"),
_("Change the size of an object."),
_("Change the size of _PARAM0_: set to _PARAM2_ x _PARAM3_"),
_("Size"),
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
.AddParameter("expression", _("Width"))

View File

@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
.SetIcon("res/actions/scale24_black.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
.SetIcon("res/actions/scale24_black.png");
@@ -44,7 +46,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
_("Scale"),
_("the scale of the object (default scale is 1)"),
_("the scale"),
_("Scale"),
_("Size"),
"res/actions/scale24_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.MarkAsAdvanced();
aut.GetAllExpressions()["Value"].SetGroup("");
aut.AddExpressionAndConditionAndAction(
"number",
@@ -60,8 +63,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
_("Scale on X axis"),
_("the scale on X axis of the object (default scale is 1)"),
_("the scale on X axis"),
_("Scale"),
"res/actions/scale24_black.png")
_("Size"),
"res/actions/scaleWidth24_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
.UseStandardParameters(
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.MarkAsAdvanced();
aut.GetAllExpressions()["X"].SetGroup("");
aut.AddExpressionAndConditionAndAction(
"number",
@@ -76,8 +80,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
_("Scale on Y axis"),
_("the scale on Y axis of the object (default scale is 1)"),
_("the scale on Y axis"),
_("Scale"),
"res/actions/scale24_black.png")
_("Size"),
"res/actions/scaleHeight24_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
.UseStandardParameters(
@@ -85,6 +89,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.MarkAsAdvanced();
aut.GetAllExpressions()["Y"].SetGroup("");
}
} // namespace gd

View File

@@ -0,0 +1,58 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the GNU Lesser General Public
* License.
*/
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Tools/Localization.h"
using namespace std;
namespace gd {
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("TextContainerCapability",
_("Text capability"),
_("Animate objects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
.SetIcon("res/conditions/text24_black.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"TextContainerBehavior",
_("Text capability"),
"Text",
_("Access objects text."),
"",
"res/conditions/text24_black.png",
"TextContainerBehavior",
std::make_shared<gd::Behavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetHidden();
aut.AddExpressionAndConditionAndAction(
"string",
"Value",
_("Text"),
_("the text"),
_("the text"),
"",
"res/conditions/text24_black.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TextContainerBehavior")
.UseStandardParameters(
"string", gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Text")))
.MarkAsSimple();
aut.GetAllStrExpressions()["Value"].SetGroup("");
}
} // namespace gd

View File

@@ -35,6 +35,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
.AddInstructionOrExpressionGroupMetadata(_("Events and control flow"))
.SetIcon("res/conditions/toujours24_black.png");
// This condition is deprecated as this does not bring anything new
// and can be confusing or misleading for beginners.
extension
.AddCondition("Always",
_("Always"),
@@ -46,7 +48,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
"res/conditions/toujours_black.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
.MarkAsAdvanced()
.SetHidden();
// Compatibility with GD <= 5.0.127
extension
@@ -114,8 +117,9 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
// Compatibility with GD <= 5.0.127
extension
.AddDuplicatedCondition(
"Egal", "BuiltinCommonInstructions::CompareNumbers", {.unscoped = true})
.AddDuplicatedCondition("Egal",
"BuiltinCommonInstructions::CompareNumbers",
{.unscoped = true})
.SetHidden();
// end of compatibility code
@@ -135,8 +139,9 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
// Compatibility with GD <= 5.0.127
extension
.AddDuplicatedCondition(
"StrEqual", "BuiltinCommonInstructions::CompareStrings", {.unscoped = true})
.AddDuplicatedCondition("StrEqual",
"BuiltinCommonInstructions::CompareStrings",
{.unscoped = true})
.SetHidden();
// end of compatibility code

View File

@@ -38,6 +38,8 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
.SetDefaultValue("0")
.AddParameter("expression", _("Y position of the origin"), "", true)
.SetDefaultValue("0")
.AddParameter("expression", _("Z position of the origin"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
}

View File

@@ -5,6 +5,7 @@
*/
#include "AllBuiltinExtensions.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
using namespace std;
namespace gd {
@@ -57,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
extension
.AddCondition("DoesSceneExist",
_("Does scene exist"),
_("Check if scene exists."),
_("Check if a scene exists."),
_("Scene _PARAM1_ exists"),
"",
"res/actions/texte.png",
@@ -163,6 +164,45 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
"res/actions/window.png")
.SetHelpPath("/interface/scene-editor/events")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddAction("PrioritizeLoadingOfScene",
_("Preload scene"),
_("Preload a scene resources as soon as possible in background."),
_("Preload scene _PARAM1_ in background"),
"",
"res/actions/hourglass_black.svg",
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Name of the new scene"))
.MarkAsAdvanced();
extension.AddExpressionAndCondition("number",
"SceneLoadingProgress",
_("Scene loading progress"),
_("The progress of resources loading in background for a scene (between 0 and 1)."),
_("_PARAM0_ loading progress"),
_(""),
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Scene name"))
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
extension
.AddCondition("AreSceneAssetsLoaded",
_("Scene preloaded"),
_("Check if scene resources have finished to load in background."),
_("Scene _PARAM1_ was preloaded in background"),
"",
"res/actions/hourglass_black.svg",
"res/actions/hourglass_black.svg")
.SetHelpPath("/all-features/resources-loading")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Scene name"))
.MarkAsAdvanced();
}
} // namespace gd

View File

@@ -30,7 +30,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.AddObject<SpriteObject>("Sprite",
_("Sprite"),
_("Animated object which can be used for "
"most elements of a game"),
"most elements of a game."),
"CppPlatform/Extensions/spriteicon.png")
.SetCategoryFullName(_("General"))
.AddDefaultBehavior("EffectCapability::EffectBehavior")
@@ -408,6 +408,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.SetHidden()
.MarkAsSimple();
// Deprecated
obj.AddCondition("ScaleWidth",
_("Scale on X axis"),
_("Compare the scale of the width of an object."),
@@ -415,7 +416,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Size"),
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters(
"number",
@@ -423,6 +424,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Scale (1 by default)")))
.MarkAsAdvanced();
// Deprecated
obj.AddCondition("ScaleHeight",
_("Scale on Y axis"),
_("Compare the scale of the height of an object."),
@@ -430,7 +432,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Size"),
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.UseStandardRelationalOperatorParameters(
"number",

View File

@@ -87,8 +87,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
extension
.AddAction(
"SetWindowSize",
_("Change the size of the game window"),
_("This action changes the size of the game window. Note that this "
_("Game window size"),
_("Changes the size of the game window. Note that this "
"will only work on platform supporting this operation: games "
"running in browsers or on mobile phones can not update their "
"window size. Game resolution can still be updated."),

View File

@@ -5,6 +5,7 @@
*/
#include "ExpressionMetadata.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/String.h"
namespace gd {
@@ -46,16 +47,14 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: supplementaryInformation); // Otherwise don't change anything
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& (supplementaryInformation.find(
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
? (supplementaryInformation.empty()
? ""
: extensionNamespace + supplementaryInformation)
: supplementaryInformation));
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.

View File

@@ -8,6 +8,7 @@
#include <algorithm>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
@@ -64,17 +65,14 @@ InstructionMetadata& InstructionMetadata::AddParameter(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
supplementaryInformation //... so prefix it with the
// extension
// namespace.
)
: supplementaryInformation); // Otherwise don't change anything
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& (supplementaryInformation.find(
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
? (supplementaryInformation.empty()
? ""
: extensionNamespace + supplementaryInformation)
: supplementaryInformation));
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -190,7 +188,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
gd::String templateSentence = _("<subject> of _PARAM0_ <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
@@ -200,7 +198,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
gd::String templateSentence = _("<subject> <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")

View File

@@ -14,6 +14,7 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/String.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
@@ -391,20 +392,19 @@ MetadataProvider::GetBehaviorAnyExpressionMetadata(const gd::Platform& platform,
}
const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& node) {
if (!node.behaviorName.empty()) {
gd::String behaviorType =
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, node.behaviorName);
gd::String behaviorType =
objectsContainersList.GetTypeOfBehavior(node.behaviorName);
return MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, node.functionName);
}
else if (!node.objectName.empty()) {
gd::String objectType =
GetTypeOfObject(globalObjectsContainer, objectsContainer, node.objectName);
gd::String objectType =
objectsContainersList.GetTypeOfObject(node.objectName);
return MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, node.functionName);
}
@@ -412,10 +412,9 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
return MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
}
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& functionCall,
ExpressionNode& parameter) {
int parameterIndex = -1;
@@ -429,17 +428,15 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
return nullptr;
}
return MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
platform,
objectsContainersList,
functionCall,
parameterIndex);
}
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& functionCall,
int parameterIndex) {
// Search the parameter metadata index skipping invisible ones.
@@ -448,7 +445,7 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
ExpressionParser2::WrittenParametersFirstIndex(
functionCall.objectName, functionCall.behaviorName);
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, functionCall);
platform, objectsContainersList, functionCall);
if (IsBadExpressionMetadata(metadata)) {
return nullptr;

View File

@@ -15,6 +15,7 @@ class ExpressionMetadata;
class ExpressionMetadata;
class Platform;
class PlatformExtension;
class ObjectsContainersList;
struct FunctionCallNode;
struct ExpressionNode;
} // namespace gd
@@ -237,22 +238,19 @@ class GD_CORE_API MetadataProvider {
const gd::Platform& platform, gd::String objectType, gd::String exprType);
static const gd::ExpressionMetadata& GetFunctionCallMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& node);
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& functionCall,
ExpressionNode& parameter);
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
const gd::Platform& platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::Platform& platform,
const gd::ObjectsContainersList &objectsContainersList,
FunctionCallNode& functionCall,
int parameterIndex);

View File

@@ -191,6 +191,16 @@ class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata
return *this;
}
/**
* \see gd::InstructionMetadata::SetHelpPath
*/
MultipleInstructionMetadata &SetHelpPath(const gd::String &path) {
if (expression) expression->SetHelpPath(path);
if (condition) condition->SetHelpPath(path);
if (action) action->SetHelpPath(path);
return *this;
}
/**
* \see gd::InstructionMetadata::MarkAsSimple
*/

View File

@@ -303,12 +303,24 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
return *this;
}
/**
* \brief Return true if the instruction must be hidden in the IDE.
* \brief Return true if the object must be hidden in the IDE.
*/
bool IsHidden() const { return hidden; }
/**
* \brief Declare a usage of the 3D renderer.
*/
ObjectMetadata &MarkAsRenderedIn3D() {
isRenderedIn3D = true;
return *this;
}
/**
* \brief Return true if the object uses the 3D renderer.
*/
bool IsRenderedIn3D() const { return isRenderedIn3D; }
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
@@ -329,6 +341,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
gd::String categoryFullName;
std::set<gd::String> defaultBehaviorTypes;
bool hidden = false;
bool isRenderedIn3D = false;
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new

View File

@@ -34,6 +34,11 @@ class GD_CORE_API ParameterMetadata {
*/
gd::ValueTypeMetadata &GetValueTypeMetadata() { return valueTypeMetadata; }
/**
* \brief Return the metadata of the parameter type.
*/
const gd::ValueTypeMetadata &GetValueTypeMetadata() const { return valueTypeMetadata; }
/**
* \brief Set the metadata of the parameter type.
*/

View File

@@ -8,6 +8,7 @@
#include "GDCore/Events/Expression.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "InstructionMetadata.h"
@@ -15,6 +16,8 @@
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
namespace gd {
const ParameterMetadata ParameterMetadataTools::badParameterMetadata;
void ParameterMetadataTools::ParametersToObjectsContainer(
const gd::Project& project,
const std::vector<gd::ParameterMetadata>& parameters,
@@ -57,6 +60,55 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
}
}
void ParameterMetadataTools::ForEachParameterMatchingSearch(
const std::vector<const std::vector<gd::ParameterMetadata>*>&
parametersVectorsList,
const gd::String& search,
std::function<void(const gd::ParameterMetadata&)> cb) {
for (auto it = parametersVectorsList.rbegin();
it != parametersVectorsList.rend();
++it) {
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
for (const auto& parameterMetadata: *parametersVector) {
if (parameterMetadata.GetName().FindCaseInsensitive(search) != gd::String::npos) cb(parameterMetadata);
}
}
}
bool ParameterMetadataTools::Has(
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
const gd::String& parameterName) {
for (auto it = parametersVectorsList.rbegin();
it != parametersVectorsList.rend();
++it) {
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
for (const auto& parameterMetadata: *parametersVector) {
if (parameterMetadata.GetName() == parameterName) return true;
}
}
return false;
}
const gd::ParameterMetadata& ParameterMetadataTools::Get(
const std::vector<const std::vector<gd::ParameterMetadata>*>&
parametersVectorsList,
const gd::String& parameterName) {
for (auto it = parametersVectorsList.rbegin();
it != parametersVectorsList.rend();
++it) {
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
for (const auto& parameterMetadata: *parametersVector) {
if (parameterMetadata.GetName() == parameterName) return parameterMetadata;
}
}
return badParameterMetadata;
}
void ParameterMetadataTools::IterateOverParameters(
const std::vector<gd::Expression>& parameters,
const std::vector<gd::ParameterMetadata>& parametersMetadata,
@@ -105,8 +157,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
void ParameterMetadataTools::IterateOverParametersWithIndex(
const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer, FunctionCallNode &node,
const gd::ObjectsContainersList &objectsContainersList, FunctionCallNode &node,
std::function<void(const gd::ParameterMetadata &parameterMetadata,
std::unique_ptr<gd::ExpressionNode> &parameterNode,
size_t parameterIndex, const gd::String &lastObjectName)>
@@ -117,8 +168,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
const gd::ExpressionMetadata &metadata =
isObjectFunction ? MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer,
objectsContainer, node.objectName),
objectsContainersList.GetTypeOfObject(node.objectName),
node.functionName)
: MetadataProvider::GetAnyExpressionMetadata(
platform, node.functionName);

View File

@@ -3,9 +3,8 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef ParameterMetadataTools_H
#define ParameterMetadataTools_H
#pragma once
#include <functional>
#include <vector>
#include <memory>
#include "GDCore/String.h"
@@ -13,6 +12,7 @@ namespace gd {
class Platform;
class Project;
class ObjectsContainer;
class ObjectsContainersList;
class ParameterMetadata;
class Expression;
struct FunctionCallNode;
@@ -27,6 +27,19 @@ class GD_CORE_API ParameterMetadataTools {
const std::vector<gd::ParameterMetadata>& parameters,
gd::ObjectsContainer& outputObjectsContainer);
static void ForEachParameterMatchingSearch(
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
const gd::String& search,
std::function<void(const gd::ParameterMetadata&)> cb);
static bool Has(
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
const gd::String& parameterName);
static const gd::ParameterMetadata& Get(
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
const gd::String& parameterName);
/**
* Iterate over a list of parameters and their values.
* Callback function is called with the parameter metadata, its value
@@ -59,8 +72,7 @@ class GD_CORE_API ParameterMetadataTools {
*/
static void IterateOverParametersWithIndex(
const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer, FunctionCallNode &node,
const gd::ObjectsContainersList &objectsContainersList, FunctionCallNode &node,
std::function<void(const gd::ParameterMetadata &parameterMetadata,
std::unique_ptr<gd::ExpressionNode> &parameterNode,
size_t parameterIndex,
@@ -74,8 +86,8 @@ class GD_CORE_API ParameterMetadataTools {
static size_t GetObjectParameterIndexFor(
const std::vector<gd::ParameterMetadata>& parametersMetadata,
size_t parameterIndex);
private:
static const gd::ParameterMetadata badParameterMetadata;
};
} // namespace gd
#endif // ParameterMetadataTools_H
#endif

View File

@@ -121,6 +121,13 @@ class GD_CORE_API ValueTypeMetadata {
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
}
/**
* \brief Return true if the type is a boolean.
*/
bool IsBoolean() const {
return gd::ValueTypeMetadata::IsTypeExpression("boolean", name);
}
/**
* \brief Return true if the type of the parameter is a number.
* \note If you had a new type of parameter, also add it in the IDE (
@@ -131,6 +138,24 @@ class GD_CORE_API ValueTypeMetadata {
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
}
/**
* \brief Return true if the type is a variable but from a specific scope
* (scene, project or object). In new code, prefer to use the more generic "variable"
* parameter (which accepts any variable coming from an object or from containers in the scope).
*/
bool IsLegacyPreScopedVariable() const {
return gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(name);
}
/**
* \brief Return true if the type is a variable but from a specific scope
* (scene, project or object). In new code, prefer to use the more generic "variable"
* parameter (which accepts any variable coming from an object or from containers in the scope).
*/
static bool IsTypeLegacyPreScopedVariable(const gd::String &type) {
return type == "scenevar" || type == "globalvar" || type == "objectvar";
}
/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
@@ -175,8 +200,13 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "identifier";
} else if (type == "boolean") {
return parameterType == "yesorno" || parameterType == "trueorfalse";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
return
parameterType == "variable" || // Any variable.
// Old, "pre-scoped" variables:
parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
} else if (type == "resource") {
return parameterType == "fontResource" ||
@@ -187,7 +217,9 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "jsonResource" ||
parameterType == "tilemapResource" ||
parameterType == "tilesetResource" ||
parameterType == "model3DResource";
parameterType == "model3DResource" ||
parameterType == "atlasResource" ||
parameterType == "spineResource";
}
return false;
}
@@ -196,7 +228,7 @@ class GD_CORE_API ValueTypeMetadata {
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*
*
* \note It only maps string and number types.
*/
static const gd::String &GetExpressionPrimitiveValueType(const gd::String &parameterType);
@@ -205,7 +237,7 @@ class GD_CORE_API ValueTypeMetadata {
* \brief Return the primitive type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*
*
* \note It also maps variable and boolean types.
*/
static const gd::String &GetPrimitiveValueType(const gd::String &parameterType);

View File

@@ -264,8 +264,11 @@ class GD_CORE_API PlatformExtension {
*
* \param name The name of the behavior
* \param fullname The user friendly name of the behavior
* \param defaultName The default name of behavior instances
* \param description The user friendly description of the behavior
* \param group The behavior category label
* \param icon The icon of the behavior.
* \param className The name of the class implementing the behavior
* \param instance An instance of the behavior that
* will be used to create the behavior
* \param sharedDatasInstance Optional
@@ -288,6 +291,7 @@ class GD_CORE_API PlatformExtension {
* \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(

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "DependenciesAnalyzer.h"
#include <algorithm>
#include "GDCore/Events/Builtin/LinkEvent.h"
@@ -29,9 +28,9 @@ DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
bool DependenciesAnalyzer::Analyze() {
if (layout)
return Analyze(layout->GetEvents(), true);
return Analyze(layout->GetEvents());
else if (externalEvents)
return Analyze(externalEvents->GetEvents(), true);
return Analyze(externalEvents->GetEvents());
std::cout << "ERROR: DependenciesAnalyzer called without any layout or "
"external events.";
@@ -40,63 +39,38 @@ bool DependenciesAnalyzer::Analyze() {
DependenciesAnalyzer::~DependenciesAnalyzer() {}
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLevel) {
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
for (unsigned int i = 0; i < events.size(); ++i) {
const gd::LinkEvent* linkEvent = dynamic_cast<const gd::LinkEvent*>(&events[i]);
if (linkEvent) {
DependenciesAnalyzer analyzer(*this);
gd::String linked = linkEvent->GetTarget();
if (project.HasExternalEventsNamed(linked)) {
if (std::find(parentExternalEvents.begin(),
parentExternalEvents.end(),
linked) != parentExternalEvents.end())
return false; // Circular dependency!
externalEventsDependencies.insert(
linked); // There is a direct dependency
if (!isOnTopLevel) notTopLevelExternalEventsDependencies.insert(linked);
analyzer.AddParentExternalEvents(linked);
if (!analyzer.Analyze(project.GetExternalEvents(linked).GetEvents(),
isOnTopLevel))
linked) != parentExternalEvents.end()) {
// Circular dependency!
return false;
}
bool wasDependencyJustAdded = externalEventsDependencies.insert(linked).second;
if (wasDependencyJustAdded) {
parentExternalEvents.push_back(linked);
if (!Analyze(project.GetExternalEvents(linked).GetEvents()))
return false;
parentExternalEvents.pop_back();
}
} else if (project.HasLayoutNamed(linked)) {
if (std::find(parentScenes.begin(), parentScenes.end(), linked) !=
parentScenes.end())
return false; // Circular dependency!
scenesDependencies.insert(linked); // There is a direct dependency
if (!isOnTopLevel) notTopLevelScenesDependencies.insert(linked);
analyzer.AddParentScene(linked);
if (!analyzer.Analyze(project.GetLayout(linked).GetEvents(),
isOnTopLevel))
parentScenes.end()) {
// Circular dependency!
return false;
}
// Update with indirect dependencies.
scenesDependencies.insert(analyzer.GetScenesDependencies().begin(),
analyzer.GetScenesDependencies().end());
externalEventsDependencies.insert(
analyzer.GetExternalEventsDependencies().begin(),
analyzer.GetExternalEventsDependencies().end());
sourceFilesDependencies.insert(
analyzer.GetSourceFilesDependencies().begin(),
analyzer.GetSourceFilesDependencies().end());
notTopLevelScenesDependencies.insert(
analyzer.GetNotTopLevelScenesDependencies().begin(),
analyzer.GetNotTopLevelScenesDependencies().end());
notTopLevelExternalEventsDependencies.insert(
analyzer.GetNotTopLevelExternalEventsDependencies().begin(),
analyzer.GetNotTopLevelExternalEventsDependencies().end());
if (!isOnTopLevel) {
notTopLevelScenesDependencies.insert(
analyzer.GetScenesDependencies().begin(),
analyzer.GetScenesDependencies().end());
notTopLevelExternalEventsDependencies.insert(
analyzer.GetExternalEventsDependencies().begin(),
analyzer.GetExternalEventsDependencies().end());
}
bool wasDependencyJustAdded = scenesDependencies.insert(linked).second;
if (wasDependencyJustAdded) {
parentScenes.push_back(linked);
if (!Analyze(project.GetLayout(linked).GetEvents()))
return false;
parentScenes.pop_back();
}
}
}
@@ -112,45 +86,9 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLev
// Analyze sub events dependencies
if (events[i].CanHaveSubEvents()) {
if (!Analyze(events[i].GetSubEvents(), false)) return false;
if (!Analyze(events[i].GetSubEvents())) return false;
}
}
return true;
}
gd::String DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene() {
if (!externalEvents) {
std::cout << "ERROR: ExternalEventsCanBeCompiledForAScene called without "
"external events set!"
<< std::endl;
return "";
}
gd::String sceneName;
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
// For each layout, compute the dependencies and the dependencies which are
// not coming from a top level event.
DependenciesAnalyzer analyzer(project, project.GetLayout(i));
if (!analyzer.Analyze()) continue; // Analyze failed -> Cyclic dependencies
const std::set<gd::String>& dependencies =
analyzer.GetExternalEventsDependencies();
const std::set<gd::String>& notTopLevelDependencies =
analyzer.GetNotTopLevelExternalEventsDependencies();
// Check if the external events is a dependency, and that is is only present
// as a link on the top level.
if (dependencies.find(externalEvents->GetName()) != dependencies.end() &&
notTopLevelDependencies.find(externalEvents->GetName()) ==
notTopLevelDependencies.end()) {
if (!sceneName.empty())
return ""; // External events can be compiled only if one scene is
// including them.
else
sceneName = project.GetLayout(i).GetName();
}
}
return sceneName; // External events can be compiled and used for the scene.
}
#endif

View File

@@ -39,11 +39,6 @@ class GD_CORE_API DependenciesAnalyzer {
/**
* \brief Constructor for analyzing the dependencies of external events.
*
* You can also call then
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene to check if the
* external events can be compiled separately and called by a scene. \see
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
*/
DependenciesAnalyzer(const gd::Project& project_,
const gd::ExternalEvents& externalEvents);
@@ -60,18 +55,6 @@ class GD_CORE_API DependenciesAnalyzer {
*/
bool Analyze();
/**
* Check if the external events (passed in the constructor) can be compiled
* and called by a single scene:<br> This is possible when the link calling
* the external events does not have any parent event and when this situation
* occurs only in a single scene and not in another.
*
* \return The name of the scene which is able to call the compiled external
* events. If empty, no scene is able to call them. (So external events have
* to be included directly by links).
*/
gd::String ExternalEventsCanBeCompiledForAScene();
/**
* \brief Return the scenes being dependencies of the scene or external events
* passed in the constructor.
@@ -96,25 +79,6 @@ class GD_CORE_API DependenciesAnalyzer {
return sourceFilesDependencies;
};
/**
* \brief Return the scenes being dependencies of the scene or external events
* passed in the constructor, but being not top level dependencies: The links
* including them are not a top level events (i.e: They have a parent event).
*/
const std::set<gd::String>& GetNotTopLevelScenesDependencies() const {
return notTopLevelScenesDependencies;
};
/**
* \brief Return the external events being dependencies of the scene or
* external events passed in the constructor, but being not top level
* dependencies: The links including them are not a top level events (i.e:
* They have a parent event).
*/
const std::set<gd::String>& GetNotTopLevelExternalEventsDependencies() const {
return notTopLevelExternalEventsDependencies;
};
private:
/**
* \brief Analyze the dependencies of the events.
@@ -124,32 +88,11 @@ class GD_CORE_API DependenciesAnalyzer {
* (they have no parents). \return false if a circular dependency exists, true
* otherwise.
*/
bool Analyze(const gd::EventsList& events, bool isOnTopLevel);
void AddParentScene(gd::String parentScene) {
parentScenes.push_back(parentScene);
};
void AddParentExternalEvents(gd::String parentExternalEvents_) {
parentExternalEvents.push_back(parentExternalEvents_);
};
/**
* Return true if all links pointing to external events called \a
* externalEventsName are only at the top level of \a events. The function
* return false as soon as it discover a link to external events which is not
* at the top level ( i.e: It has a parent event ).
*
* \warning The function assumes that there are not cyclic dependencies.
*/
bool CheckIfExternalEventsIsLinkedOnlyAtTopLevel(
const gd::String& externalEventsName,
std::vector<std::shared_ptr<gd::BaseEvent> >& events);
bool Analyze(const gd::EventsList& events);
std::set<gd::String> scenesDependencies;
std::set<gd::String> externalEventsDependencies;
std::set<gd::String> sourceFilesDependencies;
std::set<gd::String> notTopLevelScenesDependencies;
std::set<gd::String> notTopLevelExternalEventsDependencies;
std::vector<gd::String>
parentScenes; ///< Used to check for circular dependencies.
std::vector<gd::String>

View File

@@ -11,6 +11,8 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/String.h"
using namespace std;
@@ -47,7 +49,14 @@ bool ArbitraryEventsWorker::VisitEvent(gd::BaseEvent& event) {
for (std::size_t j = 0; j < actionsVectors.size(); ++j)
VisitInstructionList(*actionsVectors[j], false);
return false;
auto allExpressionsWithMetadata = event.GetAllExpressionsWithMetadata();
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
shouldDelete |= VisitEventExpression(
*expressionAndMetadata.first, expressionAndMetadata.second);
}
return shouldDelete;
}
bool ArbitraryEventsWorker::VisitLinkEvent(gd::LinkEvent& linkEvent) {
@@ -75,6 +84,11 @@ bool ArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction,
return DoVisitInstruction(instruction, isCondition);
}
bool ArbitraryEventsWorker::VisitEventExpression(gd::Expression& expression,
const gd::ParameterMetadata& metadata) {
return DoVisitEventExpression(expression, metadata);
}
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {}
@@ -141,6 +155,12 @@ void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& inst
DoVisitInstruction(instruction, isCondition);
}
void ReadOnlyArbitraryEventsWorker::VisitEventExpression(const gd::Expression& expression,
const gd::ParameterMetadata& metadata) {
DoVisitEventExpression(expression, metadata);
}
void ReadOnlyArbitraryEventsWorker::StopAnyEventIteration() {
shouldStopIteration = true;
}

View File

@@ -10,6 +10,7 @@
#include <vector>
#include "GDCore/Events/InstructionsList.h"
#include "GDCore/Events/EventVisitor.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h"
namespace gd {
class Instruction;
@@ -17,6 +18,8 @@ class BaseEvent;
class LinkEvent;
class EventsList;
class ObjectsContainer;
class Expression;
class ParameterMetadata;
} // namespace gd
namespace gd {
@@ -47,6 +50,7 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
void VisitInstructionList(gd::InstructionsList& instructions,
bool areConditions);
bool VisitInstruction(gd::Instruction& instruction, bool isCondition);
bool VisitEventExpression(gd::Expression& expression, const gd::ParameterMetadata& metadata);
/**
* Called to do some work on an event list.
@@ -62,9 +66,9 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
/**
* Called to do some work on a link event.
*
*
* Note that DoVisitEvent is also called with this event.
*
*
* \return true if the event must be deleted from the events list, false
* otherwise (default).
*/
@@ -85,6 +89,16 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
bool isCondition) {
return false;
};
/**
* Called to do some work on an expression of an event.
* \return true if the event must be deleted from the list, false
* otherwise (default).
*/
virtual bool DoVisitEventExpression(gd::Expression& expression,
const gd::ParameterMetadata& metadata) {
return false;
}
};
/**
@@ -99,8 +113,7 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
: public ArbitraryEventsWorker {
public:
ArbitraryEventsWorkerWithContext()
: currentGlobalObjectsContainer(nullptr),
currentObjectsContainer(nullptr){};
: projectScopedContainers(nullptr){};
virtual ~ArbitraryEventsWorkerWithContext();
/**
@@ -108,30 +121,27 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
* giving the objects container on which the events are applying to.
*/
void Launch(gd::EventsList& events,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_) {
currentGlobalObjectsContainer = &globalObjectsContainer_;
currentObjectsContainer = &objectsContainer_;
const gd::ProjectScopedContainers& projectScopedContainers_) {
projectScopedContainers = &projectScopedContainers_;
ArbitraryEventsWorker::Launch(events);
};
void Launch(gd::EventsList& events) = delete;
protected:
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
const gd::ProjectScopedContainers& GetProjectScopedContainers() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentGlobalObjectsContainer;
return *projectScopedContainers;
};
const gd::ObjectsContainer& GetObjectsContainer() {
const gd::ObjectsContainersList& GetObjectsContainersList() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentObjectsContainer;
return projectScopedContainers->GetObjectsContainersList();
};
private:
const gd::ObjectsContainer* currentGlobalObjectsContainer;
const gd::ObjectsContainer* currentObjectsContainer;
const gd::ProjectScopedContainers* projectScopedContainers;
};
/**
@@ -162,6 +172,7 @@ protected:
void VisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions);
void VisitInstruction(const gd::Instruction& instruction, bool isCondition);
void VisitEventExpression(const gd::Expression& expression, const gd::ParameterMetadata& metadata);
/**
* Called to do some work on an event list.
@@ -175,7 +186,7 @@ protected:
/**
* Called to do some work on a link event.
*
*
* Note that DoVisitEvent is also called with this event.
*/
virtual void DoVisitLinkEvent(const gd::LinkEvent& linkEvent) {};
@@ -192,6 +203,13 @@ protected:
virtual void DoVisitInstruction(const gd::Instruction& instruction,
bool isCondition) {};
/**
* Called to do some work on an expression of an event.
*/
virtual void DoVisitEventExpression(const gd::Expression& expression,
const gd::ParameterMetadata& metadata) {
}
bool shouldStopIteration;
};
@@ -207,8 +225,7 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
: public ReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorkerWithContext()
: currentGlobalObjectsContainer(nullptr),
currentObjectsContainer(nullptr){};
: projectScopedContainers(nullptr){};
virtual ~ReadOnlyArbitraryEventsWorkerWithContext();
/**
@@ -216,30 +233,22 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
* giving the objects container on which the events are applying to.
*/
void Launch(const gd::EventsList& events,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_) {
currentGlobalObjectsContainer = &globalObjectsContainer_;
currentObjectsContainer = &objectsContainer_;
const gd::ProjectScopedContainers& projectScopedContainers_) {
projectScopedContainers = &projectScopedContainers_;
ReadOnlyArbitraryEventsWorker::Launch(events);
};
void Launch(gd::EventsList& events) = delete;
protected:
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
const gd::ProjectScopedContainers& GetProjectScopedContainers() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentGlobalObjectsContainer;
};
const gd::ObjectsContainer& GetObjectsContainer() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentObjectsContainer;
return *projectScopedContainers;
};
private:
const gd::ObjectsContainer* currentGlobalObjectsContainer;
const gd::ObjectsContainer* currentObjectsContainer;
const gd::ProjectScopedContainers* projectScopedContainers;
};
} // namespace gd

View File

@@ -4,7 +4,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"

View File

@@ -4,7 +4,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"

View File

@@ -31,14 +31,10 @@ namespace gd {
class GD_CORE_API ExpressionBehaviorRenamer
: public ExpressionParser2NodeWorker {
public:
ExpressionBehaviorRenamer(const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_,
const gd::String& objectName_,
ExpressionBehaviorRenamer(const gd::String& objectName_,
const gd::String& oldBehaviorName_,
const gd::String& newBehaviorName_)
: hasDoneRenaming(false),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
objectName(objectName_),
oldBehaviorName(oldBehaviorName_),
newBehaviorName(newBehaviorName_){};
@@ -97,8 +93,6 @@ class GD_CORE_API ExpressionBehaviorRenamer
private:
bool hasDoneRenaming;
const gd::ObjectsContainer& globalObjectsContainer;
const gd::ObjectsContainer& objectsContainer;
const gd::String& objectName; // The object name for which the behavior
// must be replaced.
const gd::String& oldBehaviorName;
@@ -132,9 +126,7 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
} else {
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionBehaviorRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),
objectName,
ExpressionBehaviorRenamer renamer(objectName,
oldBehaviorName,
newBehaviorName);
node->Visit(renamer);

View File

@@ -19,6 +19,8 @@
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h"
namespace gd {
@@ -33,13 +35,11 @@ class GD_CORE_API ExpressionObjectsAnalyzer
public:
ExpressionObjectsAnalyzer(
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers &projectScopedContainers_,
const gd::String &rootType_,
EventsContext& context_) :
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
rootType(rootType_),
context(context_){};
virtual ~ExpressionObjectsAnalyzer(){};
@@ -58,6 +58,25 @@ class GD_CORE_API ExpressionObjectsAnalyzer
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ParameterMetadata::IsExpression("variable", type)) {
// Nothing to do (this can't reference an object)
} else {
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
// This is an object variable.
context.AddObjectName(projectScopedContainers, node.name);
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
@@ -69,26 +88,41 @@ class GD_CORE_API ExpressionObjectsAnalyzer
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
context.AddObjectName(node.identifierName);
context.AddObjectName(projectScopedContainers, node.identifierName);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
// Nothing to do (identifier is a variable but not an object).
} else {
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
// This is an object variable.
context.AddObjectName(projectScopedContainers, node.identifierName);
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.objectName.empty()) {
context.AddObjectName(node.objectName);
context.AddObjectName(projectScopedContainers, node.objectName);
if (!node.behaviorFunctionName.empty()) {
context.AddBehaviorName(node.objectName, node.objectFunctionOrBehaviorName);
context.AddBehaviorName(projectScopedContainers, node.objectName, node.objectFunctionOrBehaviorName);
}
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (!node.objectName.empty()) {
context.AddObjectName(node.objectName);
context.AddObjectName(projectScopedContainers, node.objectName);
if (!node.behaviorName.empty()) {
context.AddBehaviorName(node.objectName, node.behaviorName);
context.AddBehaviorName(projectScopedContainers, node.objectName, node.behaviorName);
}
}
for (auto& parameter : node.parameters) {
@@ -99,8 +133,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
const gd::String rootType;
EventsContext& context;
@@ -121,8 +154,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
const gd::Expression& parameterValue,
const gd::String& lastObjectName) {
AnalyzeParameter(platform,
project,
layout,
GetProjectScopedContainers(),
parameterMetadata,
parameterValue,
context,
@@ -134,8 +166,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
void EventsContextAnalyzer::AnalyzeParameter(
const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::ProjectScopedContainers& projectScopedContainers,
const gd::ParameterMetadata& metadata,
const gd::Expression& parameter,
EventsContext& context,
@@ -143,59 +174,39 @@ void EventsContextAnalyzer::AnalyzeParameter(
const auto& value = parameter.GetPlainString();
const auto& type = metadata.GetType();
if (ParameterMetadata::IsObject(type)) {
context.AddObjectName(value);
context.AddObjectName(projectScopedContainers, value);
} else if (ParameterMetadata::IsExpression("number", type)) {
auto node = parameter.GetRootNode();
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "number", context);
ExpressionObjectsAnalyzer analyzer(platform, projectScopedContainers, "number", context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsExpression("string", type)) {
auto node = parameter.GetRootNode();
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "string", context);
ExpressionObjectsAnalyzer analyzer(platform, projectScopedContainers, "string", context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsBehavior(type)) {
context.AddBehaviorName(lastObjectName, value);
context.AddBehaviorName(projectScopedContainers, lastObjectName, value);
}
}
void EventsContext::AddObjectName(const gd::String& objectOrGroupName) {
for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) {
void EventsContext::AddObjectName(const gd::ProjectScopedContainers& projectScopedContainers,
const gd::String& objectOrGroupName) {
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
for (auto& realObjectName : objectsContainersList.ExpandObjectName(objectOrGroupName)) {
objectNames.insert(realObjectName);
}
referencedObjectOrGroupNames.insert(objectOrGroupName);
}
void EventsContext::AddBehaviorName(const gd::String& objectOrGroupName,
void EventsContext::AddBehaviorName(const gd::ProjectScopedContainers& projectScopedContainers,
const gd::String& objectOrGroupName,
const gd::String& behaviorName) {
for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) {
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
for (auto& realObjectName : objectsContainersList.ExpandObjectName(objectOrGroupName)) {
objectOrGroupBehaviorNames[realObjectName].insert(behaviorName);
}
objectOrGroupBehaviorNames[objectOrGroupName].insert(behaviorName);
}
std::vector<gd::String> EventsContext::ExpandObjectsName(
const gd::String& objectName) {
// Note: this logic is duplicated in EventsCodeGenerator::ExpandObjectsName
std::vector<gd::String> realObjects;
if (project.GetObjectGroups().Has(objectName))
realObjects =
project.GetObjectGroups().Get(objectName).GetAllObjectsNames();
else if (layout.GetObjectGroups().Has(objectName))
realObjects = layout.GetObjectGroups().Get(objectName).GetAllObjectsNames();
else
realObjects.push_back(objectName);
// Ensure that all returned objects actually exists.
for (std::size_t i = 0; i < realObjects.size();) {
if (!layout.HasObjectNamed(realObjects[i]) &&
!project.HasObjectNamed(realObjects[i]))
realObjects.erase(realObjects.begin() + i);
else
++i;
}
return realObjects;
}
} // namespace gd

View File

@@ -15,6 +15,7 @@ namespace gd {
class BaseEvent;
class Platform;
class ObjectsContainer;
class ObjectsContainersList;
class Project;
class Layout;
class EventsList;
@@ -29,12 +30,13 @@ namespace gd {
*/
class GD_CORE_API EventsContext {
public:
EventsContext(gd::ObjectsContainer& project_, gd::ObjectsContainer& layout_)
: project(project_), layout(layout_){};
EventsContext(){};
virtual ~EventsContext(){};
void AddObjectName(const gd::String& objectOrGroupName);
void AddBehaviorName(const gd::String& objectOrGroupName,
void AddObjectName(const gd::ProjectScopedContainers& projectScopedContainers,
const gd::String& objectOrGroupName);
void AddBehaviorName(const gd::ProjectScopedContainers& projectScopedContainers,
const gd::String& objectOrGroupName,
const gd::String& behaviorName);
/**
@@ -59,13 +61,9 @@ class GD_CORE_API EventsContext {
}
private:
std::vector<gd::String> ExpandObjectsName(const gd::String& objectOrGroupName);
std::set<gd::String> referencedObjectOrGroupNames;
std::set<gd::String> objectNames;
std::map<gd::String, std::set<gd::String>> objectOrGroupBehaviorNames;
gd::ObjectsContainer& project;
gd::ObjectsContainer& layout;
};
/**
@@ -73,15 +71,10 @@ class GD_CORE_API EventsContext {
*
* \ingroup IDE
*/
class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorkerWithContext {
public:
EventsContextAnalyzer(const gd::Platform& platform_,
gd::ObjectsContainer& project_,
gd::ObjectsContainer& layout_)
: platform(platform_),
project(project_),
layout(layout_),
context(project, layout){};
EventsContextAnalyzer(const gd::Platform& platform_)
: platform(platform_) {};
virtual ~EventsContextAnalyzer(){};
/**
@@ -90,8 +83,7 @@ class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
const EventsContext& GetEventsContext() { return context; }
static void AnalyzeParameter(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::ProjectScopedContainers& projectScopedContainers,
const gd::ParameterMetadata& metadata,
const gd::Expression& parameter,
EventsContext& context,
@@ -102,8 +94,6 @@ class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
bool isCondition);
const gd::Platform& platform;
gd::ObjectsContainer& project;
gd::ObjectsContainer& layout;
EventsContext context;
};

View File

@@ -34,14 +34,12 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
public:
IdentifierFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers &projectScopedContainers_,
const gd::String& identifierType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
identifierType(identifierType_),
objectName(objectName_){};
virtual ~IdentifierFinderExpressionNodeWorker(){};
@@ -79,14 +77,14 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
projectScopedContainers.GetObjectsContainersList().GetTypeOfObject(objectName),
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
size_t parameterIndex = 0;
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
@@ -111,8 +109,7 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
@@ -166,8 +163,7 @@ class GD_CORE_API IdentifierFinderEventWorker
IdentifierFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
GetProjectScopedContainers(),
identifierType,
objectName);
node->Visit(searcher);
@@ -227,9 +223,10 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
platform,
identifierType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
eventWorker.Launch(layout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
@@ -238,7 +235,8 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
platform,
identifierType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
eventWorker.Launch(externalEvents.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
@@ -247,7 +245,8 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
platform,
identifierType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
eventWorker.Launch(dependencyLayout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, dependencyLayout));
}
}

View File

@@ -0,0 +1,277 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/PropertiesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Go through the nodes and rename properties,
* or signal if the instruction must be renamed if a removed property is used.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionPropertyReplacer
: public ExpressionParser2NodeWorker {
public:
ExpressionPropertyReplacer(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::PropertiesContainer& targetPropertiesContainer_,
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_,
const std::unordered_set<gd::String>& removedPropertyNames_)
: hasDoneRenaming(false),
removedPropertyUsed(false),
platform(platform_),
projectScopedContainers(projectScopedContainers_),
targetPropertiesContainer(targetPropertiesContainer_),
oldToNewPropertyNames(oldToNewPropertyNames_),
removedPropertyNames(removedPropertyNames_){};
virtual ~ExpressionPropertyReplacer(){};
bool HasDoneRenaming() const { return hasDoneRenaming; }
bool IsRemovedPropertyUsed() const { return removedPropertyUsed; }
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
auto& propertiesContainersList =
projectScopedContainers.GetPropertiesContainersList();
// The node represents a variable or an object name on which a variable
// will be accessed, or a property with a child.
// Match the potential *new* name of the property, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.name),
[&]() {
// Do nothing, it's an object variable.
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
}, [&]() {
// This is a property, check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.name);
}
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's a parameter.
if (node.child) node.child->Visit(*this);
}, [&]() {
// This is something else - potentially a deleted property.
// Check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.name);
}
if (node.child) node.child->Visit(*this);
});
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto& propertiesContainersList =
projectScopedContainers.GetPropertiesContainersList();
// Match the potential *new* name of the property, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.identifierName),
[&]() {
// Do nothing, it's an object variable.
}, [&]() {
// Do nothing, it's a variable.
}, [&]() {
// This is a property, check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.identifierName);
}
}, [&]() {
// Do nothing, it's a parameter.
}, [&]() {
// This is something else - potentially a deleted property.
// Check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.identifierName);
}
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
bool hasDoneRenaming;
bool removedPropertyUsed;
const gd::String& GetPotentialNewName(const gd::String& oldName) {
return oldToNewPropertyNames.count(oldName) >= 1
? oldToNewPropertyNames.find(oldName)->second
: oldName;
}
bool RenameOrRemovePropertyOfTargetPropertyContainer(
gd::String& propertyName) {
if (oldToNewPropertyNames.count(propertyName) >= 1) {
propertyName = oldToNewPropertyNames.find(propertyName)->second;
hasDoneRenaming = true;
return true;
} else if (removedPropertyNames.count(propertyName) >= 1) {
removedPropertyUsed = true;
return true;
}
return false; // Nothing was changed or done.
}
// Scope:
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
// Renaming or removing to do:
const gd::PropertiesContainer& targetPropertiesContainer;
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames;
const std::unordered_set<gd::String>& removedPropertyNames;
gd::String objectNameToUseForVariableAccessor;
};
bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
bool shouldDeleteInstruction = false;
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return; // Not an expression that can contain properties.
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
shouldDeleteInstruction = true;
} else if (renamer.HasDoneRenaming()) {
instruction.SetParameter(
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
});
return shouldDeleteInstruction;
}
bool EventsPropertyReplacer::DoVisitEventExpression(
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
const gd::String& type = metadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return false; // Not an expression that can contain properties.
auto node = expression.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
return true;
} else if (renamer.HasDoneRenaming()) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
return false;
}
EventsPropertyReplacer::~EventsPropertyReplacer() {}
} // namespace gd

View File

@@ -0,0 +1,56 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class PropertiesContainer;
class EventsList;
class Platform;
} // namespace gd
namespace gd {
/**
* \brief Replace in expressions and in parameters of actions or conditions,
* references to the name of a property by another.
*
* \ingroup IDE
*/
class GD_CORE_API EventsPropertyReplacer
: public ArbitraryEventsWorkerWithContext {
public:
EventsPropertyReplacer(
const gd::Platform &platform_,
const gd::PropertiesContainer &targetPropertiesContainer_,
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames_,
const std::unordered_set<gd::String> &removedPropertyNames_)
: platform(platform_),
targetPropertiesContainer(targetPropertiesContainer_),
oldToNewPropertyNames(oldToNewPropertyNames_),
removedPropertyNames(removedPropertyNames_){};
virtual ~EventsPropertyReplacer();
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
bool DoVisitEventExpression(gd::Expression &expression,
const gd::ParameterMetadata &metadata) override;
const gd::Platform &platform;
const gd::PropertiesContainer &targetPropertiesContainer;
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames;
const std::unordered_set<gd::String> &removedPropertyNames;
};
} // namespace gd

View File

@@ -20,6 +20,7 @@
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
using namespace std;
@@ -36,14 +37,12 @@ const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
public:
ExpressionObjectRenamer(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::String &rootType_,
const gd::String& objectName_,
const gd::String& objectNewName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
rootType(rootType_),
hasDoneRenaming(false),
objectName(objectName_),
@@ -51,14 +50,13 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
virtual ~ExpressionObjectRenamer(){};
static bool Rename(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::String &rootType,
gd::ExpressionNode& node,
const gd::String& objectName,
const gd::String& objectNewName) {
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectRenamer renamer(platform, globalObjectsContainer, objectsContainer, rootType, objectName, objectNewName);
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) {
ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, objectName, objectNewName);
node.Visit(renamer);
return renamer.HasDoneRenaming();
@@ -83,6 +81,28 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.name == objectName) {
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
// This is an object variable.
hasDoneRenaming = true;
node.name = objectNewName;
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
}
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
@@ -94,11 +114,29 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
node.identifierName == objectName) {
hasDoneRenaming = true;
node.identifierName = objectNewName;
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.identifierName == objectName) {
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
// This is an object variable.
hasDoneRenaming = true;
node.identifierName = objectNewName;
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
@@ -124,8 +162,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
const gd::String& objectNewName;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
const gd::String rootType;
};
@@ -138,26 +175,23 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
public:
ExpressionObjectFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers &projectScopedContainers_,
const gd::String &rootType_,
const gd::String& objectName_)
const gd::String& searchedObjectName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
rootType(rootType_),
hasObject(false),
objectName(objectName_){};
searchedObjectName(searchedObjectName_){};
virtual ~ExpressionObjectFinder(){};
static bool CheckIfHasObject(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::String &rootType,
gd::ExpressionNode& node,
const gd::String& objectName) {
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
ExpressionObjectFinder finder(platform, globalObjectsContainer, objectsContainer, rootType, objectName);
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) {
ExpressionObjectFinder finder(platform, projectScopedContainers, rootType, objectName);
node.Visit(finder);
return finder.HasFoundObject();
@@ -182,6 +216,27 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.name == searchedObjectName) {
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
// This is an object variable.
hasObject = true;
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
}
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
@@ -193,19 +248,36 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ParameterMetadata::IsObject(type) &&
node.identifierName == objectName) {
node.identifierName == searchedObjectName) {
hasObject = true;
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.identifierName == searchedObjectName) {
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
// This is an object variable.
hasObject = true;
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (node.objectName == objectName) {
if (node.objectName == searchedObjectName) {
hasObject = true;
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (node.objectName == objectName) {
if (node.objectName == searchedObjectName) {
hasObject = true;
}
for (auto& parameter : node.parameters) {
@@ -216,17 +288,15 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
private:
bool hasObject;
const gd::String& objectName;
const gd::String& searchedObjectName;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
const gd::String rootType;
};
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& actions,
gd::String oldName,
gd::String newName) {
@@ -245,7 +315,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -255,7 +325,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -265,8 +335,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
if (!actions[aId].GetSubInstructions().empty())
somethingModified =
RenameObjectInActions(platform,
project,
layout,
projectScopedContainers,
actions[aId].GetSubInstructions(),
oldName,
newName) ||
@@ -278,8 +347,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
bool EventsRefactorer::RenameObjectInConditions(
const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& conditions,
gd::String oldName,
gd::String newName) {
@@ -299,7 +367,7 @@ bool EventsRefactorer::RenameObjectInConditions(
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -309,7 +377,7 @@ bool EventsRefactorer::RenameObjectInConditions(
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -319,8 +387,7 @@ bool EventsRefactorer::RenameObjectInConditions(
if (!conditions[cId].GetSubInstructions().empty())
somethingModified =
RenameObjectInConditions(platform,
project,
layout,
projectScopedContainers,
conditions[cId].GetSubInstructions(),
oldName,
newName) ||
@@ -332,8 +399,7 @@ bool EventsRefactorer::RenameObjectInConditions(
bool EventsRefactorer::RenameObjectInEventParameters(
const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
@@ -348,7 +414,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
@@ -357,7 +423,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
@@ -366,8 +432,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
}
void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String oldName,
gd::String newName) {
@@ -376,14 +441,14 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
bool somethingModified = RenameObjectInConditions(
platform, project, layout, *conditionsVectors[j], oldName, newName);
platform, projectScopedContainers, *conditionsVectors[j], oldName, newName);
}
vector<gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
bool somethingModified = RenameObjectInActions(
platform, project, layout, *actionsVectors[j], oldName, newName);
platform, projectScopedContainers, *actionsVectors[j], oldName, newName);
}
vector<pair<gd::Expression*, gd::ParameterMetadata>>
@@ -393,8 +458,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
gd::ParameterMetadata parameterMetadata =
expressionsWithMetadata[j].second;
bool somethingModified = RenameObjectInEventParameters(platform,
project,
layout,
projectScopedContainers,
*expression,
parameterMetadata,
oldName,
@@ -403,8 +467,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
if (events[i].CanHaveSubEvents())
RenameObjectInEvents(platform,
project,
layout,
projectScopedContainers,
events[i].GetSubEvents(),
oldName,
newName);
@@ -412,8 +475,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
}
bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& actions,
gd::String name) {
bool somethingModified = false;
@@ -435,7 +497,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
deleteMe = true;
break;
}
@@ -445,7 +507,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
deleteMe = true;
break;
}
@@ -459,8 +521,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
} else if (!actions[aId].GetSubInstructions().empty())
somethingModified =
RemoveObjectInActions(platform,
globalObjectsContainer,
objectsContainer,
projectScopedContainers,
actions[aId].GetSubInstructions(),
name) ||
somethingModified;
@@ -471,8 +532,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
bool EventsRefactorer::RemoveObjectInConditions(
const gd::Platform& platform,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& conditions,
gd::String name) {
bool somethingModified = false;
@@ -495,7 +555,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
deleteMe = true;
break;
}
@@ -505,7 +565,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
deleteMe = true;
break;
}
@@ -519,8 +579,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
} else if (!conditions[cId].GetSubInstructions().empty())
somethingModified =
RemoveObjectInConditions(platform,
globalObjectsContainer,
objectsContainer,
projectScopedContainers,
conditions[cId].GetSubInstructions(),
name) ||
somethingModified;
@@ -530,8 +589,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
}
void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String name) {
for (std::size_t i = 0; i < events.size(); ++i) {
@@ -539,19 +597,19 @@ void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
bool conditionsModified = RemoveObjectInConditions(
platform, globalObjectsContainer, objectsContainer, *conditionsVectors[j], name);
platform, projectScopedContainers, *conditionsVectors[j], name);
}
vector<gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
bool actionsModified = RemoveObjectInActions(
platform, globalObjectsContainer, objectsContainer, *actionsVectors[j], name);
platform, projectScopedContainers, *actionsVectors[j], name);
}
if (events[i].CanHaveSubEvents())
RemoveObjectInEvents(
platform, globalObjectsContainer, objectsContainer, events[i].GetSubEvents(), name);
platform, projectScopedContainers, events[i].GetSubEvents(), name);
}
}
@@ -587,20 +645,20 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventModified = false;
std::vector<gd::Expression*> allObjectExpressions =
events[i].GetAllObjectExpressions();
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
auto allExpressionsWithMetadata = events[i].GetAllExpressionsWithMetadata();
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
gd::Expression* expression = expressionAndMetadata.first;
gd::String newExpressionPlainString =
matchCase ? allObjectExpressions[j]->GetPlainString().FindAndReplace(
matchCase ? expression->GetPlainString().FindAndReplace(
toReplace, newString, true)
: ReplaceAllOccurrencesCaseInsensitive(
allObjectExpressions[j]->GetPlainString(),
expression->GetPlainString(),
toReplace,
newString);
if (newExpressionPlainString !=
allObjectExpressions[j]->GetPlainString()) {
*allObjectExpressions[j] = gd::Expression(newExpressionPlainString);
if (newExpressionPlainString != expression->GetPlainString()) {
*expression = gd::Expression(newExpressionPlainString);
if (!eventModified) {
modifiedEvents.push_back(EventsSearchResult(
@@ -815,14 +873,14 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventAddedInResults = false;
std::vector<gd::Expression*> allObjectExpressions =
events[i].GetAllObjectExpressions();
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
auto allExpressionsWithMetadata = events[i].GetAllExpressionsWithMetadata();
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
gd::Expression* expression = expressionAndMetadata.first;
size_t foundPosition =
matchCase
? allObjectExpressions[j]->GetPlainString().find(search)
: allObjectExpressions[j]->GetPlainString().FindCaseInsensitive(
search);
? expression->GetPlainString().find(search)
: expression->GetPlainString().FindCaseInsensitive(search);
if (foundPosition != gd::String::npos && !eventAddedInResults) {
results.push_back(EventsSearchResult(

View File

@@ -14,6 +14,8 @@
namespace gd {
class EventsList;
class ObjectsContainer;
class ObjectsContainersList;
class ProjectScopedContainers;
class Platform;
class ExternalEvents;
class BaseEvent;
@@ -79,8 +81,7 @@ class GD_CORE_API EventsRefactorer {
* events ).
*/
static void RenameObjectInEvents(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String oldName,
gd::String newName);
@@ -89,8 +90,7 @@ class GD_CORE_API EventsRefactorer {
* Remove all actions or conditions using an object
*/
static void RemoveObjectInEvents(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String name);
@@ -136,8 +136,7 @@ class GD_CORE_API EventsRefactorer {
* \return true if something was modified.
*/
static bool RenameObjectInActions(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& instructions,
gd::String oldName,
gd::String newName);
@@ -149,8 +148,7 @@ class GD_CORE_API EventsRefactorer {
* \return true if something was modified.
*/
static bool RenameObjectInConditions(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& instructions,
gd::String oldName,
gd::String newName);
@@ -163,8 +161,7 @@ class GD_CORE_API EventsRefactorer {
*/
static bool RenameObjectInEventParameters(
const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
@@ -176,8 +173,7 @@ class GD_CORE_API EventsRefactorer {
* \return true if something was modified.
*/
static bool RemoveObjectInConditions(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& conditions,
gd::String name);
@@ -187,8 +183,7 @@ class GD_CORE_API EventsRefactorer {
* \return true if something was modified.
*/
static bool RemoveObjectInActions(const gd::Platform& platform,
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& conditions,
gd::String name);

View File

@@ -0,0 +1,404 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsVariableReplacer.h"
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Go through the nodes and rename variables,
* or signal if the instruction must be renamed if a removed variable is used.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableReplacer
: public ExpressionParser2NodeWorker {
public:
ExpressionVariableReplacer(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::VariablesContainer& targetVariablesContainer_,
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames_,
const std::unordered_set<gd::String>& removedVariableNames_)
: hasDoneRenaming(false),
removedVariableUsed(false),
platform(platform_),
projectScopedContainers(projectScopedContainers_),
forcedInitialVariablesContainer(nullptr),
targetVariablesContainer(targetVariablesContainer_),
oldToNewVariableNames(oldToNewVariableNames_),
removedVariableNames(removedVariableNames_){};
virtual ~ExpressionVariableReplacer(){};
void SetForcedInitialVariablesContainer(
const gd::VariablesContainer* forcedInitialVariablesContainer_) {
forcedInitialVariablesContainer = forcedInitialVariablesContainer_;
}
bool HasDoneRenaming() const { return hasDoneRenaming; }
bool IsRemovedVariableUsed() const { return removedVariableUsed; }
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
// The node represents a variable or an object name on which a variable
// will be accessed.
if (forcedInitialVariablesContainer) {
// A scope was forced. Honor it: it means this node represents a variable
// of the forced variables container.
if (forcedInitialVariablesContainer == &targetVariablesContainer) {
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
}
if (node.child) node.child->Visit(*this);
return;
}
// Match the potential *new* name of the variable, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.name),
[&]() {
// This represents an object.
// Remember the object name.
objectNameToUseForVariableAccessor = node.name;
if (node.child) node.child->Visit(*this);
objectNameToUseForVariableAccessor = "";
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
}
if (node.child) node.child->Visit(*this);
},
[&]() {
// This is a property.
if (node.child) node.child->Visit(*this);
},
[&]() {
// This is a parameter.
if (node.child) node.child->Visit(*this);
},
[&]() {
// This is something else - potentially a deleted variable.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
}
if (node.child) node.child->Visit(*this);
});
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
if (!objectNameToUseForVariableAccessor.empty()) {
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
objectNameToUseForVariableAccessor, targetVariablesContainer)) {
// The node represents an object variable, and this object variables are
// the target. Do the replacement or removals:
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
}
}
objectNameToUseForVariableAccessor = "";
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
objectNameToUseForVariableAccessor = "";
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
// The node represents a variable or an object variable in an expression
// (and if it's a variable reference or a value does not have any importance
// here).
if (forcedInitialVariablesContainer) {
// A scope was forced. Honor it: it means this node represents a variable
// of the forced variables container.
if (forcedInitialVariablesContainer == &targetVariablesContainer) {
RenameOrRemoveVariableOfTargetVariableContainer(node.identifierName);
}
return;
}
// Match the potential *new* name of the variable, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.identifierName),
[&]() {
// This represents an object.
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
node.identifierName, targetVariablesContainer)) {
// The node represents an object variable, and this object variables
// are the target. Do the replacement or removals:
RenameOrRemoveVariableOfTargetVariableContainer(
node.childIdentifierName);
}
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(
node.identifierName);
}
},
[&]() {
// This is a property.
},
[&]() {
// This is a parameter.
},
[&]() {
// This is something else - potentially a deleted variable.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(
node.identifierName);
}
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
const gd::ExpressionMetadata& metadata =
MetadataProvider::GetFunctionCallMetadata(
platform, projectScopedContainers.GetObjectsContainersList(), node);
for (size_t parameterIndex = 0; parameterIndex < node.parameters.size();
++parameterIndex) {
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform,
projectScopedContainers.GetObjectsContainersList(),
node,
parameterIndex);
// Handle legacy pre-scoped variable parameters: in this case, we
// force the "scope" at which starts the evalution of variables.
if (parameterMetadata && parameterMetadata->GetValueTypeMetadata()
.IsLegacyPreScopedVariable()) {
const gd::VariablesContainer* oldForcedInitialVariablesContainer =
forcedInitialVariablesContainer;
forcedInitialVariablesContainer = nullptr;
if (parameterMetadata->GetType() == "globalvar") {
forcedInitialVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
} else if (parameterMetadata->GetType() == "scenevar") {
forcedInitialVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
} else if (parameterMetadata->GetType() == "objectvar") {
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
platform,
projectScopedContainers.GetObjectsContainersList(),
node.objectName,
*node.parameters[parameterIndex].get());
forcedInitialVariablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
}
node.parameters[parameterIndex]->Visit(*this);
forcedInitialVariablesContainer = oldForcedInitialVariablesContainer;
} else {
// For any other parameter, there is no special treatment being needed.
node.parameters[parameterIndex]->Visit(*this);
}
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
bool hasDoneRenaming;
bool removedVariableUsed;
const gd::String& GetPotentialNewName(const gd::String& oldName) {
return oldToNewVariableNames.count(oldName) >= 1
? oldToNewVariableNames.find(oldName)->second
: oldName;
}
bool RenameOrRemoveVariableOfTargetVariableContainer(
gd::String& variableName) {
if (oldToNewVariableNames.count(variableName) >= 1) {
variableName = oldToNewVariableNames.find(variableName)->second;
hasDoneRenaming = true;
return true;
} else if (removedVariableNames.count(variableName) >= 1) {
removedVariableUsed = true;
return true;
}
return false; // Nothing was changed or done.
}
// Scope:
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
const gd::VariablesContainer* forcedInitialVariablesContainer;
// Renaming or removing to do:
const gd::VariablesContainer& targetVariablesContainer;
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames;
const std::unordered_set<gd::String>& removedVariableNames;
gd::String objectNameToUseForVariableAccessor;
};
const gd::VariablesContainer*
EventsVariableReplacer::FindForcedVariablesContainerIfAny(
const gd::String& type, const gd::String& lastObjectName) {
// Handle legacy pre-scoped variable parameters: in this case, we
// force the "scope" at which starts the evalution of variables.
if (type == "objectvar") {
return GetProjectScopedContainers()
.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(lastObjectName);
} else if (type == "globalvar") {
return GetProjectScopedContainers()
.GetVariablesContainersList()
.GetTopMostVariablesContainer();
} else if (type == "scenevar") {
return GetProjectScopedContainers()
.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
}
return nullptr;
}
bool EventsVariableReplacer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
bool shouldDeleteInstruction = false;
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return; // Not an expression that can contain variables.
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionVariableReplacer renamer(platform,
GetProjectScopedContainers(),
targetVariablesContainer,
oldToNewVariableNames,
removedVariableNames);
renamer.SetForcedInitialVariablesContainer(
FindForcedVariablesContainerIfAny(type, lastObjectName));
node->Visit(renamer);
if (renamer.IsRemovedVariableUsed()) {
shouldDeleteInstruction = true;
} else if (renamer.HasDoneRenaming()) {
instruction.SetParameter(
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
});
return shouldDeleteInstruction;
}
bool EventsVariableReplacer::DoVisitEventExpression(
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
const gd::String& type = metadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return false; // Not an expression that can contain variables.
auto node = expression.GetRootNode();
if (node) {
ExpressionVariableReplacer renamer(platform,
GetProjectScopedContainers(),
targetVariablesContainer,
oldToNewVariableNames,
removedVariableNames);
renamer.SetForcedInitialVariablesContainer(
FindForcedVariablesContainerIfAny(type, ""));
node->Visit(renamer);
if (renamer.IsRemovedVariableUsed()) {
return true;
} else if (renamer.HasDoneRenaming()) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
return false;
}
EventsVariableReplacer::~EventsVariableReplacer() {}
} // namespace gd

View File

@@ -0,0 +1,60 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class VariablesContainer;
class EventsList;
class Platform;
} // namespace gd
namespace gd {
/**
* \brief Replace in expressions and in parameters of actions or conditions,
* references to the name of a variable by another.
*
* \ingroup IDE
*/
class GD_CORE_API EventsVariableReplacer
: public ArbitraryEventsWorkerWithContext {
public:
EventsVariableReplacer(
const gd::Platform &platform_,
const gd::VariablesContainer &targetVariablesContainer_,
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames_,
const std::unordered_set<gd::String> &removedVariableNames_)
: platform(platform_),
targetVariablesContainer(targetVariablesContainer_),
oldToNewVariableNames(oldToNewVariableNames_),
removedVariableNames(removedVariableNames_){};
virtual ~EventsVariableReplacer();
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
bool DoVisitEventExpression(gd::Expression &expression,
const gd::ParameterMetadata &metadata) override;
const gd::VariablesContainer *FindForcedVariablesContainerIfAny(
const gd::String &type, const gd::String &lastObjectName);
const gd::Platform &platform;
const gd::VariablesContainer &targetVariablesContainer;
gd::String objectName;
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames;
const std::unordered_set<gd::String> &removedVariableNames;
};
} // namespace gd

View File

@@ -17,6 +17,7 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
@@ -34,14 +35,12 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
public:
VariableFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers &projectScopedContainers_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~VariableFinderExpressionNodeWorker(){};
@@ -60,6 +59,9 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
// We don't check variables or object variables here, because object variables only work
// if the variable is already declared.
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
@@ -70,7 +72,10 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitIdentifierNode(IdentifierNode& node) override {
// We don't check object variables here, because object variables only work
// if the variable is already declared.
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
@@ -79,7 +84,7 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
projectScopedContainers.GetObjectsContainersList().GetTypeOfObject(objectName),
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
@@ -110,8 +115,7 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
@@ -163,8 +167,7 @@ class GD_CORE_API VariableFinderEventWorker
VariableFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
GetProjectScopedContainers(),
parameterType,
objectName);
node->Visit(searcher);
@@ -252,9 +255,10 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
platform,
parameterType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
eventWorker.Launch(layout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
@@ -263,7 +267,8 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
platform,
parameterType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
eventWorker.Launch(externalEvents.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
@@ -272,7 +277,8 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
platform,
parameterType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
eventWorker.Launch(dependencyLayout.GetEvents(),
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, dependencyLayout));
}
}

View File

@@ -10,21 +10,15 @@ namespace gd {
const gd::ParameterMetadata
ExpressionCompletionDescription::badParameterMetadata;
const gd::ObjectConfiguration
ExpressionCompletionDescription::badObjectConfiguration;
/**
* \brief Turn an ExpressionCompletionDescription to a string.
*/
std::ostream& operator<<(std::ostream& os,
ExpressionCompletionDescription const& value) {
os << "{ " << value.GetCompletionKind() << ", " << value.GetType() << ", "
<< value.GetPrefix() << ", " << value.GetObjectName() << ", "
<< value.GetBehaviorName() << ", "
<< (value.IsExact() ? "exact" : "non-exact") << ", "
<< (value.IsLastParameter() ? "last parameter" : "not last parameter")
<< ", "
<< (value.HasParameterMetadata()
? gd::String::From(&value.GetParameterMetadata())
: "no parameter metadata")
<< " }";
os << value.ToString();
return os;
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
@@ -40,11 +40,10 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
* operations.
*/
static const gd::String GetType(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::ExpressionNode& node) {
gd::ExpressionLeftSideTypeFinder typeFinder(
platform, globalObjectsContainer, objectsContainer);
platform, projectScopedContainers);
node.Visit(typeFinder);
return typeFinder.GetType();
}
@@ -53,11 +52,9 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
protected:
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_)
const gd::ProjectScopedContainers &projectScopedContainers_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
type("unknown") {};
const gd::String &GetType() {
@@ -69,6 +66,14 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
// The type is decided by the first operand, unless it can (`number|string`)
// or should (`unknown`) be refined, in which case we go for the right
// operand (which got visited knowing the type of the first operand, so it's
// equal or strictly more precise than the left operand).
if (type == "unknown" || type == "number|string") {
node.rightHandSide->Visit(*this);
}
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
@@ -85,7 +90,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, node);
platform, projectScopedContainers.GetObjectsContainersList(), node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
type = "unknown";
}
@@ -95,12 +100,99 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
}
void OnVisitVariableNode(VariableNode& node) override {
type = "unknown";
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
[&]() {
// This represents an object.
// We could store it to explore the type of the variable, but in practice this
// is only called for structures/arrays with 2 levels, and we don't support structure
// type identification for now.
},
[&]() {
// This is a variable.
// We could store it to explore the type of the variable, but in practice this
// is only called for structures/arrays with 2 levels, and we don't support structure
// type identification for now.
}, [&]() {
// This is a property with more than one child - this is unsupported.
}, [&]() {
// This is a parameter with more than one child - this is unsupported.
}, [&]() {
// This is something else.
type = "unknown";
});
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
type = "unknown";
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
type = "unknown";
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName,
[&]() {
// It's an object variable.
if (projectScopedContainers.GetObjectsContainersList()
.HasObjectOrGroupWithVariableNamed(
node.identifierName, node.childIdentifierName)
== ObjectsContainersList::VariableExistence::DoesNotExist) {
type = "unknown";
return;
}
auto variableType =
projectScopedContainers.GetObjectsContainersList()
.GetTypeOfObjectOrGroupVariable(node.identifierName,
node.childIdentifierName);
ReadTypeFromVariable(variableType);
},
[&]() {
// It's a variable.
const auto& variable =
projectScopedContainers.GetVariablesContainersList().Get(
node.identifierName);
if (node.childIdentifierName.empty()) {
ReadTypeFromVariable(variable.GetType());
} else {
if (!variable.HasChild(node.childIdentifierName)) {
type = "unknown";
return;
}
ReadTypeFromVariable(
variable.GetChild(node.childIdentifierName).GetType());
}
}, [&]() {
// This is a property.
const gd::NamedPropertyDescriptor& property = projectScopedContainers
.GetPropertiesContainersList().Get(node.identifierName).second;
if (property.GetType() == "Number") {
type = "number";
} else if (property.GetType() == "Boolean") {
// Nothing - we don't know the precise type (this could be used a string or as a number)
} else {
// Assume type is String or equivalent.
type = "string";
}
}, [&]() {
// It's a parameter.
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
type = "number";
} else if (valueTypeMetadata.IsString()) {
type = "string";
} else if (valueTypeMetadata.IsBoolean()) {
// Nothing - we don't know the precise type (this could be used as a string or as a number).
} else {
type = "unknown";
}
}, [&]() {
// This is something else.
type = "unknown";
});
}
void OnVisitEmptyNode(EmptyNode& node) override {
type = "unknown";
@@ -110,11 +202,18 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
}
private:
void ReadTypeFromVariable(gd::Variable::Type variableType) {
if (variableType == gd::Variable::Number) {
type = "number";
} else if (variableType == gd::Variable::String) {
type = "string";
}
}
gd::String type;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
const gd::String rootType;
};

View File

@@ -15,7 +15,7 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
@@ -31,7 +31,7 @@ namespace gd {
/**
* \brief Find the type of the expression or sub-expression that a given node
* represents.
*
*
* The type returned by this worker is a mix of:
* - an expected type looking up like a parameter declaration
* - an actual type looking down, but only looking at the most left branch
@@ -50,12 +50,11 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
* sub-expression that a given node represents.
*/
static const gd::String GetType(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::String &rootType,
gd::ExpressionNode& node) {
gd::ExpressionTypeFinder typeFinder(
platform, globalObjectsContainer, objectsContainer, rootType);
platform, projectScopedContainers, rootType);
node.Visit(typeFinder);
return typeFinder.GetType();
}
@@ -64,12 +63,10 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
protected:
ExpressionTypeFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers &projectScopedContainers_,
const gd::String &rootType_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
rootType(rootType_),
type(ExpressionTypeFinder::unknownType),
child(nullptr) {};
@@ -114,10 +111,13 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
type = ExpressionTypeFinder::unknownType;
}
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
platform,
globalObjectsContainer,
objectsContainer,
platform,
projectScopedContainers,
node);
// If we can infer a definitive number or string type, use it.
// Otherwise, we only know that it's number or string, and this can even
// be used as is at runtime.
if (leftSideType == ExpressionTypeFinder::numberType
|| leftSideType == ExpressionTypeFinder::stringType) {
type = leftSideType;
@@ -129,7 +129,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (child == nullptr) {
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
platform, globalObjectsContainer, objectsContainer, node);
platform, projectScopedContainers.GetObjectsContainersList(), node);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
VisitParent(node);
}
@@ -141,8 +141,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
const gd::ParameterMetadata* parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
projectScopedContainers.GetObjectsContainersList(),
node,
*child);
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
@@ -162,9 +161,8 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
}
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
platform,
globalObjectsContainer,
objectsContainer,
platform,
projectScopedContainers,
node);
if (leftSideType == ExpressionTypeFinder::numberType
|| leftSideType == ExpressionTypeFinder::stringType) {
@@ -188,8 +186,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
ExpressionNode *child;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
const gd::String rootType;
};

View File

@@ -11,16 +11,20 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/VariablesContainersList.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
using namespace std;
@@ -59,61 +63,218 @@ size_t GetMaximumParametersNumber(
} // namespace
ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::FunctionCallNode& function) {
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier) {
auto validateVariableTypeForExpression =
[this, &identifier](gd::Variable::Type type) {
// Collections type can't be used directly in expressions, a child
// must be accessed.
if (type == Variable::Structure) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable.child`."),
identifier.identifierNameLocation);
} else if (type == Variable::Array) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable[0]`."),
identifier.identifierNameLocation);
} else {
// Number, string or boolean variables can be used in expressions.
return;
}
};
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
// Unless we find something precise (like a variable or property or parameter with a known type),
// we consider this node will be of the type required by the parent.
childType = parentType;
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
[&]() {
// This represents an object.
if (identifier.childIdentifierName.empty()) {
RaiseTypeError(_("An object variable or expression should be entered."),
identifier.identifierNameLocation);
return true; // We should have found a variable.
}
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
RaiseTypeError(_("This variable does not exist on this object or group."),
identifier.childIdentifierNameLocation);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseTypeError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
identifier.childIdentifierNameLocation);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
RaiseTypeError(_("This group is empty. Add an object to this group first."),
identifier.identifierNameLocation);
return true; // We should have found a variable.
}
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
ReadChildTypeFromVariable(variableType);
return true; // We found a variable.
}, [&]() {
// This is a variable.
// Try to identify a declared variable with the name (and maybe the child
// variable).
const gd::Variable& variable =
variablesContainersList.Get(identifier.identifierName);
if (identifier.childIdentifierName.empty()) {
// Just the root variable is accessed, check it can be used in an
// expression.
validateVariableTypeForExpression(variable.GetType());
ReadChildTypeFromVariable(variable.GetType());
return true; // We found a variable.
} else {
// A child variable is accessed, check it can be used in an expression.
if (!variable.HasChild(identifier.childIdentifierName)) {
RaiseTypeError(_("No child variable with this name found."),
identifier.childIdentifierNameLocation);
return true; // We should have found a variable.
}
const gd::Variable& childVariable =
variable.GetChild(identifier.childIdentifierName);
ReadChildTypeFromVariable(childVariable.GetType());
return true; // We found a variable.
}
}, [&]() {
// This is a property.
if (!identifier.childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
identifier.childIdentifierNameLocation);
return true; // We found a property, even if the child is not allowed.
}
const gd::NamedPropertyDescriptor& property = projectScopedContainers
.GetPropertiesContainersList().Get(identifier.identifierName).second;
if (property.GetType() == "Number") {
childType = Type::Number;
} else if (property.GetType() == "Boolean") {
// Nothing - we don't know the precise type (this could be used a string or as a number)
} else {
// Assume type is String or equivalent.
childType = Type::String;
}
return true; // We found a property.
}, [&]() {
// This is a parameter.
if (!identifier.childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
identifier.childIdentifierNameLocation);
return true; // We found a parameter, even if the child is not allowed.
}
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifier.identifierName);
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
childType = Type::Number;
} else if (valueTypeMetadata.IsString()) {
childType = Type::String;
} else if (valueTypeMetadata.IsBoolean()) {
// Nothing - we don't know the precise type (this could be used as a string or as a number).
} else {
RaiseTypeError(_("This parameter is not a string, number or boolean - it can't be used in an expression."),
identifier.identifierNameLocation);
return true; // We found a parameter, even though the type is incompatible.
}
return true; // We found a parameter.
}, [&]() {
// This is something else.
return false;
});
}
ExpressionValidator::Type ExpressionValidator::ValidateFunction(
const gd::FunctionCallNode& function) {
ReportAnyError(function);
gd::String objectType = function.objectName.empty() ? "" :
GetTypeOfObject(globalObjectsContainer, objectsContainer, function.objectName);
gd::String behaviorType = function.behaviorName.empty() ? "" :
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, function.behaviorName);
const gd::ExpressionMetadata &metadata = function.behaviorName.empty() ?
function.objectName.empty() ?
MetadataProvider::GetAnyExpressionMetadata(platform, function.functionName) :
MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, function.functionName) :
MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, function.functionName);
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
gd::String objectType =
function.objectName.empty()
? ""
: objectsContainersList.GetTypeOfObject(function.objectName);
gd::String behaviorType = function.behaviorName.empty()
? ""
: objectsContainersList.GetTypeOfBehavior(function.behaviorName);
const gd::ExpressionMetadata& metadata =
function.behaviorName.empty()
? function.objectName.empty()
? MetadataProvider::GetAnyExpressionMetadata(
platform, function.functionName)
: MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, function.functionName)
: MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, function.functionName);
Type returnType = StringToType(metadata.GetReturnType());
if (!function.objectName.empty() &&
!globalObjectsContainer.HasObjectNamed(function.objectName) &&
!globalObjectsContainer.GetObjectGroups().Has(function.objectName) &&
!objectsContainer.HasObjectNamed(function.objectName) &&
!objectsContainer.GetObjectGroups().Has(function.objectName)) {
!objectsContainersList.HasObjectOrGroupNamed(function.objectName)) {
RaiseTypeError(_("This object doesn't exist."),
function.objectNameLocation, /*isFatal=*/false);
function.objectNameLocation,
/*isFatal=*/false);
return returnType;
}
if (!function.behaviorName.empty() &&
!gd::HasBehaviorInObjectOrGroup(globalObjectsContainer, objectsContainer,
function.objectName,
function.behaviorName)) {
!objectsContainersList.HasBehaviorInObjectOrGroup(function.objectName,
function.behaviorName)) {
RaiseTypeError(_("This behavior is not attached to this object."),
function.behaviorNameLocation, /*isFatal=*/false);
function.behaviorNameLocation,
/*isFatal=*/false);
return returnType;
}
if (!function.objectName.empty() &&
// If the function needs a capability on the object that may not be covered
// by all objects, check it now.
!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata &objectMetadata =
// If the function needs a capability on the object that may not be
// covered by all objects, check it now.
!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata& objectMetadata =
MetadataProvider::GetObjectMetadata(platform, objectType);
}
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
RaiseError(
"invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
function.location);
return returnType;
if (function.functionName.empty()) {
RaiseError("invalid_function_name",
_("Enter the name of the function to call."),
function.location);
} else {
RaiseError("invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
function.location);
}
return returnType;
}
// Validate the type of the function
@@ -128,9 +289,9 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
} else if (parentType != Type::Number &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"number, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
"number, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
return returnType;
}
} else if (returnType == Type::String) {
@@ -144,16 +305,16 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
} else if (parentType != Type::String &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"string, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
"string, but another type is expected:") +
" " + TypeToString(parentType),
function.location);
return returnType;
}
} else {
if (parentType != returnType) {
RaiseTypeError(
_("You tried to use an expression with the wrong return type:") + " " +
TypeToString(returnType),
_("You tried to use an expression with the wrong return type:") +
" " + TypeToString(returnType),
function.location);
return returnType;
}
@@ -162,10 +323,12 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
// Validate parameters count
size_t minParametersCount = GetMinimumParametersNumber(
metadata.parameters,
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
function.behaviorName));
size_t maxParametersCount = GetMaximumParametersNumber(
metadata.parameters,
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
function.behaviorName));
if (function.parameters.size() < minParametersCount ||
function.parameters.size() > maxParametersCount) {
gd::String expectedCountMessage =
@@ -179,28 +342,29 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
if (function.parameters.size() < minParametersCount) {
RaiseError(
"too_few_parameters",
_("You have not entered enough parameters for the expression.") + " " +
expectedCountMessage,
_("You have not entered enough parameters for the expression.") +
" " + expectedCountMessage,
function.location);
} else {
RaiseError(
"extra_parameter",
_("This parameter was not expected by this expression. Remove it "
"or verify that you've entered the proper expression name.") + " " +
expectedCountMessage,
ExpressionParserLocation(
function.parameters[maxParametersCount]->location.GetStartPosition(),
function.location.GetEndPosition() - 1));
"or verify that you've entered the proper expression name.") +
" " + expectedCountMessage,
ExpressionParserLocation(function.parameters[maxParametersCount]
->location.GetStartPosition(),
function.location.GetEndPosition() - 1));
}
return returnType;
}
// TODO: reverse the order of diagnostic?
size_t writtenParametersFirstIndex =
ExpressionParser2::WrittenParametersFirstIndex(
function.objectName, function.behaviorName);
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
function.behaviorName);
int metadataIndex = writtenParametersFirstIndex;
for (int parameterIndex = 0; parameterIndex < function.parameters.size(); parameterIndex++) {
for (int parameterIndex = 0; parameterIndex < function.parameters.size();
parameterIndex++) {
auto& parameter = function.parameters[parameterIndex];
while (metadata.GetParameters()[metadataIndex].IsCodeOnly()) {
// The sizes are already checked above.
@@ -208,45 +372,45 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
}
auto& parameterMetadata = metadata.GetParameters()[metadataIndex];
if (!parameterMetadata.IsOptional() || dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
if (!parameterMetadata.IsOptional() ||
dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
auto currentParentType = parentType;
parentType = StringToType(parameterMetadata.GetType());
parameter->Visit(*this);
parentType = currentParentType;
const gd::String &expectedParameterType = parameterMetadata.GetType();
const gd::String& expectedParameterType = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsExpression(
ExpressionValidator::variableTypeString, expectedParameterType)) {
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr
&& dynamic_cast<VariableNode *>(parameter.get()) == nullptr) {
RaiseError(
"malformed_variable_parameter",
_("A variable name was expected but something else was "
"written. Enter just the name of the variable for this "
"parameter."),
parameter->location);
ExpressionValidator::variableTypeString, expectedParameterType)) {
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr &&
dynamic_cast<VariableNode*>(parameter.get()) == nullptr) {
RaiseError("malformed_variable_parameter",
_("A variable name was expected but something else was "
"written. Enter just the name of the variable for this "
"parameter."),
parameter->location);
}
} else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr) {
RaiseError(
"malformed_object_parameter",
_("An object name was expected but something else was "
"written. Enter just the name of the object for this "
"parameter."),
parameter->location);
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr) {
RaiseError("malformed_object_parameter",
_("An object name was expected but something else was "
"written. Enter just the name of the object for this "
"parameter."),
parameter->location);
}
}
// String and number are already checked in children.
else if (!gd::ParameterMetadata::IsExpression(
ExpressionValidator::numberTypeString, expectedParameterType)
&& !gd::ParameterMetadata::IsExpression(
ExpressionValidator::stringTypeString, expectedParameterType)) {
RaiseError(
"unknown_parameter_type",
_("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix "
"this issue"),
parameter->location);
ExpressionValidator::numberTypeString,
expectedParameterType) &&
!gd::ParameterMetadata::IsExpression(
ExpressionValidator::stringTypeString,
expectedParameterType)) {
RaiseError("unknown_parameter_type",
_("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix "
"this issue"),
parameter->location);
}
}
metadataIndex++;
@@ -254,55 +418,60 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
return returnType;
}
// TODO factorize in a file with an enum and helpers?
const gd::String ExpressionValidator::unknownTypeString = "unknown";
const gd::String ExpressionValidator::numberTypeString = "number";
const gd::String ExpressionValidator::stringTypeString = "string";
const gd::String ExpressionValidator::numberOrStringTypeString = "number|string";
const gd::String ExpressionValidator::variableTypeString = "variable";
const gd::String ExpressionValidator::objectTypeString = "object";
const gd::String ExpressionValidator::emptyTypeString = "empty";
// TODO factorize in a file with an enum and helpers?
const gd::String ExpressionValidator::unknownTypeString = "unknown";
const gd::String ExpressionValidator::numberTypeString = "number";
const gd::String ExpressionValidator::stringTypeString = "string";
const gd::String ExpressionValidator::numberOrStringTypeString =
"number|string";
const gd::String ExpressionValidator::variableTypeString = "variable";
const gd::String ExpressionValidator::objectTypeString = "object";
const gd::String ExpressionValidator::emptyTypeString = "empty";
const gd::String &ExpressionValidator::TypeToString(Type type) {
switch (type) {
case Type::Unknown:
const gd::String& ExpressionValidator::TypeToString(Type type) {
switch (type) {
case Type::Unknown:
return unknownTypeString;
case Type::Number:
case Type::Number:
return numberTypeString;
case Type::String:
case Type::String:
return stringTypeString;
case Type::NumberOrString:
case Type::NumberOrString:
return numberOrStringTypeString;
case Type::Variable:
case Type::Variable:
return variableTypeString;
case Type::Object:
case Type::Object:
return objectTypeString;
case Type::Empty:
case Type::Empty:
return emptyTypeString;
}
return unknownTypeString;
}
return unknownTypeString;
}
ExpressionValidator::Type ExpressionValidator::StringToType(const gd::String &type) {
if (type == ExpressionValidator::numberTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString, type)) {
return Type::Number;
}
if (type == ExpressionValidator::stringTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString, type)) {
return Type::String;
}
if (type == ExpressionValidator::numberOrStringTypeString) {
return Type::NumberOrString;
}
if (type == ExpressionValidator::variableTypeString
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::variableTypeString, type)) {
return Type::Variable;
}
if (type == ExpressionValidator::objectTypeString
|| gd::ParameterMetadata::IsObject(type)) {
return Type::Object;
}
return Type::Unknown;
ExpressionValidator::Type ExpressionValidator::StringToType(
const gd::String& type) {
if (type == ExpressionValidator::numberTypeString ||
gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString,
type)) {
return Type::Number;
}
if (type == ExpressionValidator::stringTypeString ||
gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString,
type)) {
return Type::String;
}
if (type == ExpressionValidator::numberOrStringTypeString) {
return Type::NumberOrString;
}
if (type == ExpressionValidator::variableTypeString ||
gd::ParameterMetadata::IsExpression(
ExpressionValidator::variableTypeString, type)) {
return Type::Variable;
}
if (type == ExpressionValidator::objectTypeString ||
gd::ParameterMetadata::IsObject(type)) {
return Type::Object;
}
return Type::Unknown;
}
} // namespace gd

View File

@@ -13,13 +13,18 @@
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/VariablesContainersList.h"
namespace gd {
class Expression;
class ObjectsContainer;
class VariablesContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
class VariablesContainersList;
class ProjectScopedContainers;
} // namespace gd
namespace gd {
@@ -33,14 +38,13 @@ namespace gd {
class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
public:
ExpressionValidator(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers & projectScopedContainers_,
const gd::String &rootType_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
childType(Type::Unknown) {};
childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false) {};
virtual ~ExpressionValidator(){};
/**
@@ -48,11 +52,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
* any error including non-fatal ones.
*/
static bool HasNoErrors(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ProjectScopedContainers & projectScopedContainers,
const gd::String &rootType,
gd::ExpressionNode& node) {
gd::ExpressionValidator validator(platform, globalObjectsContainer, objectsContainer, rootType);
gd::ExpressionValidator validator(platform, projectScopedContainers, rootType);
node.Visit(validator);
return validator.GetAllErrors().empty();
}
@@ -82,7 +85,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
void OnVisitOperatorNode(OperatorNode& node) override {
ReportAnyError(node);
node.leftHandSide->Visit(*this);
const Type leftType = childType;
@@ -100,9 +103,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
}
else if (node.op != '+') {
RaiseOperatorError(
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts."),
RaiseOperatorError(
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts."),
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
}
} else if (leftType == Type::Object) {
@@ -121,7 +124,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
node.rightHandSide->Visit(*this);
const Type rightType = childType;
childType = leftType;
// The type is decided by the first operand, unless it can (`number|string`)
// or should (`unknown`) be refined, in which case we go for the right
// operand (which got visited knowing the type of the first operand, so it's
// equal or strictly more precise than the left operand).
childType = (leftType == Type::Unknown || leftType == Type::NumberOrString) ? leftType : rightType;
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
ReportAnyError(node);
@@ -185,28 +192,69 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
void OnVisitVariableNode(VariableNode& node) override {
ReportAnyError(node);
if (node.child) {
node.child->Visit(*this);
}
childType = Type::Variable;
if (parentType == Type::String) {
RaiseTypeError(_("Variables must be surrounded by VariableString()."),
node.location);
} else if (parentType == Type::Number) {
RaiseTypeError(_("Variables must be surrounded by Variable()."),
node.location);
} else if (parentType == Type::NumberOrString) {
RaiseTypeError(
_("Variables must be surrounded by Variable() or VariableString()."),
node.location);
} else if (parentType != Type::Variable) {
if (parentType == Type::Variable) {
childType = Type::Variable;
if (node.child) {
node.child->Visit(*this);
}
} else if (parentType == Type::String || parentType == Type::Number || parentType == Type::NumberOrString) {
// The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
childType = parentType;
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
forbidsUsageOfBracketsBecauseParentIsObject = false;
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
[&]() {
// This represents an object.
// While understood by the parser, it's forbidden to use the bracket notation just after
// an object name (`MyObject["MyVariable"]`).
forbidsUsageOfBracketsBecauseParentIsObject = true;
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
// Being in this node implies that there is at least a child - which is not supported for properties.
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
node.location);
}, [&]() {
// This is a parameter.
// Being in this node implies that there is at least a child - which is not supported for parameters.
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
node.location);
}, [&]() {
// This is something else.
RaiseTypeError(_("No object, variable or property with this name found."),
node.location);
});
if (node.child) {
node.child->Visit(*this);
}
forbidsUsageOfBracketsBecauseParentIsObject = false;
} else {
RaiseTypeError(_("You entered a variable, but this type was expected:") +
" " + TypeToString(parentType),
node.location);
if (node.child) {
node.child->Visit(*this);
}
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
ReportAnyError(node);
// In the case we accessed an object variable (`MyObject.MyVariable`),
// brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
forbidsUsageOfBracketsBecauseParentIsObject = false;
if (node.child) {
node.child->Visit(*this);
}
@@ -215,6 +263,15 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
VariableBracketAccessorNode& node) override {
ReportAnyError(node);
if (forbidsUsageOfBracketsBecauseParentIsObject) {
RaiseError("brackets_not_allowed_for_objects",
_("You can't use the brackets to access an object variable. "
"Use a dot followed by the variable name, like this: "
"`MyObject.MyVariable`."),
node.location);
}
forbidsUsageOfBracketsBecauseParentIsObject = false;
Type currentParentType = parentType;
parentType = Type::NumberOrString;
node.expression->Visit(*this);
@@ -227,27 +284,39 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitIdentifierNode(IdentifierNode& node) override {
ReportAnyError(node);
if (parentType == Type::String) {
RaiseTypeError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."),
node.location);
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so either the variable is not properly declared
// or it's a text without quotes.
RaiseTypeError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."),
node.location);
}
}
else if (parentType == Type::Number) {
RaiseTypeError(
_("You must enter a number."), node.location);
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so the variable is not properly declared.
RaiseTypeError(
_("You must enter a number."), node.location);
}
}
else if (parentType == Type::NumberOrString) {
RaiseTypeError(
_("You must enter a number or a text, wrapped inside double quotes "
"(example: \"Hello world\")."),
node.location);
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so either the variable is not properly declared
// or it's a text without quotes.
RaiseTypeError(
_("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
node.location);
}
}
else if (parentType != Type::Object && parentType != Type::Variable) {
// It can't happen.
RaiseTypeError(
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
node.location);
childType = parentType;
} else {
childType = parentType;
}
childType = parentType;
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
ReportAnyError(node);
@@ -278,6 +347,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
private:
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function);
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
if (node.diagnostic && node.diagnostic->IsError()) {
@@ -291,7 +361,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
}
void RaiseError(const gd::String &type,
void RaiseError(const gd::String &type,
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) {
auto diagnostic = gd::make_unique<ExpressionParserError>(
type, message, location);
@@ -315,6 +385,16 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
RaiseError("invalid_operator", message, location);
}
void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
if (variableType == gd::Variable::Number) {
childType = Type::Number;
} else if (variableType == gd::Variable::String) {
childType = Type::String;
} else {
// Nothing - we don't know the precise type (this could be used as a string or as a number).
}
}
static Type StringToType(const gd::String &type);
static const gd::String &TypeToString(Type type);
static const gd::String unknownTypeString;
@@ -329,11 +409,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
std::vector<ExpressionParserDiagnostic*> fatalErrors;
std::vector<ExpressionParserDiagnostic*> allErrors;
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
Type childType;
Type parentType;
Type childType; ///< The type "discovered" down the tree and passed up.
Type parentType; ///< The type "required" by the top of the tree.
bool forbidsUsageOfBracketsBecauseParentIsObject;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
};
} // namespace gd

View File

@@ -15,6 +15,7 @@
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
@@ -31,6 +32,10 @@ namespace gd {
* \brief Find the object name that should be used as a context of the
* expression or sub-expression that a given node represents.
*
* This is needed because of the legacy convention where a "objectvar"
* parameter represents a variable of the object represented by the previous "object"
* parameter.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWorker {
@@ -41,12 +46,11 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
* context of the expression or sub-expression that a given node represents.
*/
static const gd::String GetObjectName(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
const gd::ObjectsContainer &objectsContainer,
const gd::ObjectsContainersList &objectsContainersList,
const gd::String& rootObjectName,
gd::ExpressionNode& node) {
gd::ExpressionVariableOwnerFinder typeFinder(
platform, globalObjectsContainer, objectsContainer, rootObjectName);
platform, objectsContainersList, rootObjectName);
node.Visit(typeFinder);
return typeFinder.GetObjectName();
}
@@ -55,12 +59,10 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
protected:
ExpressionVariableOwnerFinder(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ObjectsContainersList &objectsContainersList_,
const gd::String& rootObjectName_)
: platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
objectsContainersList(objectsContainersList_),
rootObjectName(rootObjectName_),
objectName(""),
variableNode(nullptr) {};
@@ -126,9 +128,8 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
}
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
platform,
objectsContainersList,
functionCall,
parameterIndex);
if (parameterMetadata == nullptr
@@ -142,9 +143,8 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
for (int previousIndex = parameterIndex - 1; previousIndex >= 0; previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform,
globalObjectsContainer,
objectsContainer,
platform,
objectsContainersList,
functionCall,
previousIndex);
if (previousParameterMetadata != nullptr
@@ -162,8 +162,7 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
gd::ExpressionNode *variableNode;
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ObjectsContainersList &objectsContainersList;
const gd::String &rootObjectName;
};

View File

@@ -0,0 +1,374 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Contains a variables container or a variable. Useful
* to refer to the parent of a variable (which can be a VariablesContainer
* or another Variable).
*/
struct VariableAndItsParent {
const gd::VariablesContainer* parentVariablesContainer;
const gd::Variable* parentVariable;
};
/**
* \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.
*
* \see gd::ExpressionParser2
*/
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);
node.Visit(typeFinder);
return typeFinder.variableAndItsParent;
}
virtual ~ExpressionVariableParentFinder(){};
protected:
ExpressionVariableParentFinder(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_)
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
variableNode(nullptr),
thisIsALegacyPrescopedVariable(false),
bailOutBecauseEmptyVariableName(false),
legacyPrescopedVariablesContainer(nullptr),
variableAndItsParent{} {};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The node represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
childVariableNames.insert(childVariableNames.begin(), node.name);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.name,
[&]() {
// This is an object.
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.name);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList().Has(
node.name)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.name),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
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).
bailOutBecauseEmptyVariableName = true;
}
childVariableNames.insert(childVariableNames.begin(), node.name);
if (node.parent) node.parent->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
// This node is not necessarily a variable node.
// It will be checked when visiting the FunctionCallNode, just after.
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The identifier represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
childVariableNames.insert(childVariableNames.begin(),
node.identifierName);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.identifierName,
[&]() {
// This is an object.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.identifierName);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
if (projectScopedContainers.GetVariablesContainersList().Has(
node.identifierName)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.identifierName),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
// Add a child with an empty name, which will be interpreted as
// "take the first child/item of the structure/array".
childVariableNames.insert(childVariableNames.begin(), "");
if (node.parent) node.parent->Visit(*this);
}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
if (variableNode == nullptr) {
return;
}
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == variableNode) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
const auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, parameterIndex);
if (parameterMetadata == nullptr) return; // Unexpected
// Support for legacy pre-scoped variables:
if (parameterMetadata->GetValueTypeMetadata().IsLegacyPreScopedVariable()) {
if (parameterMetadata->GetType() == "objectvar") {
// Legacy convention where a "objectvar"
// parameter represents a variable of the object represented by the
// previous "object" parameter. The object on which the function is
// called is returned if no previous parameters are objects.
gd::String objectName = functionCall.objectName;
for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, previousIndex);
if (previousParameterMetadata != nullptr &&
gd::ParameterMetadata::IsObject(
previousParameterMetadata->GetType())) {
auto previousParameterNode =
functionCall.parameters[previousIndex].get();
IdentifierNode* objectNode =
dynamic_cast<IdentifierNode*>(previousParameterNode);
objectName = objectNode->identifierName;
break;
}
}
legacyPrescopedVariablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "scenevar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "globalvar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
}
} else {
thisIsALegacyPrescopedVariable = false;
legacyPrescopedVariablesContainer = nullptr;
}
}
private:
VariableAndItsParent WalkUntilLastParent(
const gd::Variable& variable,
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.
const gd::Variable* currentVariable = &variable;
// Walk until size - 1 as we want the last parent.
for (size_t index = startIndex; index + 1 < childVariableNames.size();
++index) {
const gd::String& childName = childVariableNames[index];
if (childName.empty()) {
if (currentVariable->GetChildrenCount() == 0) {
// The array or structure is empty, we can't walk through it - there
// is no "parent".
return {};
}
if (currentVariable->GetType() == gd::Variable::Array) {
currentVariable = &currentVariable->GetAtIndex(0);
} else {
currentVariable =
currentVariable->GetAllChildren().begin()->second.get();
}
} else {
if (!currentVariable->HasChild(childName)) {
// Non existing child - there is no "parent".
return {};
}
currentVariable = &currentVariable->GetChild(childName);
}
}
// Return the last parent of the chain of variables (so not the last
// variable but the one before it).
return {.parentVariable = currentVariable};
}
VariableAndItsParent WalkUntilLastParent(
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.
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;
if (childVariableNames.size() == 1 || !variable)
return {// Only one child: the parent is the variables container itself.
.parentVariablesContainer = &variablesContainer};
return WalkUntilLastParent(*variable, childVariableNames, 1);
}
gd::ExpressionNode* variableNode;
std::vector<gd::String> childVariableNames;
bool thisIsALegacyPrescopedVariable;
bool bailOutBecauseEmptyVariableName;
const gd::VariablesContainer* legacyPrescopedVariablesContainer;
VariableAndItsParent variableAndItsParent;
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
};
} // namespace gd

View File

@@ -31,16 +31,14 @@ namespace gd {
class GD_CORE_API ExpressionParameterMover
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterMover(const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_,
ExpressionParameterMover(const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::String& behaviorType_,
const gd::String& objectType_,
const gd::String& functionName_,
std::size_t oldIndex_,
std::size_t newIndex_)
: hasDoneMoving(false),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
behaviorType(behaviorType_),
objectType(objectType_),
functionName(functionName_),
@@ -98,16 +96,16 @@ class GD_CORE_API ExpressionParameterMover
// This refactor only applies on events object functions
// and events object functions doesn't exist yet.
// This is a dead code.
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
const gd::String& thisObjectType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
if (thisObjectType == objectType) {
moveParameter(node.parameters, 1);
hasDoneMoving = true;
}
} else if (!behaviorType.empty() && !node.behaviorName.empty()) {
// Move parameter of a behavior function
const gd::String& thisBehaviorType = gd::GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, node.behaviorName);
const gd::String& thisBehaviorType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfBehavior(node.behaviorName);
if (thisBehaviorType == behaviorType) {
moveParameter(node.parameters, 2);
hasDoneMoving = true;
@@ -126,8 +124,7 @@ class GD_CORE_API ExpressionParameterMover
private:
bool hasDoneMoving;
const gd::ObjectsContainer& globalObjectsContainer;
const gd::ObjectsContainer& objectsContainer;
const gd::ProjectScopedContainers& projectScopedContainers;
const gd::String& behaviorType; // The behavior type of the function which
// must have a parameter moved (optional).
const gd::String& objectType; // The object type of the function which
@@ -155,8 +152,7 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
auto node = expression.GetRootNode();
if (node) {
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
GetObjectsContainer(),
ExpressionParameterMover mover(GetProjectScopedContainers(),
behaviorType,
objectType,
functionName,

View File

@@ -23,22 +23,20 @@
namespace gd {
/**
* \brief Go through the nodes and change the given object name to a new one.
* \brief Go through the nodes and change the given function name to a new name.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionFunctionRenamer
: public ExpressionParser2NodeWorker {
public:
ExpressionFunctionRenamer(const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_,
ExpressionFunctionRenamer(const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::String& behaviorType_,
const gd::String& objectType_,
const gd::String& oldFunctionName_,
const gd::String& newFunctionName_)
: hasDoneRenaming(false),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
projectScopedContainers(projectScopedContainers_),
behaviorType(behaviorType_),
objectType(objectType_),
oldFunctionName(oldFunctionName_),
@@ -71,16 +69,18 @@ class GD_CORE_API ExpressionFunctionRenamer
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitIdentifierNode(IdentifierNode& node) override {
// Nothing to do as this is either a variable, an object variable a property or something else
// but not an expression.
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
// Behavior function name
if (!behaviorType.empty() &&
node.behaviorFunctionName == oldFunctionName) {
const gd::String& thisBehaviorType =
gd::GetTypeOfBehavior(globalObjectsContainer,
objectsContainer,
node.objectFunctionOrBehaviorName);
const gd::String& thisBehaviorType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfBehavior(
node.objectFunctionOrBehaviorName);
if (thisBehaviorType == behaviorType) {
node.behaviorFunctionName = newFunctionName;
hasDoneRenaming = true;
@@ -90,8 +90,8 @@ class GD_CORE_API ExpressionFunctionRenamer
// Object function name
if (behaviorType.empty() && !objectType.empty() &&
node.objectFunctionOrBehaviorName == oldFunctionName) {
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
const gd::String& thisObjectType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
if (thisObjectType == objectType) {
node.objectFunctionOrBehaviorName = newFunctionName;
hasDoneRenaming = true;
@@ -104,16 +104,16 @@ class GD_CORE_API ExpressionFunctionRenamer
if (behaviorType.empty() && !objectType.empty() &&
!node.objectName.empty()) {
// Replace an object function
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
const gd::String& thisObjectType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
if (thisObjectType == objectType) {
node.functionName = newFunctionName;
hasDoneRenaming = true;
}
} else if (!behaviorType.empty() && !node.behaviorName.empty()) {
// Replace a behavior function
const gd::String& thisBehaviorType = gd::GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, node.behaviorName);
const gd::String& thisBehaviorType = projectScopedContainers
.GetObjectsContainersList().GetTypeOfBehavior(node.behaviorName);
if (thisBehaviorType == behaviorType) {
node.functionName = newFunctionName;
hasDoneRenaming = true;
@@ -132,8 +132,7 @@ class GD_CORE_API ExpressionFunctionRenamer
private:
bool hasDoneRenaming;
const gd::ObjectsContainer& globalObjectsContainer;
const gd::ObjectsContainer& objectsContainer;
const gd::ProjectScopedContainers& projectScopedContainers;
const gd::String& behaviorType; // The behavior type for which the expression
// must be replaced (optional).
const gd::String& objectType; // The object type for which the expression
@@ -159,8 +158,7 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
auto node = expression.GetRootNode();
if (node) {
ExpressionFunctionRenamer renamer(GetGlobalObjectsContainer(),
GetObjectsContainer(),
ExpressionFunctionRenamer renamer(GetProjectScopedContainers(),
behaviorType,
objectType,
oldFunctionName,

View File

@@ -34,13 +34,12 @@ class GD_CORE_API ExpressionIdentifierStringFinder
public:
ExpressionIdentifierStringFinder(
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::String &expressionPlainString_,
const gd::String &parameterType_, const gd::String &objectName_,
const gd::String &layerName_, const gd::String &oldName_)
: platform(platform_), globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
expressionPlainString(expressionPlainString_),
parameterType(parameterType_), objectName(objectName_),
layerName(layerName_), oldName(oldName_){};
@@ -82,7 +81,7 @@ protected:
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
gd::String lastLayerName;
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
platform, globalObjectsContainer, objectsContainer, node,
platform, projectScopedContainers.GetObjectsContainersList(), node,
[&](const gd::ParameterMetadata &parameterMetadata,
std::unique_ptr<gd::ExpressionNode> &parameterNode,
size_t parameterIndex, const gd::String &lastObjectName) {
@@ -123,8 +122,7 @@ protected:
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
const gd::ProjectScopedContainers &projectScopedContainers;
/// It's used to extract parameter content.
const gd::String &expressionPlainString;
const gd::String &oldName;
@@ -176,7 +174,7 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionIdentifierStringFinder finder(
platform, GetGlobalObjectsContainer(), GetObjectsContainer(),
platform, GetProjectScopedContainers(),
parameterValue.GetPlainString(), parameterType, objectName,
layerName, oldName);
node->Visit(finder);

View File

@@ -25,6 +25,9 @@ const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& projec
void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType());
if (metadata.GetMetadata().IsRenderedIn3D()) {
result.MarkAsHaving3DObjects();
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
@@ -108,6 +111,33 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
if (gd::ParameterMetadata::IsExpression("variable", type)) {
// Nothing to do (this can't reference an object)
} else {
GetProjectScopedContainers().MatchIdentifierWithName<void>(node.name,
[&]() {
// This represents an object.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.name);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
}
}, [&]() {
// This is a variable.
}, [&]() {
// This is a property.
}, [&]() {
// This is a parameter.
}, [&]() {
// This is something else.
});
}
if (node.child) node.child->Visit(*this);
};
@@ -127,9 +157,10 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
// Add extensions bound to Objects/Behaviors/Functions
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetGlobalObjectsContainer(),
GetObjectsContainer(), rootType, node);
if (gd::ParameterMetadata::IsObject(type)) {
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
if (gd::ParameterMetadata::IsObject(type) ||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
// An object or object variable is used.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());

View File

@@ -44,6 +44,13 @@ public:
return usedRequiredFiles;
}
/**
* \brief Return true when at least 1 object uses the 3D renderer.
*/
bool Has3DObjects() const {
return has3DObjects;
}
/**
* The extensions used by the project (or part of it).
*/
@@ -59,10 +66,15 @@ public:
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
void MarkAsHaving3DObjects() {
has3DObjects = true;
}
private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
bool has3DObjects = false;
};
class GD_CORE_API UsedExtensionsFinder

View File

@@ -43,7 +43,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
struct dirent *lecture;
DIR *rep;
rep = opendir(directory.c_str());
int l = 0;
if (rep == NULL) {
cout << "Unable to open Extensions (" << directory << ") directory."
@@ -63,8 +62,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
LoadExtension(directory + "/" + lec, platform, forgiving);
librariesLoaded.push_back(directory + "/" + lec);
l++;
}
}
@@ -103,7 +100,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
struct dirent *lecture;
DIR *rep;
rep = opendir(directory.c_str());
int l = 0;
if (rep == NULL) {
cout << "Unable to open Extensions (" << directory << ") directory."
@@ -118,7 +114,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
lec.find(".xgd" + suffix, lec.length() - 4 - suffix.length()) !=
string::npos) {
librariesLoaded.push_back(directory + "/" + lec);
l++;
}
}

View File

@@ -0,0 +1,222 @@
/*
* 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 "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::map<gd::String, gd::String> &resourcesFileNameMap) {
auto cleanObject = object.Clone();
cleanObject->GetVariables().Clear();
cleanObject->GetEffects().Clear();
for (auto &&behaviorName : cleanObject->GetAllBehaviorNames()) {
cleanObject->RemoveBehavior(behaviorName);
}
gd::String extensionName = GetObjectExtensionName(*cleanObject);
std::map<gd::String, gd::String> resourcesNameReverseMap;
gd::ObjectAssetSerializer::RenameObjectResourceFiles(
project, *cleanObject, "", objectFullName, resourcesFileNameMap,
resourcesNameReverseMap);
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 &&newResourceName : resourcesInUse.GetAllResources()) {
if (newResourceName.length() == 0) {
continue;
}
auto &resource = resourcesManager.GetResource(
resourcesNameReverseMap.find(newResourceName) !=
resourcesNameReverseMap.end()
? resourcesNameReverseMap[newResourceName]
: newResourceName);
SerializerElement &resourceElement = resourcesElement.AddChild("resource");
resource.SerializeTo(resourceElement);
// Override name and file because the project and the asset don't use the
// same one.
resourceElement.SetAttribute("kind", resource.GetKind());
resourceElement.SetAttribute("name", newResourceName);
auto &oldFilePath = resource.GetFile();
resourceElement.SetAttribute("file",
resourcesFileNameMap.find(oldFilePath) !=
resourcesFileNameMap.end()
? resourcesFileNameMap[oldFilePath]
: oldFilePath);
}
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");
}
void ObjectAssetSerializer::RenameObjectResourceFiles(
gd::Project &project, gd::Object &object,
const gd::String &destinationDirectory, const gd::String &objectFullName,
std::map<gd::String, gd::String> &resourcesFileNameMap,
std::map<gd::String, gd::String> &resourcesNameReverseMap) {
gd::AssetResourcePathCleaner assetResourcePathCleaner(
project.GetResourcesManager(), resourcesFileNameMap,
resourcesNameReverseMap);
object.GetConfiguration().ExposeResources(assetResourcePathCleaner);
// Use asset store script naming conventions for sprite resource files.
if (object.GetConfiguration().GetType() == "Sprite") {
gd::SpriteObject &spriteConfiguration =
dynamic_cast<gd::SpriteObject &>(object.GetConfiguration());
std::map<gd::String, gd::String> normalizedFileNames;
for (std::size_t animationIndex = 0;
animationIndex < spriteConfiguration.GetAnimationsCount();
animationIndex++) {
auto &animation = spriteConfiguration.GetAnimation(animationIndex);
auto &direction = animation.GetDirection(0);
const gd::String &animationName =
animation.GetName().empty()
? gd::String::From(animationIndex)
: animation.GetName().FindAndReplace("_", " ", true);
// Search frames that share the same resource.
std::map<gd::String, std::vector<int>> frameIndexes;
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount();
frameIndex++) {
auto &frame = direction.GetSprite(frameIndex);
if (frameIndexes.find(frame.GetImageName()) == frameIndexes.end()) {
std::vector<int> emptyVector;
frameIndexes[frame.GetImageName()] = emptyVector;
}
auto &indexes = frameIndexes[frame.GetImageName()];
indexes.push_back(frameIndex);
}
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount();
frameIndex++) {
auto &frame = direction.GetSprite(frameIndex);
auto oldName = frame.GetImageName();
if (normalizedFileNames.find(oldName) != normalizedFileNames.end()) {
gd::LogWarning("The resource \"" + oldName +
"\" is shared by several animations.");
continue;
}
gd::String newName = objectFullName;
if (spriteConfiguration.GetAnimationsCount() > 1) {
newName += "_" + animationName;
}
if (direction.GetSpritesCount() > 1) {
newName += "_";
auto &indexes = frameIndexes[frame.GetImageName()];
for (size_t i = 0; i < indexes.size(); i++) {
newName += gd::String::From(indexes.at(i) + 1);
if (i < indexes.size() - 1) {
newName += ";";
}
}
}
gd::String extension = oldName.substr(oldName.find_last_of("."));
newName += extension;
frame.SetImageName(newName);
normalizedFileNames[oldName] = newName;
}
}
for (std::map<gd::String, gd::String>::const_iterator it =
resourcesFileNameMap.begin();
it != resourcesFileNameMap.end(); ++it) {
if (!it->first.empty()) {
gd::String originFile = it->first;
gd::String destinationFile = it->second;
resourcesFileNameMap[originFile] = normalizedFileNames[destinationFile];
}
}
auto clonedResourcesNameReverseMap = resourcesNameReverseMap;
resourcesNameReverseMap.clear();
for (std::map<gd::String, gd::String>::const_iterator it =
clonedResourcesNameReverseMap.begin();
it != clonedResourcesNameReverseMap.end(); ++it) {
if (!it->first.empty()) {
gd::String newResourceName = it->first;
gd::String oldResourceName = it->second;
resourcesNameReverseMap[normalizedFileNames[newResourceName]] =
oldResourceName;
}
}
}
}
} // namespace gd

View File

@@ -0,0 +1,64 @@
/*
* 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 resourcesFileNameMap The map from project resource file paths to
* asset resource file paths.
*/
static void
SerializeTo(gd::Project &project, const gd::Object &object,
const gd::String &objectFullName, SerializerElement &element,
std::map<gd::String, gd::String> &resourcesFileNameMap);
~ObjectAssetSerializer(){};
private:
ObjectAssetSerializer(){};
static void RenameObjectResourceFiles(
gd::Project &project, gd::Object &object,
const gd::String &destinationDirectory, const gd::String &objectFullName,
std::map<gd::String, gd::String> &resourcesFileNameMap,
std::map<gd::String, gd::String> &resourcesNameReverseMap);
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.
@@ -63,14 +73,10 @@ void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName){
};
void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(audioName) &&
resources->GetResource(audioName).GetKind() == "audio") {
// Nothing to do, the audio is a reference to a proper resource.
return;
}
if (resourcesManager->HasResource(audioName) &&
resourcesManager->GetResource(audioName).GetKind() == "audio") {
// Nothing to do, the audio is a reference to a proper resource.
return;
}
// For compatibility with older projects (where events were referring to files
@@ -80,14 +86,10 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
};
void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(fontName) &&
resources->GetResource(fontName).GetKind() == "font") {
// Nothing to do, the font is a reference to a proper resource.
return;
}
if (resourcesManager->HasResource(fontName) &&
resourcesManager->GetResource(fontName).GetKind() == "font") {
// Nothing to do, the font is a reference to a proper resource.
return;
}
// For compatibility with older projects (where events were referring to files
@@ -96,12 +98,7 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
ExposeFile(fontName);
};
void ArbitraryResourceWorker::ExposeResources(
gd::ResourcesManager* resourcesManager) {
if (!resourcesManager) return;
resourcesManagers.push_back(resourcesManager);
void ArbitraryResourceWorker::ExposeResources() {
std::vector<gd::String> resources = resourcesManager->GetAllResourceNames();
for (std::size_t i = 0; i < resources.size(); i++) {
if (resourcesManager->GetResource(resources[i]).UseFile())
@@ -110,9 +107,6 @@ void ArbitraryResourceWorker::ExposeResources(
}
void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
if (resourcesManagers.empty()) return;
gd::ResourcesManager* resourcesManager = resourcesManagers[0];
gd::Resource& resource = resourcesManager->GetResource(resourceName);
if (!resource.GetMetadata().empty()) {
@@ -136,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 +171,7 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
}
if (resourceType == "tilemap") {
ExposeTilemap(resourceName);
ExposeEmbeddeds(resourceName);
return;
}
if (resourceType == "tileset") {
@@ -184,12 +180,21 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
}
if (resourceType == "json") {
ExposeJson(resourceName);
ExposeEmbeddeds(resourceName);
return;
}
if (resourceType == "video") {
ExposeVideo(resourceName);
return;
}
if (resourceType == "atlas") {
ExposeAtlas(resourceName);
return;
}
if (resourceType == "spine") {
ExposeSpine(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}
@@ -243,10 +248,12 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
} else if (parameterMetadata.GetType() == "jsonResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeJson(updatedParameterValue);
worker.ExposeEmbeddeds(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilemapResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeTilemap(updatedParameterValue);
worker.ExposeEmbeddeds(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "tilesetResource") {
gd::String updatedParameterValue = parameterValue;
@@ -256,19 +263,20 @@ 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);
}
});
return false;
};
void LaunchResourceWorkerOnEvents(const gd::Project& project,
gd::EventsList& events,
gd::ArbitraryResourceWorker& worker) {
gd::ResourceWorkerInEventsWorker eventsWorker(project, worker);
eventsWorker.Launch(events);
}
gd::ResourceWorkerInEventsWorker
GetResourceWorkerOnEvents(const gd::Project &project,
gd::ArbitraryResourceWorker &worker) {
@@ -293,8 +301,8 @@ void ResourceWorkerInObjectsWorker::DoVisitBehavior(gd::Behavior &behavior){
gd::ResourceWorkerInObjectsWorker
GetResourceWorkerOnObjects(const gd::Project &project,
gd::ArbitraryResourceWorker &worker) {
gd::ResourceWorkerInObjectsWorker eventsWorker(project, worker);
return eventsWorker;
gd::ResourceWorkerInObjectsWorker resourcesWorker(project, worker);
return resourcesWorker;
}
} // namespace gd

View File

@@ -37,13 +37,14 @@ namespace gd {
* \see ResourcesMergingHelper
* \see gd::ResourcesInUseHelper
*
* \see gd::LaunchResourceWorkerOnEvents
* \see gd::GetResourceWorkerOnEvents
*
* \ingroup IDE
*/
class GD_CORE_API ArbitraryResourceWorker {
public:
ArbitraryResourceWorker(){};
public:
ArbitraryResourceWorker(gd::ResourcesManager &resourcesManager_)
: resourcesManager(&resourcesManager_){};
virtual ~ArbitraryResourceWorker();
/**
@@ -52,7 +53,7 @@ class GD_CORE_API ArbitraryResourceWorker {
* first to ensure that resources are known so that images, shaders & audio
* can make reference to them.
*/
void ExposeResources(gd::ResourcesManager *resourcesManager);
void ExposeResources();
/**
* \brief Expose a resource from a given type.
@@ -95,6 +96,16 @@ class GD_CORE_API ArbitraryResourceWorker {
* \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.
@@ -122,10 +133,8 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeEmbeddeds(gd::String &resourceName);
protected:
const std::vector<gd::ResourcesManager *> &GetResources() {
return resourcesManagers;
};
protected:
gd::ResourcesManager * resourcesManager;
private:
/**
@@ -133,8 +142,6 @@ class GD_CORE_API ArbitraryResourceWorker {
* exposed as file (see ExposeFile).
*/
void ExposeResource(gd::Resource &resource);
std::vector<gd::ResourcesManager *> resourcesManagers;
};
/**

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

@@ -0,0 +1,19 @@
#include "ObjectsUsingResourceCollector.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
namespace gd {
void ObjectsUsingResourceCollector::DoVisitObject(gd::Object& object) {
gd::ResourceNameMatcher resourceNameMatcher(*resourcesManager, resourceName);
object.GetConfiguration().ExposeResources(resourceNameMatcher);
if (resourceNameMatcher.AnyResourceMatches()) {
objectNames.push_back(object.GetName());
}
};
ObjectsUsingResourceCollector::~ObjectsUsingResourceCollector() {}
} // namespace gd

View File

@@ -0,0 +1,97 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <vector>
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/String.h"
namespace gd {
class Object;
} // namespace gd
namespace gd {
class GD_CORE_API ObjectsUsingResourceCollector
: public ArbitraryObjectsWorker {
public:
ObjectsUsingResourceCollector(gd::ResourcesManager &resourcesManager_,
const gd::String &resourceName_)
: resourcesManager(&resourcesManager_), resourceName(resourceName_){};
virtual ~ObjectsUsingResourceCollector();
std::vector<gd::String>& GetObjectNames() { return objectNames; }
private:
void DoVisitObject(gd::Object& object) override;
std::vector<gd::String> objectNames;
gd::String resourceName;
gd::ResourcesManager *resourcesManager;
};
class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
public:
ResourceNameMatcher(gd::ResourcesManager &resourcesManager,
const gd::String &resourceName_)
: resourceName(resourceName_),
matchesResourceName(false), gd::ArbitraryResourceWorker(
resourcesManager){};
virtual ~ResourceNameMatcher(){};
bool AnyResourceMatches() { return matchesResourceName; }
void Reset() { matchesResourceName = false; }
private:
virtual void ExposeFile(gd::String& resource) override{
/*Don't care, we just read resource names*/
};
virtual void ExposeImage(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeAudio(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeJson(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeTilemap(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeTileset(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeVideo(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
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;
}
gd::String resourceName;
bool matchesResourceName;
};
}; // namespace gd

View File

@@ -18,8 +18,9 @@ namespace gd {
std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
gd::Project& project, const gd::String& resourceType) {
std::vector<gd::String> unusedResources;
// Search for resources used in the project
gd::ResourcesInUseHelper resourcesInUse;
gd::ResourcesInUseHelper resourcesInUse(project.GetResourcesManager());
gd::ResourceExposer::ExposeWholeProjectResources(project, resourcesInUse);
std::set<gd::String>& usedResources = resourcesInUse.GetAll(resourceType);

View File

@@ -25,8 +25,29 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
bool updateOriginalProject,
bool preserveAbsoluteFilenames,
bool preserveDirectoryStructure) {
if (updateOriginalProject) {
gd::ProjectResourcesCopier::CopyAllResourcesTo(
originalProject, originalProject, fs, destinationDirectory,
preserveAbsoluteFilenames, preserveDirectoryStructure);
} else {
gd::Project clonedProject = originalProject;
gd::ProjectResourcesCopier::CopyAllResourcesTo(
originalProject, clonedProject, fs, destinationDirectory,
preserveAbsoluteFilenames, preserveDirectoryStructure);
}
return true;
}
bool ProjectResourcesCopier::CopyAllResourcesTo(
gd::Project& originalProject,
gd::Project& clonedProject,
AbstractFileSystem& fs,
gd::String destinationDirectory,
bool preserveAbsoluteFilenames,
bool preserveDirectoryStructure) {
// Check if there are some resources with absolute filenames
gd::ResourcesAbsolutePathChecker absolutePathChecker(fs);
gd::ResourcesAbsolutePathChecker absolutePathChecker(originalProject.GetResourcesManager(), fs);
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, absolutePathChecker);
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
@@ -34,24 +55,18 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
<< destinationDirectory << "..." << std::endl;
// Get the resources to be copied
gd::ResourcesMergingHelper resourcesMergingHelper(fs);
gd::ResourcesMergingHelper resourcesMergingHelper(
clonedProject.GetResourcesManager(), fs);
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
resourcesMergingHelper.PreserveDirectoriesStructure(
preserveDirectoryStructure);
resourcesMergingHelper.PreserveAbsoluteFilenames(
preserveAbsoluteFilenames);
if (updateOriginalProject) {
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, resourcesMergingHelper);
} else {
std::shared_ptr<gd::Project> project(new gd::Project(originalProject));
gd::ResourceExposer::ExposeWholeProjectResources(*project, resourcesMergingHelper);
}
resourcesMergingHelper.PreserveAbsoluteFilenames(preserveAbsoluteFilenames);
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
resourcesMergingHelper);
// Copy resources
map<gd::String, gd::String>& resourcesNewFilename =
resourcesMergingHelper.GetAllResourcesOldAndNewFilename();
unsigned int i = 0;
for (map<gd::String, gd::String>::const_iterator it =
resourcesNewFilename.begin();
it != resourcesNewFilename.end();
@@ -71,8 +86,6 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
destinationFile + _("\"."));
}
}
++i;
}
return 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,8 +48,14 @@ class GD_CORE_API ProjectResourcesCopier {
bool updateOriginalProject,
bool preserveAbsoluteFilenames = true,
bool preserveDirectoryStructure = true);
private:
static bool CopyAllResourcesTo(gd::Project& originalProject,
gd::Project& clonedProject,
gd::AbstractFileSystem& fs,
gd::String destinationDirectory,
bool preserveAbsoluteFilenames = true,
bool preserveDirectoryStructure = true);
};
} // namespace gd
#endif // PROJECTRESOURCESCOPIER_H

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef RESOURCESABSOLUTEPATHCHECKER_H
#define RESOURCESABSOLUTEPATHCHECKER_H
#pragma once
#include "GDCore/IDE/AbstractFileSystem.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
@@ -22,10 +21,10 @@ namespace gd {
*/
class GD_CORE_API ResourcesAbsolutePathChecker
: public ArbitraryResourceWorker {
public:
ResourcesAbsolutePathChecker(AbstractFileSystem& fileSystem)
: ArbitraryResourceWorker(),
hasAbsoluteFilenames(false),
public:
ResourcesAbsolutePathChecker(gd::ResourcesManager &resourcesManager,
AbstractFileSystem &fileSystem)
: ArbitraryResourceWorker(resourcesManager), hasAbsoluteFilenames(false),
fs(fileSystem){};
virtual ~ResourcesAbsolutePathChecker(){};
@@ -47,5 +46,3 @@ class GD_CORE_API ResourcesAbsolutePathChecker
};
} // namespace gd
#endif // RESOURCESABSOLUTEPATHCHECKER_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>
@@ -33,10 +31,12 @@ std::set<gd::String> & usedImages = resourcesInUse.GetAllImages();
* \ingroup IDE
*/
class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
public:
ResourcesInUseHelper() : gd::ArbitraryResourceWorker(){};
public:
ResourcesInUseHelper(gd::ResourcesManager &resourcesManager)
: 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"); };
@@ -46,6 +46,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
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;
@@ -56,6 +58,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
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;
};
@@ -63,35 +67,42 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
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;
@@ -101,10 +112,11 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
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

@@ -28,11 +28,11 @@ namespace gd {
* \ingroup IDE
*/
class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
public:
ResourcesMergingHelper(gd::AbstractFileSystem& fileSystem)
: ArbitraryResourceWorker(),
preserveDirectoriesStructure(false),
preserveAbsoluteFilenames(false),
public:
ResourcesMergingHelper(gd::ResourcesManager &resourcesManager,
gd::AbstractFileSystem &fileSystem)
: ArbitraryResourceWorker(resourcesManager),
preserveDirectoriesStructure(false), preserveAbsoluteFilenames(false),
fs(fileSystem){};
virtual ~ResourcesMergingHelper(){};
@@ -64,19 +64,25 @@ class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
* 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

@@ -22,13 +22,16 @@ namespace gd {
*/
class ResourcesRenamer : public gd::ArbitraryResourceWorker {
public:
/**
* @brief Constructor taking the map from old name to new name.
* @param oldToNewNames_ A map associating to a resource name the new name to
* use.
*/
ResourcesRenamer(const std::map<gd::String, gd::String>& oldToNewNames_)
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
/**
* @brief Constructor taking the map from old name to new name.
* @param oldToNewNames_ A map associating to a resource name the new name to
* use.
*/
ResourcesRenamer(gd::ResourcesManager &resourcesManager,
const std::map<gd::String, gd::String> &oldToNewNames_)
: gd::ArbitraryResourceWorker(resourcesManager),
oldToNewNames(oldToNewNames_){};
virtual ~ResourcesRenamer(){};
virtual void ExposeFile(gd::String& resourceFileName) override{
@@ -62,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

@@ -0,0 +1,37 @@
/*
* GDevelop JS Platform
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "SceneResourcesFinder.h"
#include "GDCore/IDE/ResourceExposer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
std::set<gd::String> SceneResourcesFinder::FindProjectResources(gd::Project &project) {
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
gd::ResourceExposer::ExposeProjectResources(project, resourceWorker);
return resourceWorker.resourceNames;
}
std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &project,
gd::Layout &layout) {
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
gd::ResourceExposer::ExposeLayoutResources(project, layout, resourceWorker);
return resourceWorker.resourceNames;
}
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
if (resourceName.empty()) {
return;
}
resourceNames.insert(resourceName);
}
} // namespace gd

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