Compare commits

...

162 Commits

Author SHA1 Message Date
AlexandreS
c4e230d9ba Split text input padding into 2 separate properties (#7338)
Don't show in changelog
2025-01-23 16:12:18 +01:00
D8H
56ab2fdd05 Revert "Fix used extension check false positive from usused object or behavior events (#7331)" (#7340)
This reverts commit cb24f191fd.

Don't show in changelog
2025-01-23 14:11:13 +01:00
github-actions[bot]
872f4032ff Update translations [skip ci] (#7337)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-23 11:56:54 +01:00
D8H
428b4c21a8 Disable panel sprite tilling by default (#7335)
* This ensures the rendering is more performant by default
2025-01-23 09:43:40 +01:00
D8H
cb24f191fd Fix used extension check false positive from usused object or behavior events (#7331)
- Don't show in changelog
2025-01-22 17:17:39 +01:00
github-actions[bot]
994f6bf150 Update translations [skip ci] (#7334)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2025-01-22 15:55:56 +01:00
AlexandreS
3e2f05460a Bump newIDE version (#7333) 2025-01-22 15:46:04 +01:00
github-actions[bot]
c775cde9df Update translations [skip ci] (#7318)
Co-authored-by: NeylMahfouf2608 <26115084+NeylMahfouf2608@users.noreply.github.com>
2025-01-22 15:37:51 +01:00
AlexandreS
6ab736a048 Return exact matches when searching for an instruction (#7330) 2025-01-22 15:35:12 +01:00
Neyl
f805182ce4 Improvement features for Input text box (#7308)
padding, max-length and text-align can now be configured on the text input object
new condition "Input is submitted" to check if the input has been submitted with the keyboard on all platforms
2025-01-22 14:20:29 +01:00
D8H
a14f1a187e Fix a few typo in Physics3D (#7332)
- Don't show in changelog
2025-01-22 11:35:52 +01:00
D8H
d08bf97471 Fix conflict between variable or property and parameter in variable setters (#7329)
- Don't show in changelog
2025-01-21 19:16:43 +01:00
D8H
093e15e105 Fix EventsFunctionsContainer copy constructor now copy the owner (#7327)
- Don't show in changelog
2025-01-20 14:35:37 +01:00
D8H
60e36f37b1 Remove EventsFunctionsExtension inheritance to EventsFunctionsContainer (#7326)
- developer changelog only
2025-01-20 11:29:55 +01:00
D8H
ad7ce3c725 [3D character] Fix the "is falling" condition from being true when the character is going up (#7325) 2025-01-19 01:00:43 +01:00
D8H
c6f83f4431 Fix behavior property generation and value. (#7324) 2025-01-19 01:00:05 +01:00
D8H
4a685db574 Fix parameters list not updating when the function configuration was changed (#7321) 2025-01-17 20:06:08 +01:00
D8H
f3dea010fb Fix syntax errors not showing up on mouse button and key parameters (#7320)
- Don't show in changelog
2025-01-17 20:05:29 +01:00
D8H
5025e03dff Fix 3D physics characters to keep rotation around X and Y intact (#7319) 2025-01-17 15:42:46 +01:00
Florian Rival
64771c6faf Give back focus to preview, when updating a preview on the web-app 2025-01-17 13:10:09 +01:00
github-actions[bot]
d4987c4739 Update translations [skip ci] (#7314)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-17 10:48:59 +01:00
Clément Pasteau
56575e7afd Introduce new subscription dialog design for creators without a plan (#7316) 2025-01-17 10:39:04 +01:00
Florian Rival
9a0df077b3 Fix a crash when opening a project missing a custom object that was loaded in the previously opened project (#7317) 2025-01-16 23:27:32 +01:00
AlexandreS
4bf3ef8ad4 Use the folder structure when choosing an object/group in an action/condition (#7297) 2025-01-16 12:03:58 +01:00
Clément Pasteau
6242e8c97a Fix not being able to drag the app from the top bar (#7315)
Do not show in changelog
2025-01-16 12:02:47 +01:00
D8H
d6c99b27af Allow to use parameters and properties in the variable action and condition (#7124) 2025-01-15 23:28:18 +01:00
AlexandreS
7844ee102e Fix issue that was not showing cubes when they have all their faces shown with no textures (#7313) 2025-01-15 18:17:38 +01:00
github-actions[bot]
7d713d9428 Update translations [skip ci] (#7311)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-15 17:36:12 +01:00
Clément Pasteau
22056d3c08 Fix adding draggable space only on title bar (#7312)
Do not show in changelog
2025-01-15 17:28:43 +01:00
D8H
2ce35a1520 Allow 3D physics characters to push each other (#7292) 2025-01-15 10:52:21 +01:00
AlexandreS
b04c15f23c Add objects installed from the asset store in selected folder (#7287) 2025-01-15 10:32:15 +01:00
D8H
2ab246696b Add autocompletion for keyboard key properties in the behavior editor (#7310) 2025-01-15 09:57:45 +01:00
Florian Rival
6b4c00c987 Add missing JS files thumbnail
Don't show in changelog
2025-01-15 09:52:56 +01:00
github-actions[bot]
9c4a190a4d Update translations [skip ci] (#7300)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-15 09:50:36 +01:00
Clément Pasteau
1070a489e7 Fix dialog overflow because of cross (#7309)
Don't show in changelog
2025-01-15 09:24:37 +01:00
Clément Pasteau
d2a8cb6727 Improve title bar on multiple platforms optimizing the space at the top of the app (#7306) 2025-01-14 17:29:55 +01:00
D8H
2638c4f593 Fix getter and setter generation for animation properties (#7307)
- Don't show in changelog
2025-01-14 14:51:27 +01:00
D8H
4be2386efe Allow to toggle between drop-down list and expression for mouse button and key parameters (#7305) 2025-01-14 13:53:10 +01:00
AlexandreS
8be099d50a Add possibility to open GDevelop in the browser on the premium course (#7304) 2025-01-13 19:12:14 +01:00
Clément Pasteau
713698d3d6 Fix wrong margins for dense design of sub card (#7303)
Do not show in changelog
2025-01-13 13:32:11 +01:00
Clément Pasteau
8b36908fd8 Merge project manager & app burger menu buttons (#7289)
* The goal of this change is to simplify the top left corner of the app, by merging the App burger menu and the Project manager button into one. Having 2 buttons was causing confusion in the flow of using the app, by mistakenly pressing the wrong button.
* The app menu buttons that are useful are now displayed at the top of the project manager drawer, on the relevant platforms: Web, Windows & mobile, because other platforms have a built-in OS menu.
2025-01-13 10:11:32 +01:00
D8H
c0722dc441 [2D Physics] Reduce the CPU footprint of static objects (#7299) 2025-01-10 16:39:20 +01:00
D8H
9c6a1bed2c [3D Physics] Reduce the CPU footprint of static objects (#7298) 2025-01-10 12:58:53 +01:00
github-actions[bot]
a8a4d14ee1 Update translations [skip ci] (#7291)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-10 10:23:16 +01:00
Florian Rival
b27abfbe5d Add text vertical alignment in quick customzation
Don't show in changelog
2025-01-10 10:07:37 +01:00
Neyl
28b585aefa Allow behavior properties to be an animation name, displayed with a selector 2025-01-09 14:21:04 +01:00
AlexandreS
0623b6acd9 Fix variable list usability (#7290)
- When renaming a variable in the instance panel:
  - the caret position is kept
  - the user can erase the whole field without the name being replaced by "Unnamed" instantly
- When opening the variables editor from a variable field, the currently selected variable is selected in the list (even if child of a variable)
2025-01-08 16:44:13 +01:00
github-actions[bot]
1a833bc388 Update translations [skip ci] (#7272)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-07 11:04:19 +01:00
D8H
827f187e10 Add Pixi and Three type definitions for JS events (#7266) 2025-01-07 10:34:51 +01:00
Florian Rival
0f25b80a66 Fix margins
Don't show in changelog
2025-01-06 22:06:15 +01:00
Clément Pasteau
fbf9710cc5 Use premium colors in most places where a subscription can be purchased (#7284) 2025-01-06 20:56:49 +01:00
Clément Pasteau
3d80709029 Fix correctly exporting desktop icon when project is saved in the cloud (#7282) 2025-01-06 17:54:59 +01:00
AlexandreS
5ab9c565b0 Simplify shop navigation state to avoid useless renderings (#7283)
Only show in developer changelog
2025-01-06 17:01:41 +01:00
Florian Rival
e237fc4b21 Add experimental support for importing JS source files in extensions (#7278)
* See more about using [JavaScript on this page](https://wiki.gdevelop.io/gdevelop5/events/js-code/javascript-in-extensions/#experimental-new-option-javascript-files-in-your-project).

Only show in developer changelog
2025-01-06 11:02:28 +01:00
D8H
18892f97f3 Remove unused import and fix a typo (#7277) 2025-01-04 11:19:42 +01:00
D8H
0ec0654032 Allow to make custom objects private to an extension (#7275) 2025-01-03 19:36:38 +01:00
D8H
8e29c723e8 [3D physics character] Fix a 1-frame delay on the "is falling" condition (#7276) 2025-01-03 19:33:33 +01:00
Clément Pasteau
6acde2865a Fix opening windows in foreground on windows everywhere in app (#7274)
* Ensure electron/remote is used
2025-01-02 16:36:43 +01:00
Clément Pasteau
49e176a98f Scroll to projects when opening from dashboard (#7273)
Do not show in changelog
2025-01-02 15:16:30 +01:00
D8H
71c2a0be01 Add a property to choose the maximum stair height a 3D character can walk (#7265) 2025-01-02 13:18:46 +01:00
Clément Pasteau
4ad1a0dd68 Bump 221 (#7271) 2025-01-02 11:20:00 +01:00
github-actions[bot]
a556690307 Update translations [skip ci] (#7269)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-02 11:17:46 +01:00
Clément Pasteau
6950f323b3 Disable type checking in JS Events until all autocompletions are properly handled (#7270) 2025-01-02 11:09:59 +01:00
github-actions[bot]
de129f6cc4 Update translations [skip ci] (#7252)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-02 10:57:33 +01:00
Clément Pasteau
b3b88a0445 Fix wrong prop name & add translations (#7268)
Do not show in changelog
2025-01-02 10:57:08 +01:00
D8H
8e8f3a7d55 Fix the collision conditions for 3D physics characters (#7264)
* Migrate to Jolt 0.31.0
2024-12-31 16:58:08 +01:00
D8H
cc6b4a283a Enable type checking on the Dialog Tree implementation (#7263)
- Don't show in changelog
2024-12-31 13:03:16 +01:00
D8H
f393f36bad Add a tooltip on some physics properties (#7262) 2024-12-30 18:23:20 +01:00
Florian Rival
5261f5a431 Remove logs for audio events when manipulating a sound/music on an unused channel (#7260) 2024-12-29 15:50:05 +01:00
AlexandreS
1a6cf8d69a Paginate feedbacks (#7259)
Also:
* Display 3 last projects only in context menu
2024-12-27 14:09:45 +01:00
AlexandreS
e93b38fee4 Fix asset preview being too zoomed in (#7255) 2024-12-27 14:08:06 +01:00
Florian Rival
22c7215071 Increase bottom drawer bottom margin on mobile
Don't show in changelog
2024-12-22 13:06:01 +01:00
Florian Rival
a221990c57 Ensure Home tab still shown when project opened on small screens
* Also smoothly scroll to the active tab
2024-12-22 12:43:33 +01:00
AlexandreS
85f6e74a5c Fix mobile horizontal scroll (#7254)
Don't show in changelog
2024-12-20 10:57:46 +01:00
Florian Rival
6577432b27 Bump newIDE version 2024-12-19 16:59:15 +01:00
AlexandreS
16d94b5e38 When duplicating a project, ask for the new name and if link with game should be kept (#7253)
Don't show in changelog
2024-12-19 16:50:38 +01:00
Florian Rival
88a2060364 Fix warning 2024-12-19 16:12:59 +01:00
Florian Rival
2d0ffee102 Improve subscription dialog and profile 2024-12-19 15:39:35 +01:00
D8H
aa30f3c465 Fix a regression on parameter editor lock (#7249)
Do not show in changelog
2024-12-19 11:43:41 +01:00
github-actions[bot]
cfcb4b557f Update translations [skip ci] (#7248)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-12-19 11:42:58 +01:00
Clément Pasteau
d8db679a1d Show "Get Premium" button with enhanced colors (#7251) 2024-12-19 11:33:03 +01:00
Florian Rival
01503d46c1 Bump newIDE version 2024-12-18 18:16:59 +01:00
Florian Rival
02d44bbba4 Remove unused API method to initialize game rendering [skip ci] (#7235) 2024-12-18 18:16:13 +01:00
D8H
b6d8170a00 [3D character] Allow to set higher speed than the maximum speed (#7245)
Don't show in changelog
2024-12-18 18:15:50 +01:00
github-actions[bot]
554c4c8f58 Update translations [skip ci] (#7239)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-12-18 18:14:59 +01:00
D8H
9e29146841 Fix a regression on custom object life-cycle parameters not being locked (#7247) 2024-12-18 18:03:41 +01:00
AlexandreS
189e971cd2 Fix: Highlight the currently opened project if multiple projects with same uuid (#7244)
Don't show in changelog
2024-12-18 17:54:46 +01:00
Florian Rival
deab962081 Fix regression in extensions loading (#7242) 2024-12-18 12:26:56 +01:00
D8H
e2281dfd82 Add 3D physics and 3D character behaviors (#7149)
* The 3D physics engine is powered by [Jolt Physics](https://github.com/jrouwe/JoltPhysics) a modern, performant, battle-tested, fully featured 3D physics engine used in AAA games, like Horizon: Forbidden West. The new 3D physics engine is perfect for making FPS, TPS, 3D platformer and in the future 3D racing games or any 3D game.
* Similar to the 2D physics engine, you can add the 3D Physics behavior to your object. For example, add the behavior to platforms with the type set to "Static".
* You can choose the collider shape: box, sphere, cylinder or capsule and modify physics properties of the objects having this behavior - much like the 2D physics engine.
* For characters, a dedicated "3D Physics Character" behavior is available. Pair it with one or more additional behaviors to get controls working out of the box: "3D Shooter keyboard mapper", "3D Platformer keyboard mapper", etc...
* Take a look at the new examples to give it a try! New examples and behaviors will be progressively added.
2024-12-17 18:00:48 +01:00
AlexandreS
44daf709e4 Fix asset store display speed and reduce the burst of requests (#7241) 2024-12-17 17:37:02 +01:00
Florian Rival
c9e5272367 Allow to browse marketing boosts from analytics screen
Also fix a warning

Don't show in changelog
2024-12-17 16:38:11 +01:00
AlexandreS
63584d171f Change scene editor title depending if instance or object edited on mobile (#7240) 2024-12-17 14:07:51 +01:00
D8H
092efbe462 Merge the function properties and parameters tabs in the extension editor (#7231) 2024-12-17 13:18:38 +01:00
AlexandreS
fc86b4e2dd Fix: Apply z offset to instance even if no z in instance data (#7238) 2024-12-17 08:46:06 +01:00
github-actions[bot]
3415626552 Update translations [skip ci] (#7208)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-12-16 16:36:27 +01:00
AlexandreS
fd2e87cc5c Polishing the new Create tab (#7236)
Don't show in changelog
2024-12-16 15:46:32 +01:00
Clément Pasteau
ecc8c3176d Remove unnecessary templates page (#7234)
Do not show in changelog
2024-12-16 12:13:12 +01:00
Clément Pasteau
1c855226a8 Fix loader on games list (#7233)
Don't show in changelog
2024-12-16 11:22:59 +01:00
Clément Pasteau
558d3a869c Merge Build & Manage tabs (#7217)
* The games you create are now accessible in one place only, simplifying the whole creation experience
* From the Create tab, you can now access the projects you're working on, as well as manage the games you have published
2024-12-16 11:01:43 +01:00
Florian Rival
c22f8afaf1 Fix installing extensions with dependencies from assets (#7232)
Only show in developer changelog
2024-12-14 18:46:58 +01:00
Florian Rival
7ece6c6759 Fix warning 2024-12-14 17:50:49 +01:00
D8H
f1ac388c46 Adapt the condition column size in the Events Sheet (#7230)
* Also change the default extension editor layout.
2024-12-14 16:21:42 +01:00
D8H
8a0045b3b0 Rename events-function parameters in events (#7215) 2024-12-13 19:57:14 +01:00
AlexandreS
4bcac31489 Introduce step-by-step course to master GDevelop (#7223) 2024-12-13 17:52:32 +01:00
Dima Lifanchuk
ff1086ce3b Expose initializeRenderers and initializeCanvas to allow initializing the game rendering manually (#7224) 2024-12-13 16:01:18 +01:00
D8H
e5d77da357 Add and fix tests on boolean variable conditions (#7227)
- Don't show in changelog
2024-12-13 15:10:51 +01:00
AlexandreS
536b0d5c38 Fix title missing in curriculum lessons (#7226) 2024-12-13 09:53:51 +01:00
Florian Rival
ada7dba959 Fix installing assets with resource kinds like bitmap texts (#7222) 2024-12-09 18:29:20 +01:00
Florian Rival
1b0c088f71 Add error display for some obvious semantic/type errors in JavaScript code blocks 2024-12-03 18:25:25 +01:00
Florian Rival
b9b09c1fef Use a better looking invalid/missing texture placeholder (#7218) 2024-12-02 17:26:57 +01:00
Florian Rival
066c8cd387 Add description and screenshots for Rich PWA install (desktop) 2024-12-02 16:24:03 +01:00
Florian Rival
a06ef20011 Improve scripts to download release artifacts in parallel [skip ci]
Don't show in changelog
2024-11-30 20:33:25 +01:00
Florian Rival
2052db6a39 Fix test 2024-11-30 14:06:55 +01:00
danvervlad
35fd7e972b Allow to provide a canvas to be used for the game rendering instead of creating a new one (#7199)
Only show in developer changelog
2024-11-29 17:51:12 +01:00
D8H
d110e83f6f Fix a crash when Physics behavior is used on object smaller than 0.1 pixel (#7209) 2024-11-29 15:49:02 +01:00
Florian Rival
13bdfa4379 Add a test for ensuring default behaviors on objects (#7211) 2024-11-29 15:38:47 +01:00
D8H
689bc014f3 Fix isUsingLegacyInstancesRenderer attribute not being read (#7210)
- only show in dev changelog
2024-11-29 15:28:31 +01:00
Florian Rival
a4f7aa6c43 Fix default behaviors not added properly to objects in functions (#7206) 2024-11-29 14:13:09 +01:00
Florian Rival
39690d6a02 Bump newIDE version 2024-11-28 16:52:26 +01:00
github-actions[bot]
3260b285af Update translations [skip ci] (#7201)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-11-28 16:28:11 +01:00
D8H
8743f70aaf Disable the variable editor button for object parameters (#7202) 2024-11-28 16:05:49 +01:00
Florian Rival
eae75bdc72 Fix potential crashes in events function edition (#7204)
* Also hide object groups tabs in functions (unless there are groups already) - it's not recommended to use them anymore
2024-11-28 14:00:01 +01:00
D8H
825cff7ba3 Fix function configuration editor layout when shown as a side panel (#7203) 2024-11-28 13:57:23 +01:00
D8H
aa12248d86 Fix custom objects inner area expansion on Z axis (#7200) 2024-11-27 14:54:45 +01:00
github-actions[bot]
d0f3abc38d Update translations [skip ci] (#7196)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-11-27 09:32:36 +01:00
D8H
e111706a27 Fix variables from being renamed with a property (#7186) 2024-11-26 17:58:59 +01:00
Florian Rival
e4f3db71c5 Allow opening a project linked to a game directly from the game dashboard (#7167) 2024-11-26 17:43:10 +01:00
Clément Pasteau
08a7949056 Fix keys (#7195)
Do not show in changelog
2024-11-26 15:42:04 +01:00
Florian Rival
1912916778 Add an option to keep layer centered when the game size changes, or keep top-left fixed (as before) (#7188)
* Also fix anchor behavior when used on layers where the camera was moved
2024-11-26 15:41:11 +01:00
D8H
59685bc4c4 Fix function sentence field content not wrapping (#7191) 2024-11-26 12:38:08 +01:00
github-actions[bot]
717948c558 Update translations [skip ci] (#7171)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-11-26 11:44:43 +01:00
D8H
a81c45f91a Fix position conflicts between Physics and other movement behaviors (#7189) 2024-11-26 11:25:41 +01:00
Clément Pasteau
76eaa747c9 Rework Game Creation dialog (#7179)
* It now becomes the entry point for creating a new game suggesting:
  * the empty project option (as before)
  * NEW: starting points for your projects, to bootstrap your platformer, top-down or physics game. They are extremely simple template, that can be used to avoid starting from scratch, which can be scary & difficult.
  * the list of finished games that be remixed into your own, with a search.
2024-11-25 14:20:58 +01:00
AlexandreS
26c95d1745 Show error box to user when an error occurred creating students (#7187) 2024-11-25 11:47:33 +01:00
Florian Rival
e0c72fd113 Improve typing of EventsFunctionContext 2024-11-22 17:24:20 +01:00
Clément Pasteau
2a6e98c27f Update publish flow to allow creating links if game is not owned (#7184) 2024-11-22 09:57:46 +01:00
AlexandreS
5f01ce8701 Initialize opacity at 255 for tilemaps (#7182)
Don't show in changelog
2024-11-21 15:52:20 +01:00
AlexandreS
6ed0e8e4cc Fix: Build tilemap renderer from an empty tilemap to be able to create tilemaps in-game (#7181) 2024-11-21 14:48:17 +01:00
Clément Pasteau
fbea483609 Remove games dashboard new feature highlight (#7180) 2024-11-21 14:44:51 +01:00
Aurélien Vivet
90004e3f83 Remove dead code regarding "alwaysLoaded" (#7175)
Only show in developer changelog
2024-11-20 21:32:00 +01:00
D8H
e6343dfe18 Fix the parameter name field to avoid changing the name when no change was done (#7178) 2024-11-20 18:48:04 +01:00
Florian Rival
ac6b64ba9b Add an option to extend width or height and never crop the game area (#7177) 2024-11-20 16:53:47 +01:00
Florian Rival
44b18cb111 Upload files in batch for web-app previews (#7176)
Only show in developer changelog
2024-11-18 17:36:00 +01:00
Clément Pasteau
c5fc7e08f5 Update marketing boosts display (#7174) 2024-11-18 15:30:22 +01:00
Florian Rival
9eada905f9 Add "Create new folder" menu item inside "Move to folder" menu (#7172) 2024-11-18 11:28:40 +01:00
Clément Pasteau
13aab9a8e8 Improve LAN preview network to detect local ip properly (#7170) 2024-11-18 10:29:17 +01:00
github-actions[bot]
dda85cf630 Update translations [skip ci] (#7163)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-11-15 10:39:57 +01:00
Clément Pasteau
0f81e4c088 Fixes game dashboard (#7166)
Do not show in changelog
2024-11-14 18:27:52 +01:00
Clément Pasteau
5419493349 Fix height when switching to mobile design (#7165) 2024-11-14 16:41:23 +01:00
Clément Pasteau
272766c705 Automatically use a screenshot from the latest preview as the game thumbnail if none are set (#7156) 2024-11-14 12:07:09 +01:00
D8H
a06138b31e Fix sprite scaling factor when a custom size is set (#7164) 2024-11-14 11:09:07 +01:00
Florian Rival
74a7ba5a09 Bump newIDE version 2024-11-14 09:54:25 +01:00
github-actions[bot]
3914d0377f Update translations [skip ci] (#7158)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-11-14 09:50:18 +01:00
D8H
092b29fa0e Fix uninitialized members in default constructor of ProjectScopedContainers (#7162)
Don't show in changelog
2024-11-13 17:39:20 +01:00
D8H
16762960dc Allow legacy scene variable parameters to use extension variables (#7121) 2024-11-13 17:17:02 +01:00
Clément Pasteau
33101ead64 Fix correctly using the app icon when exporting to desktop (#7161) 2024-11-13 17:08:24 +01:00
D8H
de73d617b0 Fix missing error in expressions for missing object variables with children (#7159) 2024-11-13 16:16:46 +01:00
D8H
446b0db05f Reduce the risk of name collisions between objects, variables, parameters and properties (#7148) 2024-11-13 15:16:41 +01:00
Clément Pasteau
66ab7abab7 Fix default color values for shape painter (#7155) 2024-11-13 11:59:53 +01:00
Clément Pasteau
a80b540f06 Fix game dashboard param (#7157)
Don't show in changelog
2024-11-13 11:56:00 +01:00
github-actions[bot]
38761aeec1 Update translations [skip ci] (#7153)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-11-13 11:00:11 +01:00
Clément Pasteau
602dc9d791 Improve login & signup dialogs design (#7152) 2024-11-13 10:23:02 +01:00
679 changed files with 43795 additions and 14740 deletions

1
.gitattributes vendored
View File

@@ -3,6 +3,7 @@ Extensions/ParticleSystem/SPARK/* linguist-vendored
Extensions/PhysicsBehavior/Box2D/* linguist-vendored
Extensions/PhysicsBehavior/box2djs/* linguist-vendored
Extensions/Physics2Behavior/box2d.js linguist-vendored
Extensions/Physics3DBehavior/*.wasm-compat.js linguist-vendored
Extensions/BBText/pixi-multistyle-text/* linguist-vendored
Extensions/P2P/A_peer.js linguist-vendored
Extensions/Multiplayer/peer.js linguist-vendored

View File

@@ -1358,12 +1358,30 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesConta
return "getProperty" + property.GetName() + "As" + type + "()";
}
gd::String EventsCodeGenerator::GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property) {
return "getProperty" + property.GetName() + "()";
}
gd::String EventsCodeGenerator::GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode) {
return "setProperty" + property.GetName() + "(" + operandCode + ")";
}
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
return "getParameter" + parameter.GetName() + "As" + type + "()";
}
gd::String EventsCodeGenerator::GenerateParameterGetterWithoutCasting(
const gd::ParameterMetadata &parameter) {
return "getParameter" + parameter.GetName() + "()";
}
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_)

View File

@@ -467,7 +467,14 @@ class GD_CORE_API EventsCodeGenerator {
*/
virtual gd::String GetCodeNamespace() { return ""; };
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE, ANY_VARIABLE };
enum VariableScope {
LAYOUT_VARIABLE = 0,
PROJECT_VARIABLE,
OBJECT_VARIABLE,
ANY_VARIABLE,
VARIABLE_OR_PROPERTY,
VARIABLE_OR_PROPERTY_OR_PARAMETER
};
/**
* Generate a single unique number for the specified instruction.
@@ -510,6 +517,11 @@ class GD_CORE_API EventsCodeGenerator {
GenerateAnyOrSceneVariableGetter(const gd::Expression &variableExpression,
EventsCodeGenerationContext &context);
virtual gd::String GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode);
protected:
virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString);
@@ -565,7 +577,8 @@ protected:
const gd::String& variableName,
const VariableScope& scope,
gd::EventsCodeGenerationContext& context,
const gd::String& objectName) {
const gd::String& objectName,
bool hasChild) {
// This code is only used as a mock.
// See the real implementation in GDJS.
if (scope == LAYOUT_VARIABLE) {
@@ -573,7 +586,9 @@ protected:
} else if (scope == PROJECT_VARIABLE) {
return "getProjectVariable(" + variableName + ")";
} else if (scope == ANY_VARIABLE) {
} else if (scope == ANY_VARIABLE || scope == VARIABLE_OR_PROPERTY ||
scope == VARIABLE_OR_PROPERTY_OR_PARAMETER) {
// TODO Split the 3 cases to make tests stronger.
return "getAnyVariable(" + variableName + ")";
}
@@ -627,11 +642,18 @@ protected:
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property);
virtual gd::String GenerateParameterGetter(
const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String
GenerateParameterGetterWithoutCasting(const gd::ParameterMetadata &parameter);
/**
* \brief Generate the code to reference an object which is
* in an empty/null state.

View File

@@ -135,18 +135,20 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
EventsCodeGenerator::VariableScope scope =
type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
: type == "variableOrProperty"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY
: type == "variableOrPropertyOrParameter"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER
: 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);
node.name, scope, context, objectName, node.child != nullptr);
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.
@@ -163,7 +165,7 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
output += codeGenerator.GenerateGetVariable(
node.name, gd::EventsCodeGenerator::ANY_VARIABLE, context, "");
node.name, gd::EventsCodeGenerator::ANY_VARIABLE, context, "", node.child != nullptr);
if (node.child) node.child->Visit(*this);
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
@@ -184,8 +186,9 @@ void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
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);
output += codeGenerator.GenerateGetVariable(
node.name, gd::EventsCodeGenerator::OBJECT_VARIABLE, context,
objectNameToUseForVariableAccessor, node.child != nullptr);
// We have accessed an object variable, from now we can continue accessing the child variables
// (including using the bracket notation).
@@ -222,24 +225,27 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
output +=
codeGenerator.GenerateObject(node.identifierName, type, context);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
EventsCodeGenerator::VariableScope scope =
EventsCodeGenerator::VariableScope scope =
type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
: type == "variableOrProperty"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY
: type == "variableOrPropertyOrParameter"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER
: 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.identifierName, scope, context, objectName);
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
codeGenerator.GetPlatform(), codeGenerator.GetObjectsContainersList(),
rootObjectName, node);
output += codeGenerator.GenerateGetVariable(
node.identifierName, scope, context, objectName,
!node.childIdentifierName.empty());
if (!node.childIdentifierName.empty()) {
output +=
codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
} else {
const auto& variablesContainersList = codeGenerator.GetProjectScopedContainers().GetVariablesContainersList();
const auto& propertiesContainersList = codeGenerator.GetProjectScopedContainers().GetPropertiesContainersList();
@@ -249,12 +255,13 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
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);
node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE,
context, node.identifierName, !node.childIdentifierName.empty());
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
output += codeGenerator.GenerateGetVariable(
node.identifierName, gd::EventsCodeGenerator::ANY_VARIABLE, context,
"");
node.identifierName, gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER, context,
"", !node.childIdentifierName.empty());
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}

View File

@@ -18,8 +18,6 @@ namespace gd {
EventsList BaseEvent::badSubEvents;
VariablesContainer BaseEvent::badLocalVariables;
std::vector<gd::String> BaseEvent::emptyDependencies;
gd::String BaseEvent::emptySourceFile;
BaseEvent::BaseEvent()
: totalTimeDuringLastSession(0),

View File

@@ -175,26 +175,6 @@ class GD_CORE_API BaseEvent {
noExpr;
return noExpr;
};
/**
* \brief Returns the dependencies on source files of the project.
* \note Default implementation returns an empty list of dependencies. This is
* fine for most events that are not related to adding custom user source
* code.
*/
virtual const std::vector<gd::String>& GetSourceFileDependencies() const {
return emptyDependencies;
};
/**
* \brief Returns the name of the source file associated with the event
* \note Default implementation returns an empty string. This is fine for most
* events that are not related to adding custom user source code.
*/
virtual const gd::String& GetAssociatedGDManagedSourceFile(
gd::Project& project) const {
return emptySourceFile;
};
///@}
/** \name Code generation
@@ -327,8 +307,6 @@ class GD_CORE_API BaseEvent {
static gd::EventsList badSubEvents;
static gd::VariablesContainer badLocalVariables;
static std::vector<gd::String> emptyDependencies;
static gd::String emptySourceFile;
};
/**

View File

@@ -35,7 +35,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("key", _("Key"));
.AddParameter("key", _("Key to check"))
.SetHidden();
extension
.AddCondition("KeyReleased",
@@ -46,33 +47,32 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("key", _("Key"));
.AddParameter("key", _("Key to check"))
.SetHidden();
extension
.AddCondition("KeyFromTextPressed",
_("Key pressed (text expression)"),
_("Check if a key, retrieved from the result of the "
"expression, is pressed"),
_("Key pressed"),
_("Check if a key is pressed"),
_("_PARAM1_ key is pressed"),
"",
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
.AddParameter("keyboardKey", _("Key to check"))
.MarkAsSimple();
extension
.AddCondition("KeyFromTextReleased",
_("Key released (text expression)"),
_("Check if a key, retrieved from the result of the "
"expression, was just released"),
_("Key released"),
_("Check if a key was just released"),
_("_PARAM1_ key is released"),
"",
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
.AddParameter("keyboardKey", _("Key to check"))
.MarkAsSimple();
extension
.AddCondition("AnyKeyPressed",

View File

@@ -252,7 +252,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("mouse", _("Button to check"))
.MarkAsSimple();
.MarkAsSimple()
.SetHidden();
// Support for deprecated names:
extension.AddDuplicatedCondition("SourisBouton", "MouseButtonPressed")
@@ -262,49 +263,41 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition("MouseButtonReleased",
_("Mouse button released"),
_("Check if the specified mouse button was released."),
_("_PARAM1_ mouse button was released"),
_("Touch or _PARAM1_ mouse button is released"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("mouse", _("Button to check"))
.MarkAsSimple();
.MarkAsSimple()
.SetHidden();
extension
.AddCondition(
"MouseButtonFromTextPressed",
_("Mouse button pressed or touch held (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, is pressed."),
_("_PARAM1_ mouse button is pressed"),
_("Mouse button pressed or touch held"),
_("Check if the specified mouse button is pressed or "
"if a touch is in contact with the screen."),
_("Touch or _PARAM1_ mouse button is down"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("stringWithSelector",
_("Expression generating the mouse button to check"),
"[\"Left\", \"Right\", \"Middle\"]")
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
.AddParameter("mouseButton", _("Button to check"))
.MarkAsSimple();
extension
.AddCondition(
"MouseButtonFromTextReleased",
_("Mouse button released (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, was just released."),
_("_PARAM1_ mouse button is released"),
_("Mouse button released"),
_("Check if the specified mouse button was released."),
_("Touch or _PARAM1_ mouse button is released"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("stringWithSelector",
_("Expression generating the mouse button to check"),
"[\"Left\", \"Right\", \"Middle\"]")
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
.AddParameter("mouseButton", _("Button to check"))
.MarkAsSimple();
extension
.AddExpressionAndCondition("number",

View File

@@ -33,7 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions());
@@ -45,7 +45,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions());
@@ -58,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
@@ -73,7 +73,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions());
@@ -85,7 +85,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions());
@@ -98,7 +98,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.AddParameter("operator", _("Value"), "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.

View File

@@ -272,7 +272,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its

View File

@@ -174,6 +174,7 @@ public:
virtual const gd::String &GetFullName() const = 0;
virtual const gd::String &GetDescription() const = 0;
virtual const gd::String &GetIconFilename() const = 0;
virtual bool IsPrivate() const = 0;
/**
* \brief Return a reference to a map containing the names of the actions

View File

@@ -46,30 +46,25 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24,
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
gd::LogFatalError(
"Error: Event-based objects don't have blueprint. "
"This method should not never be called.");
return nullptr;
}) {}
: name(name_),
iconFilename(icon24x24),
extensionNamespace(extensionNamespace_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
}
ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24,
CreateFunPtr createFunPtrP)
: name(name_),
iconFilename(icon24x24),
createFunPtr(createFunPtrP),
extensionNamespace(extensionNamespace_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
CreateFunPtr createFunPtr_)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24) {
createFunPtr = createFunPtr_;
}
gd::InstructionMetadata& ObjectMetadata::AddCondition(

View File

@@ -39,6 +39,8 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
/**
* \brief Construct an object metadata, using a "blueprint" object that will
* be copied when a new object is requested.
*
* \note This is used for objects declared in JavaScript extensions.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -47,9 +49,9 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
const gd::String& icon24x24_,
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_);
/**
* \brief Construct an object metadata, without "blueprint" object
* \brief Construct an object metadata.
*
* \note This is used by events based objects.
* \note This is used by events based objects ("custom objects").
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -60,14 +62,17 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
/**
* \brief Construct an object metadata, with a function that will be called
* to instantiate a new object.
*
* \note This is used for objects declared in C++ extensions.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24_,
CreateFunPtr createFunPtrP);
ObjectMetadata() : createFunPtr(NULL) {}
CreateFunPtr createFunPtr_);
ObjectMetadata() {}
virtual ~ObjectMetadata(){};
/**
@@ -246,6 +251,11 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
return *this;
}
ObjectMetadata& ResetDefaultBehaviorsJustForTesting() {
defaultBehaviorTypes.clear();
return *this;
}
const gd::String& GetName() const override { return name; }
const gd::String& GetFullName() const override { return fullname; }
const gd::String& GetCategoryFullName() const { return categoryFullName; }
@@ -295,6 +305,22 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
ObjectMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
/**
* \brief Set the object to be hidden in the IDE.
*
@@ -339,7 +365,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
std::vector<gd::String> includeFiles;
gd::String className;
CreateFunPtr createFunPtr;
CreateFunPtr createFunPtr = nullptr;
private:
gd::String extensionNamespace;
@@ -351,6 +377,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
gd::String iconFilename;
gd::String categoryFullName;
std::set<gd::String> defaultBehaviorTypes;
bool isPrivate = false;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;

View File

@@ -6,6 +6,8 @@
#include "ParameterMetadataTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
@@ -13,8 +15,6 @@
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "InstructionMetadata.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
namespace gd {
const ParameterMetadata ParameterMetadataTools::badParameterMetadata;
@@ -23,42 +23,109 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
const gd::Project& project,
const ParameterMetadataContainer& parameters,
gd::ObjectsContainer& outputObjectsContainer) {
outputObjectsContainer.GetObjects().clear();
// Keep track of all objects and their behaviors names, so we can remove
// those who are in the container but not in the parameters anymore.
std::set<gd::String> allObjectNames;
std::map<gd::String, std::set<gd::String>> allObjectNonDefaultBehaviorNames;
gd::String lastObjectName;
for (std::size_t i = 0; i < parameters.GetParametersCount(); ++i) {
const auto& parameter = parameters.GetParameter(i);
if (parameter.GetName().empty()) continue;
if (gd::ParameterMetadata::IsObject(parameter.GetType())) {
outputObjectsContainer.InsertNewObject(
project,
parameter.GetExtraInfo(),
parameter.GetName(),
outputObjectsContainer.GetObjectsCount());
auto &valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsObject()) {
const gd::String& objectName = parameter.GetName();
const gd::String& objectType = parameter.GetExtraInfo();
allObjectNames.insert(objectName);
// Check if we can keep the existing object.
if (outputObjectsContainer.HasObjectNamed(objectName)) {
const gd::Object& object = outputObjectsContainer.GetObject(objectName);
if (object.GetType() != objectType) {
// Object type has changed, remove it so it is re-created.
outputObjectsContainer.RemoveObject(objectName);
}
}
if (outputObjectsContainer.HasObjectNamed(objectName)) {
// Keep the existing object, ensure the default behaviors
// are all present (and no more than required by the object type).
// Non default behaviors coming from parameters will be added or removed later.
project.EnsureObjectDefaultBehaviors(outputObjectsContainer.GetObject(objectName));
} else {
// Create a new object (and its default behaviors) if needed.
outputObjectsContainer.InsertNewObject(
project,
objectType,
objectName,
outputObjectsContainer.GetObjectsCount());
}
// Memorize the last object name. By convention, parameters that require
// an object (mainly, "objectvar" and "behavior") should be placed after
// the object in the list of parameters (if possible, just after).
// Search "lastObjectName" in the codebase for other place where this
// convention is enforced.
lastObjectName = parameter.GetName();
} else if (gd::ParameterMetadata::IsBehavior(parameter.GetType())) {
lastObjectName = objectName;
} else if (valueTypeMetadata.IsBehavior()) {
if (!lastObjectName.empty()) {
if (outputObjectsContainer.HasObjectNamed(lastObjectName)) {
const gd::Object& object =
outputObjectsContainer.GetObject(lastObjectName);
gd::String behaviorName = parameter.GetName();
const gd::String& behaviorName = parameter.GetName();
const gd::String& behaviorType = parameter.GetExtraInfo();
gd::Object& object = outputObjectsContainer.GetObject(lastObjectName);
allObjectNonDefaultBehaviorNames[lastObjectName].insert(behaviorName);
// Check if we can keep the existing behavior.
if (object.HasBehaviorNamed(behaviorName)) {
if (object.GetBehavior(behaviorName).GetTypeName() !=
behaviorType) {
// Behavior type has changed, remove it so it is re-created.
object.RemoveBehavior(behaviorName);
}
}
if (!object.HasBehaviorNamed(behaviorName)) {
outputObjectsContainer.GetObject(lastObjectName)
.AddNewBehavior(
project, parameter.GetExtraInfo(), behaviorName);
object.AddNewBehavior(
project, parameter.GetExtraInfo(), behaviorName);
}
}
}
}
}
// Remove objects that are not in the parameters anymore.
std::set<gd::String> objectNamesInContainer =
outputObjectsContainer.GetAllObjectNames();
for (const auto& objectName : objectNamesInContainer) {
if (allObjectNames.find(objectName) == allObjectNames.end()) {
outputObjectsContainer.RemoveObject(objectName);
}
}
// Remove behaviors of objects that are not in the parameters anymore.
for (const auto& objectName : allObjectNames) {
if (!outputObjectsContainer.HasObjectNamed(objectName)) {
// Should not happen.
continue;
}
auto& object = outputObjectsContainer.GetObject(objectName);
const auto& allBehaviorNames = allObjectNonDefaultBehaviorNames[objectName];
for (const auto& behaviorName : object.GetAllBehaviorNames()) {
if (object.GetBehavior(behaviorName).IsDefaultBehavior()) {
// Default behaviors are already ensured to be all present
// (and no more than required by the object type).
continue;
}
if (allBehaviorNames.find(behaviorName) == allBehaviorNames.end()) {
object.RemoveBehavior(behaviorName);
}
}
}
}
void ParameterMetadataTools::ForEachParameterMatchingSearch(

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 "GDCore/String.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
/**
* \brief Contains information about a source file that must be included
* when an extension is used.
*/
class GD_CORE_API SourceFileMetadata {
public:
/**
* Construct a new dependency metadata, though you probably want to call
* `AddSourceFile` on gd::PlatformExtension.
*
* \see gd::PlatformExtension
*/
SourceFileMetadata() {};
SourceFileMetadata& SetResourceName(const gd::String& resourceName_) {
resourceName = resourceName_;
return *this;
};
SourceFileMetadata& SetIncludePosition(const gd::String& includePosition_) {
includePosition = includePosition_;
return *this;
};
const gd::String& GetResourceName() const { return resourceName; };
gd::String& GetResourceName() { return resourceName; };
const gd::String& GetIncludePosition() const { return includePosition; };
void SerializeTo(SerializerElement& element) const {
element.AddChild("resourceName").SetStringValue(resourceName);
element.AddChild("includePosition").SetStringValue(includePosition);
}
void UnserializeFrom(const SerializerElement& element) {
resourceName = element.GetStringAttribute("resourceName");
includePosition = element.GetStringAttribute("includePosition", "last");
}
private:
gd::String resourceName; ///< The name of the resource in the project.
gd::String includePosition = "last"; ///< "first" or "last".
};
} // namespace gd

View File

@@ -79,6 +79,8 @@ const gd::String ValueTypeMetadata::colorValueType = "color";
const gd::String ValueTypeMetadata::choiceValueType = "stringWithSelector";
const gd::String ValueTypeMetadata::behaviorValueType = "behavior";
const gd::String ValueTypeMetadata::leaderboardIdValueType = "leaderboardId";
const gd::String ValueTypeMetadata::objectAnimationNameValueType = "objectAnimationName";
const gd::String ValueTypeMetadata::keyboardKeyValueType = "keyboardKey";
const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
const gd::String &propertyType) {
@@ -94,8 +96,12 @@ const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
return behaviorValueType;
} else if (propertyType == "LeaderboardId") {
return leaderboardIdValueType;
} else if (propertyType == "ObjectAnimationName") {
return objectAnimationNameValueType;
} else if (propertyType == "KeyboardKey") {
return keyboardKeyValueType;
}
// For "String" or default
// For "String", "Resource" or default
return stringValueType;
};

View File

@@ -129,13 +129,36 @@ class GD_CORE_API ValueTypeMetadata {
}
/**
* \brief Return true if the type of the parameter is a number.
* \brief Return true if the type of the parameter is a variable.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::GetPrimitiveValueType(name) == "variable";
return gd::ValueTypeMetadata::IsVariable(name);
}
/**
* \brief Return true if the type of the parameter is a variable.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsVariable(const gd::String &type) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(type) == "variable";
}
/**
* \brief Return true if the type of the parameter is a variable and not a
* property or a parameter.
*/
bool IsVariableOnly() const {
return
// Any variable.
name == "variable" ||
// Old, "pre-scoped" variables:
name == "objectvar" || name == "globalvar" ||
name == "scenevar";
}
/**
@@ -202,15 +225,20 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "keyboardKey" ||
parameterType == "mouseButton" ||
parameterType == "identifier";
} else if (type == "boolean") {
return parameterType == "yesorno" || parameterType == "trueorfalse";
} else if (type == "variable") {
return
parameterType == "variable" || // Any variable.
// Old, "pre-scoped" variables:
parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
// Any variable.
parameterType == "variable" ||
parameterType == "variableOrProperty" ||
parameterType == "variableOrPropertyOrParameter" ||
// Old, "pre-scoped" variables:
parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
} else if (type == "resource") {
return parameterType == "fontResource" ||
parameterType == "audioResource" ||
@@ -306,6 +334,8 @@ class GD_CORE_API ValueTypeMetadata {
static const gd::String choiceValueType;
static const gd::String behaviorValueType;
static const gd::String leaderboardIdValueType;
static const gd::String objectAnimationNameValueType;
static const gd::String keyboardKeyValueType;
};
} // namespace gd

View File

@@ -38,12 +38,14 @@ bool Platform::AddExtension(std::shared_ptr<gd::PlatformExtension> extension) {
extensionsLoaded.push_back(extension);
// Load all creation/destruction functions for objects provided by the
// extension
// Load all creation functions for objects provided by the
// extension.
vector<gd::String> objectsTypes = extension->GetExtensionObjectsTypes();
for (std::size_t i = 0; i < objectsTypes.size(); ++i) {
creationFunctionTable[objectsTypes[i]] =
extension->GetObjectCreationFunctionPtr(objectsTypes[i]);
CreateFunPtr createFunPtr = extension->GetObjectCreationFunctionPtr(objectsTypes[i]);
if (createFunPtr != nullptr) {
creationFunctionTable[objectsTypes[i]] = createFunPtr;
}
}
for (const auto& it :
@@ -62,7 +64,9 @@ void Platform::RemoveExtension(const gd::String& name) {
if (extension->GetName() == name) {
vector<gd::String> objectsTypes = extension->GetExtensionObjectsTypes();
for (std::size_t i = 0; i < objectsTypes.size(); ++i) {
creationFunctionTable.erase(objectsTypes[i]);
if (creationFunctionTable.find(objectsTypes[i]) != creationFunctionTable.end()) {
creationFunctionTable.erase(objectsTypes[i]);
}
}
}
}

View File

@@ -215,6 +215,11 @@ gd::DependencyMetadata& PlatformExtension::AddDependency() {
return extensionDependenciesMetadata.back();
}
gd::SourceFileMetadata& PlatformExtension::AddSourceFile() {
extensionSourceFilesMetadata.push_back(SourceFileMetadata());
return extensionSourceFilesMetadata.back();
}
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
const gd::String& fullname,
@@ -463,10 +468,22 @@ PlatformExtension::GetAllStrExpressions() {
return strExpressionsInfos;
}
const std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() const {
return extensionDependenciesMetadata;
}
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
return extensionDependenciesMetadata;
}
const std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() const {
return extensionSourceFilesMetadata;
}
std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() {
return extensionSourceFilesMetadata;
}
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
return eventsInfos;
}

View File

@@ -13,6 +13,7 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionOrExpressionGroupMetadata.h"
@@ -214,6 +215,7 @@ class GD_CORE_API PlatformExtension {
const gd::String& icon);
gd::DependencyMetadata& AddDependency();
gd::SourceFileMetadata& AddSourceFile();
/**
* \brief Declare a new object as being part of the extension.
@@ -552,6 +554,24 @@ class GD_CORE_API PlatformExtension {
*/
std::vector<gd::DependencyMetadata>& GetAllDependencies();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::DependencyMetadata>& GetAllDependencies() const;
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const;
/**
* \brief Return a reference to a map containing the names of the actions,
* related to the object type, and the metadata associated with.
@@ -687,6 +707,7 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
std::vector<gd::SourceFileMetadata> extensionSourceFilesMetadata;
std::map<gd::String, gd::EventMetadata> eventsInfos;
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
std::map<gd::String, InstructionOrExpressionGroupMetadata>

View File

@@ -12,7 +12,6 @@
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
const gd::Layout& layout_)
@@ -74,16 +73,6 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
}
}
// Search for source files dependencies
std::vector<gd::String> dependencies =
events[i].GetSourceFileDependencies();
sourceFilesDependencies.insert(dependencies.begin(), dependencies.end());
const gd::String& associatedSourceFile =
events[i].GetAssociatedGDManagedSourceFile(const_cast<gd::Project&>(project));
if (!associatedSourceFile.empty())
sourceFilesDependencies.insert(associatedSourceFile);
// Analyze sub events dependencies
if (events[i].CanHaveSubEvents()) {
if (!Analyze(events[i].GetSubEvents())) return false;

View File

@@ -71,14 +71,6 @@ class GD_CORE_API DependenciesAnalyzer {
return externalEventsDependencies;
};
/**
* \brief Return the source files being dependencies of the scene or external
* events passed in the constructor.
*/
const std::set<gd::String>& GetSourceFilesDependencies() const {
return sourceFilesDependencies;
};
private:
/**
* \brief Analyze the dependencies of the events.
@@ -92,7 +84,6 @@ class GD_CORE_API DependenciesAnalyzer {
std::set<gd::String> scenesDependencies;
std::set<gd::String> externalEventsDependencies;
std::set<gd::String> sourceFilesDependencies;
std::vector<gd::String>
parentScenes; ///< Used to check for circular dependencies.
std::vector<gd::String>

View File

@@ -0,0 +1,243 @@
/*
* 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/EventsParameterReplacer.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 parameters.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParameterReplacer
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterReplacer(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
bool isParentTypeAVariable_,
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_)
: hasDoneRenaming(false),
platform(platform_),
projectScopedContainers(projectScopedContainers_),
isParentTypeAVariable(isParentTypeAVariable_),
oldToNewPropertyNames(oldToNewPropertyNames_){};
virtual ~ExpressionParameterReplacer(){};
bool HasDoneRenaming() const { return hasDoneRenaming; }
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 {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
return;
}
// The node represents a variable or an object name on which a variable
// will be accessed, or a property with a child.
projectScopedContainers.MatchIdentifierWithName<void>(
// The property name is changed after the refactor operation.
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);
}, [&]() {
// Do nothing, it's a property.
if (node.child) node.child->Visit(*this);
}, [&]() {
// This is a parameter
RenameParameter(node.name);
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's something else.
if (node.child) node.child->Visit(*this);
});
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
isParentTypeAVariable = false;
node.expression->Visit(*this);
isParentTypeAVariable = isGrandParentTypeAVariable;
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
return;
}
projectScopedContainers.MatchIdentifierWithName<void>(
// The property name is changed after the refactor operation
node.identifierName,
[&]() {
// Do nothing, it's an object variable.
}, [&]() {
// Do nothing, it's a variable.
}, [&]() {
// Do nothing, it's a property.
}, [&]() {
// This is a parameter.
RenameParameter(node.identifierName);
}, [&]() {
// Do nothing, it's something else.
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
for (auto &parameter : node.parameters) {
const auto &parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform, projectScopedContainers.GetObjectsContainersList(),
node, *parameter);
if (!parameterMetadata) {
continue;
}
const auto &parameterTypeMetadata =
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsParameterReplacer::CanContainParameter(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly();
parameter->Visit(*this);
}
}
isParentTypeAVariable = isGrandParentTypeAVariable;
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
bool hasDoneRenaming;
bool RenameParameter(
gd::String& name) {
if (oldToNewPropertyNames.count(name) >= 1) {
name = oldToNewPropertyNames.find(name)->second;
hasDoneRenaming = true;
return true;
}
return false; // Nothing was changed or done.
}
// Scope:
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
// Renaming to do
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames;
gd::String objectNameToUseForVariableAccessor;
bool isParentTypeAVariable;
};
bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
if (!gd::EventsParameterReplacer::CanContainParameter(
parameterMetadata.GetValueTypeMetadata())) {
return;
}
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
parameterMetadata.GetValueTypeMetadata().IsVariableOnly(),
oldToNewPropertyNames);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {
instruction.SetParameter(
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
});
return false;
}
bool EventsParameterReplacer::DoVisitEventExpression(
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
if (!gd::EventsParameterReplacer::CanContainParameter(
metadata.GetValueTypeMetadata())) {
return false;
}
auto node = expression.GetRootNode();
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
return false;
}
bool EventsParameterReplacer::CanContainParameter(
const gd::ValueTypeMetadata &valueTypeMetadata) {
return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() ||
valueTypeMetadata.IsString();
}
EventsParameterReplacer::~EventsParameterReplacer() {}
} // namespace gd

View File

@@ -0,0 +1,52 @@
/*
* 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 parameter by another.
*
* \ingroup IDE
*/
class GD_CORE_API EventsParameterReplacer
: public ArbitraryEventsWorkerWithContext {
public:
EventsParameterReplacer(
const gd::Platform &platform_,
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames_)
: platform(platform_),
oldToNewPropertyNames(oldToNewPropertyNames_){};
virtual ~EventsParameterReplacer();
static bool CanContainParameter(const gd::ValueTypeMetadata &valueTypeMetadata);
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
bool DoVisitEventExpression(gd::Expression &expression,
const gd::ParameterMetadata &metadata) override;
const gd::Platform &platform;
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames;
};
} // namespace gd

View File

@@ -41,6 +41,7 @@ class GD_CORE_API ExpressionPropertyReplacer
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::PropertiesContainer& targetPropertiesContainer_,
bool isParentTypeAVariable_,
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_,
const std::unordered_set<gd::String>& removedPropertyNames_)
: hasDoneRenaming(false),
@@ -48,6 +49,7 @@ class GD_CORE_API ExpressionPropertyReplacer
platform(platform_),
projectScopedContainers(projectScopedContainers_),
targetPropertiesContainer(targetPropertiesContainer_),
isParentTypeAVariable(isParentTypeAVariable_),
oldToNewPropertyNames(oldToNewPropertyNames_),
removedPropertyNames(removedPropertyNames_){};
virtual ~ExpressionPropertyReplacer(){};
@@ -69,16 +71,21 @@ class GD_CORE_API ExpressionPropertyReplacer
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
return;
}
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),
// The property name is changed after the refactor operation.
node.name,
[&]() {
// Do nothing, it's an object variable.
if (node.child) node.child->Visit(*this);
@@ -100,16 +107,7 @@ class GD_CORE_API ExpressionPropertyReplacer
// 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);
}
// Do nothing, it's something else.
if (node.child) node.child->Visit(*this);
});
}
@@ -118,17 +116,24 @@ class GD_CORE_API ExpressionPropertyReplacer
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
isParentTypeAVariable = false;
node.expression->Visit(*this);
isParentTypeAVariable = isGrandParentTypeAVariable;
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
return;
}
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),
// The property name is changed after the refactor operation
node.identifierName,
[&]() {
// Do nothing, it's an object variable.
}, [&]() {
@@ -145,22 +150,29 @@ class GD_CORE_API ExpressionPropertyReplacer
}, [&]() {
// 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);
}
// Do nothing, it's something else.
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
for (auto &parameter : node.parameters) {
const auto &parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform, projectScopedContainers.GetObjectsContainersList(),
node, *parameter);
if (!parameterMetadata) {
continue;
}
const auto &parameterTypeMetadata =
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsPropertyReplacer::CanContainProperty(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly();
parameter->Visit(*this);
}
}
isParentTypeAVariable = isGrandParentTypeAVariable;
}
void OnVisitEmptyNode(EmptyNode& node) override {}
@@ -168,12 +180,6 @@ class GD_CORE_API ExpressionPropertyReplacer
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) {
@@ -198,6 +204,7 @@ class GD_CORE_API ExpressionPropertyReplacer
const std::unordered_set<gd::String>& removedPropertyNames;
gd::String objectNameToUseForVariableAccessor;
bool isParentTypeAVariable;
};
bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
@@ -216,20 +223,16 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
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.
if (!gd::EventsPropertyReplacer::CanContainProperty(
parameterMetadata.GetValueTypeMetadata())) {
return;
}
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
parameterMetadata.GetValueTypeMetadata().IsVariableOnly(),
oldToNewPropertyNames, removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
@@ -246,20 +249,16 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
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.
if (!gd::EventsPropertyReplacer::CanContainProperty(
metadata.GetValueTypeMetadata())) {
return false;
}
auto node = expression.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames,
removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
@@ -272,6 +271,12 @@ bool EventsPropertyReplacer::DoVisitEventExpression(
return false;
}
bool EventsPropertyReplacer::CanContainProperty(
const gd::ValueTypeMetadata &valueTypeMetadata) {
return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() ||
valueTypeMetadata.IsString();
}
EventsPropertyReplacer::~EventsPropertyReplacer() {}
} // namespace gd

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
@@ -41,6 +42,8 @@ class GD_CORE_API EventsPropertyReplacer
removedPropertyNames(removedPropertyNames_){};
virtual ~EventsPropertyReplacer();
static bool CanContainProperty(const gd::ValueTypeMetadata &valueTypeMetadata);
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;

View File

@@ -22,6 +22,7 @@
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
using namespace std;
@@ -51,17 +52,17 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
static bool Rename(const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::String &rootType,
gd::ExpressionNode& node,
const gd::String& objectName,
const gd::String& objectNewName) {
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) {
ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, objectName, objectNewName);
const gd::String &rootType, gd::ExpressionNode &node,
const gd::String &objectName,
const gd::String &objectNewName) {
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers,
rootType, node)) {
ExpressionObjectRenamer renamer(platform, projectScopedContainers,
rootType, objectName, objectNewName);
node.Visit(renamer);
return renamer.HasDoneRenaming();
}
return false;
}
@@ -83,7 +84,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
void OnVisitVariableNode(VariableNode& node) override {
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
if (gd::ValueTypeMetadata::IsVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.name == objectName) {
@@ -119,7 +120,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
node.identifierName == objectName) {
hasDoneRenaming = true;
node.identifierName = objectNewName;
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
} else if (gd::ValueTypeMetadata::IsVariable(type)) {
// Nothing to do (this can't reference an object)
} else {
if (node.identifierName == objectName) {
@@ -295,183 +296,114 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
const gd::String rootType;
};
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& actions,
gd::String oldName,
gd::String newName) {
bool somethingModified = false;
/**
* \brief Replace in expressions and in parameters of actions or conditions,
* references to the name of an object by another.
*
* \ingroup IDE
*/
class GD_CORE_API EventsObjectReplacer
: public ArbitraryEventsWorkerWithContext {
public:
EventsObjectReplacer(const gd::Platform &platform_,
const gd::ObjectsContainer &targetedObjectsContainer_,
const gd::String &oldObjectName_,
const gd::String &newObjectName_)
: platform(platform_),
targetedObjectsContainer(targetedObjectsContainer_),
oldObjectName(oldObjectName_), newObjectName(newObjectName_){};
for (std::size_t aId = 0; aId < actions.size(); ++aId) {
const gd::InstructionMetadata& instrInfos =
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
actions[aId].GetParameter(pNb).GetPlainString() == oldName)
actions[aId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
virtual ~EventsObjectReplacer() {}
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
actions[aId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override {
if (&targetedObjectsContainer !=
GetProjectScopedContainers()
.GetObjectsContainersList()
.GetObjectsContainerFromObjectName(oldObjectName)) {
return false;
}
const auto &metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
if (!actions[aId].GetSubInstructions().empty())
somethingModified =
RenameObjectInActions(platform,
projectScopedContainers,
actions[aId].GetSubInstructions(),
oldName,
newName) ||
somethingModified;
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(), metadata.GetParameters(),
[&](const gd::ParameterMetadata &parameterMetadata,
const gd::Expression &parameterValue, size_t parameterIndex,
const gd::String &lastObjectName) {
if (!gd::EventsObjectReplacer::CanContainObject(
parameterMetadata.GetValueTypeMetadata())) {
return;
}
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionObjectRenamer renamer(
platform, GetProjectScopedContainers(),
parameterMetadata.GetValueTypeMetadata().GetName(),
oldObjectName, newObjectName);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {
instruction.SetParameter(
parameterIndex,
ExpressionParser2NodePrinter::PrintNode(*node));
}
}
});
return false;
}
return somethingModified;
}
bool EventsRefactorer::RenameObjectInConditions(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& conditions,
gd::String oldName,
gd::String newName) {
bool somethingModified = false;
for (std::size_t cId = 0; cId < conditions.size(); ++cId) {
const gd::InstructionMetadata& instrInfos =
MetadataProvider::GetConditionMetadata(platform,
conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType()) &&
conditions[cId].GetParameter(pNb).GetPlainString() == oldName)
conditions[cId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters.GetParameter(pNb).GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters.GetParameter(pNb).GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
conditions[cId].SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
bool DoVisitEventExpression(gd::Expression &expression,
const gd::ParameterMetadata &metadata) override {
if (&targetedObjectsContainer !=
GetProjectScopedContainers()
.GetObjectsContainersList()
.GetObjectsContainerFromObjectName(oldObjectName)) {
return false;
}
if (!gd::EventsObjectReplacer::CanContainObject(
metadata.GetValueTypeMetadata())) {
return false;
}
if (!conditions[cId].GetSubInstructions().empty())
somethingModified =
RenameObjectInConditions(platform,
projectScopedContainers,
conditions[cId].GetSubInstructions(),
oldName,
newName) ||
somethingModified;
}
return somethingModified;
}
bool EventsRefactorer::RenameObjectInEventParameters(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
gd::String newName) {
bool somethingModified = false;
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()) &&
expression.GetPlainString() == oldName)
expression = gd::Expression(newName);
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number",
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
if (node) {
ExpressionObjectRenamer renamer(platform, GetProjectScopedContainers(),
metadata.GetValueTypeMetadata().GetName(),
oldObjectName, newObjectName);
node->Visit(renamer);
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
if (renamer.HasDoneRenaming()) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string",
parameterMetadata.GetType())) {
auto node = expression.GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
return false;
}
return somethingModified;
}
bool CanContainObject(const gd::ValueTypeMetadata &valueTypeMetadata) {
return valueTypeMetadata.IsObject() || valueTypeMetadata.IsVariable() ||
valueTypeMetadata.IsNumber() || valueTypeMetadata.IsString();
}
const gd::Platform &platform;
const gd::ObjectsContainer &targetedObjectsContainer;
const gd::String &oldObjectName;
const gd::String &newObjectName;
};
void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
const gd::ObjectsContainer &targetedObjectsContainer,
gd::String oldName,
gd::String newName) {
for (std::size_t i = 0; i < events.size(); ++i) {
vector<gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
bool somethingModified = RenameObjectInConditions(
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, projectScopedContainers, *actionsVectors[j], oldName, newName);
}
vector<pair<gd::Expression*, gd::ParameterMetadata>>
expressionsWithMetadata = events[i].GetAllExpressionsWithMetadata();
for (std::size_t j = 0; j < expressionsWithMetadata.size(); ++j) {
gd::Expression* expression = expressionsWithMetadata[j].first;
gd::ParameterMetadata parameterMetadata =
expressionsWithMetadata[j].second;
bool somethingModified = RenameObjectInEventParameters(platform,
projectScopedContainers,
*expression,
parameterMetadata,
oldName,
newName);
}
if (events[i].CanHaveSubEvents())
RenameObjectInEvents(platform,
projectScopedContainers,
events[i].GetSubEvents(),
oldName,
newName);
}
gd::EventsObjectReplacer eventsParameterReplacer(platform, targetedObjectsContainer, oldName, newName);
eventsParameterReplacer.Launch(events, projectScopedContainers);
}
bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,

View File

@@ -83,6 +83,7 @@ class GD_CORE_API EventsRefactorer {
static void RenameObjectInEvents(const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
const gd::ObjectsContainer &targetedObjectsContainer,
gd::String oldName,
gd::String newName);
@@ -121,44 +122,6 @@ class GD_CORE_API EventsRefactorer {
virtual ~EventsRefactorer(){};
private:
/**
* Replace all occurrences of an object name by another name in an action
* ( include : objects in parameters and in math/text expressions ).
*
* \return true if something was modified.
*/
static bool RenameObjectInActions(const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& instructions,
gd::String oldName,
gd::String newName);
/**
* Replace all occurrences of an object name by another name in a condition
* ( include : objects in parameters and in math/text expressions ).
*
* \return true if something was modified.
*/
static bool RenameObjectInConditions(const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::InstructionsList& instructions,
gd::String oldName,
gd::String newName);
/**
* Replace all occurrences of an object name by another name in an expression
* with the specified metadata
* ( include : objects or objects in math/text expressions ).
*
* \return true if something was modified.
*/
static bool RenameObjectInEventParameters(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::Expression& expression,
gd::ParameterMetadata parameterMetadata,
gd::String oldName,
gd::String newName);
/**
* Remove all conditions of the list using an object
*

View File

@@ -71,13 +71,18 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(lastObjectName);
}
} else if (type == "variableOrProperty") {
variablesContainer =
&GetProjectScopedContainers()
.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyName(variableName);
} else {
if (GetProjectScopedContainers().GetVariablesContainersList().Has(
variableName)) {
variablesContainer =
&GetProjectScopedContainers()
.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName);
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName);
}
}

View File

@@ -122,7 +122,7 @@ class GD_CORE_API ExpressionVariableReplacer
[&]() {
// This is a variable.
if (&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(node.name) ==
.GetVariablesContainerFromVariableOrPropertyOrParameterName(node.name) ==
&targetVariablesContainer) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
@@ -235,7 +235,7 @@ class GD_CORE_API ExpressionVariableReplacer
[&]() {
// This is a variable.
if (&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(
.GetVariablesContainerFromVariableOrPropertyOrParameterName(
node.identifierName) == &targetVariablesContainer) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:

View File

@@ -1034,7 +1034,7 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName)
.GetSourceType());
completions.push_back(description);
@@ -1086,7 +1086,7 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName)
.GetSourceType());
completions.push_back(description);

View File

@@ -68,19 +68,26 @@ size_t GetMaximumParametersNumber(
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier) {
return ValidateObjectVariableOrVariableOrProperty(identifier.identifierName, identifier.identifierNameLocation, identifier.childIdentifierName, identifier.childIdentifierNameLocation);
}
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::String &identifierName,
const gd::ExpressionParserLocation identifierNameLocation,
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation) {
auto validateVariableTypeForExpression =
[this, &identifier](gd::Variable::Type type) {
[this, &identifierNameLocation](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);
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);
identifierNameLocation);
} else {
// Number, string or boolean variables can be used in expressions.
return;
@@ -96,38 +103,41 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// we consider this node will be of the type required by the parent.
childType = parentType;
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
return projectScopedContainers.MatchIdentifierWithName<bool>(identifierName,
[&]() {
// This represents an object.
if (identifier.childIdentifierName.empty()) {
if (childIdentifierName.empty()) {
RaiseTypeError(_("An object variable or expression should be entered."),
identifier.identifierNameLocation);
identifierNameLocation);
return true; // We should have found a variable.
}
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
auto variableExistence =
objectsContainersList.HasObjectOrGroupWithVariableNamed(
identifierName, childIdentifierName);
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
RaiseUndeclaredVariableError(_("This variable does not exist on this object or group."),
identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
childIdentifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseUndeclaredVariableError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
childIdentifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
RaiseUndeclaredVariableError(_("This group is empty. Add an object to this group first."),
identifier.identifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
identifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(
identifierName, childIdentifierName);
ReadChildTypeFromVariable(variableType);
return true; // We found a variable.
@@ -137,9 +147,9 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// Try to identify a declared variable with the name (and maybe the child
// variable).
const gd::Variable& variable =
variablesContainersList.Get(identifier.identifierName);
variablesContainersList.Get(identifierName);
if (identifier.childIdentifierName.empty()) {
if (childIdentifierName.empty()) {
// Just the root variable is accessed, check it can be used in an
// expression.
validateVariableTypeForExpression(variable.GetType());
@@ -148,29 +158,29 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
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)) {
if (!variable.HasChild(childIdentifierName)) {
RaiseTypeError(_("No child variable with this name found."),
identifier.childIdentifierNameLocation);
childIdentifierNameLocation);
return true; // We should have found a variable.
}
const gd::Variable& childVariable =
variable.GetChild(identifier.childIdentifierName);
variable.GetChild(childIdentifierName);
ReadChildTypeFromVariable(childVariable.GetType());
return true; // We found a variable.
}
}, [&]() {
// This is a property.
if (!identifier.childIdentifierName.empty()) {
if (!childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
identifier.childIdentifierNameLocation);
childIdentifierNameLocation);
return true; // We found a property, even if the child is not allowed.
}
const gd::NamedPropertyDescriptor &property =
propertiesContainersList.Get(identifier.identifierName).second;
propertiesContainersList.Get(identifierName).second;
if (property.GetType() == "Number") {
childType = Type::Number;
@@ -179,7 +189,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// or as a number)
} else if (property.GetType() == "Behavior") {
RaiseTypeError(_("Behaviors can't be used as a value in expressions."),
identifier.identifierNameLocation);
identifierNameLocation);
} else {
// Assume type is String or equivalent.
childType = Type::String;
@@ -188,14 +198,14 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
return true; // We found a property.
}, [&]() {
// This is a parameter.
if (!identifier.childIdentifierName.empty()) {
if (!childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
identifier.childIdentifierNameLocation);
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& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifierName);
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
childType = Type::Number;
@@ -205,7 +215,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// 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);
identifierNameLocation);
return true; // We found a parameter, even though the type is incompatible.
}
@@ -450,10 +460,12 @@ const gd::String& ExpressionValidator::TypeToString(Type type) {
case Type::NumberOrString:
return numberOrStringTypeString;
case Type::Variable:
return variableTypeString;
// This function is only used to display errors.
// Users don't care if it's legacy or not or
// if it allows properties and parameters.
case Type::VariableOrProperty:
case Type::VariableOrPropertyOrParameter:
case Type::LegacyVariable:
// This function is only used to display error.
// Users don't care if it's legacy or not.
return variableTypeString;
case Type::Object:
return objectTypeString;
@@ -483,8 +495,11 @@ ExpressionValidator::Type ExpressionValidator::StringToType(
ExpressionValidator::variableTypeString, type)) {
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
return Type::LegacyVariable;
}
else {
} else if (type == "variableOrProperty") {
return Type::VariableOrProperty;
} else if (type == "variableOrPropertyOrParameter") {
return Type::VariableOrPropertyOrParameter;
} else {
return Type::Variable;
}
}

View File

@@ -14,6 +14,7 @@
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/VariablesContainersList.h"
#include "GDCore/Project/VariablesContainer.h"
namespace gd {
class Expression;
@@ -45,7 +46,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false),
currentParameterExtraInfo(&extraInfo_) {};
currentParameterExtraInfo(&extraInfo_),
variableObjectName(),
variableObjectNameLocation() {};
virtual ~ExpressionValidator(){};
/**
@@ -200,10 +203,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitVariableNode(VariableNode& node) override {
ReportAnyError(node);
if (parentType == Type::Variable) {
if (parentType == Type::Variable ||
parentType == Type::VariableOrProperty ||
parentType == Type::VariableOrPropertyOrParameter) {
childType = parentType;
CheckVariableExistence(node.location, node.name);
CheckVariableExistence(node.location, node.name, node.child != nullptr);
if (node.child) {
node.child->Visit(*this);
}
@@ -213,7 +218,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
if (node.child) {
node.child->Visit(*this);
}
} else if (parentType == Type::String || parentType == Type::Number || parentType == Type::NumberOrString) {
} 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;
@@ -225,7 +231,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
[&]() {
// This represents an object.
variableObjectName = node.name;
variableObjectNameLocation = node.nameLocation;
// While understood by the parser, it's forbidden to use the bracket notation just after
// an object name (`MyObject["MyVariable"]`).
forbidsUsageOfBracketsBecauseParentIsObject = true;
@@ -264,7 +271,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
ReportAnyError(node);
// TODO Also check child-variables existence on a path with only VariableAccessor to raise non-fatal errors.
if (!variableObjectName.empty()) {
ValidateObjectVariableOrVariableOrProperty(variableObjectName,
variableObjectNameLocation,
node.name, node.nameLocation);
variableObjectName = "";
}
// In the case we accessed an object variable (`MyObject.MyVariable`),
// brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
forbidsUsageOfBracketsBecauseParentIsObject = false;
@@ -277,6 +290,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
VariableBracketAccessorNode& node) override {
ReportAnyError(node);
variableObjectName = "";
if (forbidsUsageOfBracketsBecauseParentIsObject) {
RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
_("You can't use the brackets to access an object variable. "
@@ -325,11 +339,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
_("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::Variable) {
CheckVariableExistence(node.location, node.identifierName);
}
else if (parentType != Type::Object && parentType != Type::LegacyVariable) {
} else if (parentType == Type::Variable ||
parentType == Type::VariableOrProperty ||
parentType == Type::VariableOrPropertyOrParameter) {
CheckVariableExistence(node.location, node.identifierName, !node.childIdentifierName.empty());
} else if (parentType != Type::Object &&
parentType != Type::LegacyVariable) {
// It can't happen.
RaiseTypeError(
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
@@ -365,13 +380,31 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
childType = Type::Empty;
}
private:
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, LegacyVariable, Object, Empty};
private:
enum Type {
Unknown = 0,
Number,
String,
NumberOrString,
Variable,
LegacyVariable,
Object,
Empty,
VariableOrProperty,
VariableOrPropertyOrParameter
};
Type ValidateFunction(const gd::FunctionCallNode& function);
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
bool ValidateObjectVariableOrVariableOrProperty(
const gd::String &identifierName,
const gd::ExpressionParserLocation identifierNameLocation,
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation);
void CheckVariableExistence(const ExpressionParserLocation &location, const gd::String& name) {
if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") {
void CheckVariableExistence(const ExpressionParserLocation &location,
const gd::String &name, bool hasChild) {
if (!currentParameterExtraInfo ||
*currentParameterExtraInfo != "AllowUndeclaredVariable") {
projectScopedContainers.MatchIdentifierWithName<void>(
name,
[&]() {
@@ -386,19 +419,28 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
},
[&]() {
// This is a property.
// This error won't happen unless the priority is changed.
RaiseVariableNameCollisionError(
_("This variable has the same name as a property. Consider "
"renaming one or the other."),
location, name);
if (parentType != Type::VariableOrProperty &&
parentType != Type::VariableOrPropertyOrParameter) {
RaiseVariableNameCollisionError(
_("This variable has the same name as a property. Consider "
"renaming one or the other."),
location, name);
} else if (hasChild) {
RaiseMalformedVariableParameter(
_("Properties can't have children."), location, name);
}
},
[&]() {
// This is a parameter.
// This error won't happen unless the priority is changed.
RaiseVariableNameCollisionError(
_("This variable has the same name as a parameter. Consider "
"renaming one or the other."),
location, name);
if (parentType != Type::VariableOrPropertyOrParameter) {
RaiseVariableNameCollisionError(
_("This variable has the same name as a parameter. Consider "
"renaming one or the other."),
location, name);
} else if (hasChild) {
RaiseMalformedVariableParameter(
_("Properties can't have children."), location, name);
}
},
[&]() {
// This is something else.
@@ -460,6 +502,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
message, location, false, variableName, objectName);
}
void RaiseMalformedVariableParameter(const gd::String &message,
const ExpressionParserLocation &location,
const gd::String &variableName) {
RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
message, location, true, variableName, "");
}
void RaiseTypeError(const gd::String &message,
const ExpressionParserLocation &location,
bool isFatal = true) {
@@ -505,6 +554,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
Type childType; ///< The type "discovered" down the tree and passed up.
Type parentType; ///< The type "required" by the top of the tree.
bool forbidsUsageOfBracketsBecauseParentIsObject;
gd::String variableObjectName;
gd::ExpressionParserLocation variableObjectNameLocation;
const gd::String *currentParameterExtraInfo;
const gd::Platform &platform;
const gd::ProjectScopedContainers &projectScopedContainers;

View File

@@ -215,7 +215,7 @@ class GD_CORE_API ExpressionVariablePathFinder
if (projectScopedContainers.GetVariablesContainersList().Has(identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(identifier));
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
@@ -223,12 +223,28 @@ class GD_CORE_API ExpressionVariablePathFinder
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
// This is a property.
if (parameterType != "objectvar" &&
projectScopedContainers.GetVariablesContainersList().Has(
identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
// There is no support for "children" of properties.
}
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
// This is a parameter.
if (parameterType != "objectvar" &&
projectScopedContainers.GetVariablesContainersList().Has(
identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
// There is no support for "children" of parameters.
}
},
[&]() {
// Ignore unrecognised identifiers here.

View File

@@ -13,6 +13,18 @@
namespace gd {
void UsedExtensionsResult::AddUsedExtension(const gd::PlatformExtension& extension) {
usedExtensions.insert(extension.GetName());
usedSourceFiles.insert(usedSourceFiles.end(),
extension.GetAllSourceFiles().begin(),
extension.GetAllSourceFiles().end());
}
void UsedExtensionsResult::AddUsedBuiltinExtension(const gd::String& extensionName) {
usedExtensions.insert(extensionName);
}
const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
@@ -28,9 +40,9 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
if (metadata.GetMetadata().IsRenderedIn3D()) {
result.MarkAsHaving3DObjects();
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};
@@ -39,12 +51,12 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
for (auto &&includeFile : metadata.GetMetadata().requiredFiles) {
result.GetUsedRequiredFiles().insert(includeFile);
result.AddUsedRequiredFiles(includeFile);
}
};
@@ -57,9 +69,9 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
gd::ParameterMetadataTools::IterateOverParameters(
@@ -77,7 +89,7 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
rootType = "number";
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
});
return false;
@@ -110,7 +122,7 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
@@ -123,9 +135,9 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
// This represents an object.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.name);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}, [&]() {
// This is a variable.
@@ -143,13 +155,13 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
@@ -163,9 +175,9 @@ void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
// An object or object variable is used.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}
};
@@ -187,9 +199,9 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};

View File

@@ -9,6 +9,8 @@
#include <set>
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/String.h"
@@ -44,6 +46,10 @@ public:
return usedRequiredFiles;
}
const std::vector<gd::SourceFileMetadata>& GetUsedSourceFiles() const {
return usedSourceFiles;
}
/**
* \brief Return true when at least 1 object uses the 3D renderer.
*/
@@ -51,20 +57,10 @@ public:
return has3DObjects;
}
/**
* The extensions used by the project (or part of it).
*/
std::set<gd::String> &GetUsedExtensions() { return usedExtensions; }
/**
* The include files used at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedIncludeFiles() { return usedIncludeFiles; }
/**
* The additional files required at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
void AddUsedExtension(const gd::PlatformExtension& extension);
void AddUsedBuiltinExtension(const gd::String& extensionName);
void AddUsedIncludeFiles(const gd::String& includeFile) { usedIncludeFiles.insert(includeFile); }
void AddUsedRequiredFiles(const gd::String& requiredFile) { usedRequiredFiles.insert(requiredFile); }
void MarkAsHaving3DObjects() {
has3DObjects = true;
@@ -74,6 +70,7 @@ private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
std::vector<gd::SourceFileMetadata> usedSourceFiles;
bool has3DObjects = false;
};

View File

@@ -9,7 +9,10 @@
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
//#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ParameterMetadataContainer.h"
#include "GDCore/Project/PropertiesContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
@@ -24,15 +27,16 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputObjectsContainer) {
// Functions scope for objects is defined according
// to parameters
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.GetObjectGroups().Clear();
// to parameters.
auto &parameters = eventsFunction.GetParametersForEvents(functionContainer);
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project,
parameters,
outputObjectsContainer);
// TODO: in theory we should ensure stability of the groups across calls
// to this function. BUT groups in functions should probably have never been
// supported, so we're phasing this out in the UI.
outputObjectsContainer.GetObjectGroups() = eventsFunction.GetObjectGroups();
}
@@ -97,25 +101,73 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
"for the parent. ");
return;
}
gd::EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
eventsBasedObject, outputObjectsContainer);
}
void EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
const gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& outputObjectsContainer) {
auto &children = eventsBasedObject.GetObjects().GetObjects();
for (auto &childObject : children) {
auto child = childObject.get();
outputObjectsContainer.InsertObject(
*child, outputObjectsContainer.GetObjectsCount());
void EventsFunctionTools::ParametersToVariablesContainer(
const ParameterMetadataContainer &parameters,
gd::VariablesContainer &outputVariablesContainer) {
if (outputVariablesContainer.GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters) {
throw std::logic_error("Tried to generate a variables container from "
"parameters with the wrong source type.");
}
auto &childrenGroups = eventsBasedObject.GetObjects().GetObjectGroups();
for (size_t index = 0; index < childrenGroups.Count(); ++index) {
auto &childGroup = childrenGroups.Get(index);
outputObjectsContainer.GetObjectGroups().Insert(
childGroup, outputObjectsContainer.GetObjectGroups().Count());
outputVariablesContainer.Clear();
gd::String lastObjectName;
for (std::size_t i = 0; i < parameters.GetParametersCount(); ++i) {
const auto &parameter = parameters.GetParameter(i);
if (parameter.GetName().empty())
continue;
auto &valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetValue(0);
} else if (valueTypeMetadata.IsString()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetString("");
} else if (valueTypeMetadata.IsBoolean()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetBool(false);
}
}
}
void EventsFunctionTools::PropertiesToVariablesContainer(
const PropertiesContainer &properties,
gd::VariablesContainer &outputVariablesContainer) {
if (outputVariablesContainer.GetSourceType() !=
gd::VariablesContainer::SourceType::Properties) {
throw std::logic_error("Tried to generate a variables container from "
"properties with the wrong source type.");
}
outputVariablesContainer.Clear();
gd::String lastObjectName;
for (std::size_t i = 0; i < properties.GetCount(); ++i) {
const auto &property = properties.Get(i);
if (property.GetName().empty())
continue;
auto &propertyType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
if (propertyType == "number") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetValue(0);
} else if (propertyType == "string") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetString("");
} else if (propertyType == "boolean") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetBool(false);
}
}
}

View File

@@ -12,6 +12,9 @@ namespace gd {
class Project;
class EventsFunctionsContainer;
class ObjectsContainer;
class ParameterMetadataContainer;
class PropertiesContainer;
class VariablesContainer;
class ParameterMetadata;
class EventsFunction;
class EventsBasedBehavior;
@@ -69,8 +72,12 @@ class GD_CORE_API EventsFunctionTools {
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputObjectsContainer);
static void CopyEventsBasedObjectChildrenToObjectsContainer(
const gd::EventsBasedObject &eventsBasedObject,
gd::ObjectsContainer &outputObjectsContainer);
static void ParametersToVariablesContainer(
const ParameterMetadataContainer &parameters,
gd::VariablesContainer &outputVariablesContainer);
static void PropertiesToVariablesContainer(
const PropertiesContainer &properties,
gd::VariablesContainer &outputVariablesContainer);
};
} // namespace gd

View File

@@ -62,6 +62,11 @@ void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
// do.
};
void ArbitraryResourceWorker::ExposeJavaScript(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.
@@ -195,6 +200,10 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
ExposeSpine(resourceName);
return;
}
if (resourceType == "javascript") {
ExposeJavaScript(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}

View File

@@ -96,7 +96,7 @@ public:
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);
/**
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
*/
@@ -112,6 +112,11 @@ public:
*/
virtual void ExposeVideo(gd::String &videoName);
/**
* \brief Expose a JavaScript file, which is always a reference to a "javascript" resource.
*/
virtual void ExposeJavaScript(gd::String &javaScriptName);
/**
* \brief Expose a bitmap font, which is always a reference to a "bitmapFont" resource.
*/

View File

@@ -38,6 +38,10 @@ void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) {
ExposeResourceAsFile(videoName);
}
void AssetResourcePathCleaner::ExposeJavaScript(gd::String &javaScriptResourceName) {
ExposeResourceAsFile(javaScriptResourceName);
}
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) {
ExposeResourceAsFile(bitmapFontName);
}

View File

@@ -46,6 +46,7 @@ public:
void ExposeTileset(gd::String &tilesetName) override;
void ExposeVideo(gd::String &videoName) override;
void ExposeBitmapFont(gd::String &bitmapFontName) override;
void ExposeJavaScript(gd::String &javaScriptResourceName) override;
void ExposeFile(gd::String &resource) override;
protected:

View File

@@ -73,6 +73,9 @@ public:
virtual void ExposeVideo(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeJavaScript(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};

View File

@@ -60,6 +60,7 @@ public:
if (resourceType == "model3D") return allModel3Ds;
if (resourceType == "atlas") return allAtlases;
if (resourceType == "spine") return allSpines;
if (resourceType == "javascript") return allJavaScripts;
return emptyResources;
};
@@ -88,6 +89,9 @@ public:
virtual void ExposeVideo(gd::String& resourceName) override {
allVideos.insert(resourceName);
};
virtual void ExposeJavaScript(gd::String& resourceName) override {
allJavaScripts.insert(resourceName);
};
virtual void ExposeBitmapFont(gd::String& resourceName) override {
allBitmapFonts.insert(resourceName);
};
@@ -114,6 +118,7 @@ public:
std::set<gd::String> allModel3Ds;
std::set<gd::String> allAtlases;
std::set<gd::String> allSpines;
std::set<gd::String> allJavaScripts;
std::set<gd::String> emptyResources;
static const std::vector<gd::String> resourceTypes;

View File

@@ -59,6 +59,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeVideo(gd::String& videoResourceName) override {
RenameIfNeeded(videoResourceName);
};
virtual void ExposeJavaScript(gd::String& javaScriptResourceName) override {
RenameIfNeeded(javaScriptResourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontName) override {
RenameIfNeeded(bitmapFontName);
};

View File

@@ -74,6 +74,9 @@ private:
void ExposeVideo(gd::String &videoResourceName) override {
AddUsedResource(videoResourceName);
};
void ExposeJavaScript(gd::String &javaScriptResourceName) override {
AddUsedResource(javaScriptResourceName);
};
void ExposeBitmapFont(gd::String &bitmapFontName) override {
AddUsedResource(bitmapFontName);
};

View File

@@ -26,8 +26,8 @@ namespace gd {
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorker &worker) {
// See also gd::Project::ExposeResources for a method that traverses the whole
// project (this time for resources).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources).
ExposeProjectEventsWithoutExtensions(project, worker);
@@ -106,16 +106,16 @@ void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
worker.Launch(dependencyLayout.GetEvents());
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and ExposeProjectEffects (this time for
// effects).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources)
// and ExposeProjectEffects (this time for effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
@@ -147,7 +147,8 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorker &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
@@ -169,12 +170,18 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorkerWithContext &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainer;
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, *eventsFunction,
parameterObjectsContainer);
parameterObjectsContainer, parameterVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -206,14 +213,32 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker) {
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension,
eventsBasedBehavior,
propertyVariablesContainer,
worker);
}
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker) {
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
for (auto &&eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project, eventsFunctionsExtension, eventsBasedBehavior,
*eventsFunction, parameterObjectsContainers);
*eventsFunction, parameterObjectsContainers,
parameterVariablesContainer, propertyVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -233,14 +258,31 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker) {
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject,
propertyVariablesContainer, worker);
}
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker) {
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsFunctionsExtension, eventsBasedObject,
*eventsFunction, parameterObjectsContainers);
*eventsFunction, parameterObjectsContainers,
parameterVariablesContainer, propertyVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -284,7 +326,7 @@ void ProjectBrowserHelper::ExposeProjectFunctions(
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
worker.Launch(eventsFunctionsExtension);
worker.Launch(eventsFunctionsExtension.GetEventsFunctions());
for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()

View File

@@ -19,6 +19,7 @@ class ArbitraryEventsFunctionsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
class VariablesContainer;
} // namespace gd
namespace gd {
@@ -127,6 +128,20 @@ public:
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* behavior.
*
* This should be the preferred way to traverse all the events of an
* event-based behavior.
*/
static void ExposeEventsBasedBehaviorEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* object.
@@ -152,6 +167,20 @@ public:
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an
* event-based object.
*/
static void ExposeEventsBasedObjectEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all ObjectContainers of the project
* (global, layouts...)

View File

@@ -63,7 +63,7 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project &project) {
eventsBasedObject.GetPropertyDescriptors().GetInternalVector().clear();
}
extension.GetEventsBasedBehaviors().Clear();
extension.ClearEventsFunctions();
extension.GetEventsFunctions().ClearEventsFunctions();
}
}

View File

@@ -245,9 +245,11 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
bool PropertyFunctionGenerator::CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property) {
auto &type = property.GetType();
if (type != "Boolean" && type != "Number" && type != "String" &&
type != "Choice" && type != "Color" && type != "LeaderboardId") {
const auto &primitiveType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
if (primitiveType != "boolean" && primitiveType != "number" &&
primitiveType != "string") {
return false;
}

View File

@@ -5,21 +5,22 @@
*/
#include "ResourceExposer.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/String.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
namespace gd {
void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeWholeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// See also gd::ProjectBrowserHelper::ExposeProjectEvents for a method that
// traverse the whole project (this time for events) and ExposeProjectEffects
// (this time for effects).
@@ -31,13 +32,11 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeProjectEvents(
project, eventWorker);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventWorker);
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeProjectObjects(
project, objectWorker);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, objectWorker);
// Expose layer effect resources
for (std::size_t layoutIndex = 0; layoutIndex < project.GetLayoutsCount();
@@ -52,28 +51,36 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
}
// Expose loading screen background image if present
auto& loadingScreen = project.GetLoadingScreen();
auto &loadingScreen = project.GetLoadingScreen();
if (loadingScreen.GetBackgroundImageResourceName() != "")
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
// Expose extension source files
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ExposeExtensionResources(eventsFunctionsExtension, worker);
}
}
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// Expose global objects configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
objectWorker.Launch(project.GetObjects());
}
void ResourceExposer::ExposeLayoutResources(
gd::Project &project, gd::Layout &layout,
gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker) {
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
@@ -87,15 +94,15 @@ void ResourceExposer::ExposeLayoutResources(
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
eventWorker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
project, layout, eventWorker);
// Exposed extension event resources
// Note that using resources in extensions is very unlikely and probably not
@@ -103,12 +110,14 @@ void ResourceExposer::ExposeLayoutResources(
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
project, eventsFunctionsExtension, eventWorker);
}
}
void ResourceExposer::ExposeEffectResources(
gd::Platform &platform, gd::Effect &effect,
gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker) {
auto &effectMetadata =
MetadataProvider::GetEffectMetadata(platform, effect.GetEffectType());
@@ -127,11 +136,20 @@ void ResourceExposer::ExposeEffectResources(
worker.ExposeResourceWithType(resourceType,
potentiallyUpdatedResourceName);
if (potentiallyUpdatedResourceName != resourceName) {
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
effect.SetStringParameter(propertyName,
potentiallyUpdatedResourceName);
}
}
}
}
}
} // namespace gd
void ResourceExposer::ExposeExtensionResources(
gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker) {
for (auto &sourceFile : extension.GetAllSourceFiles()) {
worker.ExposeJavaScript(sourceFile.GetResourceName());
}
}
} // namespace gd

View File

@@ -9,9 +9,10 @@ namespace gd {
class Platform;
class Project;
class ArbitraryResourceWorker;
class EventsFunctionsExtension;
class Effect;
class Layout;
} // namespace gd
} // namespace gd
namespace gd {
@@ -19,7 +20,7 @@ namespace gd {
* \brief
*/
class GD_CORE_API ResourceExposer {
public:
public:
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources, sometimes update their filename or any other work or resources.
@@ -34,7 +35,7 @@ public:
/**
* @brief Expose only the resources used globally on a project.
*
*
* It doesn't include resources used in layouts.
*/
static void ExposeProjectResources(gd::Project &project,
@@ -42,17 +43,25 @@ public:
/**
* @brief Expose the resources used in a given layout.
*
*
* It doesn't include resources used globally.
*/
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
static void ExposeLayoutResources(gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in a given effect.
*/
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
static void ExposeEffectResources(gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in an extension.
*/
static void ExposeExtensionResources(gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker);
};
} // namespace gd
} // namespace gd

View File

@@ -19,6 +19,7 @@
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
#include "GDCore/IDE/Events/CustomObjectTypeRenamer.h"
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
#include "GDCore/IDE/Events/EventsParameterReplacer.h"
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h"
@@ -599,7 +600,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// instructions after they are renamed.
// Free expressions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
if (eventsFunction->IsExpression()) {
renameEventsFunction(*eventsFunction);
}
@@ -616,7 +618,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
}
// Free instructions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameEventsFunction(*eventsFunction);
}
@@ -696,11 +699,12 @@ void WholeProjectRefactorer::RenameEventsFunction(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &oldFunctionName, const gd::String &newFunctionName) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(oldFunctionName))
const auto &eventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(oldFunctionName))
return;
const gd::EventsFunction &eventsFunction =
eventsFunctionsExtension.GetEventsFunction(oldFunctionName);
eventsFunctions.GetEventsFunction(oldFunctionName);
const WholeProjectBrowser wholeProjectExposer;
DoRenameEventsFunction(
@@ -713,7 +717,7 @@ void WholeProjectRefactorer::RenameEventsFunction(
if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition) {
for (auto &&otherFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&otherFunction : eventsFunctions.GetInternalVector()) {
if (otherFunction->GetFunctionType() ==
gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
@@ -816,16 +820,79 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
}
}
void WholeProjectRefactorer::RenameParameter(
gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &oldParameterName, const gd::String &newParameterName) {
auto &parameters = eventsFunction.GetParameters();
if (!parameters.HasParameterNamed(oldParameterName))
return;
auto &parameter = parameters.GetParameter(oldParameterName);
if (parameter.GetValueTypeMetadata().IsObject()) {
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, eventsFunction,
parameterObjectsContainer, oldParameterName, newParameterName, false);
} else if (parameter.GetValueTypeMetadata().IsBehavior()) {
size_t behaviorParameterIndex = parameters.GetParameterPosition(parameter);
size_t objectParameterIndex =
gd::ParameterMetadataTools::GetObjectParameterIndexFor(
parameters, behaviorParameterIndex);
if (objectParameterIndex == gd::String::npos) {
return;
}
const gd::String &objectName =
parameters.GetParameter(objectParameterIndex).GetName();
gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(),
objectName, oldParameterName,
newParameterName);
behaviorRenamer.Launch(eventsFunction.GetEvents(), projectScopedContainers);
} else {
// Rename parameter names directly used as an identifier.
std::unordered_map<gd::String, gd::String> oldToNewParameterNames = {
{oldParameterName, newParameterName}};
gd::EventsParameterReplacer eventsParameterReplacer(
project.GetCurrentPlatform(), oldToNewParameterNames);
eventsParameterReplacer.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
// Rename parameter names in legacy expressions and instructions
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "functionParameterName", oldParameterName,
newParameterName);
projectElementRenamer.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
}
}
void WholeProjectRefactorer::ChangeParameterType(
gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &parameterName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(parameterName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
eventsVariableInstructionTypeSwitcher.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &functionName, std::size_t oldIndex,
std::size_t newIndex) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(functionName))
const auto &eventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(functionName))
return;
const gd::EventsFunction &eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
eventsFunctions.GetEventsFunction(functionName);
const gd::String &eventsFunctionType =
gd::PlatformExtension::GetEventsFunctionFullType(
@@ -1129,6 +1196,42 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
}
void WholeProjectRefactorer::ChangeEventsBasedBehaviorPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &propertyName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(propertyName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension, eventsBasedBehavior,
propertyVariablesContainer, eventsVariableInstructionTypeSwitcher);
}
void WholeProjectRefactorer::ChangeEventsBasedObjectPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &propertyName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(propertyName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject,
propertyVariablesContainer, eventsVariableInstructionTypeSwitcher);
}
void WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
gd::Project &project, gd::Object &object, const gd::String &behaviorType,
const gd::String &behaviorName) {
@@ -1705,6 +1808,15 @@ void WholeProjectRefactorer::BehaviorsAddedToObjectInScene(
void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
gd::Project &project, gd::Layout &layout, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) {
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
project, layout, layout.GetObjects(), oldName, newName, isObjectGroup);
}
void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
gd::Project &project, gd::Layout &layout,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName, const gd::String &newName, bool isObjectGroup) {
if (oldName == newName || newName.empty() || oldName.empty())
return;
@@ -1714,7 +1826,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers, layout.GetEvents(),
oldName, newName);
layout.GetObjects(), oldName, newName);
// Object groups can't have instances or be in other groups
if (!isObjectGroup) {
@@ -1731,7 +1843,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
auto &externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
externalEvents.GetEvents(), oldName, newName);
externalEvents.GetEvents(), layout.GetObjects(), oldName, newName);
}
// Rename object in external layouts
@@ -1977,8 +2089,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
auto *function = functionUniquePtr.get();
WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, *function, oldName, newName,
isObjectGroup);
project, projectScopedContainers, *function,
eventsBasedObject.GetObjects(), oldName, newName, isObjectGroup);
}
// Object groups can't have instances or be in other groups
@@ -1995,11 +2107,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
gd::Project &project,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) {
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName, const gd::String &newName, bool isObjectGroup) {
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
eventsFunction.GetEvents(), oldName, newName);
eventsFunction.GetEvents(), targetedObjectsContainer, oldName, newName);
// Object groups can't be in other groups
if (!isObjectGroup) {
@@ -2025,7 +2138,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
if (layout.GetObjects().HasObjectNamed(oldName))
continue;
ObjectOrGroupRenamedInScene(project, layout, oldName, newName,
ObjectOrGroupRenamedInScene(project, layout, project.GetObjects(), oldName, newName,
isObjectGroup);
}
}

View File

@@ -176,6 +176,31 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldFunctionName,
const gd::String& newFunctionName);
/**
* \brief Refactor the function **before** a parameter is renamed.
*
* \warning Do the renaming of the specified parameter after calling this.
* This is because the function is expected to have its old name for the
* refactoring.
*/
static void
RenameParameter(gd::Project &project,
gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &oldParameterName,
const gd::String &newParameterName);
/**
* \brief Refactor the function **after** a parameter has changed of type.
*/
static void
ChangeParameterType(gd::Project &project,
gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &parameterName);
/**
* \brief Refactor the project **before** an events function parameter
* is moved.
@@ -268,6 +293,26 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **after** a property of a behavior has
* changed of type.
*/
static void ChangeEventsBasedBehaviorPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &propertyName);
/**
* \brief Refactor the project **after** a property of an object has
* changed of type.
*/
static void ChangeEventsBasedObjectPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &propertyName);
/**
* \brief Add a behavior to an object and add required behaviors if necessary
* to fill every behavior properties of the added behaviors.
@@ -529,6 +574,7 @@ class GD_CORE_API WholeProjectRefactorer {
gd::Project& project,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction& eventsFunction,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);
@@ -649,6 +695,12 @@ class GD_CORE_API WholeProjectRefactorer {
virtual ~WholeProjectRefactorer(){};
private:
static void ObjectOrGroupRenamedInScene(gd::Project &project,
gd::Layout &scene,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName,
const gd::String &newName,
bool isObjectGroup);
static std::vector<gd::String> GetAssociatedExternalLayouts(
gd::Project& project, gd::Layout& layout);
static std::vector<gd::String>

View File

@@ -23,6 +23,9 @@ void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
gd::SerializerElement& eventsFunctionsElement =
element.AddChild("eventsFunctions");
@@ -36,6 +39,7 @@ void AbstractEventsBasedEntity::UnserializeFrom(
description = element.GetStringAttribute("description");
name = element.GetStringAttribute("name");
fullName = element.GetStringAttribute("fullName");
isPrivate = element.GetBoolAttribute("private");
const gd::SerializerElement& eventsFunctionsElement =
element.GetChild("eventsFunctions");

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 GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#define GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#pragma once
#include <vector>
#include "GDCore/Project/NamedPropertyDescriptor.h"
@@ -40,6 +39,21 @@ class GD_CORE_API AbstractEventsBasedEntity {
*/
AbstractEventsBasedEntity* Clone() const { return new AbstractEventsBasedEntity(*this); };
/**
* \brief Check if the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
AbstractEventsBasedEntity& SetPrivate(bool isPrivate_) {
isPrivate = isPrivate_;
return *this;
}
/**
* \brief Get the description of the behavior or object, that is displayed in the
* editor.
@@ -151,8 +165,7 @@ class GD_CORE_API AbstractEventsBasedEntity {
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::PropertiesContainer propertyDescriptors;
gd::String extensionName;
bool isPrivate = false;
};
} // namespace gd
#endif // GDCORE_ABSTRACTEVENTSBASEDENTITY_H

View File

@@ -22,15 +22,17 @@ void CustomConfigurationHelper::InitializeContent(
gd::SerializerElement &configurationContent) {
for (auto &&property : properties.GetInternalVector()) {
auto &element = configurationContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property->GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (primitiveType == "string" || valueType == "behavior") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
@@ -43,23 +45,25 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// Copy the property
objectProperties[propertyName] = *property;
auto &newProperty = objectProperties[propertyName];
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property->GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
if (primitiveType == "string" || valueType == "behavior") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
newProperty.SetValue(gd::String::From(
configurationContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetBoolValue()
? "true"
@@ -85,15 +89,16 @@ bool CustomConfigurationHelper::UpdateProperty(
const auto &property = properties.Get(propertyName);
auto &element = configurationContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(property.GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (primitiveType == "string" || valueType == "behavior") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
element.SetBoolValue(newValue == "1");
}

View File

@@ -21,9 +21,6 @@ EventsBasedBehavior::EventsBasedBehavior()
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
AbstractEventsBasedEntity::SerializeTo(element);
element.SetAttribute("objectType", objectType);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
sharedPropertyDescriptors.SerializeElementsTo(
"propertyDescriptor", element.AddChild("sharedPropertyDescriptors"));
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
@@ -39,7 +36,6 @@ void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
AbstractEventsBasedEntity::UnserializeFrom(project, element);
objectType = element.GetStringAttribute("objectType");
isPrivate = element.GetBoolAttribute("private");
sharedPropertyDescriptors.UnserializeElementsFrom(
"propertyDescriptor", element.GetChild("sharedPropertyDescriptors"));
if (element.HasChild("quickCustomizationVisibility")) {

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 GDCORE_EVENTSBASEDBEHAVIOR_H
#define GDCORE_EVENTSBASEDBEHAVIOR_H
#pragma once
#include <vector>
#include "GDCore/Project/AbstractEventsBasedEntity.h"
@@ -75,17 +74,11 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
EventsBasedBehavior& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
@@ -149,11 +142,8 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
gd::PropertiesContainer sharedPropertyDescriptors;
QuickCustomization::Visibility quickCustomizationVisibility;
};
} // namespace gd
#endif // GDCORE_EVENTSBASEDBEHAVIOR_H

View File

@@ -23,7 +23,8 @@ EventsBasedObject::EventsBasedObject()
areaMinZ(0),
areaMaxX(64),
areaMaxY(64),
areaMaxZ(64) {
areaMaxZ(64),
objectsContainer(gd::ObjectsContainer::SourceType::Object) {
}
EventsBasedObject::~EventsBasedObject() {}
@@ -90,7 +91,7 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
}
initialInstances.UnserializeFrom(element.GetChild("instances"));
if (element.HasAttribute("isUsingLegacyInstancesRenderer")) {
if (element.HasChild("isUsingLegacyInstancesRenderer")) {
isUsingLegacyInstancesRenderer =
element.GetBoolAttribute("isUsingLegacyInstancesRenderer", false);
}

View File

@@ -72,6 +72,15 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
return *this;
}
/**
* \brief Set that the object is private - it can't be used outside of its
* extension.
*/
EventsBasedObject& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
/**
* \brief Declare a usage of the 3D renderer.
*/

View File

@@ -33,6 +33,20 @@ public:
EventsFunctionsContainer(FunctionOwner source_) : owner(source_) {}
EventsFunctionsContainer(const EventsFunctionsContainer &other)
: owner(other.owner) {
Init(other);
}
EventsFunctionsContainer &operator=(const EventsFunctionsContainer &other) {
if (this != &other) {
owner = other.owner;
Init(other);
}
return *this;
}
/**
* \brief Get the source of the function container.
*

View File

@@ -15,14 +15,13 @@
namespace gd {
EventsFunctionsExtension::EventsFunctionsExtension() :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension),
eventsFunctionsContainer(gd::EventsFunctionsContainer::FunctionOwner::Extension),
globalVariables(gd::VariablesContainer::SourceType::ExtensionGlobal),
sceneVariables(gd::VariablesContainer::SourceType::ExtensionScene) {}
EventsFunctionsExtension::EventsFunctionsExtension(
const EventsFunctionsExtension& other) :
gd::EventsFunctionsContainer(
eventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {
Init(other);
}
@@ -48,7 +47,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
previewIconUrl = other.previewIconUrl;
iconUrl = other.iconUrl;
helpPath = other.helpPath;
EventsFunctionsContainer::Init(other);
eventsFunctionsContainer = other.eventsFunctionsContainer;
eventsBasedBehaviors = other.eventsBasedBehaviors;
eventsBasedObjects = other.eventsBasedObjects;
globalVariables = other.GetGlobalVariables();
@@ -87,10 +86,18 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
for (auto& dependency : dependencies)
SerializeDependencyTo(dependency, dependenciesElement.AddChild(""));
if (!sourceFiles.empty()) {
auto& sourceFilesElement = element.AddChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (auto& sourceFile : sourceFiles)
sourceFile.SerializeTo(sourceFilesElement.AddChild(""));
}
GetGlobalVariables().SerializeTo(element.AddChild("globalVariables"));
GetSceneVariables().SerializeTo(element.AddChild("sceneVariables"));
SerializeEventsFunctionsTo(element.AddChild("eventsFunctions"));
eventsFunctionsContainer.SerializeEventsFunctionsTo(
element.AddChild("eventsFunctions"));
eventsBasedBehaviors.SerializeElementsTo(
"eventsBasedBehavior", element.AddChild("eventsBasedBehaviors"));
eventsBasedObjects.SerializeElementsTo(
@@ -159,6 +166,17 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
dependencies.push_back(
UnserializeDependencyFrom(dependenciesElement.GetChild(i)));
sourceFiles.clear();
if (element.HasChild("sourceFiles")) {
const auto& sourceFilesElement = element.GetChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (size_t i = 0; i < sourceFilesElement.GetChildrenCount(); ++i) {
SourceFileMetadata sourceFile;
sourceFile.UnserializeFrom(sourceFilesElement.GetChild(i));
sourceFiles.push_back(sourceFile);
}
}
globalVariables.UnserializeFrom(element.GetChild("globalVariables"));
sceneVariables.UnserializeFrom(element.GetChild("sceneVariables"));
@@ -187,7 +205,8 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
gd::Project& project,
const SerializerElement& element) {
UnserializeEventsFunctionsFrom(project, element.GetChild("eventsFunctions"));
eventsFunctionsContainer.UnserializeEventsFunctionsFrom(
project, element.GetChild("eventsFunctions"));
eventsBasedBehaviors.UnserializeElementsFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));

View File

@@ -8,6 +8,7 @@
#include <vector>
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
@@ -35,7 +36,7 @@ namespace gd {
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
class GD_CORE_API EventsFunctionsExtension {
public:
EventsFunctionsExtension();
EventsFunctionsExtension(const EventsFunctionsExtension&);
@@ -180,6 +181,21 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
return originIdentifier;
}
/**
* \brief Return a reference to the functions of the events based behavior or object.
*/
EventsFunctionsContainer& GetEventsFunctions() {
return eventsFunctionsContainer;
}
/**
* \brief Return a const reference to the functions of the events based
* behavior or object.
*/
const EventsFunctionsContainer& GetEventsFunctions() const {
return eventsFunctionsContainer;
}
/** \name Dependencies
*/
///@{
@@ -289,6 +305,42 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
const gd::String& eventsFunctionName);
///@}
/** \name Source files
*/
///@{
/**
* \brief Adds a new source file.
*/
gd::SourceFileMetadata& AddSourceFile() {
gd::SourceFileMetadata sourceFile;
sourceFiles.push_back(sourceFile);
return sourceFiles.back();
};
/**
* \brief Removes a source file.
*/
void RemoveSourceFileAt(size_t index) {
sourceFiles.erase(sourceFiles.begin() + index);
};
/**
* \brief Returns the list of source files.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() {
return sourceFiles;
};
/**
* \brief Returns the list of source files.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const {
return sourceFiles;
};
///@}
private:
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
@@ -336,7 +388,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
std::vector<gd::DependencyMetadata> dependencies;
std::vector<gd::SourceFileMetadata> sourceFiles;
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::VariablesContainer globalVariables;
gd::VariablesContainer sceneVariables;
};

View File

@@ -15,6 +15,7 @@ Camera Layer::badCamera;
Layer::Layer()
: renderingType(""),
defaultCameraBehavior("top-left-anchored-if-never-moved"),
isVisible(true),
isLocked(false),
isLightingLayer(false),
@@ -40,6 +41,9 @@ void Layer::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("renderingType", GetRenderingType());
element.SetAttribute("cameraType", GetCameraType());
if (GetDefaultCameraBehavior() != "top-left-anchored-if-never-moved") {
element.SetAttribute("defaultCameraBehavior", GetDefaultCameraBehavior());
}
element.SetAttribute("visibility", GetVisibility());
element.SetAttribute("isLocked", IsLocked());
element.SetAttribute("isLightingLayer", IsLightingLayer());
@@ -80,6 +84,7 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name", "", "Name"));
SetRenderingType(element.GetStringAttribute("renderingType", ""));
SetCameraType(element.GetStringAttribute("cameraType", "perspective"));
SetDefaultCameraBehavior(element.GetStringAttribute("defaultCameraBehavior", "top-left-anchored-if-never-moved"));
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
SetLocked(element.GetBoolAttribute("isLocked", false));
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));

View File

@@ -109,6 +109,12 @@ class GD_CORE_API Layer {
renderingType = renderingType_;
}
const gd::String& GetDefaultCameraBehavior() const { return defaultCameraBehavior; }
void SetDefaultCameraBehavior(const gd::String& defaultCameraBehavior_) {
defaultCameraBehavior = defaultCameraBehavior_;
}
const gd::String& GetCameraType() const { return cameraType; }
void SetCameraType(const gd::String& cameraType_) {
@@ -275,6 +281,7 @@ class GD_CORE_API Layer {
gd::String name; ///< The name of the layer
gd::String renderingType; ///< The rendering type: "" (empty), "2d", "3d" or
///< "2d+3d".
gd::String defaultCameraBehavior;
gd::String cameraType;
bool isVisible; ///< True if the layer is visible
bool isLocked; ///< True if the layer is locked

View File

@@ -36,7 +36,10 @@ namespace gd {
gd::BehaviorsSharedData Layout::badBehaviorSharedData("", "");
Layout::Layout(const Layout& other) { Init(other); }
Layout::Layout(const Layout &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Scene) {
Init(other);
}
Layout& Layout::operator=(const Layout& other) {
if (this != &other) Init(other);
@@ -53,7 +56,8 @@ Layout::Layout()
stopSoundsOnStartup(true),
standardSortMethod(true),
disableInputWhenNotFocused(true),
variables(gd::VariablesContainer::SourceType::Scene) {}
variables(gd::VariablesContainer::SourceType::Scene),
objectsContainer(gd::ObjectsContainer::SourceType::Scene) {}
void Layout::SetName(const gd::String& name_) {
name = name_;

View File

@@ -137,6 +137,15 @@ void ObjectFolderOrObject::RemoveRecursivelyObjectNamed(
}
};
void ObjectFolderOrObject::Clear() {
if (IsFolder()) {
for (auto& it : children) {
it->Clear();
}
children.clear();
}
};
bool ObjectFolderOrObject::IsADescendantOf(
const ObjectFolderOrObject& otherObjectFolderOrObject) {
if (parent == nullptr) return false;

View File

@@ -134,6 +134,10 @@ class GD_CORE_API ObjectFolderOrObject {
* the instance children and recursively does it for every folder children.
*/
void RemoveRecursivelyObjectNamed(const gd::String& name);
/**
* \brief Clears all children
*/
void Clear();
/**
* \brief Inserts an instance representing the given object at the given

View File

@@ -20,8 +20,8 @@ namespace gd {
/**
* \brief Represents an object group.
*
* Objects groups do not really contains objects : They are just used in events,
* so as to create events which can be applied to several objects.
* Objects groups do not really contains objects: they are just used in events,
* to create events which can be applied to several objects.
*
* \ingroup PlatformDefinition
*/

View File

@@ -7,16 +7,18 @@
#include <algorithm>
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectFolderOrObject.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/PolymorphicClone.h"
namespace gd {
ObjectsContainer::ObjectsContainer() {
ObjectsContainer::ObjectsContainer(
const ObjectsContainer::SourceType sourceType_)
: sourceType(sourceType_) {
rootFolder = gd::make_unique<gd::ObjectFolderOrObject>("__ROOT");
}
@@ -26,14 +28,14 @@ ObjectsContainer::ObjectsContainer(const ObjectsContainer& other) {
Init(other);
}
ObjectsContainer& ObjectsContainer::operator=(
const ObjectsContainer& other) {
ObjectsContainer& ObjectsContainer::operator=(const ObjectsContainer& other) {
if (this != &other) Init(other);
return *this;
}
void ObjectsContainer::Init(const gd::ObjectsContainer& other) {
sourceType = other.sourceType;
initialObjects = gd::Clone(other.initialObjects);
objectGroups = other.objectGroups;
// The objects folders are not copied.
@@ -66,7 +68,7 @@ void ObjectsContainer::AddMissingObjectsInRootFolder() {
void ObjectsContainer::UnserializeObjectsFrom(
gd::Project& project, const SerializerElement& element) {
initialObjects.clear();
Clear();
element.ConsiderAsArrayOf("object", "Objet");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
const SerializerElement& objectElement = element.GetChild(i);
@@ -181,6 +183,11 @@ void ObjectsContainer::RemoveObject(const gd::String& name) {
initialObjects.erase(objectIt);
}
void ObjectsContainer::Clear() {
rootFolder->Clear();
initialObjects.clear();
}
void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
gd::ObjectFolderOrObject& objectFolderOrObject,
gd::ObjectsContainer& newContainer,
@@ -205,6 +212,14 @@ void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
objectFolderOrObject, newParentFolder, newPosition);
}
std::set<gd::String> ObjectsContainer::GetAllObjectNames() const {
std::set<gd::String> names;
for (const auto& object : initialObjects) {
names.insert(object->GetName());
}
return names;
}
std::vector<const ObjectFolderOrObject*>
ObjectsContainer::GetAllObjectFolderOrObjects() const {
std::vector<const ObjectFolderOrObject*> results;

View File

@@ -3,10 +3,11 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_OBJECTSCONTAINER_H
#define GDCORE_OBJECTSCONTAINER_H
#pragma once
#include <memory>
#include <vector>
#include <set>
#include "GDCore/String.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ObjectFolderOrObject.h"
@@ -35,15 +36,25 @@ namespace gd {
*/
class GD_CORE_API ObjectsContainer {
public:
enum SourceType {
Unknown,
Global,
Scene,
Object,
Function,
};
/**
* \brief Default constructor creating a container without any objects.
* \brief Constructor creating a container without any objects.
*/
ObjectsContainer();
ObjectsContainer(const SourceType sourceType);
virtual ~ObjectsContainer();
ObjectsContainer(const ObjectsContainer&);
ObjectsContainer& operator=(const ObjectsContainer& rhs);
SourceType GetSourceType() const { return sourceType; }
/** \name Objects management
* Members functions related to objects management.
*/
@@ -57,7 +68,7 @@ class GD_CORE_API ObjectsContainer {
/**
* \brief Return a reference to the object called \a name.
*/
Object& GetObject(const gd::String& name);
gd::Object& GetObject(const gd::String& name);
/**
* \brief Return a reference to the object called \a name.
@@ -68,7 +79,7 @@ class GD_CORE_API ObjectsContainer {
* \brief Return a reference to the object at position \a index in the objects
* list
*/
Object& GetObject(std::size_t index);
gd::Object& GetObject(std::size_t index);
/**
* \brief Return a reference to the object at position \a index in the objects
@@ -155,6 +166,11 @@ class GD_CORE_API ObjectsContainer {
gd::ObjectFolderOrObject& newParentFolder,
std::size_t newPosition);
/**
* \brief Clear all groups of the container.
*/
void Clear();
/**
* Provide a raw access to the vector containing the objects
*/
@@ -168,6 +184,8 @@ class GD_CORE_API ObjectsContainer {
const std::vector<std::unique_ptr<gd::Object> >& GetObjects() const {
return initialObjects;
}
std::set<gd::String> GetAllObjectNames() const;
///@}
/**
@@ -232,6 +250,7 @@ class GD_CORE_API ObjectsContainer {
gd::ObjectGroupsContainer objectGroups;
private:
SourceType sourceType = Unknown;
std::unique_ptr<gd::ObjectFolderOrObject> rootFolder;
/**
@@ -242,5 +261,3 @@ class GD_CORE_API ObjectsContainer {
};
} // namespace gd
#endif // GDCORE_OBJECTSCONTAINER_H

View File

@@ -416,7 +416,8 @@ gd::String ObjectsContainersList::GetTypeOfObject(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfObject(emptyObjectsContainer, *objectsContainers[0],
objectName, true);
}
@@ -439,7 +440,8 @@ bool ObjectsContainersList::HasBehaviorInObjectOrGroup(
return false;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::HasBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, true);
@@ -468,7 +470,8 @@ gd::String ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, searchInGroups);
@@ -495,7 +498,8 @@ gd::String ObjectsContainersList::GetTypeOfBehavior(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfBehavior(emptyObjectsContainer, *objectsContainers[0],
behaviorName, searchInGroups);
}
@@ -521,7 +525,8 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetBehaviorsOfObject(emptyObjectsContainer,
*objectsContainers[0], objectName,
searchInGroups);
@@ -546,7 +551,8 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorNamesInObjectOrGroup(
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetBehaviorNamesInObjectOrGroup(emptyObjectsContainer,
*objectsContainers[0], objectOrGroupName, behaviorType,
searchInGroups);
@@ -615,6 +621,28 @@ std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
return animationNames;
}
const ObjectsContainer *
ObjectsContainersList::GetObjectsContainerFromObjectName(
const gd::String &objectOrGroupName) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
++it) {
if ((*it)->HasObjectNamed(objectOrGroupName) ||
(*it)->GetObjectGroups().Has(objectOrGroupName)) {
return *it;
}
}
return nullptr;
}
const gd::ObjectsContainer::SourceType
ObjectsContainersList::GetObjectsContainerSourceType(
const gd::String &objectOrGroupName) const {
const auto *objectsContainer =
GetObjectsContainerFromObjectName(objectOrGroupName);
return objectsContainer ? objectsContainer->GetSourceType()
: gd::ObjectsContainer::SourceType::Unknown;
}
const gd::ObjectsContainer &
ObjectsContainersList::GetObjectsContainer(std::size_t index) const {
return *objectsContainers[index];

View File

@@ -2,12 +2,12 @@
#include <vector>
#include "Variable.h"
#include "GDCore/Project/ObjectsContainer.h"
namespace gd {
class String;
class Project;
class Layout;
class ObjectsContainer;
class VariablesContainer;
class Object;
class ObjectConfiguration;
@@ -190,6 +190,19 @@ class GD_CORE_API ObjectsContainersList {
std::function<void(const gd::String& variableName,
const gd::Variable& variable)> fn) const;
/**
* \brief Return the source type of the container for the specified object or
* group of objects.
*/
const gd::ObjectsContainer::SourceType GetObjectsContainerSourceType(
const gd::String& objectOrGroupName) const;
/**
* Get the objects container for for the specified object or group of objects.
*/
const ObjectsContainer *
GetObjectsContainerFromObjectName(const gd::String &objectOrGroupName) const;
/**
* \brief Return a the objects container at position \a index.
*

View File

@@ -30,7 +30,6 @@
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -67,7 +66,6 @@ Project::Project()
isAntialisingEnabledOnMobile(false),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
@@ -75,19 +73,18 @@ Project::Project()
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()),
variables(gd::VariablesContainer::SourceType::Global) {}
variables(gd::VariablesContainer::SourceType::Global),
objectsContainer(gd::ObjectsContainer::SourceType::Global) {}
Project::~Project() {}
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));
void Project::EnsureObjectDefaultBehaviors(gd::Object& object) const {
auto& platform = GetCurrentPlatform();
auto& project = *this;
auto& objectType = object.GetType();
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
const gd::String& behaviorType) {
auto& behaviorMetadata =
@@ -97,17 +94,47 @@ std::unique_ptr<gd::Object> Project::CreateObject(
" has an unknown default behavior: " + behaviorType);
return;
}
auto* behavior = object->AddNewBehavior(
project, behaviorType, behaviorMetadata.GetDefaultName());
behavior->SetDefaultBehavior(true);
const gd::String& behaviorName = behaviorMetadata.GetDefaultName();
// Check if we can keep a behavior that would have been already set up on the object.
if (object.HasBehaviorNamed(behaviorName)) {
const auto& behavior = object.GetBehavior(behaviorName);
if (!behavior.IsDefaultBehavior() || behavior.GetTypeName() != behaviorType) {
// Behavior type has changed, remove it so it is re-created.
object.RemoveBehavior(behaviorName);
}
}
if (!object.HasBehaviorNamed(behaviorName)) {
auto* behavior = object.AddNewBehavior(
project, behaviorType, behaviorName);
behavior->SetDefaultBehavior(true);
}
};
auto &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (!MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
// Add all default behaviors.
const auto& defaultBehaviorTypes = objectMetadata.GetDefaultBehaviors();
for (auto &behaviorType : defaultBehaviorTypes) {
addDefaultBehavior(behaviorType);
}
// Ensure there are no default behaviors that would not be required left on the object.
for (const auto& behaviorName : object.GetAllBehaviorNames()) {
auto& behavior = object.GetBehavior(behaviorName);
if (!behavior.IsDefaultBehavior()) {
// Non default behaviors are not handled by this function.
continue;
}
if (defaultBehaviorTypes.find(behavior.GetTypeName()) == defaultBehaviorTypes.end()) {
object.RemoveBehavior(behaviorName);
}
}
}
// During project deserialization, event-based object metadata are not yet
// generated. Default behaviors will be added by
@@ -115,6 +142,14 @@ std::unique_ptr<gd::Object> Project::CreateObject(
else if (!project.HasEventsBasedObject(objectType)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
}
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));
EnsureObjectDefaultBehaviors(*object);
return std::move(object);
}
@@ -705,9 +740,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
watermark.UnserializeFrom(propElement.GetChild("watermark"));
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
authorIds.clear();
auto& authorIdsElement = propElement.GetChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -827,36 +859,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
eventsFunctionsExtensions.clear();
const SerializerElement& eventsFunctionsExtensionsElement =
element.GetChild("eventsFunctionsExtensions");
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// First, only unserialize behaviors and objects names.
// As event based objects can contains custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
gd::EventsFunctionsExtension& newEventsFunctionsExtension =
InsertNewEventsFunctionsExtension("",
GetEventsFunctionsExtensionsCount());
newEventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensions.at(extensionIndex)
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
UnserializeAndInsertExtensionsFrom(eventsFunctionsExtensionsElement);
objectsContainer.GetObjectGroups().UnserializeFrom(
element.GetChild("objectsGroups", 0, "ObjectGroups"));
@@ -909,36 +912,86 @@ void Project::UnserializeFrom(const SerializerElement& element) {
InsertNewExternalLayout("", GetExternalLayoutsCount());
newExternalLayout.UnserializeFrom(externalLayoutElement);
}
}
externalSourceFiles.clear();
const SerializerElement& externalSourceFilesElement =
element.GetChild("externalSourceFiles", 0, "ExternalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile", "SourceFile");
for (std::size_t i = 0; i < externalSourceFilesElement.GetChildrenCount();
void Project::UnserializeAndInsertExtensionsFrom(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
std::map<gd::String, size_t> extensionNameToElementIndex;
// First, only unserialize behaviors and objects names.
// As event based objects can contains custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& sourceFileElement =
externalSourceFilesElement.GetChild(i);
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
extensionNameToElementIndex[name] = i;
gd::SourceFile& newSourceFile = InsertNewSourceFile("", "");
newSourceFile.UnserializeFrom(sourceFileElement);
gd::EventsFunctionsExtension& eventsFunctionsExtension =
HasEventsFunctionsExtensionNamed(name)
? GetEventsFunctionsExtension(name)
: InsertNewEventsFunctionsExtension(
name, GetEventsFunctionsExtensionsCount());
eventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
if (extensionIndex == gd::String::npos) {
// Should never happen because the extension was added in the first pass.
gd::LogError("Can't find extension " + extensionName + " in the list of extensions in second pass of unserialization.");
continue;
}
auto& partiallyLoadedExtension = eventsFunctionsExtensions.at(extensionIndex);
if (extensionNameToElementIndex.find(extensionName) == extensionNameToElementIndex.end()) {
// Should never happen because the extension element is present.
gd::LogError("Can't find extension element to unserialize for " + extensionName + " in second pass of unserialization.");
continue;
}
size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);
partiallyLoadedExtension
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
}
std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
// Some extension have custom objects, which have child objects coming from other extension.
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// Some extension have custom objects, which have child objects coming from other extension.
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
// This implies: an order on the extension unserialization (and no cycles).
// At the beginning, everything is yet to be loaded.
std::map<gd::String, size_t> extensionNameToElementIndex;
std::vector<gd::String> remainingExtensionNames(
eventsFunctionsExtensions.size());
for (std::size_t i = 0; i < eventsFunctionsExtensions.size(); ++i) {
remainingExtensionNames[i] = eventsFunctionsExtensions.at(i)->GetName();
eventsFunctionsExtensionsElement.GetChildrenCount());
for (std::size_t i = 0; i < eventsFunctionsExtensionsElement.GetChildrenCount(); ++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
remainingExtensionNames[i] = name;
extensionNameToElementIndex[name] = i;
}
// Helper allowing to find if an extension has an object that depends on
// Helper allowing to find if an extension has an object that depends on
// at least one other object from another extension that is not loaded yet.
auto isDependentFromRemainingExtensions =
[&remainingExtensionNames](
@@ -984,10 +1037,10 @@ std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
foundAnyExtension = false;
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
auto extensionName = remainingExtensionNames[i];
size_t extensionIndex =
GetEventsFunctionsExtensionPosition(extensionName);
size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensionsElement.GetChild(elementIndex);
if (!isDependentFromRemainingExtensions(
eventsFunctionsExtensionElement)) {
@@ -1038,7 +1091,6 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
watermark.SerializeTo(propElement.AddChild("watermark"));
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
auto& authorIdsElement = propElement.AddChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -1126,13 +1178,6 @@ void Project::SerializeTo(SerializerElement& element) const {
for (std::size_t i = 0; i < externalLayouts.size(); ++i)
externalLayouts[i]->SerializeTo(
externalLayoutsElement.AddChild("externalLayout"));
SerializerElement& externalSourceFilesElement =
element.AddChild("externalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile");
for (std::size_t i = 0; i < externalSourceFiles.size(); ++i)
externalSourceFiles[i]->SerializeTo(
externalSourceFilesElement.AddChild("sourceFile"));
}
bool Project::IsNameSafe(const gd::String& name) {
@@ -1173,65 +1218,11 @@ gd::String Project::GetSafeName(const gd::String& name) {
return newName;
}
bool Project::HasSourceFile(gd::String name, gd::String language) const {
vector<std::unique_ptr<SourceFile> >::const_iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return false;
return language.empty() || (*sourceFile)->GetLanguage() == language;
Project::Project(const Project &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Global) {
Init(other);
}
gd::SourceFile& Project::GetSourceFile(const gd::String& name) {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
const gd::SourceFile& Project::GetSourceFile(const gd::String& name) const {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
void Project::RemoveSourceFile(const gd::String& name) {
std::vector<std::unique_ptr<gd::SourceFile> >::iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return;
externalSourceFiles.erase(sourceFile);
}
gd::SourceFile& Project::InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position) {
if (HasSourceFile(name, language)) return GetSourceFile(name);
gd::SourceFile& newlyInsertedSourceFile = *(
*(externalSourceFiles.emplace(position < externalSourceFiles.size()
? externalSourceFiles.begin() + position
: externalSourceFiles.end(),
new SourceFile())));
newlyInsertedSourceFile.SetLanguage(language);
newlyInsertedSourceFile.SetFileName(name);
return newlyInsertedSourceFile;
}
Project::Project(const Project& other) { Init(other); }
Project& Project::operator=(const Project& other) {
if (this != &other) Init(other);
@@ -1293,10 +1284,6 @@ void Project::Init(const gd::Project& game) {
externalLayouts = gd::Clone(game.externalLayouts);
eventsFunctionsExtensions = gd::Clone(game.eventsFunctionsExtensions);
useExternalSourceFiles = game.useExternalSourceFiles;
externalSourceFiles = gd::Clone(game.externalSourceFiles);
variables = game.GetVariables();
projectFile = game.GetProjectFile();

View File

@@ -32,7 +32,6 @@ class Object;
class ObjectConfiguration;
class VariablesContainer;
class ArbitraryResourceWorker;
class SourceFile;
class Behavior;
class BehaviorsSharedData;
class BaseEvent;
@@ -523,13 +522,7 @@ class GD_CORE_API Project {
std::unique_ptr<gd::Object> CreateObject(const gd::String& type,
const gd::String& name) const;
/**
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
void EnsureObjectDefaultBehaviors(gd::Object& object) const;
/**
* Create an event of the given type.
@@ -899,6 +892,16 @@ class GD_CORE_API Project {
const EventsFunctionsExtension& eventsFunctionExtension,
std::size_t position);
/**
* \brief Unserialize and insert in the project the extensions.
*
* Unserialization is done in two passe to allow dependencies between extensions.
*
* \note If an extension with the same name already exists, it will be overwritten.
*/
void UnserializeAndInsertExtensionsFrom(
const gd::SerializerElement& eventsFunctionsExtensionsElement);
/**
* \brief Delete the events functions extension named "name".
*/
@@ -1015,60 +1018,22 @@ class GD_CORE_API Project {
static gd::String GetSafeName(const gd::String& name);
///@}
/** \name External source files
* To manage external C++ or Javascript source files used by the game
*/
///@{
/**
* \brief Return true if the game activated the use of external source files.
*/
bool UseExternalSourceFiles() const { return useExternalSourceFiles; }
/**
* \brief Return a const reference to the vector containing all the source
* files used by the game.
*/
const std::vector<std::unique_ptr<gd::SourceFile> >& GetAllSourceFiles()
const {
return externalSourceFiles;
}
/**
* \brief Return true if the source file with the specified name is used by
* the game. \param name The filename of the source file. \param language
* Optional. If specified, check that the source file that exists is in this
* language.
*/
bool HasSourceFile(gd::String name, gd::String language = "") const;
/**
* Return a reference to the external source file with the given name.
*/
SourceFile& GetSourceFile(const gd::String& name);
/**
* Return a reference to the external source file with the given name.
*/
const SourceFile& GetSourceFile(const gd::String& name) const;
/**
* Remove the specified source file.
*/
void RemoveSourceFile(const gd::String& name);
/**
* Add a new source file the specified position in the external source files
* list.
*/
gd::SourceFile& InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position = -1);
///@}
gd::WholeProjectDiagnosticReport& GetWholeProjectDiagnosticReport() {
return wholeProjectDiagnosticReport;
}
/**
* @brief Get the project extensions names in the order they have to be
* unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
*
* \warning This is only public to allow testing - don't use it in the editor.
*/
static std::vector<gd::String> GetUnserializingOrderExtensionNames(
const gd::SerializerElement& eventsFunctionsExtensionsElement);
private:
/**
* Initialize from another game. Used by copy-ctor and assign-op.
@@ -1077,12 +1042,12 @@ class GD_CORE_API Project {
void Init(const gd::Project& project);
/**
* @brief Get the project extensions names in the order they have to be unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::vector<gd::String> GetUnserializingOrderExtensionNames(const gd::SerializerElement &eventsFunctionsExtensionsElement);
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
gd::String name; ///< Game name
gd::String description; ///< Game description
@@ -1125,10 +1090,6 @@ class GD_CORE_API Project {
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
bool useExternalSourceFiles =
false; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.

View File

@@ -4,12 +4,55 @@
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/PlatformExtension.h"
namespace gd {
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(
const gd::Project &project, const gd::Layout &layout) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
project, layout),
VariablesContainersList::
MakeNewVariablesContainersListForProjectAndLayout(project, layout),
&project.GetVariables(), &layout.GetVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForProject(
const gd::Project &project) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProject(project),
VariablesContainersList::MakeNewVariablesContainersListForProject(
project),
&project.GetVariables(), nullptr,
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersFor(
const gd::ObjectsContainer &globalObjectsContainers,
const gd::ObjectsContainer &objectsContainers) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
globalObjectsContainers, objectsContainers),
VariablesContainersList::MakeNewEmptyVariablesContainersList(),
nullptr, nullptr,
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
};
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtension(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension) {
@@ -18,6 +61,8 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtensi
ObjectsContainersList::MakeNewEmptyObjectsContainersList(),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
@@ -25,22 +70,29 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtensi
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer) {
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunctionsExtension, eventsFunction, parameterObjectsContainer);
project, eventsFunctionsExtension.GetEventsFunctions(), eventsFunction,
parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
MakeNewVariablesContainersListForFreeEventsFunction(
eventsFunctionsExtension, eventsFunction,
parameterVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddParameters(
eventsFunction.GetParametersForEvents(eventsFunctionsExtension));
projectScopedContainers.AddParameters(eventsFunction.GetParametersForEvents(
eventsFunctionsExtension.GetEventsFunctions()));
return projectScopedContainers;
};
@@ -50,7 +102,9 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
project,
@@ -62,7 +116,11 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
MakeNewVariablesContainersListForBehaviorEventsFunction(
eventsFunctionsExtension, eventsBasedBehavior, eventsFunction,
parameterVariablesContainer, propertyVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
@@ -81,17 +139,23 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForObjectEventsFunction(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
project, eventsBasedObject, eventsFunction, parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
eventsBasedObject.GetObjects(),
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
MakeNewVariablesContainersListForObjectEventsFunction(
eventsFunctionsExtension, eventsBasedObject, eventsFunction,
parameterVariablesContainer, propertyVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
@@ -114,7 +178,7 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsBasedObject(
// created below.
// Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
// in the codebase.
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.Clear();
outputObjectsContainer.GetObjectGroups().Clear();
// This object named "Object" represents the parent and is used by events.
@@ -129,20 +193,14 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsBasedObject(
eventsFunctionsExtension.GetName(), eventsBasedObject.GetName()),
"Object", outputObjectsContainer.GetObjectsCount());
// TODO: We should avoid to do a copy of the objects container here - as this results
// in an objects container that contains temporary objects. This can create issues in the
// UI (for example, a tree view that keeps references on objects).
// Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
// in the codebase.
gd::EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
eventsBasedObject, outputObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
outputObjectsContainer),
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
eventsBasedObject.GetObjects(), outputObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(

View File

@@ -5,13 +5,11 @@
#include "ObjectsContainersList.h"
#include "PropertiesContainersList.h"
#include "VariablesContainersList.h"
#include "VariablesContainer.h"
namespace gd {
class Project;
class ObjectsContainer;
class ObjectsContainersList;
class VariablesContainersList;
class PropertiesContainersList;
class NamedPropertyDescriptor;
class ParameterMetadataContainer;
class BaseEvent;
@@ -19,7 +17,7 @@ class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
} // namespace gd
} // namespace gd
namespace gd {
@@ -38,51 +36,29 @@ class ProjectScopedContainers {
ProjectScopedContainers(
const gd::ObjectsContainersList &objectsContainersList_,
const gd::VariablesContainersList &variablesContainersList_,
const gd::VariablesContainer *legacyGlobalVariables_,
const gd::VariablesContainer *legacySceneVariables_,
const gd::PropertiesContainersList &propertiesContainersList_)
: objectsContainersList(objectsContainersList_),
variablesContainersList(variablesContainersList_),
legacyGlobalVariables(legacyGlobalVariables_),
legacySceneVariables(legacySceneVariables_),
propertiesContainersList(propertiesContainersList_){};
virtual ~ProjectScopedContainers(){};
static ProjectScopedContainers
MakeNewProjectScopedContainersForProjectAndLayout(const gd::Project &project,
const gd::Layout &layout) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
project, layout),
VariablesContainersList::
MakeNewVariablesContainersListForProjectAndLayout(project, layout),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
const gd::Layout &layout);
static ProjectScopedContainers
MakeNewProjectScopedContainersForProject(const gd::Project &project) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProject(
project),
VariablesContainersList::
MakeNewVariablesContainersListForProject(project),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
MakeNewProjectScopedContainersForProject(const gd::Project &project);
/**
* @deprecated Use another method for an explicit context instead.
*/
static ProjectScopedContainers MakeNewProjectScopedContainersFor(
const gd::ObjectsContainer &globalObjectsContainers,
const gd::ObjectsContainer &objectsContainers) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
globalObjectsContainers, objectsContainers),
VariablesContainersList::MakeNewEmptyVariablesContainersList(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
};
const gd::ObjectsContainer &objectsContainers);
static ProjectScopedContainers
MakeNewProjectScopedContainersForEventsFunctionsExtension(
@@ -93,8 +69,9 @@ class ProjectScopedContainers {
MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& parameterObjectsContainer);
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForBehaviorEventsFunction(
@@ -102,7 +79,9 @@ class ProjectScopedContainers {
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForObjectEventsFunction(
@@ -110,7 +89,9 @@ class ProjectScopedContainers {
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForEventsBasedObject(
@@ -148,9 +129,17 @@ class ProjectScopedContainers {
std::function<ReturnType()> notFoundCallback) const {
if (objectsContainersList.HasObjectOrGroupNamed(name))
return objectCallback();
else if (variablesContainersList.Has(name))
else if (variablesContainersList.Has(name)) {
const auto &variablesContainer =
variablesContainersList.GetVariablesContainerFromVariableOrPropertyOrParameterName(name);
const auto sourceType = variablesContainer.GetSourceType();
if (sourceType == gd::VariablesContainer::SourceType::Properties) {
return propertyCallback();
} else if (sourceType == gd::VariablesContainer::SourceType::Parameters) {
return parameterCallback();
}
return variableCallback();
else if (ParameterMetadataTools::Has(parametersVectorsList, name))
} else if (ParameterMetadataTools::Has(parametersVectorsList, name))
return parameterCallback();
else if (propertiesContainersList.Has(name))
return propertyCallback();
@@ -221,6 +210,24 @@ class ProjectScopedContainers {
return variablesContainersList;
};
/**
* @brief Return the global variables of the current scene or the current
* extension. It allows legacy "globalvar" parameters to accept extension
* variables.
*/
const gd::VariablesContainer *GetLegacyGlobalVariables() const {
return legacyGlobalVariables;
};
/**
* @brief Return the scene variables of the current scene or the current
* extension. It allows legacy "scenevar" parameters to accept extension
* variables.
*/
const gd::VariablesContainer *GetLegacySceneVariables() const {
return legacySceneVariables;
};
const gd::PropertiesContainersList &GetPropertiesContainersList() const {
return propertiesContainersList;
};
@@ -231,11 +238,14 @@ class ProjectScopedContainers {
/** Do not use - should be private but accessible to let Emscripten create a
* temporary. */
ProjectScopedContainers(){};
ProjectScopedContainers()
: legacyGlobalVariables(nullptr), legacySceneVariables(nullptr){};
private:
private:
gd::ObjectsContainersList objectsContainersList;
gd::VariablesContainersList variablesContainersList;
const gd::VariablesContainer *legacyGlobalVariables;
const gd::VariablesContainer *legacySceneVariables;
gd::PropertiesContainersList propertiesContainersList;
std::vector<const ParameterMetadataContainer *> parametersVectorsList;
};

View File

@@ -97,6 +97,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<AtlasResource>();
else if (kind == "spine")
return std::make_shared<SpineResource>();
else if (kind == "javascript")
return std::make_shared<JavaScriptResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -173,9 +175,6 @@ std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties()
properties[_("Smooth the image")]
.SetValue(smooth ? "true" : "false")
.SetType("Boolean");
properties[_("Always loaded in memory")]
.SetValue(alwaysLoaded ? "true" : "false")
.SetType("Boolean");
return properties;
}
@@ -184,8 +183,6 @@ bool ImageResource::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == _("Smooth the image"))
smooth = value == "1";
else if (name == _("Always loaded in memory"))
alwaysLoaded = value == "1";
return true;
}
@@ -569,14 +566,12 @@ void ImageResource::SetFile(const gd::String& newFile) {
}
void ImageResource::UnserializeFrom(const SerializerElement& element) {
alwaysLoaded = element.GetBoolAttribute("alwaysLoaded");
smooth = element.GetBoolAttribute("smoothed");
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void ImageResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("alwaysLoaded", alwaysLoaded);
element.SetAttribute("smoothed", smooth);
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
@@ -774,6 +769,20 @@ void AtlasResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}
void JavaScriptResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void JavaScriptResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void JavaScriptResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {

View File

@@ -166,7 +166,7 @@ class GD_CORE_API Resource {
*/
class GD_CORE_API ImageResource : public Resource {
public:
ImageResource() : Resource(), smooth(true), alwaysLoaded(false) {
ImageResource() : Resource(), smooth(true) {
SetKind("image");
};
virtual ~ImageResource(){};
@@ -210,7 +210,6 @@ class GD_CORE_API ImageResource : public Resource {
void SetSmooth(bool enable = true) { smooth = enable; }
bool smooth; ///< True if smoothing filter is applied
bool alwaysLoaded; ///< True if the image must always be loaded in memory.
private:
gd::String file;
};
@@ -548,6 +547,32 @@ class GD_CORE_API AtlasResource : public Resource {
gd::String file;
};
/**
* \brief Describe a video file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API JavaScriptResource : public Resource {
public:
JavaScriptResource() : Resource() { SetKind("javascript"); };
virtual ~JavaScriptResource(){};
virtual JavaScriptResource* Clone() const override {
return new JavaScriptResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*

View File

@@ -1,36 +0,0 @@
/*
* GDevelop Core
* 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)
#include "GDCore/Project/SourceFile.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
SourceFile::SourceFile() : gdManaged(false) {
// ctor
}
SourceFile::~SourceFile() {
// dtor
}
void SourceFile::SerializeTo(SerializerElement& element) const {
element.SetAttribute("filename", filename);
element.SetAttribute("language", language);
element.SetAttribute("gdManaged", gdManaged);
}
void SourceFile::UnserializeFrom(const SerializerElement& element) {
filename = element.GetStringAttribute("filename");
language = element.GetStringAttribute("language", "C++");
gdManaged = element.GetBoolAttribute("gdManaged");
}
} // namespace gd
#endif

View File

@@ -1,92 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef SOURCEFILE_H
#define SOURCEFILE_H
#include <ctime>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
class BaseEvent;
namespace gd {
/**
* \brief Represents a "physical" source file.
*
* Source file can be compiled (or just integrated to the exported project)
* by platforms. Most of the time, special events are provided to use functions
* created in such files.
*/
class GD_CORE_API SourceFile {
public:
SourceFile();
virtual ~SourceFile();
/**
* \brief Return a pointer to a new SourceFile constructed from this one.
*/
SourceFile* Clone() const { return new SourceFile(*this); };
/**
* \brief Get the filename
*/
gd::String GetFileName() const { return filename; };
/**
* \brief Change the filename
*/
void SetFileName(gd::String filename_) { filename = filename_; };
/**
* \brief Serialize the source file.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the source file.
*/
void UnserializeFrom(const SerializerElement& element);
/**
* \brief Set if the file is hidden from the user point of view and is only
* managed by GDevelop
*/
void SetGDManaged(bool gdManaged_) { gdManaged = gdManaged_; };
/**
* \brief Return true if the file is hidden from the user point of view and is
* only managed by GDevelop
*/
bool IsGDManaged() const { return gdManaged; };
/**
* \brief Change the language of the source file
*/
void SetLanguage(gd::String lang) { language = lang; }
/**
* \brief Get the language of the source file
*/
const gd::String& GetLanguage() const { return language; }
private:
gd::String filename; ///< Filename
bool gdManaged; ///< True if the source file is hidden from the user point of
///< view and is managed only by GDevelop.
gd::String language; ///< String identifying the language of this source file
///< (typically "C++ or "Javascript").
std::weak_ptr<BaseEvent>
associatedGdEvent; ///< When a source file is GD-managed, it is usually
///< created for a specific event. This member is not
///< saved: It is the event responsibility to call
///< SetAssociatedEvent.
};
} // namespace gd
#endif // SOURCEFILE_H

View File

@@ -34,7 +34,9 @@ class GD_CORE_API VariablesContainer {
Object,
Local,
ExtensionGlobal,
ExtensionScene
ExtensionScene,
Parameters,
Properties,
};
VariablesContainer();

View File

@@ -6,6 +6,7 @@
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/IDE/EventsFunctionTools.h"
namespace gd {
@@ -41,6 +42,78 @@ VariablesContainersList::MakeNewVariablesContainersListForEventsFunctionsExtensi
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForFreeEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(extension.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 3;
return variablesContainersList;
}
VariablesContainersList VariablesContainersList::
MakeNewVariablesContainersListForBehaviorEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedBehavior.GetSharedPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedBehavior.GetPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(
eventsBasedBehavior.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 5;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForObjectEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedObject.GetPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(
eventsBasedObject.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 4;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListPushing(
const VariablesContainersList& variablesContainersList, const gd::VariablesContainer& variablesContainer) {
@@ -74,7 +147,7 @@ const Variable& VariablesContainersList::Get(const gd::String& name) const {
}
const VariablesContainer &
VariablesContainersList::GetVariablesContainerFromVariableName(
VariablesContainersList::GetVariablesContainerFromVariableOrPropertyOrParameterName(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
@@ -84,6 +157,34 @@ VariablesContainersList::GetVariablesContainerFromVariableName(
return badVariablesContainer;
}
const VariablesContainer &VariablesContainersList::
GetVariablesContainerFromVariableOrPropertyName(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
if ((*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters &&
(*it)->Has(variableName))
return **it;
}
return badVariablesContainer;
}
const VariablesContainer &VariablesContainersList::
GetVariablesContainerFromVariableNameOnly(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
if ((*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters &&
(*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Properties &&
(*it)->Has(variableName))
return **it;
}
return badVariablesContainer;
}
std::size_t
VariablesContainersList::GetVariablesContainerPositionFromVariableName(
const gd::String &variableName) const {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <vector>
#include <functional>
#include <vector>
namespace gd {
class String;
@@ -9,7 +9,10 @@ class Layout;
class VariablesContainer;
class Variable;
class EventsFunctionsExtension;
} // namespace gd
class EventsBasedBehavior;
class EventsBasedObject;
class EventsFunction;
} // namespace gd
namespace gd {
@@ -24,20 +27,42 @@ namespace gd {
* \ingroup PlatformDefinition
*/
class GD_CORE_API VariablesContainersList {
public:
public:
virtual ~VariablesContainersList(){};
static VariablesContainersList
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project& project,
const gd::Layout& layout);
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project &project,
const gd::Layout &layout);
static VariablesContainersList
MakeNewVariablesContainersListForProject(const gd::Project& project);
MakeNewVariablesContainersListForProject(const gd::Project &project);
static VariablesContainersList
MakeNewVariablesContainersListForEventsFunctionsExtension(
const gd::EventsFunctionsExtension &extension);
static VariablesContainersList
MakeNewVariablesContainersListForFreeEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer);
static VariablesContainersList
MakeNewVariablesContainersListForBehaviorEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static VariablesContainersList
MakeNewVariablesContainersListForObjectEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static VariablesContainersList MakeNewVariablesContainersListPushing(
const VariablesContainersList &variablesContainersList,
const gd::VariablesContainer &variablesContainer);
@@ -50,28 +75,31 @@ class GD_CORE_API VariablesContainersList {
/**
* \brief Return true if the specified variable is in one of the containers.
*/
bool Has(const gd::String& name) const;
bool Has(const gd::String &name) const;
/**
* \brief Return a reference to the variable called \a name.
*/
const Variable& Get(const gd::String& name) const;
const Variable &Get(const gd::String &name) const;
/**
* \brief Return true if the specified variable container is present.
*/
bool HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const;
bool
HasVariablesContainer(const gd::VariablesContainer &variablesContainer) const;
// TODO: Rename GetTopMostVariablesContainer and GetBottomMostVariablesContainer
// to give a clearer access to segments of the container list.
// For instance, a project tree segment and an event tree segment.
// TODO: Rename GetTopMostVariablesContainer and
// GetBottomMostVariablesContainer to give a clearer access to segments of the
// container list. For instance, a project tree segment and an event tree
// segment.
/**
* Get the variables container at the top of the scope (so the most "global" one).
* \brief Avoid using apart when a scope must be forced.
* Get the variables container at the top of the scope (so the most "global"
* one). \brief Avoid using apart when a scope must be forced.
*/
const VariablesContainer* GetTopMostVariablesContainer() const {
if (variablesContainers.empty()) return nullptr;
const VariablesContainer *GetTopMostVariablesContainer() const {
if (variablesContainers.empty())
return nullptr;
return variablesContainers.front();
};
@@ -80,16 +108,29 @@ class GD_CORE_API VariablesContainersList {
* (so the most "local" one) excluding local variables.
* \brief Avoid using apart when a scope must be forced.
*/
const VariablesContainer* GetBottomMostVariablesContainer() const {
if (variablesContainers.empty()) return nullptr;
const VariablesContainer *GetBottomMostVariablesContainer() const {
if (variablesContainers.empty())
return nullptr;
return variablesContainers.at(firstLocalVariableContainerIndex - 1);
}
/**
* Get the variables container for a given variable or property or parameter.
*/
const VariablesContainer &
GetVariablesContainerFromVariableOrPropertyOrParameterName(const gd::String &variableName) const;
/**
* Get the variables container for a given variable or property.
*/
const VariablesContainer &
GetVariablesContainerFromVariableOrPropertyName(const gd::String &variableName) const;
/**
* Get the variables container for a given variable.
*/
const VariablesContainer &
GetVariablesContainerFromVariableName(const gd::String &variableName) const;
GetVariablesContainerFromVariableNameOnly(const gd::String &variableName) const;
/**
* Get the variables container index for a given variable.
@@ -109,43 +150,47 @@ class GD_CORE_API VariablesContainersList {
* \warning Trying to access to a not existing variable container will result
* in undefined behavior.
*/
const gd::VariablesContainer& GetVariablesContainer(std::size_t index) const {
const gd::VariablesContainer &GetVariablesContainer(std::size_t index) const {
return *variablesContainers.at(index);
}
/**
* \brief Return the number variable containers.
*/
std::size_t GetVariablesContainersCount() const { return variablesContainers.size(); }
std::size_t GetVariablesContainersCount() const {
return variablesContainers.size();
}
/**
* \brief Call the callback for each variable having a name matching the specified search.
* \brief Call the callback for each variable having a name matching the
* specified search.
*/
void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
void ForEachVariableMatchingSearch(
const gd::String &search,
std::function<void(const gd::String &name, const gd::Variable &variable)>
fn) const;
/**
* \brief Push a new variables container to the context.
*/
void Push(const gd::VariablesContainer& variablesContainer) {
void Push(const gd::VariablesContainer &variablesContainer) {
variablesContainers.push_back(&variablesContainer);
};
/**
* \brief Pop a variables container from the context.
*/
void Pop() {
variablesContainers.pop_back();
};
void Pop() { variablesContainers.pop_back(); };
/** Do not use - should be private but accessible to let Emscripten create a
* temporary. */
VariablesContainersList() : firstLocalVariableContainerIndex(0){};
/** Do not use - should be private but accessible to let Emscripten create a temporary. */
VariablesContainersList(): firstLocalVariableContainerIndex(0) {};
private:
std::vector<const gd::VariablesContainer*> variablesContainers;
private:
std::vector<const gd::VariablesContainer *> variablesContainers;
std::size_t firstLocalVariableContainerIndex;
static Variable badVariable;
static VariablesContainer badVariablesContainer;
};
} // namespace gd
} // namespace gd

View File

@@ -268,8 +268,9 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
auto& function = extension.InsertNewEventsFunction("MyFreeFunction", 0);
auto &function = extension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeFunction", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithResources");
@@ -777,8 +778,9 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
auto& function = extension.InsertNewEventsFunction("MyFreeFunction", 0);
auto &function = extension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeFunction", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithResources");

View File

@@ -32,7 +32,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetScenesDependencies().find("Layout2") !=
analyzer.GetScenesDependencies().end());
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 0);
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a simple external events dependency") {
@@ -55,7 +54,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a transitive scene and external events dependency") {
@@ -87,7 +85,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a (nested) circular dependency with scenes") {

View File

@@ -263,41 +263,81 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
extension->SetExtensionInformation(
"BuiltinVariables", "My testing extension for variables", "", "", "");
extension
->AddCondition("NumberVariable",
"Variable value",
"Compare the number value of a variable.",
"The variable _PARAM0_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", gd::ParameterOptions::MakeNewOptions());
extension
->AddCondition("StringVariable",
"Variable value",
"Compare the text (string) of a variable.",
"The variable _PARAM0_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", gd::ParameterOptions::MakeNewOptions());
extension
->AddCondition(
"BooleanVariable",
"Variable value",
"Compare the boolean value of a variable.",
"The variable _PARAM0_ is _PARAM1_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("trueorfalse", "");
extension
->AddAction("SetNumberVariable",
"Do something with number variables",
"This does something with variables",
"Do something with variables",
"Change variable value",
"Modify the number value of a variable.",
"the variable _PARAM0_",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "number")
.AddParameter("number", "Value")
.SetFunctionName("setNumberVariable");
extension
->AddAction("SetStringVariable",
"Do something with string variables",
"This does something with variables",
"Do something with variables",
"Change text variable",
"Modify the text (string) of a variable.",
"the variable _PARAM0_",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "string")
.AddParameter("string", "Value")
.SetFunctionName("setStringVariable");
extension
->AddAction("SetBooleanVariable",
"Do something with boolean variables",
"This does something with variables",
"Do something with variables",
"Change boolean variable",
"Modify the boolean value of a variable.",
"Change the variable _PARAM0_: _PARAM1_",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
@@ -340,8 +380,8 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("object", _("Object 2 parameter"))
.AddParameter("object", "Object 1 parameter")
.AddParameter("object", "Object 2 parameter")
.SetFunctionName("doSomethingWithObjects");
extension
@@ -358,6 +398,17 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("soundfile", "Parameter 3 (an audio resource)")
.SetFunctionName("doSomethingWithResources");
extension
->AddAction("DoSomethingWithAnyVariable",
"Do something with variables",
"This does something with variables",
"Do something with variables please",
"",
"",
"")
.AddParameter("variable", "Any variable")
.SetFunctionName("doSomethingWithAnyVariable");
extension
->AddAction("DoSomethingWithLegacyPreScopedVariables",
"Do something with variables",

View File

@@ -38,6 +38,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
"Function2.x");
REQUIRE(eventsFunctionContainer.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionContainer.GetOwner() ==
gd::EventsFunctionsContainer::FunctionOwner::Extension);
REQUIRE(eventsFunctionContainer2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionContainer2.GetEventsFunction(0).GetName() ==
"Function1.y");

View File

@@ -11,9 +11,10 @@
TEST_CASE("EventsFunctionsExtension", "[common]") {
SECTION("Sanity checks") {
gd::EventsFunctionsExtension eventsFunctionExtension;
eventsFunctionExtension.InsertNewEventsFunction("Function1", 0);
eventsFunctionExtension.InsertNewEventsFunction("Function2", 1);
eventsFunctionExtension.InsertNewEventsFunction("Function3", 2);
auto &freeEventsFunctions = eventsFunctionExtension.GetEventsFunctions();
freeEventsFunctions.InsertNewEventsFunction("Function1", 0);
freeEventsFunctions.InsertNewEventsFunction("Function2", 1);
freeEventsFunctions.InsertNewEventsFunction("Function3", 2);
eventsFunctionExtension.GetEventsBasedBehaviors().InsertNew("MyBehavior",
0);
eventsFunctionExtension.GetEventsBasedBehaviors().InsertNew("MyBehavior2",
@@ -22,12 +23,13 @@ TEST_CASE("EventsFunctionsExtension", "[common]") {
// Check that copy operator is working
gd::EventsFunctionsExtension eventsFunctionExtension2 =
eventsFunctionExtension;
REQUIRE(eventsFunctionExtension2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension2.GetEventsFunction(0).GetName() ==
auto &freeEventsFunctions2 = eventsFunctionExtension2.GetEventsFunctions();
REQUIRE(freeEventsFunctions2.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions2.GetEventsFunction(0).GetName() ==
"Function1");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(1).GetName() ==
"Function2");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionExtension2.GetEventsBasedBehaviors().GetCount() == 2);
REQUIRE(
@@ -39,21 +41,21 @@ TEST_CASE("EventsFunctionsExtension", "[common]") {
// Check that the copy has not somehow shared the same pointers
// to the events functions.
eventsFunctionExtension.GetEventsFunction(1).SetName("Function2.x");
eventsFunctionExtension2.GetEventsFunction(0).SetName("Function1.y");
REQUIRE(eventsFunctionExtension.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension.GetEventsFunction(0).GetName() ==
freeEventsFunctions.GetEventsFunction(1).SetName("Function2.x");
freeEventsFunctions2.GetEventsFunction(0).SetName("Function1.y");
REQUIRE(freeEventsFunctions.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions.GetEventsFunction(0).GetName() ==
"Function1");
REQUIRE(eventsFunctionExtension.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions.GetEventsFunction(1).GetName() ==
"Function2.x");
REQUIRE(eventsFunctionExtension.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionExtension2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension2.GetEventsFunction(0).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions2.GetEventsFunction(0).GetName() ==
"Function1.y");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(1).GetName() ==
"Function2");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(2).GetName() ==
"Function3");
}
}

View File

@@ -1737,10 +1737,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors().size() == 3);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"An object variable or expression should be entered.");
REQUIRE(validator.GetFatalErrors()[2]->GetMessage() ==
"A name should be entered after the dot.");
}
}
@@ -1817,6 +1819,19 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid object variables (non existing variable with child)") {
{
auto node =
parser.ParseExpression("MySpriteObject.MyNonExistingVariable.MyNonExistingChild");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"This variable does not exist on this object or group.");
}
}
SECTION("Invalid object (object entered without any variable)") {
{
auto node =
@@ -2020,6 +2035,319 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Valid property (in variableOrPropertyOrParameter parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Valid property (in variableOrProperty parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Invalid property (in variable parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(
platform, projectScopedContainersWithProperties, "variable");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a property. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid property (property with child in variableOrProperty parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
{
auto node = parser.ParseExpression("MyProperty.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Invalid property (property with child in variableOrPropertyOrParameter parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
{
auto node = parser.ParseExpression("MyProperty.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Valid parameter (in variableOrPropertyOrParameter parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Invalid parameter (in variableOrProperty parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a parameter. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid parameter (in variable parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(
platform, projectScopedContainersWithParameters, "variable");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a parameter. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid parameter (parameter with child in variableOrPropertyOrParameter parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
{
auto node = parser.ParseExpression("MyParameter.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Valid parameter") {
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");
@@ -2376,10 +2704,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors().size() == 3);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"An object variable or expression should be entered.");
REQUIRE(validator.GetFatalErrors()[2]->GetMessage() ==
"A name should be entered after the dot.");
}

View File

@@ -0,0 +1,74 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/Project/Project.h"
#include <algorithm>
#include "DummyPlatform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/String.h"
#include "catch.hpp"
TEST_CASE("Project::EnsureObjectDefaultBehaviors", "[common]") {
SECTION("Check that default behaviors are added to an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto myObject = project.CreateObject("MyExtension::Sprite", "MyObject");
REQUIRE(myObject->GetType() == "MyExtension::Sprite");
REQUIRE(myObject->GetAllBehaviorNames().size() == 0);
project.EnsureObjectDefaultBehaviors(*myObject);
REQUIRE(myObject->GetAllBehaviorNames().size() == 0);
// Modify the "Sprite" extension to add a default behavior to the object.
const auto& allExtensions = platform.GetAllPlatformExtensions();
auto spriteExtensionIt = std::find_if(
allExtensions.begin(),
allExtensions.end(),
[&](const std::shared_ptr<gd::PlatformExtension>& extension) {
return extension->GetName() == "MyExtension";
});
REQUIRE(spriteExtensionIt != allExtensions.end());
auto spriteExtension = *spriteExtensionIt;
auto& spriteObjectMetadata =
spriteExtension->GetObjectMetadata("MyExtension::Sprite");
REQUIRE(gd::MetadataProvider::IsBadObjectMetadata(spriteObjectMetadata) ==
false);
spriteObjectMetadata.AddDefaultBehavior(
"FlippableCapability::FlippableBehavior");
// Ensure the default behavior is added.
project.EnsureObjectDefaultBehaviors(*myObject);
REQUIRE(myObject->GetAllBehaviorNames().size() == 1);
REQUIRE(myObject->GetAllBehaviorNames()[0] == "Flippable");
REQUIRE(myObject->GetBehavior("Flippable").GetTypeName() == "FlippableCapability::FlippableBehavior");
// Ensure default behaviors are adapted if the object default behaviors are modified.
// While this can not happen with pre-coded extensions, it can happen with custom objects.
spriteObjectMetadata.ResetDefaultBehaviorsJustForTesting();
spriteObjectMetadata.AddDefaultBehavior(
"ResizableCapability::ResizableBehavior");
spriteObjectMetadata.AddDefaultBehavior(
"ScalableCapability::ScalableBehavior");
project.EnsureObjectDefaultBehaviors(*myObject);
REQUIRE(myObject->GetAllBehaviorNames().size() == 2);
REQUIRE(myObject->GetAllBehaviorNames()[0] == "Resizable");
REQUIRE(myObject->GetAllBehaviorNames()[1] == "Scale");
REQUIRE(myObject->GetBehavior("Resizable").GetTypeName() == "ResizableCapability::ResizableBehavior");
REQUIRE(myObject->GetBehavior("Scale").GetTypeName() == "ScalableCapability::ScalableBehavior");
}
}

View File

@@ -0,0 +1,312 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include <algorithm>
#include "DummyPlatform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "catch.hpp"
TEST_CASE("Project::GetUnserializingOrderExtensionNames", "[common]") {
SECTION("Unserialization order is correct when nothing to load") {
gd::SerializerElement emptyElement;
std::vector<gd::String> orderedNames =
gd::Project::GetUnserializingOrderExtensionNames(emptyElement);
REQUIRE(orderedNames.size() == 0);
}
SECTION("One extension with no dependencies") {
gd::SerializerElement extensionsElement = gd::Serializer::FromJSON(
R"([
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension1",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": []
}
])");
std::vector<gd::String> orderedNames =
gd::Project::GetUnserializingOrderExtensionNames(extensionsElement);
REQUIRE(orderedNames.size() == 1);
REQUIRE(orderedNames[0] == "Extension1");
}
SECTION("One extension with a dependency outside the loaded extensions") {
gd::SerializerElement extensionsElement = gd::Serializer::FromJSON(
R"([
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension1DependsOtherExtension",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": [
{
"areaMaxX": 64,
"areaMaxY": 64,
"areaMaxZ": 64,
"areaMinX": 0,
"areaMinY": 0,
"areaMinZ": 0,
"defaultName": "Joystick",
"description": "Joystick for touchscreens.",
"fullName": "Multitouch Joystick",
"name": "SpriteMultitouchJoystick",
"eventsFunctions": [],
"propertyDescriptors": [],
"objects": [
{
"name": "Thumb",
"type": "OtherExtension::Whatever"
}
],
"objectsFolderStructure": {
"folderName": "__ROOT",
"children": []
},
"objectsGroups": [],
"layers": [],
"instances": []
}
]
}
])");
std::vector<gd::String> orderedNames =
gd::Project::GetUnserializingOrderExtensionNames(extensionsElement);
REQUIRE(orderedNames.size() == 1);
REQUIRE(orderedNames[0] == "Extension1DependsOtherExtension");
}
SECTION("4 extensions with dependencies on each others") {
gd::SerializerElement extensionsElement = gd::Serializer::FromJSON(
R"([
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension4DependsOn1And3",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": [
{
"areaMaxX": 64,
"areaMaxY": 64,
"areaMaxZ": 64,
"areaMinX": 0,
"areaMinY": 0,
"areaMinZ": 0,
"defaultName": "Joystick",
"description": "Joystick for touchscreens.",
"fullName": "Multitouch Joystick",
"name": "SpriteMultitouchJoystick",
"eventsFunctions": [],
"propertyDescriptors": [],
"objects": [
{
"name": "Thumb",
"type": "OtherExtension::Whatever"
},
{
"name": "Thumb2",
"type": "Extension1DependsNothing::Whatever"
}
],
"objectsFolderStructure": {
"folderName": "__ROOT",
"children": []
},
"objectsGroups": [],
"layers": [],
"instances": []
},
{
"areaMaxX": 64,
"areaMaxY": 64,
"areaMaxZ": 64,
"areaMinX": 0,
"areaMinY": 0,
"areaMinZ": 0,
"defaultName": "Joystick",
"description": "Joystick for touchscreens.",
"fullName": "Multitouch Joystick",
"name": "SpriteMultitouchJoystick",
"eventsFunctions": [],
"propertyDescriptors": [],
"objects": [
{
"name": "Thumb",
"type": "OtherExtension::Whatever"
},
{
"name": "Thumb2",
"type": "Extension3DependingOn2::Whatever"
}
],
"objectsFolderStructure": {
"folderName": "__ROOT",
"children": []
},
"objectsGroups": [],
"layers": [],
"instances": []
}
]
},
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension3DependingOn2",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": [
{
"areaMaxX": 64,
"areaMaxY": 64,
"areaMaxZ": 64,
"areaMinX": 0,
"areaMinY": 0,
"areaMinZ": 0,
"defaultName": "Joystick",
"description": "Joystick for touchscreens.",
"fullName": "Multitouch Joystick",
"name": "SpriteMultitouchJoystick",
"eventsFunctions": [],
"propertyDescriptors": [],
"objects": [
{
"name": "Thumb",
"type": "OtherExtension::Whatever"
},
{
"name": "Thumb2",
"type": "Extension2DependsNothing::Whatever"
}
],
"objectsFolderStructure": {
"folderName": "__ROOT",
"children": []
},
"objectsGroups": [],
"layers": [],
"instances": []
}
]
},
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension2DependsNothing",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": []
},
{
"author": "",
"category": "Input",
"extensionNamespace": "",
"fullName": "3D character keyboard mapper",
"helpPath": "",
"iconUrl": "fake-icon-url",
"name": "Extension1DependsNothing",
"previewIconUrl": "fake-preview-icon-url",
"shortDescription": "3D platformer and 3D shooter keyboard controls.",
"version": "1.0.0",
"description": "3D platformer and 3D shooter keyboard controls.",
"tags": [],
"authorIds": [],
"dependencies": [],
"globalVariables": [],
"sceneVariables": [],
"eventsFunctions": [],
"eventsBasedBehaviors": [],
"eventsBasedObjects": []
}
])");
std::vector<gd::String> orderedNames =
gd::Project::GetUnserializingOrderExtensionNames(extensionsElement);
REQUIRE(orderedNames.size() == 4);
REQUIRE(orderedNames[0] == "Extension2DependsNothing");
REQUIRE(orderedNames[1] == "Extension1DependsNothing");
REQUIRE(orderedNames[2] == "Extension3DependingOn2");
REQUIRE(orderedNames[3] == "Extension4DependsOn1And3");
}
}

View File

@@ -1,30 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "catch.hpp"
TEST_CASE("SourceFile", "[common]") {
SECTION("Basics") {
gd::Project project;
project.InsertNewSourceFile("test.cpp", "C++");
project.InsertNewSourceFile("test.js", "Javascript");
REQUIRE(project.HasSourceFile("test.cpp", "C++") == true);
REQUIRE(project.HasSourceFile("test.cpp", "JS") == false);
REQUIRE(project.HasSourceFile("test.cpp") == true);
gd::SourceFile& cppSourceFile = project.GetSourceFile("test.cpp");
REQUIRE(cppSourceFile.GetFileName() == "test.cpp");
REQUIRE(cppSourceFile.GetLanguage() == "C++");
project.RemoveSourceFile("test.cpp");
REQUIRE(project.HasSourceFile("test.cpp") == false);
REQUIRE(project.HasSourceFile("test.js") == true);
}
}

View File

@@ -1540,7 +1540,7 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, scene);
REQUIRE(&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName("MyVariable") == &scene.GetVariables());
.GetVariablesContainerFromVariableOrPropertyOrParameterName("MyVariable") == &scene.GetVariables());
// Do the changes and launch the refactoring.
scene.GetVariables().ResetPersistentUuid();
@@ -1722,7 +1722,8 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
auto &extension = project.InsertNewEventsFunctionsExtension("Extension", 0);
extension.GetSceneVariables().InsertNew("MySceneVariable").SetValue(123);
auto &function = extension.InsertNewEventsFunction("MyFunction", 0);
auto &function =
extension.GetEventsFunctions().InsertNewEventsFunction("MyFunction", 0);
gd::StandardEvent &event =
dynamic_cast<gd::StandardEvent &>(function.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard"));

File diff suppressed because it is too large Load Diff

View File

@@ -112,10 +112,25 @@ namespace gdjs {
* @return The Z position of the rendered object.
*/
getDrawableZ(): float {
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
let minZ = 0;
if (this._innerArea) {
minZ = this._innerArea.min[2];
} else {
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
minZ = this._minZ;
}
const absScaleZ = this.getScaleZ();
if (!this._flippedZ) {
return this._z + minZ * absScaleZ;
} else {
return (
this._z +
(-minZ - this.getUnscaledDepth() + 2 * this.getUnscaledCenterZ()) *
absScaleZ
);
}
return this._z + this._minZ;
}
/**
@@ -238,10 +253,39 @@ namespace gdjs {
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* @return the internal top bound of the object according to its children.
*/
getInnerAreaMinZ(): number {
if (this._innerArea) {
return this._innerArea.min[2];
}
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return this._minZ;
}
/**
* @return the internal bottom bound of the object according to its children.
*/
getInnerAreaMaxZ(): number {
if (this._innerArea) {
return this._innerArea.max[2];
}
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return this._maxZ;
}
/**
* @return the internal width of the object according to its children.
*/
getUnscaledDepth(): float {
if (this._innerArea) {
return this._innerArea.max[2] - this._innerArea.min[2];
}
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
@@ -280,6 +324,9 @@ namespace gdjs {
if (this.hasCustomRotationCenter()) {
return this._customCenterZ;
}
if (this._innerArea) {
return (this._innerArea.min[2] + this._innerArea.max[2]) / 2;
}
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}

View File

@@ -2332,9 +2332,9 @@ module.exports = {
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
this._faceResourceNames = ['', '', '', '', '', ''];
this._faceVisibilities = [true, true, true, true, true, true];
this._shouldRepeatTextureOnFace = [true, true, true, true, true, true];
this._faceResourceNames = new Array(6).fill(null);
this._faceVisibilities = new Array(6).fill(null);
this._shouldRepeatTextureOnFace = new Array(6).fill(null);
this._facesOrientation = 'Y';
this._backFaceUpThroughWhichAxisRotation = 'X';
this._shouldUseTransparentTexture = false;

View File

@@ -110,13 +110,15 @@ namespace gdjs {
}
//Calculate the distances from the window's bounds.
const topLeftPixel = this._convertCoords(
instanceContainer,
layer,
this.owner.getDrawableX(),
this.owner.getDrawableY(),
workingPoint
);
const topLeftPixel = this._relativeToOriginalWindowSize
? [this.owner.getDrawableX(), this.owner.getDrawableY()]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX(),
this.owner.getDrawableY(),
workingPoint
);
// Left edge
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
@@ -141,13 +143,18 @@ namespace gdjs {
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const bottomRightPixel = this._convertCoords(
instanceContainer,
layer,
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
workingPoint
);
const bottomRightPixel = this._relativeToOriginalWindowSize
? [
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
]
: this._convertInverseCoords(
instanceContainer,
layer,
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
workingPoint
);
// Right edge
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
@@ -226,17 +233,17 @@ namespace gdjs {
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const topLeftCoord = this._convertInverseCoords(
const topLeftCoord = this._convertCoords(
instanceContainer,
layer,
leftPixel,
topPixel,
workingPoint
);
const left = topLeftCoord[0];
const top = topLeftCoord[1];
let left = topLeftCoord[0];
let top = topLeftCoord[1];
const bottomRightCoord = this._convertInverseCoords(
const bottomRightCoord = this._convertCoords(
instanceContainer,
layer,
rightPixel,

View File

@@ -0,0 +1,39 @@
declare namespace bondage {
export class Runner {
yarnNodes: any;
variables: any;
functions: any;
visited: any;
load(data: any[]): void;
setVariableStorage(storage: any): void;
registerFunction(name: string, func): void;
run(startNode: string): any;
evalNodes(nodes: any[], yarnNodeData: any): any;
handleSelections(selections: any[]): any;
evaluateAssignment(node: any): any;
evaluateConditional(node: any): any;
evaluateExpressionOrLiteral(node): any;
}
export class Result {}
export class TextResult extends Result {
text: string;
data: any;
lineNum: number;
}
export class CommandResult extends Result {
text: string;
data: any;
lineNum: number;
}
export class OptionsResult extends Result {
options: string[];
lineNum: number[];
selected: number;
select(index: number): void;
}
}

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