Compare commits

...

207 Commits

Author SHA1 Message Date
Florian Rival
ffa7708a6d Add back the project manager icon in the home page for now
To avoid disturbing existing users too much and because otherwise it's super hidden in the main menu.
Will be better once "existing projects" listing is improved with a button to open the project manager.

Don't show in changelog
2022-12-08 16:13:13 +01:00
Florian Rival
2185c25e35 Improve menus on the web-app
* Faster navigation: submenus open when their item is hovered.
* Shortcuts are now shown like on the desktop app.
* Menu items height are fixed and always the same.
2022-12-08 16:00:20 +01:00
Florian Rival
db11465b87 Improve the split buttons and the preview button to make it clearer (#4642)
Don't show in changelog
2022-12-08 15:33:04 +01:00
AlexandreS
48c532ad71 Add object name in instruction keys and ids to avoid duplicates (#4623)
Do not show in changelog
2022-12-08 14:59:00 +01:00
AlexandreS
72319df6f1 Add possibility to install any resource in project (#4640)
Do not show in changelog
2022-12-08 12:23:14 +01:00
Florian Rival
c596335b7b Add some space to drag the titlebar even if a lot of tabs are opened
Don't show in changelog
2022-12-08 11:21:00 +01:00
github-actions[bot]
a9b42de64b Update translations [skip ci] (#4639)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-12-08 10:40:17 +01:00
AlexandreS
64ea3461d0 Make autocomplete dropdowns more visible (#4634)
Do not show in changelog
2022-12-08 09:34:25 +01:00
Florian Rival
cef473ca0e Update GitHub screenshot [skip ci]
Don't show in changelog
2022-12-07 18:12:14 +01:00
github-actions[bot]
9378b325d0 Update translations [skip ci] (#4635)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-12-07 17:56:45 +01:00
Clément Pasteau
238043655c Send gdevelopVersion for in-app-tutorials (#4636)
Do not show in changelog
2022-12-07 17:39:52 +01:00
Florian Rival
731a64aa0f Redesign the top tabs and toolbars in the app, so the app looks modern and takes less space on the top of the screens (#4630)
* On desktop, the titlebar is now gone and replaced by the tabs and a button to open the main menu of the app.
* On the web-app, if installed (available on Chrome and Edge), the titlebar can also be retracted. Whatever your system or device, the menu can be accessed from the top left.
* The top toolbars have been entirely redesigned to be integrated below the tabs - which is more intuitive for new users. All the icons have been reworked to be cleaner, take less space, and be more modern.
* The debugger toolbar was also improved to display just Play/Pause according to the game state.
* Finally, the project manager was slightly redesigned to use new icons.
2022-12-07 17:22:38 +01:00
github-actions[bot]
e1e7344ca5 Update translations [skip ci] (#4617)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-12-07 17:04:51 +01:00
Clément Pasteau
a5d5f0b7a5 Fix allowing to type "/" inside the path for project folder (#4633)
Do not show in changelog
2022-12-07 16:49:02 +01:00
Clément Pasteau
3dcbe46fc7 Improve warning when a project autosave is detected on Desktop (#4632)
* Autosave will not be displayed if the files are the same
* Prevent showing a warning when a project with an autosave is opened after being zipped
* Improve overall wording and dialogs
2022-12-07 16:46:38 +01:00
Florian Rival
4d3d96a889 Fix SemiControlledAutoComplete not using the value entered when pressing Enter in a InlineParameterEditor
Don't show in changelog
2022-12-07 12:03:10 +01:00
Clément Pasteau
23631cc2ce Allow entering a donation link on the user profile (#4628)
* Accepts any type of donation page, Ko-fi, Patreon, Paypal, Github...
* Once entered, it will appear on your Liluo.io Game and Profile pages
2022-12-06 17:21:34 +01:00
AlexandreS
3937b59d73 Use same border radius between command palette and autocomplete picker (#4629)
Do not show in changelog
2022-12-06 11:44:54 +01:00
Florian Rival
3b594ded7d Add Enter as a key to validate changes to a parameter in an event sheet (#4621)
* Shift+Enter can be used to enter new lines in expressions when editing a parameter inline
2022-12-06 10:37:55 +01:00
AlexandreS
e8a7cad17c Restore possibility to search texts with parenthesis in events parameters (#4625) 2022-12-06 10:30:18 +01:00
Clément Pasteau
2557cf556c Allow leaderboard color customisation (#4612)
* Silver and Gold subscribers can now customize their leaderboard with 4 different colors
2022-12-05 18:05:37 +01:00
AlexandreS
7c51f0af81 Add link to asset store license + Add message when no projects in the list (#4624)
Do not show in changelog
2022-12-05 16:56:41 +01:00
D8H
dc146a7411 Add a command to generate an action and an expression for a custom object property (#4620)
* Don't show in changelog
2022-12-04 20:27:17 +01:00
D8H
7620bac88a Enable serialization of JS code events and extension descriptions as arrays of strings (#4613)
* Only show in developer changelog
2022-12-04 13:38:08 +01:00
AlexandreS
c51e4be22d Display password prompt when requesting checkout session for asset pack (#4616)
Do not show in changelog
2022-12-02 18:17:38 +01:00
github-actions[bot]
98499dfc53 Update translations [skip ci] (#4610)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-12-02 12:17:48 +01:00
AlexandreS
36de82c93f Few fixes (#4611)
Do not show in changelog
2022-12-02 11:55:43 +01:00
Daniel R
7e97edcad0 Add a new tab "Folders" in Preferences to allow to choose the default folder where local projects are created (#4582) 2022-12-02 11:50:58 +01:00
D8H
5abb0fd9e3 Fix boolean property condition sentences (#4605) 2022-12-02 11:45:13 +01:00
D8H
d80c21244f Boolean property generated actions no longer miss the "Value" parameter (#4594) 2022-12-02 11:44:17 +01:00
D8H
70f4d545b4 Display some behavior properties in one row (#4606) 2022-12-02 11:37:32 +01:00
github-actions[bot]
85355c3f17 Update translations [skip ci] (#4609)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-12-01 17:27:19 +01:00
github-actions[bot]
044cc5354f Update translations [skip ci] (#4607)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-12-01 17:11:46 +01:00
AlexandreS
5ff02b11fc Fix a few MUI component imports (#4608)
Do not show in changelog
2022-12-01 17:05:23 +01:00
github-actions[bot]
9bf24f9baf Update translations [skip ci] (#4604)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-12-01 16:30:12 +01:00
AlexandreS
958482ee44 Suggest a new and more complete 3-chapter in-app tutorial (#4476) 2022-12-01 16:18:42 +01:00
AlexandreS
7a1fb5b033 Improve loading of Home Build section to reduce layout shifts (#4603) 2022-12-01 11:28:49 +01:00
D8H
33b2fb0168 Show unit of measurements on properties of built-in extensions (#4576) 2022-11-30 21:18:54 +01:00
Clément Pasteau
5cad2be194 Improve Toggle styling (#4602) 2022-11-30 15:23:43 +01:00
Clément Pasteau
2b38aa5445 Simplify assets loading to prevent unnecessary calls (#4601)
Do not show in changelog
2022-11-30 14:26:38 +01:00
D8H
32279f02e9 Fix function name collision between event-based objects from the same extension (#4598) 2022-11-30 14:10:52 +01:00
Clément Pasteau
7452358656 Trigger Rename & Focus for Object, Group, Layout, Events (#4591) 2022-11-30 10:52:15 +01:00
D8H
d2ec483d71 Changing custom object opacity now works (#4597) 2022-11-29 10:43:17 +01:00
github-actions[bot]
4cb62d15bf Update translations [skip ci] (#4589)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-28 15:53:08 +01:00
AlexandreS
96bdbfc70b Correctly deactivate cloud projects on desktop app in prod (#4592)
Do not show in changelog
2022-11-28 15:47:55 +01:00
Clément Pasteau
a64815f500 Fix missing text formatting (#4587)
Do not show in changelog
2022-11-28 15:47:39 +01:00
Clément Pasteau
5ab8a3cd73 Improve initial dialog logic to avoid opening tab on subsequent profile openings. (#4590)
Do not show in changelog
2022-11-28 15:45:12 +01:00
D8H
7d0ecf113a Add a command to generate an action and an expression for a property (#4565) 2022-11-28 14:30:03 +01:00
D8H
de30049182 Show parameters description in expression documentations (#4585) 2022-11-28 13:19:42 +01:00
Clément Pasteau
c2a1fb63e1 Fix opening initial dialog again after closing it (#4588) 2022-11-28 12:34:22 +01:00
github-actions[bot]
8562019953 Update translations [skip ci] (#4572)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-11-28 10:34:59 +01:00
Florian Rival
f8232953b5 Add support for cloud projects to the desktop app (#4240)
* Also add support for the asset store in the objects editors - like on the web-app.
2022-11-27 18:09:27 +01:00
AlexandreS
d52ac6fc3e Send subscription dialog opening reason (#4586)
Do not show in changelog
2022-11-25 18:12:03 +01:00
Clément Pasteau
be4aab6d5c Allow opening up the feedback tab from URL query params (#4574)
Do not show in changelog
2022-11-25 17:18:32 +01:00
Clément Pasteau
4ed8ff15dd Improve Asset pack install dialog (#4583)
Do not show in changelog
2022-11-25 11:33:00 +01:00
Clément Pasteau
2dd1105a10 Improve Game Feedback management (#4579)
* Display the average game ratings at the top
* Add an option to mark all as read
* improve readability by adding a number for each rating
2022-11-25 10:12:11 +01:00
Clément Pasteau
0fce6aaaaa Fix missing scroll on points and collision mask editors (#4578) 2022-11-24 15:51:22 +01:00
Florian Rival
29797b7a2f Add analytics data when opening an asset pack or an asset (#4573)
Don't show in changelog
2022-11-24 09:41:24 +01:00
Clément Pasteau
7df7f9f458 Remove help button on additional info dialog (#4570)
Do not show in changelog
2022-11-23 15:20:52 +01:00
github-actions[bot]
fc61fa2c54 Update translations [skip ci] (#4571)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-23 15:20:36 +01:00
D8H
fccd3b8f2e Fix the drag and drop of the extension editor. (#4568)
* Items were not drop at the right position when they were moved down.
2022-11-23 15:08:33 +01:00
github-actions[bot]
5ca91cfac4 Update translations [skip ci] (#4569)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-23 15:08:17 +01:00
Clément Pasteau
628d18ac5a Improve login and signup flow (#4566)
* Small design improvements to the Log in & Sign up flows
* A dialog is now shown for every user on signup to gather a bit of information about who they are.
  * This information stays of course private and is helpful to gather information about GDevelop's usage
2022-11-23 14:58:17 +01:00
github-actions[bot]
61d5c08549 Update translations [skip ci] (#4555)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-23 14:54:05 +01:00
Clément Pasteau
34497c2783 Fix cleaning the resource URL when added from the resource store (#4567)
Do not show in changelog
2022-11-22 19:11:18 +01:00
Clément Pasteau
4627facfd5 Improve Searchbar's design and usability (#4553) 2022-11-21 16:16:07 +01:00
D8H
aee15424f2 Save the scroll position of the asset store (#4528) 2022-11-18 17:06:10 +01:00
D8H
6c44f6e937 Physics2 behavior no longer step before the first frame (#4559)
* It allows events to access to the initial object positions.
2022-11-18 17:05:28 +01:00
Clément Pasteau
a115df260f Revert time before showing warning in object edition (#4558)
Do not show in changelog
2022-11-18 10:24:56 +01:00
Clément Pasteau
6e7bc9c809 Improve the snackbar messages when saving a cloud project (#4554) 2022-11-16 16:42:23 +01:00
Clément Pasteau
57ce8dcbbf Bump to 5.1.151 (#4552) 2022-11-15 14:50:36 +01:00
Clément Pasteau
f86d0197c1 Fix stories crashing storybook (#4551)
Do not show in changelog
2022-11-15 14:49:05 +01:00
github-actions[bot]
c7b0f345c7 Update translations [skip ci] (#4549)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-15 14:44:40 +01:00
Clément Pasteau
9803b4b8f4 Fix points and collision editor not showing scrollbars (#4550)
Do not show in changelog
2022-11-15 14:30:59 +01:00
D8H
ef4815a37e Tabs no longer disappear when scrolling in the extension editor dialogs (#4545) 2022-11-15 12:00:34 +01:00
D8H
fd7d27e727 Include tags in the search for extensions and examples (#4544) 2022-11-15 11:55:52 +01:00
D8H
ce94c6ae2f Fix a typo in the "Related expression and condition" field (#4548)
* Don't show in changelog
2022-11-15 11:46:10 +01:00
Florian Rival
c638a38275 Add a button to redeem a code for subscriptions (#4542) 2022-11-14 17:57:25 +01:00
github-actions[bot]
e539fdb0e9 Update translations (#4536)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-14 11:22:27 +01:00
Clément Pasteau
2a9193e4c2 Hide warning if backdrop behavior is set to cancel (#4543)
Do not show in changelog
2022-11-14 11:13:35 +01:00
github-actions[bot]
cf8361be0c Update translations (#4534)
Do not show in changelog
2022-11-10 15:57:05 +01:00
Clément Pasteau
2ba5f03e67 Fix animation preview not taking full space (#4535)
Do not show in changelog
2022-11-10 15:53:14 +01:00
Clément Pasteau
d9435eda5a Bump version to 5.1.150 (#4530) 2022-11-10 13:28:04 +01:00
github-actions[bot]
bc807acf8a Update translations [skip ci] (#4512)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-11-10 13:27:52 +01:00
Clément Pasteau
194b60aa72 Rework Dialogs and tabs design (#4520)
* Overall redesign or the dialogs to display a title and more margins, helping buttons be more discernable from the content + cross at the top to close them easily.
* Redesign of the tabs to be more intuitive.
2022-11-10 12:59:54 +01:00
D8H
02bbdfecd4 Allow event-based behaviors to declare scene properties (#4464) 2022-11-10 11:43:16 +01:00
D8H
bf19ec5a68 [PathFinding] Improve pathfinding with a property to smooth the path. (#4515) 2022-11-10 11:40:07 +01:00
Florian Rival
59094110ca Serialize JS code events and extension descriptions as arrays of strings to make collaboration/reviews easier (#4468)
Only show in developer changelog
2022-11-09 18:39:23 +01:00
D8H
a35ead0ab2 Panel sprite objects now respect the resource smoothing option (#4526) 2022-11-09 15:21:31 +01:00
Clément Pasteau
669c10c462 Slightly improve animation preview zoom for the image to fit the canvas (#4527) 2022-11-09 14:16:57 +01:00
Clément Pasteau
2859f2a3b6 Improve Selection of resources in parameters (#4524)
* A button is now available to pick a file
* When there is only 1 external editor like Piskel, the button is made more visible
2022-11-09 10:41:35 +01:00
D8H
be675fc5e6 [Platformer] Make tests with events side effects more realistic (#4518)
* Don't show in changelog
2022-11-08 12:41:48 +01:00
Aurélien Vivet
2be238bc9e Fix Inconsistency in JSON Parsing events wording (#4514) 2022-11-08 11:26:36 +01:00
AlexandreS
5c08fb06d8 Display leaderboard limit in leaderboard admin for free accounts (#4497) 2022-11-07 16:52:18 +01:00
D8H
8ec96a7446 Fix the color property definition editor layout (#4513)
* Don't show in changelog
2022-11-07 15:30:25 +01:00
D8H
792ce82a28 Add a setting to show the custom object editor (#4503) 2022-11-07 12:06:33 +01:00
github-actions[bot]
da444d53cd Update translations [skip ci] (#4511)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-11-07 10:38:28 +01:00
D8H
62a6d42488 Add autocompletion for choices and color property private actiton and condition parameters (#4481) 2022-11-07 10:24:53 +01:00
D8H
53e70c5a8f Add conditions to compare function parameter values (#4498) 2022-11-07 10:21:55 +01:00
Clément Pasteau
74ffba0f17 Fix Posthog alias (#4496)
Do not show in changelog
2022-11-07 10:18:26 +01:00
Clément Pasteau
4e03cf559d Handle new format for public asset resources (#4457)
Do not show in changelog
2022-11-07 10:16:12 +01:00
github-actions[bot]
4f5690503e Update translations [skip ci] (#4459)
* Don't show in changelog
2022-11-06 13:30:23 +01:00
D8H
8ac7284118 Allow to declare private event-based behaviors (#4479)
* It allows event-based objects to use behaviors internally without showing them to extension users.
2022-11-05 20:45:29 +01:00
D8H
16cdc9a047 Fix: parameter default value changes were not saved (#4489)
* Don't show in changelog
2022-11-04 18:16:46 +01:00
Clément Pasteau
ee43afa301 Show a warning when closing an editor dialog with a few unsaved changes (#4490)
* Applies on multiple editors: Object, Object group, Project properties, Variables list and Layers
2022-11-04 16:20:43 +01:00
AlexandreS
92bc846190 Add possibility to use markdown for asset descriptions (#4492)
Do not show in changelog
2022-11-04 14:18:38 +01:00
D8H
beb832be8b Fix flip instruction icons for custom objects (#4482)
* Don't show in changlog
2022-11-03 21:30:43 +01:00
D8H
961c78b468 Fix number expression field. (#4483)
* Don't show in changelog
2022-11-03 21:30:00 +01:00
D8H
2d7467bc27 Avoid to use regex look ahead as Safari doesn't support it. (#4480) 2022-11-03 09:33:16 +01:00
Clément Pasteau
0a9d9b940b Fix Animation Preview not taking full height and being stuck when duplicating an image (#4470) 2022-11-02 13:41:35 +01:00
D8H
f8bd457346 Translate event-based extension categories (#4471) 2022-11-02 10:21:35 +01:00
AlexandreS
6deeae76c5 Do not prevent inline parameter editor to be opened after dragging an action/condition 2022-11-02 10:18:31 +01:00
Clément Pasteau
473b16bbc1 Introduce no-unused-vars ts rule for GDJS & Extensions (#4469)
Do not show in changelog
2022-10-31 14:52:03 +01:00
Clément Pasteau
20a328dcd6 Fix Changelog displaying extensions bullet points properly (#4472) 2022-10-31 14:49:34 +01:00
AlexandreS
578aae7a69 Fix animation finished condition firing too early (#4444)
The condition was firing as soon as the animation was entering its last frame.
It now waits for the last frame to be displayed long enough (based on frame interval duration).
This fix applies to all future "Animation finished" conditions, current ones are not affected.
2022-10-31 09:06:15 +01:00
D8H
27f89b1b0f Fix events sheet warping when it contains a JS event (#4466) 2022-10-30 19:51:13 +01:00
D8H
bc75d6003c Add back parts of a revert for the TextImput fix (#4453)
* Don't show in changelog
2022-10-30 14:20:53 +01:00
D8H
fd03deb4ea Allow event extensions to define conditions and actions with an operator (#3909) 2022-10-29 23:20:10 +02:00
D8H
71f20d7852 Refactor to make some ParameterMetadata attributes private (type, supplementaryInformation and optional) (#4437) 2022-10-29 13:02:18 +02:00
D8H
9d121d0085 Suffix properties private instruction names with "property" to avoid them to overlap public ones (#4436) 2022-10-29 13:01:07 +02:00
Clément Pasteau
6292e338bc Fix importing path correctly on web (#4458) 2022-10-28 17:58:24 +02:00
Clément Pasteau
c5eb0bcc00 Bump version to 5.1.149 (#4451) 2022-10-28 11:23:50 +02:00
github-actions[bot]
a732fda4d9 Update translations [skip ci] (#4430)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-10-28 11:20:33 +02:00
Clément Pasteau
398bff8492 Fix input not being positioned properly (#4450) 2022-10-28 11:11:24 +02:00
D8H
f30e92a953 Make scene properties look the same as behavior properties (#4448)
* For instance, scene properties are used by the Physics2 behavior.
2022-10-27 21:48:52 +02:00
AlexandreS
8210c25acb UI improvements (#4440)
- Make some popovers and panels more discernable from the background
- Use the same drag and drop behavior for layers list as for the objects list on the scene editor
- Add object icons in the list of objects in a group
2022-10-27 10:23:05 +02:00
AlexandreS
6a13940e17 Add possibility to send instances to back or to front (Z order) in context menu (#4443) 2022-10-27 09:47:54 +02:00
AlexandreS
622aa7c08c Add warning message when updating liluo.io thumbnail from the project icons dialog (#4438) 2022-10-26 11:52:49 +02:00
Clément Pasteau
a71558a490 Create condition to know when a draggable object was just dropped (#4441) 2022-10-26 09:31:14 +02:00
Clément Pasteau
37539aa788 Rename Panel actions for consistency (#4439) 2022-10-25 18:45:23 +02:00
AlexandreS
789f819f25 Fix events sheet not wrapping on small screens (#4434) 2022-10-25 10:43:27 +02:00
AlexandreS
52ebfb8100 Remove starting value in tween variable actions
Also:
- Change phrasing for object tweens
2022-10-25 08:53:37 +02:00
Clément Pasteau
ecc5c689d2 Revert service worker update (#4432)
Do not show in changelog
2022-10-24 18:48:30 +02:00
Florian Rival
5b1e169557 Show variables that were used in the events, but not defined, in the autocompletions by default
* If you've not activated this since this was introduced, you can do so in the preferences.
2022-10-24 17:43:30 +02:00
Clément Pasteau
386e23b042 Bump version to 5.1.148 (#4429) 2022-10-24 12:11:18 +02:00
github-actions[bot]
fc76bafc7c Update translations [skip ci] (#4393)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-10-24 12:11:00 +02:00
AlexandreS
de53f4df4f Fix project name not updating in recent project files after saving (#4428) 2022-10-24 12:02:16 +02:00
D8H
9fedf124db Allow assets to declare extension dependencies without behaviors (#4354) 2022-10-22 14:40:47 +02:00
Clément Pasteau
2c68bb4bdd Modify the asset store to handle priced asset packs 2022-10-21 18:16:38 +02:00
AlexandreS
2360cf899f Performance optimization: Prevent pixi from rendering if any dialog is opened (#4415) 2022-10-21 15:01:04 +02:00
AlexandreS
3fc57c2b06 Add some particle emitter actions conditions and expressions (#4419)
- Max number of displayed particles
- Particle rotation min and max speeds
- Additive rendering setting
2022-10-21 14:04:44 +02:00
AlexandreS
b4f41e96ae Fix: Set line particle emitter origin at 0 to prevent rotation offset (#4421) 2022-10-21 12:29:09 +02:00
AlexandreS
b20108ddcb Fix particle emitter incoherent rotation speed of the particles (#4417) 2022-10-21 12:28:09 +02:00
AlexandreS
a58b039994 Update instance properties panel after a layer was created or renamed (#4420) 2022-10-21 12:13:57 +02:00
D8H
a7f218622e Add a button to export custom objects for the asset store (#4363)
* Don't show in changelog
2022-10-21 11:38:32 +02:00
AlexandreS
8d95eb4269 Fix: Ensure particle max force is not zero to prevent PIXI bug (#4418) 2022-10-20 18:20:27 +02:00
AlexandreS
4e46690418 Create homemade solution to display in app tutorials (#4394)
Do not show in changelog
2022-10-20 15:39:12 +02:00
Florian Rival
db1737281e Add word wrap in the code editors to avoid horizontal scrolling (#4413)
* Also fix some text overflowing out of the screen for some languages.

Fix #4412
2022-10-19 14:38:57 +02:00
AlexandreS
d5eecda570 Improve new scrollbars on scene editor canvas (#4411)
Do not show in changelog
2022-10-19 11:22:07 +02:00
D8H
80cb6d697c Handle custom objects at runtime (#4294)
* Don't show in changelog
2022-10-18 13:36:48 +02:00
Clément Pasteau
6b08fec747 Add missing translation for pasting an object (#4408) 2022-10-18 12:31:00 +02:00
D8H
2c92ce74ce Fix custom object configurations copy constructor (#4404)
* Don't show in changelog
2022-10-17 16:26:27 +02:00
D8H
30643ced07 Add some stories for event-based objects (#4401)
* Don't show in changelog
2022-10-16 23:57:18 +02:00
Peter Anderson
bf27761cff Fix grammar: change 'let' to 'leave'. (#4399) 2022-10-16 13:43:14 +02:00
Florian Rival
58df3aeca1 Show an error message if a sentence of a function of an extension is using a parameter that does not exist (#4395) 2022-10-14 23:15:30 +02:00
AlexandreS
69f8961e9e Fix: Keep variable name checks only for top level variables (#4392) 2022-10-14 15:57:59 +02:00
github-actions[bot]
1abb39ec17 Update translations [skip ci] (#4360)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-10-14 14:31:07 +02:00
Clément Pasteau
c5226c1e45 Fix clearing storage provider on project close (#4390)
Do not show in changelog
2022-10-14 14:06:02 +02:00
Florian Rival
d40789005b Add link to Liluo.io in the README
Don't show in changelog
2022-10-13 18:24:02 +02:00
D8H
fcbe5c364e Add a field to define behavior property descriptions (#4389) 2022-10-13 15:38:09 +02:00
D8H
df3433c55f Add autocompletion for timers, tweens and other extensions identifiers (#4386) 2022-10-13 13:39:53 +02:00
D8H
7d892fd976 Update extension field hints to fit the good practices (#4375) 2022-10-13 13:19:51 +02:00
D8H
56320d7253 Move some stories to LayoutEditor and ObjectEditor (#4388)
* Add a story for a custom object property editor.
* Add a story for the particle emitter editor.
2022-10-13 09:51:49 +02:00
AlexandreS
05acd061bd Fix leaderboard entry creation duplicate conditions to avoid spamming servers 2022-10-12 18:03:01 +02:00
Clément Pasteau
7991ceb351 Improve the editor scrollbars to be more intuitive (#4377) 2022-10-12 16:35:27 +02:00
Clément Pasteau
bf6d18ccbe Notify when a new version is available to be automatically installed on the web (#4371) 2022-10-12 16:31:58 +02:00
AlexandreS
536b7dcc62 Fix: Wrap text of game feedback when it's a single really long word 2022-10-12 11:45:51 +02:00
AlexandreS
a157f32d4a Fix: Update window border in scene editor when the project resolution is changed 2022-10-12 11:02:37 +02:00
Clément Pasteau
49f579f32d Allow using private assets in the asset store (#4355)
* Once a pack is bought, the assets are visible in the asset store directly
* The asset can be added when the project is saved locally, or on the cloud
* Fix Storage Provider being correctly reset when closing/opening a project
2022-10-11 13:59:40 +02:00
Clément Pasteau
8f4ecd373f Get Game categories from backend allowing more flexibility for jams (#4370)
Do not show in changelog
2022-10-07 18:32:33 +02:00
Aurélien Vivet
a3a53415b1 Add missing id for the onboarding (#4369)
Don't show in changelog
2022-10-07 15:49:13 +02:00
Aurélien Vivet
c278c0a432 Fix parameters order in the events sheet sentence of "Change the gradient of the text" action (#4368) 2022-10-07 10:40:39 +02:00
AlexandreS
862f270b83 Fix extension icon store displaying white hard-to-see icons on light theme 2022-10-07 09:56:55 +02:00
AlexandreS
7232dbc2fa Fix: Reinitialize object default hitboxes when reinitializing it (#4362) 2022-10-06 17:38:39 +02:00
AlexandreS
78cbe48718 Fix points editor not updating correctly when deleting points 2022-10-06 12:40:04 +02:00
Florian Rival
e153d295de Bump newIDE version 2022-10-06 10:35:33 +02:00
AlexandreS
1b8510655e Revert "Add expression and condition to get highest z order of a layer (#4346)" (#4359)
Don't show in changelog
2022-10-06 10:34:41 +02:00
github-actions[bot]
bd88127563 Update translations [skip ci] (#4342)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-10-06 10:25:39 +02:00
AlexandreS
01b9f09604 Fix: Add parameter to file reading actions to remove CR characters from files (#4352)
⚠️ If you had encountered an issue linked to this and changed your events to fix it, you might need to update your project and set this new parameter to "No"
2022-10-05 14:11:21 +02:00
Florian Rival
0db30f02c9 Add support for opening onboarding directly on the web-app
https://editor.gdevelop.io/?initial-dialog=onboarding

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

Do not show in changelog
2022-09-27 15:19:06 +02:00
AlexandreS
6996ff452d Fix various memory leaks when using the app (#4323) 2022-09-23 18:34:34 +02:00
AlexandreS
da7934c6ac Add context menu items to manipulate the view on the scene editor (#4307)
- Return to initial position (view matches the game resolution)
- Fit zoom to selected instances
- Fit zoom to the whole scene
- Select all instances of an object on the scene (in the context menu of an object)
2022-09-23 13:27:40 +02:00
Clément Pasteau
90bebcb404 Create GDevelop Authentication extension
* This is an experimental extension!
* It allows you to provide a login/register form to your players, with 1 action
* It connects the player automatically when they launch your game again
* It also provides a new action to submit a leaderboard entry without having to enter a username
* This is the beginning of Player Authentication and more features will come allowing creators to interact with their players
2022-09-23 10:44:58 +02:00
D8H
a29e7aae44 Bump newIDE version (#4317) 2022-09-22 12:22:11 +02:00
D8H
52201e2a36 Fix tiles rotation that was no longer applied in tile maps (#4315) 2022-09-22 10:25:38 +02:00
1040 changed files with 52985 additions and 22523 deletions

View File

@@ -43,7 +43,7 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
std::size_t relationalOperatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "relationalOperator")
if (instrInfos.parameters[i].GetType() == "relationalOperator")
relationalOperatorIndex = i;
}
// Ensure that there is at least one parameter after the relational operator
@@ -95,7 +95,7 @@ gd::String EventsCodeGenerator::GenerateOperatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -164,7 +164,7 @@ gd::String EventsCodeGenerator::GenerateCompoundOperatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -215,7 +215,7 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -293,7 +293,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
// Verify that there are no mismatchs between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].type)) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter =
condition.GetParameter(pNb).GetPlainString();
@@ -303,11 +303,11 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
}
@@ -485,7 +485,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
// Verify that there are no mismatchs between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].type)) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
@@ -493,11 +493,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
}
@@ -670,6 +670,18 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
return outputCode;
}
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
if (operatorString == "=") {
return "==";
}
if (operatorString != "<" && operatorString != ">" &&
operatorString != "<=" && operatorString != ">=" && operatorString != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
return "==";
}
return operatorString;
}
gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::Expression& parameter,
const gd::ParameterMetadata& metadata,
@@ -679,31 +691,24 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
supplementaryParametersTypes) {
gd::String argOutput;
if (ParameterMetadata::IsExpression("number", metadata.type)) {
if (ParameterMetadata::IsExpression("number", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
} else if (ParameterMetadata::IsExpression("string", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("variable", metadata.type)) {
} else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, metadata.type, parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.type)) {
*this, context, metadata.GetType(), parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.GetType())) {
// It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects.
argOutput =
GenerateObject(parameter.GetPlainString(), metadata.type, context);
} else if (metadata.type == "relationalOperator") {
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
argOutput != "<=" && argOutput != ">=" && argOutput != "!=") {
cout << "Warning: Bad relational operator: Set to == by default." << endl;
argOutput = "==";
}
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
} else if (metadata.GetType() == "relationalOperator") {
argOutput += GenerateRelationalOperatorCodes(parameter.GetPlainString());
argOutput = "\"" + argOutput + "\"";
} else if (metadata.type == "operator") {
} else if (metadata.GetType() == "operator") {
argOutput += parameter.GetPlainString();
if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
argOutput != "/" && argOutput != "*") {
@@ -712,28 +717,28 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
}
argOutput = "\"" + argOutput + "\"";
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
} else if (ParameterMetadata::IsBehavior(metadata.GetType())) {
argOutput = GenerateGetBehaviorNameCode(parameter.GetPlainString());
} else if (metadata.type == "key") {
} else if (metadata.GetType() == "key") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "audioResource" ||
metadata.type == "bitmapFontResource" ||
metadata.type == "fontResource" ||
metadata.type == "imageResource" ||
metadata.type == "jsonResource" ||
metadata.type == "videoResource" ||
} else if (metadata.GetType() == "audioResource" ||
metadata.GetType() == "bitmapFontResource" ||
metadata.GetType() == "fontResource" ||
metadata.GetType() == "imageResource" ||
metadata.GetType() == "jsonResource" ||
metadata.GetType() == "videoResource" ||
// Deprecated, old parameter names:
metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "mouse") {
} else if (metadata.GetType() == "mouse") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "yesorno") {
} else if (metadata.GetType() == "yesorno") {
auto parameterString = parameter.GetPlainString();
argOutput += (parameterString == "yes" || parameterString == "oui")
? GenerateTrue()
: GenerateFalse();
} else if (metadata.type == "trueorfalse") {
} else if (metadata.GetType() == "trueorfalse") {
auto parameterString = parameter.GetPlainString();
// This is duplicated in AdvancedExtension.cpp for GDJS
argOutput += (parameterString == "True" || parameterString == "Vrai")
@@ -741,21 +746,21 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
: GenerateFalse();
}
// Code only parameter type
else if (metadata.type == "inlineCode") {
argOutput += metadata.supplementaryInformation;
else if (metadata.GetType() == "inlineCode") {
argOutput += metadata.GetExtraInfo();
} else {
// Try supplementary types if provided
if (supplementaryParametersTypes) {
for (std::size_t i = 0; i < supplementaryParametersTypes->size(); ++i) {
if ((*supplementaryParametersTypes)[i].first == metadata.type)
if ((*supplementaryParametersTypes)[i].first == metadata.GetType())
argOutput += (*supplementaryParametersTypes)[i].second;
}
}
// Type unknown
if (argOutput.empty()) {
if (!metadata.type.empty())
cout << "Warning: Unknown type of parameter \"" << metadata.type
if (!metadata.GetType().empty())
cout << "Warning: Unknown type of parameter \"" << metadata.GetType()
<< "\"." << std::endl;
argOutput += "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
}
@@ -1030,7 +1035,7 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
for (std::size_t i = 0; i < instrInfos.parameters.size();
++i) // Some conditions already have a "conditionInverted" parameter
{
if (instrInfos.parameters[i].type == "conditionInverted")
if (instrInfos.parameters[i].GetType() == "conditionInverted")
conditionAlreadyTakeCareOfInversion = true;
}
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
@@ -1051,7 +1056,7 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
// Prepare call
// Add a static_cast if necessary
gd::String objectFunctionCallNamePart =
(!instrInfos.parameters[0].supplementaryInformation.empty())
(!instrInfos.parameters[0].GetExtraInfo().empty())
? "static_cast<" + objInfo.className + "*>(" +
GetObjectListName(objectName, context) + "[i])->" +
instrInfos.codeExtraInformation.functionCallName

View File

@@ -481,6 +481,9 @@ class GD_CORE_API EventsCodeGenerator {
*/
size_t GenerateSingleUsageUniqueIdForEventsList();
virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString);
protected:
/**
* \brief Generate the code for a single parameter.

View File

@@ -55,6 +55,10 @@ void Instruction::SetParameter(std::size_t nb, const gd::Expression& val) {
parameters[nb] = val;
}
void Instruction::AddParameter(const gd::Expression& val) {
parameters.push_back(val);
}
std::shared_ptr<Instruction> GD_CORE_API
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction) {
std::shared_ptr<Instruction> copy =

View File

@@ -123,6 +123,11 @@ class GD_CORE_API Instruction {
*/
void SetParameter(std::size_t nb, const gd::Expression& val);
/** Add a parameter at the end
* \param val The new value of the parameter
*/
void AddParameter(const gd::Expression& val);
/** \brief Get a reference to the std::vector containing the parameters.
* \return A std::vector containing the parameters
*/

View File

@@ -184,8 +184,8 @@ void EventsListSerialization::UpdateInstructionsFromGD2x(
for (std::size_t j = 0;
j < parameters.size() && j < metadata.parameters.size();
++j) {
if (metadata.parameters[j].type == "relationalOperator" ||
metadata.parameters[j].type == "operator") {
if (metadata.parameters[j].GetType() == "relationalOperator" ||
metadata.parameters[j].GetType() == "operator") {
if (j == parameters.size() - 1) {
std::cout << "ERROR: No more parameters after a [relational]operator "
"when trying to update an instruction from GD2.x";

View File

@@ -81,7 +81,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddExpression(
"GetArgumentAsNumber",
_("Get function parameter value"),
_("Get function parameter (also called \"argument\") value"),
_("Get function parameter (also called \"argument\") value."),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name");
@@ -90,10 +90,34 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddStrExpression(
"GetArgumentAsString",
_("Get function parameter text"),
_("Get function parameter (also called \"argument\") text "),
_("Get function parameter (also called \"argument\") text."),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name");
extension
.AddCondition(
"CompareArgumentAsNumber",
_("Compare function parameter value"),
_("Compare function parameter (also called \"argument\") value."),
_("Parameter _PARAM0_"),
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.UseStandardRelationalOperatorParameters("number");
extension
.AddCondition(
"CompareArgumentAsString",
_("Compare function parameter text"),
_("Compare function parameter (also called \"argument\") text."),
_("Parameter _PARAM0_"),
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.UseStandardRelationalOperatorParameters("string");
}
} // namespace gd

View File

@@ -886,7 +886,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("expression", _("Time in seconds"))
.SetHidden();
@@ -900,7 +900,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("relationalOperator", _("Sign of the test"), "time")
.AddParameter("expression", _("Time in seconds"))
.SetManipulatedType("number");
@@ -913,7 +913,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timerPaused24.png",
"res/conditions/timerPaused.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction(
@@ -926,7 +926,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "objectTimer");
obj.AddAction("PauseObjectTimer",
_("Pause an object timer"),
@@ -936,7 +936,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/pauseTimer24.png",
"res/actions/pauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction("UnPauseObjectTimer",
@@ -947,7 +947,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/unPauseTimer24.png",
"res/actions/unPauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction("RemoveObjectTimer",
@@ -958,7 +958,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddExpression("X",
@@ -1127,7 +1127,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Object timers"),
"res/actions/time.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "objectTimer");
obj.AddExpression("AngleToObject",
_("Angle between two objects"),

View File

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

View File

@@ -96,7 +96,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
"JSONToVariableStructure",
_("Convert JSON to a scene variable"),
_("Parse a JSON object and store it into a scene variable"),
_("Parse JSON string _PARAM0_ and store it into variable _PARAM1_"),
_("Convert JSON string _PARAM0_ and store it into variable _PARAM1_"),
"",
"res/actions/net24.png",
"res/actions/net.png")
@@ -108,7 +108,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
.AddAction("JSONToGlobalVariableStructure",
_("Convert JSON to global variable"),
_("Parse a JSON object and store it into a global variable"),
_("Parse JSON string _PARAM0_ and store it into global "
_("Convert JSON string _PARAM0_ and store it into global "
"variable _PARAM1_"),
"",
"res/actions/net24.png",

View File

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

View File

@@ -22,6 +22,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/sprite");
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
.SetIcon("CppPlatform/Extensions/spriteicon.png");
gd::ObjectMetadata& obj =
extension
@@ -322,6 +324,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/conditions/animation24.png",
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.MarkAsSimple()
.SetHidden();
obj.AddCondition("AnimationEnded2",
_("Animation finished"),
_("Check if the animation being played by the Sprite object "
"is finished."),
_("The animation of _PARAM0_ is finished"),
_("Animations and images"),
"res/conditions/animation24.png",
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.MarkAsSimple();

View File

@@ -36,7 +36,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Time in seconds"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.SetHidden();
extension
@@ -50,7 +50,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.AddParameter("relationalOperator", _("Sign of the test"), "time")
.AddParameter("expression", _("Time in seconds"))
.SetManipulatedType("number");
@@ -78,7 +78,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timerPaused24.png",
"res/conditions/timerPaused.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -93,7 +93,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
extension
.AddAction("PauseTimer",
@@ -105,7 +105,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/pauseTimer24.png",
"res/actions/pauseTimer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -118,7 +118,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/unPauseTimer24.png",
"res/actions/unPauseTimer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -131,7 +131,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -155,7 +155,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"",
"res/timer_black.svg",
"res/timer_black.svg")
.AddParameter("expression", "Time to wait in seconds")
.AddParameter("expression", _("Time to wait in seconds"))
.SetHelpPath("/all-features/timers-and-time/wait-action");
extension
@@ -191,7 +191,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"",
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
extension
.AddExpression("TimeFromStart",

View File

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

View File

@@ -236,7 +236,6 @@ class GD_CORE_API BehaviorMetadata {
}
const gd::String& GetName() const;
#if defined(GD_IDE_ONLY)
const gd::String& GetFullName() const { return fullname; }
const gd::String& GetDefaultName() const { return defaultName; }
const gd::String& GetDescription() const { return description; }
@@ -257,7 +256,21 @@ class GD_CORE_API BehaviorMetadata {
* \note An empty string means the base object, so any object.
*/
const gd::String& GetObjectType() const { return objectType; }
#endif
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
BehaviorMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
/**
* \brief Return the associated gd::Behavior, handling behavior contents.
@@ -315,6 +328,7 @@ class GD_CORE_API BehaviorMetadata {
gd::String group;
gd::String iconFilename;
gd::String objectType;
bool isPrivate = false;
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
std::shared_ptr<gd::Behavior> instance;

View File

@@ -37,22 +37,24 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
gd::ParameterMetadata info;
info.type = type;
info.SetType(type);
info.description = description;
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
info.SetOptional(parameterIsOptional);
info.SetExtraInfo(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: supplementaryInformation; // Otherwise don't change anything
: supplementaryInformation); // Otherwise don't change anything
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -64,9 +66,9 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
gd::ExpressionMetadata& ExpressionMetadata::AddCodeOnlyParameter(
const gd::String& type, const gd::String& supplementaryInformation) {
gd::ParameterMetadata info;
info.type = type;
info.SetType(type);
info.codeOnly = true;
info.supplementaryInformation = supplementaryInformation;
info.SetExtraInfo(supplementaryInformation);
parameters.push_back(info);
return *this;

View File

@@ -222,6 +222,18 @@ class GD_CORE_API ExpressionMetadata {
return *this;
};
/**
* \brief Set the additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter), for the last added parameter.
*
* \see AddParameter
*/
ExpressionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
return *this;
}
/**
* \brief Mark this (object) expression as requiring the specified capability,
* offered by the base object.
@@ -256,7 +268,30 @@ class GD_CORE_API ExpressionMetadata {
*/
ExpressionCodeGenerationInformation& GetCodeExtraInformation() {
return codeExtraInformation;
};
}
/**
* \brief Erase any existing include file and add the specified include.
*/
ExpressionMetadata &SetIncludeFile(const gd::String &includeFile) {
codeExtraInformation.SetIncludeFile(includeFile);
return *this;
}
/**
* \brief Add a file to the already existing include files.
*/
ExpressionMetadata &AddIncludeFile(const gd::String &includeFile) {
codeExtraInformation.AddIncludeFile(includeFile);
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
return codeExtraInformation.GetIncludeFiles();
}
ExpressionCodeGenerationInformation codeExtraInformation;

View File

@@ -55,15 +55,17 @@ InstructionMetadata& InstructionMetadata::AddParameter(
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
ParameterMetadata info;
info.type = type;
info.SetType(type);
info.description = description;
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
info.SetOptional(parameterIsOptional);
info.SetExtraInfo(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
@@ -71,7 +73,7 @@ InstructionMetadata& InstructionMetadata::AddParameter(
// extension
// namespace.
)
: supplementaryInformation; // Otherwise don't change anything
: supplementaryInformation); // Otherwise don't change anything
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -83,17 +85,19 @@ InstructionMetadata& InstructionMetadata::AddParameter(
InstructionMetadata& InstructionMetadata::AddCodeOnlyParameter(
const gd::String& type, const gd::String& supplementaryInformation) {
ParameterMetadata info;
info.type = type;
info.SetType(type);
info.codeOnly = true;
info.supplementaryInformation = supplementaryInformation;
info.SetExtraInfo(supplementaryInformation);
parameters.push_back(info);
return *this;
}
InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
const gd::String& type, const gd::String& typeExtraInfo) {
const gd::String& expressionValueType =
gd::ValueTypeMetadata::GetPrimitiveValueType(type);
SetManipulatedType(expressionValueType);
if (type == "boolean") {
AddParameter("yesorno", _("New value"));
@@ -117,8 +121,8 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
"_PARAM" + gd::String::From(valueParamIndex) + "_");
}
} else {
AddParameter("operator", _("Modification's sign"), type);
AddParameter(type == "number" ? "expression" : type, _("Value"));
AddParameter("operator", _("Modification's sign"), expressionValueType);
AddParameter(type, _("Value"), typeExtraInfo);
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;
@@ -151,8 +155,10 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
InstructionMetadata&
InstructionMetadata::UseStandardRelationalOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
const gd::String& type, const gd::String& typeExtraInfo) {
const gd::String& expressionValueType =
gd::ValueTypeMetadata::GetPrimitiveValueType(type);
SetManipulatedType(expressionValueType);
if (type == "boolean") {
if (isObjectInstruction || isBehaviorInstruction) {
@@ -168,8 +174,8 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
templateSentence.FindAndReplace("<subject>", sentence);
}
} else {
AddParameter("relationalOperator", _("Sign of the test"), type);
AddParameter(type == "number" ? "expression" : type, _("Value to compare"));
AddParameter("relationalOperator", _("Sign of the test"), expressionValueType);
AddParameter(type, _("Value to compare"), typeExtraInfo);
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;

View File

@@ -206,7 +206,7 @@ class GD_CORE_API InstructionMetadata {
if (!parameters.empty())
parameters.back().SetLongDescription(longDescription);
return *this;
};
}
/**
* \brief Set the additional information, used for some parameters
@@ -218,20 +218,26 @@ class GD_CORE_API InstructionMetadata {
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
return *this;
};
}
/**
* \brief Add the default parameters for an instruction manipulating the
* specified type ("string", "number") with the default operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardOperatorParameters(const gd::String &type);
InstructionMetadata &UseStandardOperatorParameters(const gd::String &type, const gd::String& typeExtraInfo = "");
/**
* \brief Add the default parameters for an instruction comparing the
* specified type ("string", "number") with the default relational operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardRelationalOperatorParameters(
const gd::String &type);
const gd::String &type, const gd::String& typeExtraInfo = "");
/**
* \brief Mark the instruction as an object instruction. Automatically called
@@ -276,7 +282,7 @@ class GD_CORE_API InstructionMetadata {
*/
const gd::String &GetRequiredBaseObjectCapability() const {
return requiredBaseObjectCapability;
};
}
/**
* \brief Consider that the instruction is easy for a user to understand.
@@ -487,6 +493,29 @@ class GD_CORE_API InstructionMetadata {
return codeExtraInformation.SetAsyncFunctionName(functionName);
}
/**
* \brief Erase any existing include file and add the specified include.
*/
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) {
codeExtraInformation.SetIncludeFile(includeFile);
return *this;
}
/**
* \brief Add a file to the already existing include files.
*/
InstructionMetadata &AddIncludeFile(const gd::String &includeFile) {
codeExtraInformation.AddIncludeFile(includeFile);
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
return codeExtraInformation.GetIncludeFiles();
};
std::vector<ParameterMetadata> parameters;
private:

View File

@@ -80,6 +80,16 @@ class GD_CORE_API MultipleInstructionMetadata {
return *this;
};
/**
* \see gd::InstructionMetadata::SetParameterExtraInfo
*/
MultipleInstructionMetadata &SetParameterExtraInfo(const gd::String &defaultValue) {
if (expression) expression->SetParameterExtraInfo(defaultValue);
if (condition) condition->SetParameterExtraInfo(defaultValue);
if (action) action->SetParameterExtraInfo(defaultValue);
return *this;
};
/**
* \see gd::InstructionMetadata::SetParameterLongDescription
*/
@@ -116,9 +126,9 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::UseStandardOperatorParameters
* \see gd::InstructionMetadata::UseStandardRelationalOperatorParameters
*/
MultipleInstructionMetadata &UseStandardParameters(const gd::String &type) {
if (condition) condition->UseStandardRelationalOperatorParameters(type);
if (action) action->UseStandardOperatorParameters(type);
MultipleInstructionMetadata &UseStandardParameters(const gd::String &type, const gd::String& typeExtraInfo = "") {
if (condition) condition->UseStandardRelationalOperatorParameters(type, typeExtraInfo);
if (action) action->UseStandardOperatorParameters(type, typeExtraInfo);
return *this;
}
@@ -154,6 +164,33 @@ class GD_CORE_API MultipleInstructionMetadata {
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
if (expression)
return expression->GetCodeExtraInformation().GetIncludeFiles();
if (condition)
return condition->GetCodeExtraInformation().GetIncludeFiles();
if (action)
return action->GetCodeExtraInformation().GetIncludeFiles();
// It can't actually happen.
throw std::logic_error("no instruction metadata");
}
/**
* \see gd::InstructionMetadata::SetPrivate
*/
MultipleInstructionMetadata &SetPrivate() {
if (expression)
expression->SetPrivate();
if (condition)
condition->SetPrivate();
if (action)
action->SetPrivate();
return *this;
}
/**
* \see gd::InstructionMetadata::MarkAsSimple
*/

View File

@@ -10,43 +10,26 @@
namespace gd {
ParameterMetadata::ParameterMetadata() : optional(false), codeOnly(false) {}
ParameterMetadata::ParameterMetadata() : codeOnly(false) {}
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", type);
element.SetAttribute("supplementaryInformation", supplementaryInformation);
element.SetAttribute("optional", optional);
valueTypeMetadata.SerializeTo(element);
element.SetAttribute("description", description);
element.SetAttribute("longDescription", longDescription);
element.SetAttribute("codeOnly", codeOnly);
element.SetAttribute("defaultValue", defaultValue);
if (!longDescription.empty()) {
element.SetAttribute("longDescription", longDescription);
}
if (codeOnly) {
element.SetAttribute("codeOnly", codeOnly);
}
element.SetAttribute("name", name);
}
void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
type = element.GetStringAttribute("type");
supplementaryInformation =
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
valueTypeMetadata.UnserializeFrom(element);
description = element.GetStringAttribute("description");
longDescription = element.GetStringAttribute("longDescription");
codeOnly = element.GetBoolAttribute("codeOnly");
defaultValue = element.GetStringAttribute("defaultValue");
name = element.GetStringAttribute("name");
}
// TODO factorize in a file with an enum and helpers?
const gd::String ParameterMetadata::numberType = "number";
const gd::String ParameterMetadata::stringType = "string";
const gd::String &ParameterMetadata::GetExpressionValueType(const gd::String &parameterType) {
if (parameterType == "number" || gd::ParameterMetadata::IsExpression("number", parameterType)) {
return ParameterMetadata::numberType;
}
if (parameterType == "string" || gd::ParameterMetadata::IsExpression("string", parameterType)) {
return ParameterMetadata::stringType;
}
return parameterType;
}
} // namespace gd

View File

@@ -6,16 +6,13 @@
#ifndef PARAMETER_METADATA_H
#define PARAMETER_METADATA_H
#if defined(GD_IDE_ONLY)
#include <map>
#include <memory>
#include "GDCore/String.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
namespace gd {
class Project;
class Layout;
class EventsCodeGenerator;
class EventsCodeGenerationContext;
class SerializerElement;
} // namespace gd
@@ -32,17 +29,32 @@ class GD_CORE_API ParameterMetadata {
ParameterMetadata();
virtual ~ParameterMetadata(){};
/**
* \brief Return the metadata of the parameter type.
*/
gd::ValueTypeMetadata &GetValueTypeMetadata() { return valueTypeMetadata; }
/**
* \brief Set the metadata of the parameter type.
*/
ParameterMetadata &SetValueTypeMetadata(const gd::ValueTypeMetadata &valueTypeMetadata_) {
valueTypeMetadata = valueTypeMetadata_;
return *this;
}
/**
* \brief Return the type of the parameter.
* \see gd::ParameterMetadata::IsObject
* \deprecated Use gd::ValueTypeMetadata instead.
*/
const gd::String &GetType() const { return type; }
const gd::String &GetType() const { return valueTypeMetadata.GetName(); }
/**
* \brief Set the type of the parameter.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetType(const gd::String &type_) {
type = type_;
valueTypeMetadata.SetName(type_);
return *this;
}
@@ -71,29 +83,33 @@ class GD_CORE_API ParameterMetadata {
* \brief Return an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
* \deprecated Use gd::ValueTypeMetadata instead.
*/
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
const gd::String &GetExtraInfo() const { return valueTypeMetadata.GetExtraInfo(); }
/**
* \brief Set an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
supplementaryInformation = supplementaryInformation_;
valueTypeMetadata.SetExtraInfo(supplementaryInformation_);
return *this;
}
/**
* \brief Return true if the parameter is optional.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
bool IsOptional() const { return optional; }
bool IsOptional() const { return valueTypeMetadata.IsOptional(); }
/**
* \brief Set if the parameter is optional.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetOptional(bool optional_ = true) {
optional = optional_;
valueTypeMetadata.SetOptional(optional_);
return *this;
}
@@ -128,13 +144,15 @@ class GD_CORE_API ParameterMetadata {
/**
* \brief Get the default value for the parameter.
*/
const gd::String &GetDefaultValue() const { return defaultValue; }
const gd::String &GetDefaultValue() const {
return valueTypeMetadata.GetDefaultValue();
}
/**
* \brief Set the default value, if the parameter is optional.
*/
ParameterMetadata &SetDefaultValue(const gd::String &defaultValue_) {
defaultValue = defaultValue_;
valueTypeMetadata.SetDefaultValue(defaultValue_);
return *this;
}
@@ -151,26 +169,27 @@ class GD_CORE_API ParameterMetadata {
return *this;
}
// TODO Remove these deprecated functions.
/**
* \brief Return true if the type of the parameter is representing one object
* (or more, i.e: an object group).
*
* \see gd::ParameterMetadata::GetType
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
parameterType == "objectList" ||
parameterType == "objectListOrEmptyIfJustDeclared" ||
parameterType == "objectListOrEmptyWithoutPicking";
return gd::ValueTypeMetadata::IsTypeObject(parameterType);
}
/**
* \brief Return true if the type of the parameter is "behavior".
*
* \see gd::ParameterMetadata::GetType
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsBehavior(const gd::String &parameterType) {
return parameterType == "behavior";
return gd::ValueTypeMetadata::IsTypeBehavior(parameterType);
}
/**
@@ -179,42 +198,22 @@ class GD_CORE_API ParameterMetadata {
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsExpression(const gd::String &type,
const gd::String &parameterType) {
if (type == "number") {
return parameterType == "expression" || parameterType == "camera" ||
parameterType == "forceMultiplier";
} else if (type == "string") {
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName" ||
parameterType == "layerEffectName" ||
parameterType == "layerEffectParameterName" ||
parameterType == "objectEffectName" ||
parameterType == "objectEffectParameterName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
}
return false;
return gd::ValueTypeMetadata::IsTypeExpression(type, parameterType);
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static const gd::String &GetExpressionValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
static const gd::String &GetExpressionValueType(const gd::String &parameterType) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(parameterType);
}
/** \name Serialization
*/
@@ -232,22 +231,17 @@ class GD_CORE_API ParameterMetadata {
// TODO: Deprecated public fields. Any direct usage should be moved to
// getter/setter.
gd::String type; ///< Parameter type
gd::String supplementaryInformation; ///< Used if needed
bool optional; ///< True if the parameter is optional
gd::String description; ///< Description shown in editor
bool codeOnly; ///< True if parameter is relative to code generation only,
///< i.e. must not be shown in editor
private:
gd::ValueTypeMetadata valueTypeMetadata; ///< Parameter type
gd::String longDescription; ///< Long description shown in the editor.
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
gd::String name; ///< The name of the parameter to be used in code
///< generation. Optional.
};
} // namespace gd
#endif
#endif // PARAMETER_METADATA_H

View File

@@ -85,7 +85,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
const gd::Expression& parameterValue =
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
const gd::Expression& parameterValueOrDefault =
parameterValue.GetPlainString().empty() && parameterMetadata.optional
parameterValue.GetPlainString().empty() && parameterMetadata.IsOptional()
? Expression(parameterMetadata.GetDefaultValue())
: parameterValue;

View File

@@ -0,0 +1,70 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ValueTypeMetadata.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
ValueTypeMetadata::ValueTypeMetadata() : optional(false) {}
void ValueTypeMetadata::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", name);
if (!supplementaryInformation.empty()) {
element.SetAttribute("supplementaryInformation", supplementaryInformation);
}
if (optional) {
element.SetAttribute("optional", optional);
}
if (!defaultValue.empty()) {
element.SetAttribute("defaultValue", defaultValue);
}
}
void ValueTypeMetadata::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("type");
supplementaryInformation =
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
defaultValue = element.GetStringAttribute("defaultValue");
}
const gd::String ValueTypeMetadata::numberType = "number";
const gd::String ValueTypeMetadata::stringType = "string";
const gd::String &ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "number" || gd::ValueTypeMetadata::IsTypeExpression("number", parameterType)) {
return ValueTypeMetadata::numberType;
}
if (parameterType == "string" || gd::ValueTypeMetadata::IsTypeExpression("string", parameterType)) {
return ValueTypeMetadata::stringType;
}
return parameterType;
}
const gd::String ValueTypeMetadata::numberValueType = "number";
const gd::String ValueTypeMetadata::booleanValueType = "boolean";
const gd::String ValueTypeMetadata::colorValueType = "color";
const gd::String ValueTypeMetadata::choiceValueType = "stringWithSelector";
const gd::String ValueTypeMetadata::stringValueType = "string";
const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
const gd::String &propertyType) {
if (propertyType == "Number") {
return numberValueType;
} else if (propertyType == "Boolean") {
return booleanValueType;
} else if (propertyType == "Color") {
return colorValueType;
} else if (propertyType == "Choice") {
return choiceValueType;
}
// For "String" or default
return stringValueType;
};
} // namespace gd

View File

@@ -0,0 +1,231 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef VALUE_TYPE_METADATA_H
#define VALUE_TYPE_METADATA_H
#include <map>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
} // namespace gd
namespace gd {
/**
* \brief Define a type for parameters of a function (action, condition or
* expression) or the returned value of an expression.
*
* \see gd::EventsFunction
* \ingroup Events
*/
class GD_CORE_API ValueTypeMetadata {
public:
ValueTypeMetadata();
virtual ~ValueTypeMetadata(){};
/**
* \brief Return the string representation of the type.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Set the string representation of the type.
*/
ValueTypeMetadata &SetName(const gd::String &name_) {
name = name_;
return *this;
}
/**
* \brief Return an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
/**
* \brief Set an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
ValueTypeMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
supplementaryInformation = supplementaryInformation_;
return *this;
}
/**
* \brief Return true if the parameter is optional.
*/
bool IsOptional() const { return optional; }
/**
* \brief Set if the parameter is optional.
*/
ValueTypeMetadata &SetOptional(bool optional_ = true) {
optional = optional_;
return *this;
}
/**
* \brief Get the default value for the parameter.
*/
const gd::String &GetDefaultValue() const { return defaultValue; }
/**
* \brief Set the default value, if the parameter is optional.
*/
ValueTypeMetadata &SetDefaultValue(const gd::String &defaultValue_) {
defaultValue = defaultValue_;
return *this;
}
/**
* \brief Return true if the type is defined.
*/
bool IsDefined() const {
return !name.empty();
}
/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
*/
bool IsObject() const {
return gd::ValueTypeMetadata::IsTypeObject(name);
}
/**
* \brief Return true if the type is "behavior".
*/
bool IsBehavior() const {
return gd::ValueTypeMetadata::IsTypeBehavior(name);
}
/**
* \brief Return true if the type is an expression of the
* given type.
*/
bool IsNumber() const {
return gd::ValueTypeMetadata::IsTypeExpression("number", name);
}
/**
* \brief Return true if the type is a string.
*/
bool IsString() const {
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
}
/**
* \brief Return true if the type of the parameter is a number.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
}
/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
*/
static bool IsTypeObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
parameterType == "objectList" ||
parameterType == "objectListOrEmptyIfJustDeclared" ||
parameterType == "objectListOrEmptyWithoutPicking";
}
/**
* \brief Return true if the type is "behavior".
*/
static bool IsTypeBehavior(const gd::String &parameterType) {
return parameterType == "behavior";
}
/**
* \brief Return true if the type is an expression of the given type.
* \note If you are adding a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsTypeExpression(const gd::String &type,
const gd::String &parameterType) {
if (type == "number") {
return parameterType == "number" || parameterType == "expression" ||
parameterType == "camera" || parameterType == "forceMultiplier";
} else if (type == "string") {
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName" ||
parameterType == "layerEffectName" ||
parameterType == "layerEffectParameterName" ||
parameterType == "objectEffectName" ||
parameterType == "objectEffectParameterName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "identifier";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
}
return false;
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*/
static const gd::String &GetPrimitiveValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
/**
* \brief Return the ValueTypeMetadata name for a property type.
* \see gd::PropertyDescriptor
*/
static const gd::String &ConvertPropertyTypeToValueType(const gd::String &propertyType);
/** \name Serialization
*/
///@{
/**
* \brief Serialize the ParameterMetadata to the specified element
*/
void SerializeTo(gd::SerializerElement &element) const;
/**
* \brief Load the ParameterMetadata from the specified element
*/
void UnserializeFrom(const gd::SerializerElement &element);
///@}
private:
gd::String name; ///< Parameter type
gd::String supplementaryInformation; ///< Used if needed
bool optional; ///< True if the parameter is optional
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
static const gd::String numberValueType;
static const gd::String booleanValueType;
static const gd::String colorValueType;
static const gd::String choiceValueType;
static const gd::String stringValueType;
};
} // namespace gd
#endif // VALUE_TYPE_METADATA_H

View File

@@ -24,14 +24,12 @@
namespace gd {
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::InstructionMetadata>
PlatformExtension::badConditionsMetadata;
std::map<gd::String, gd::InstructionMetadata>
PlatformExtension::badActionsMetadata;
std::map<gd::String, gd::ExpressionMetadata>
PlatformExtension::badExpressionsMetadata;
#endif
gd::InstructionMetadata& PlatformExtension::AddCondition(
const gd::String& name,
@@ -41,7 +39,6 @@ gd::InstructionMetadata& PlatformExtension::AddCondition(
const gd::String& group,
const gd::String& icon,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
conditionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
nameWithNamespace,
@@ -53,7 +50,6 @@ gd::InstructionMetadata& PlatformExtension::AddCondition(
smallicon)
.SetHelpPath(GetHelpPath());
return conditionsInfos[nameWithNamespace];
#endif
}
gd::InstructionMetadata& PlatformExtension::AddAction(
@@ -64,7 +60,6 @@ gd::InstructionMetadata& PlatformExtension::AddAction(
const gd::String& group,
const gd::String& icon,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
actionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
nameWithNamespace,
@@ -76,7 +71,6 @@ gd::InstructionMetadata& PlatformExtension::AddAction(
smallicon)
.SetHelpPath(GetHelpPath());
return actionsInfos[nameWithNamespace];
#endif
}
gd::ExpressionMetadata& PlatformExtension::AddExpression(
@@ -85,7 +79,6 @@ gd::ExpressionMetadata& PlatformExtension::AddExpression(
const gd::String& description,
const gd::String& group,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
expressionsInfos[nameWithNamespace] = ExpressionMetadata("number",
GetNameSpace(),
@@ -96,7 +89,6 @@ gd::ExpressionMetadata& PlatformExtension::AddExpression(
smallicon)
.SetHelpPath(GetHelpPath());
return expressionsInfos[nameWithNamespace];
#endif
}
gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
@@ -105,7 +97,6 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
const gd::String& description,
const gd::String& group,
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name;
strExpressionsInfos[nameWithNamespace] = ExpressionMetadata("string",
GetNameSpace(),
@@ -116,7 +107,6 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
smallicon)
.SetHelpPath(GetHelpPath());
return strExpressionsInfos[nameWithNamespace];
#endif
}
gd::MultipleInstructionMetadata PlatformExtension::AddExpressionAndCondition(
@@ -220,12 +210,10 @@ PlatformExtension::AddExpressionAndConditionAndAction(
expression, condition, action);
}
#if defined(GD_IDE_ONLY)
gd::DependencyMetadata& PlatformExtension::AddDependency() {
extensionDependenciesMetadata.push_back(DependencyMetadata());
return extensionDependenciesMetadata.back();
}
#endif
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
@@ -317,7 +305,6 @@ gd::EventMetadata& PlatformExtension::AddEvent(
const gd::String& group_,
const gd::String& smallicon_,
std::shared_ptr<gd::BaseEvent> instance_) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace = GetNameSpace() + name_;
eventsInfos[nameWithNamespace] = gd::EventMetadata(nameWithNamespace,
fullname_,
@@ -326,7 +313,6 @@ gd::EventMetadata& PlatformExtension::AddEvent(
smallicon_,
instance_);
return eventsInfos[nameWithNamespace];
#endif
}
PlatformExtension& PlatformExtension::SetExtensionInformation(
@@ -406,8 +392,6 @@ std::vector<gd::String> PlatformExtension::GetBehaviorsTypes() const {
return behaviors;
}
#if defined(GD_IDE_ONLY)
gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName) {
gd::String newNameWithNamespace = GetNameSpace() + newActionName;
@@ -586,7 +570,6 @@ gd::BaseEventSPtr PlatformExtension::CreateEvent(
return std::shared_ptr<gd::BaseEvent>();
}
#endif
CreateFunPtr PlatformExtension::GetObjectCreationFunctionPtr(
const gd::String& objectType) const {
@@ -665,7 +648,6 @@ bool PlatformExtension::IsBuiltin() const {
builtinExtensions.end();
}
#if defined(GD_IDE_ONLY)
void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
for (std::map<gd::String, gd::InstructionMetadata>::iterator it =
GetAllActions().begin();
@@ -810,7 +792,40 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
++it;
}
}
#endif
gd::String
PlatformExtension::GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String PlatformExtension::GetBehaviorEventsFunctionFullType(
const gd::String &extensionName, const gd::String &behaviorName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String
PlatformExtension::GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
gd::String PlatformExtension::GetObjectEventsFunctionFullType(
const gd::String &extensionName, const gd::String &objectName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + objectName + separator + functionName;
}
gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName) {
const auto &separator = GetNamespaceSeparator();
return extensionName + separator + objectName;
}
PlatformExtension::PlatformExtension()
: deprecated(false), category(_("General")) {}

View File

@@ -620,7 +620,26 @@ class GD_CORE_API PlatformExtension {
*/
static gd::String GetNamespaceSeparator() { return "::"; }
private:
static gd::String GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName);
static gd::String
GetBehaviorEventsFunctionFullType(const gd::String &extensionName,
const gd::String &behaviorName,
const gd::String &functionName);
static gd::String GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName);
static gd::String
GetObjectEventsFunctionFullType(const gd::String &extensionName,
const gd::String &objectName,
const gd::String &functionName);
static gd::String GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName);
private:
/**
* Set the namespace (the string all actions/conditions/expressions start
* with).

View File

@@ -73,4 +73,54 @@ bool ArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction,
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {}
ReadOnlyArbitraryEventsWorker::~ReadOnlyArbitraryEventsWorker() {}
void ReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events) {
DoVisitEventList(events);
for (std::size_t i = 0; i < events.size(); ++i) {
VisitEvent(events[i]);
if (events[i].CanHaveSubEvents()) {
VisitEventList(events[i].GetSubEvents());
}
}
}
void ReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) {
DoVisitEvent(event);
const vector<const gd::InstructionsList*> conditionsVectors =
event.GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
VisitInstructionList(*conditionsVectors[j], true);
}
const vector<const gd::InstructionsList*> actionsVectors = event.GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
VisitInstructionList(*actionsVectors[j], false);
}
}
void ReadOnlyArbitraryEventsWorker::VisitInstructionList(
const gd::InstructionsList& instructions, bool areConditions) {
DoVisitInstructionList(instructions, areConditions);
for (std::size_t i = 0; i < instructions.size(); ++i) {
VisitInstruction(instructions[i], areConditions);
if (!instructions[i].GetSubInstructions().empty()) {
VisitInstructionList(instructions[i].GetSubInstructions(),
areConditions);
}
}
}
void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& instruction,
bool isCondition) {
DoVisitInstruction(instruction, isCondition);
}
ReadOnlyArbitraryEventsWorkerWithContext::~ReadOnlyArbitraryEventsWorkerWithContext() {}
} // namespace gd

View File

@@ -121,6 +121,101 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
const gd::ObjectsContainer* currentObjectsContainer;
};
/**
* \brief ReadOnlyArbitraryEventsWorker is an abstract class used to browse events (and
* instructions). It can be used to implement autocompletion for example.
*
* \see gd::ReadOnlyArbitraryEventsWorkerWithContext
*
* \ingroup IDE
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorker(){};
virtual ~ReadOnlyArbitraryEventsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(const gd::EventsList& events) { VisitEventList(events); };
private:
void VisitEventList(const gd::EventsList& events);
void VisitEvent(const gd::BaseEvent& event);
void VisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions);
void VisitInstruction(const gd::Instruction& instruction, bool isCondition);
/**
* Called to do some work on an event list.
*/
virtual void DoVisitEventList(const gd::EventsList& events){};
/**
* Called to do some work on an event
*/
virtual void DoVisitEvent(const gd::BaseEvent& event) {};
/**
* Called to do some work on an instruction list
*/
virtual void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions){};
/**
* Called to do some work on an instruction.
*/
virtual void DoVisitInstruction(const gd::Instruction& instruction,
bool isCondition) {};
};
/**
* \brief An events worker that will know about the context (the objects
* container). Useful for workers working on expressions notably.
*
* \see gd::ReadOnlyArbitraryEventsWorker
*
* \ingroup IDE
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
: public ReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorkerWithContext()
: currentGlobalObjectsContainer(nullptr),
currentObjectsContainer(nullptr){};
virtual ~ReadOnlyArbitraryEventsWorkerWithContext();
/**
* \brief Launch the worker on the specified events list,
* giving the objects container on which the events are applying to.
*/
void Launch(const gd::EventsList& events,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_) {
currentGlobalObjectsContainer = &globalObjectsContainer_;
currentObjectsContainer = &objectsContainer_;
ReadOnlyArbitraryEventsWorker::Launch(events);
};
void Launch(gd::EventsList& events) = delete;
protected:
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentGlobalObjectsContainer;
};
const gd::ObjectsContainer& GetObjectsContainer() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentObjectsContainer;
};
private:
const gd::ObjectsContainer* currentGlobalObjectsContainer;
const gd::ObjectsContainer* currentObjectsContainer;
};
} // namespace gd
#endif // GDCORE_ARBITRARYEVENTSWORKER_H

View File

@@ -120,7 +120,7 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.type;
const gd::String& type = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsBehavior(type)) {
if (lastObjectName == objectName) {

View File

@@ -0,0 +1,254 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EventsIdentifiersFinder.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
using namespace std;
namespace gd {
namespace {
/**
* \brief Go through the nodes to search for identifier occurrences.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API IdentifierFinderExpressionNodeWorker
: public ExpressionParser2NodeWorker {
public:
IdentifierFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String& identifierType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
identifierType(identifierType_),
objectName(objectName_){};
virtual ~IdentifierFinderExpressionNodeWorker(){};
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 (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
const bool isObjectFunction = !node.objectName.empty();
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
size_t parameterIndex = 0;
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
auto& parameterMetadata = metadata.parameters[metadataIndex];
if (parameterMetadata.IsCodeOnly()) {
continue;
}
auto& parameterNode = node.parameters[parameterIndex];
++parameterIndex;
if (considerFunction && parameterMetadata.GetType() == "identifier"
&& parameterMetadata.GetExtraInfo() == identifierType) {
// Store the value of the parameter
results.insert(
gd::ExpressionParser2NodePrinter::PrintNode(*parameterNode));
} else {
parameterNode->Visit(*this);
}
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String identifierType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
/**
* \brief Go through the events to search for identifier occurrences.
*/
class GD_CORE_API IdentifierFinderEventWorker
: public ReadOnlyArbitraryEventsWorkerWithContext {
public:
IdentifierFinderEventWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::String& identifierType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
identifierType(identifierType_),
objectName(objectName_){};
virtual ~IdentifierFinderEventWorker(){};
void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions) override {
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
auto& instruction = instructions[aId];
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
areConditions ? MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].GetType() == "identifier"
&& instrInfos.parameters[pNb].GetExtraInfo() == identifierType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName) {
results.insert(instruction.GetParameter(pNb).GetPlainString());
}
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].GetType()) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].GetType())) {
auto node = instruction.GetParameter(pNb).GetRootNode();
IdentifierFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
identifierType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].GetType())) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}
}
}
};
private:
const gd::Platform &platform;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String identifierType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
} // namespace
std::set<gd::String> EventsIdentifiersFinder::FindAllIdentifierExpressions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& contextObjectName) {
std::set<gd::String> results;
const bool isObjectIdentifier = identifierType.find("object") == 0;
// The object from the context is only relevent for object identifiers.
auto& actualObjectName = isObjectIdentifier ? contextObjectName : "";
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
identifierType,
actualObjectName);
return results;
}
void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName) {
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
}
}
} // namespace gd

View File

@@ -0,0 +1,81 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EVENTSIDENTIFIERSFINDER_H
#define EVENTSIDENTIFIERSFINDER_H
#include <set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/String.h"
namespace gd {
class Instruction;
class Platform;
class Object;
class Project;
class Layout;
} // namespace gd
namespace gd {
/**
* \brief Perform a search over a layout, searching for layout or object custom
* identifiers.
*
* \todo Refactor this class using ArbitraryEventsWorker
*
* \ingroup IDE
*/
class EventsIdentifiersFinder {
public:
EventsIdentifiersFinder(){};
virtual ~EventsIdentifiersFinder(){};
/**
* Construct a list containing all the expressions for a given identifier used
* in the layout.
*
* \param project The project to use.
* \param layout The layout to use.
* \param identifierType The identifier type to be analyzed.
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
* \return A std::set containing the names of all identifiers used.
*/
static std::set<gd::String> FindAllIdentifierExpressions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName = "");
private:
/**
* Construct a list containing all the expressions for a given identifier used
* in the layout. It searches in events dependencies.
*
* \param results A std::set to fill with the expressions used for all parameters of the
* specified identifier type
* \param platform The platform of the project
* \param project The project to use.
* \param layout The layout to use.
* \param events The events to be analyzed
* \param identifierType The identifier type to be analyzed
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*/
static void FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName = "");
};
} // namespace gd
#endif // EVENTSIDENTIFIERSFINDER_H

View File

@@ -30,7 +30,7 @@ bool EventsLeaderboardsLister::DoVisitInstruction(gd::Instruction& instruction,
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i)
if (instrInfo.GetParameter(i).type == "leaderboardId") {
if (instrInfo.GetParameter(i).GetType() == "leaderboardId") {
leaderboardIds.insert(instruction.GetParameter(i).GetPlainString());
}
return false;

View File

@@ -32,7 +32,7 @@ bool EventsLeaderboardsRenamer::DoVisitInstruction(gd::Instruction& instruction,
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.type == "leaderboardId") {
if (parameter.GetType() == "leaderboardId") {
const gd::String leaderboardId =
instruction.GetParameter(i).GetPlainString();

View File

@@ -237,12 +237,12 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[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[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
@@ -252,7 +252,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
@@ -291,12 +291,12 @@ bool EventsRefactorer::RenameObjectInConditions(
conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[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[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
@@ -306,7 +306,7 @@ bool EventsRefactorer::RenameObjectInConditions(
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
@@ -425,14 +425,14 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
actions[aId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
@@ -442,7 +442,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
}
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
@@ -485,14 +485,14 @@ bool EventsRefactorer::RemoveObjectInConditions(
conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
conditions[cId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
@@ -502,7 +502,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
}
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
@@ -773,15 +773,18 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
const gd::String& ignored_characters =
EventsRefactorer::searchIgnoredCharacters;
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
if (inEventSentences) {
// Remove ignored characters only when searching in event sentences.
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
}
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventAddedInResults = false;

View File

@@ -13,6 +13,7 @@
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
@@ -22,28 +23,28 @@
using namespace std;
namespace gd {
namespace {
/**
* \brief Go through the nodes and change the given object name to a new one.
* \brief Go through the nodes to search for variable occurrences.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParameterSearcher
class GD_CORE_API VariableFinderExpressionNodeWorker
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterSearcher(const gd::Platform &platform_,
VariableFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
std::set<gd::String>& results_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: platform(platform_),
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
results(results_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~ExpressionParameterSearcher(){};
virtual ~VariableFinderExpressionNodeWorker(){};
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
@@ -74,27 +75,34 @@ class GD_CORE_API ExpressionParameterSearcher
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
const gd::ExpressionMetadata &metadata = node.objectName.empty() ?
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName) :
const bool isObjectFunction = !node.objectName.empty();
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
node.functionName);
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
for (size_t i = 0; i < node.parameters.size() &&
i < metadata.parameters.size();
++i) {
auto& parameterMetadata = metadata.parameters[i];
size_t parameterIndex = 0;
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
auto& parameterMetadata = metadata.parameters[metadataIndex];
if (parameterMetadata.IsCodeOnly()) {
continue;
}
auto& parameterNode = node.parameters[parameterIndex];
++parameterIndex;
if (considerFunction && parameterMetadata.GetType() == parameterType) {
// Store the value of the parameter
results.insert(
gd::ExpressionParser2NodePrinter::PrintNode(*node.parameters[i]));
gd::ExpressionParser2NodePrinter::PrintNode(*parameterNode));
} else {
node.parameters[i]->Visit(*this);
parameterNode->Visit(*this);
}
}
}
@@ -112,18 +120,87 @@ class GD_CORE_API ExpressionParameterSearcher
///< account only if related to this object.
};
/**
* \brief Go through the events to search for variable occurrences.
*/
class GD_CORE_API VariableFinderEventWorker
: public ReadOnlyArbitraryEventsWorkerWithContext {
public:
VariableFinderEventWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~VariableFinderEventWorker(){};
void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions) override {
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
auto& instruction = instructions[aId];
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
areConditions ? MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].GetType() == parameterType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName)
results.insert(instruction.GetParameter(pNb).GetPlainString());
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].GetType()) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].GetType())) {
auto node = instruction.GetParameter(pNb).GetRootNode();
VariableFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
parameterType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].GetType())) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}
}
}
};
private:
const gd::Platform &platform;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String parameterType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
} // namespace
std::set<gd::String> EventsVariablesFinder::FindAllGlobalVariables(
const gd::Platform& platform, const gd::Project& project) {
std::set<gd::String> results;
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
std::set<gd::String> results2 =
FindArgumentsInEventsAndDependencies(
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
project.GetLayout(i),
"globalvar");
results.insert(results2.begin(), results2.end());
}
return results;
@@ -135,9 +212,12 @@ std::set<gd::String> EventsVariablesFinder::FindAllLayoutVariables(
const gd::Layout& layout) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
platform, project, layout, "scenevar");
results.insert(results2.begin(), results2.end());
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
"scenevar");
return results;
}
@@ -149,159 +229,51 @@ std::set<gd::String> EventsVariablesFinder::FindAllObjectVariables(
const gd::Object& object) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
"objectvar",
object.GetName());
results.insert(results2.begin(), results2.end());
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::InstructionsList& instructions,
bool instructionsAreConditions,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
instructionsAreConditions ? MetadataProvider::GetConditionMetadata(
platform, instructions[aId].GetType())
: MetadataProvider::GetActionMetadata(
platform, instructions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].type == parameterType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName)
results.insert(instructions[aId].GetParameter(pNb).GetPlainString());
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = instructions[aId].GetParameter(pNb).GetRootNode();
ExpressionParameterSearcher searcher(
platform,
project,
layout,
results,
parameterType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
lastObjectParameter =
instructions[aId].GetParameter(pNb).GetPlainString();
}
}
if (!instructions[aId].GetSubInstructions().empty())
FindArgumentsInInstructions(platform,
project,
layout,
instructions[aId].GetSubInstructions(),
instructionsAreConditions,
parameterType);
}
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEvents(
platform, project, layout, layout.GetEvents(), parameterType, objectName);
results.insert(results2.begin(), results2.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, layout, externalEvents.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, dependencyLayout, dependencyLayout.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
}
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEvents(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::EventsList& events,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
for (std::size_t i = 0; i < events.size(); ++i) {
vector<const gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
std::set<gd::String> results2 =
FindArgumentsInInstructions(platform,
project,
layout,
*conditionsVectors[j],
/*conditions=*/true,
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
vector<const gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
std::set<gd::String> results2 =
FindArgumentsInInstructions(platform,
project,
layout,
*actionsVectors[j],
/*conditions=*/false,
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
if (events[i].CanHaveSubEvents()) {
std::set<gd::String> results2 =
FindArgumentsInEvents(platform,
project,
layout,
events[i].GetSubEvents(),
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
}
return results;
}
} // namespace gd

View File

@@ -23,7 +23,6 @@ namespace gd {
* \brief Perform a search over a project or a layout, searching for layout,
* global or object variables.
*
* \todo Refactor this class using ArbitraryEventsWorker
* \todo Rework this class to return the shapes (maybe even types?) of the
* variables (in particular for structures and arrays), so we can use this
* for better autocompletions in the variables dialogs in the IDE.
@@ -74,34 +73,13 @@ class EventsVariablesFinder {
const gd::Object& object);
private:
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType
*
* \param project The project used
* \param project The layout used
* \param instructions The instructions to be analyzed
* \param instructionsAreConditions True if the instructions are conditions.
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account only
* if the last object parameter is filled with this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInInstructions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::InstructionsList& instructions,
bool instructionsAreConditions,
const gd::String& parameterType,
const gd::String& objectName = "");
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType. It searchs in events dependencies.
* parameterType. It searches in events dependencies.
*
* \param results A std::set to fill with the values used for all parameters of the
* specified type
* \param platform The platform of the project
* \param project The project used
* \param layout The layout used
@@ -110,40 +88,14 @@ class EventsVariablesFinder {
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInEventsAndDependencies(
static void FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName = "");
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType. It doesn't search in events dependencies.
*
* \param platform The platform of the project
* \param project The project used
* \param layout The layout used
* \param events The events to be analyzed
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInEvents(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::EventsList& events,
const gd::String& parameterType,
const gd::String& objectName);
};
} // namespace gd

View File

@@ -36,7 +36,7 @@ size_t GetMinimumParametersNumber(
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
if (!parameters[i].IsOptional() && !parameters[i].codeOnly) nb++;
}
return nb;

View File

@@ -150,7 +150,7 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& type = metadata.parameters[pNb].GetType();
const gd::Expression& expression = instruction.GetParameter(pNb);
auto node = expression.GetRootNode();

View File

@@ -20,6 +20,7 @@ namespace gd {
void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) {
@@ -31,8 +32,12 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
// to parameters
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.GetObjectGroups().Clear();
auto &parameters = eventsFunction.GetParametersForEvents(functionContainer);
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), outputObjectsContainer);
project,
parameters,
outputObjectsContainer);
outputObjectsContainer.GetObjectGroups() = eventsFunction.GetObjectGroups();
}
@@ -44,6 +49,7 @@ void EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project,
eventsBasedBehavior.GetEventsFunctions(),
eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer);
@@ -81,6 +87,7 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project,
eventsBasedObject.GetEventsFunctions(),
eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer);

View File

@@ -10,6 +10,7 @@
#include "GDCore/String.h"
namespace gd {
class Project;
class EventsFunctionsContainer;
class ObjectsContainer;
class ParameterMetadata;
class EventsFunction;
@@ -34,6 +35,7 @@ class GD_CORE_API EventsFunctionTools {
*/
static void FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer);

View File

@@ -5,6 +5,8 @@
*/
#include "ProjectStripper.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
@@ -12,7 +14,7 @@
namespace gd {
void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project& project) {
void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project &project) {
project.GetObjectGroups().Clear();
while (project.GetExternalEventsCount() > 0)
project.RemoveExternalEvents(project.GetExternalEvents(0).GetName());
@@ -22,7 +24,28 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project& project) {
project.GetLayout(i).GetEvents().Clear();
}
project.ClearEventsFunctionsExtensions();
// Keep the EventsBasedObject object list because it's useful for the Runtime
// to create the child-object.
for (unsigned int extensionIndex = 0;
extensionIndex < project.GetEventsFunctionsExtensionsCount();
++extensionIndex) {
auto &extension = project.GetEventsFunctionsExtension(extensionIndex);
auto &eventsBasedObjects = extension.GetEventsBasedObjects();
if (eventsBasedObjects.size() == 0) {
project.RemoveEventsFunctionsExtension(extension.GetName());
extensionIndex--;
continue;
}
for (unsigned int objectIndex = 0; objectIndex < eventsBasedObjects.size();
++objectIndex) {
auto &eventsBasedObject = eventsBasedObjects.at(objectIndex);
eventsBasedObject.SetFullName("");
eventsBasedObject.SetDescription("");
eventsBasedObject.GetEventsFunctions().GetInternalVector().clear();
eventsBasedObject.GetPropertyDescriptors().GetInternalVector().clear();
}
extension.GetEventsBasedBehaviors().Clear();
}
}
} // namespace gd
} // namespace gd

View File

@@ -0,0 +1,301 @@
/*
* GDevelop Core
* Copyright 2008-2022 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "PropertyFunctionGenerator.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
namespace gd {
void PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::NamedPropertyDescriptor &property, bool isSharedProperties) {
GenerateGetterAndSetter(project, extension, eventsBasedBehavior, property,
eventsBasedBehavior.GetObjectType(), true,
isSharedProperties);
}
void PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property) {
GenerateGetterAndSetter(project, extension, eventsBasedObject, property, "",
false, false);
}
void PropertyFunctionGenerator::GenerateGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties) {
auto &propertyName = property.GetName();
auto &functionsContainer = eventsBasedEntity.GetEventsFunctions();
gd::String capitalizedName = CapitalizeFirstLetter(property.GetName());
gd::String setterName = "Set" + capitalizedName;
gd::String functionGroupName =
(eventsBasedEntity.GetFullName().empty()
? eventsBasedEntity.GetName()
: eventsBasedEntity.GetFullName()) +
(property.GetGroup().empty()
? ""
: " " + UnCapitalizeFirstLetter(property.GetGroup())) +
" configuration";
gd::String propertyLabel =
property.GetLabel().empty() ? property.GetName() : property.GetLabel();
gd::String descriptionSubject =
(property.GetType() == "Boolean" ? "if " : "the ") +
UnCapitalizeFirstLetter(propertyLabel) +
(isSharedProperties || property.GetType() == "Boolean"
? "."
: " of the object.") +
(property.GetDescription().empty() ? ""
: " " + property.GetDescription()) +
(isSharedProperties
? " While an object is needed, this will apply to all "
"objects using the behavior."
: "");
gd::String propertyGetterName =
(isSharedProperties ? "SharedProperty" : "Property") + property.GetName();
gd::String getterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(), propertyGetterName);
gd::String setterType =
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
extension.GetName(), eventsBasedEntity.GetName(),
"Set" + propertyGetterName);
gd::String getterName = capitalizedName;
gd::String numberOrString =
property.GetType() == "Number" ? "Number" : "String";
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
auto &getter = functionsContainer.InsertNewEventsFunction(
getterName, functionsContainer.GetEventsFunctionsCount());
auto &expressionType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType());
// TODO Stop replacing number by expression when it"s handled by the UI
// and released.
auto &legacyExpressionType =
expressionType == "number" ? "expression" : expressionType;
getter.GetExpressionType()
.SetName(legacyExpressionType)
.SetExtraInfo(GetStringifiedExtraInfo(property));
getter.SetFullName(propertyLabel).SetGroup(functionGroupName);
if (property.GetType() == "Boolean") {
getter.SetFunctionType(gd::EventsFunction::Condition)
.SetDescription("Check " + descriptionSubject)
.SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel));
} else {
getter.SetFunctionType(gd::EventsFunction::ExpressionAndCondition)
.SetDescription(descriptionSubject)
.SetSentence("the " + UnCapitalizeFirstLetter(propertyLabel));
}
auto &event =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
if (property.GetType() == "Boolean") {
gd::Instruction condition;
condition.SetType(getterType);
condition.AddParameter("Object");
if (isBehavior) {
condition.AddParameter("Behavior");
}
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType("SetReturnBoolean");
action.AddParameter("True");
event.GetActions().Insert(action, 0);
} else {
gd::Instruction action;
action.SetType("SetReturn" + numberOrString);
gd::String receiver = isBehavior ? "Object.Behavior::" : "Object.";
gd::String propertyPrefix =
(isSharedProperties ? "SharedProperty" : "Property");
action.AddParameter(receiver + propertyPrefix + property.GetName() +
"()");
event.GetActions().Insert(action, 0);
}
}
if (!functionsContainer.HasEventsFunctionNamed(setterName)) {
auto &setter = functionsContainer.InsertNewEventsFunction(
setterName, functionsContainer.GetEventsFunctionsCount());
if (property.GetType() == "Boolean") {
setter.SetFunctionType(gd::EventsFunction::Action)
.SetFullName(propertyLabel)
.SetGroup(functionGroupName)
.SetDescription("Change " + descriptionSubject)
.SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel) +
(isBehavior ? ": _PARAM2_" : ": _PARAM1_"));
gd::ParameterMetadata objectParameter;
objectParameter.SetType("object")
.SetName("Object")
.SetDescription("Object")
.SetExtraInfo(objectType);
if (!isBehavior) {
gd::String objectFullType = gd::PlatformExtension::GetObjectFullType(
extension.GetName(), eventsBasedEntity.GetName());
objectParameter.SetExtraInfo(objectFullType);
}
setter.GetParameters().push_back(objectParameter);
if (isBehavior) {
gd::ParameterMetadata behaviorParameter;
gd::String behaviorFullType =
gd::PlatformExtension::GetBehaviorFullType(
extension.GetName(), eventsBasedEntity.GetName());
behaviorParameter.SetType("behavior")
.SetName("Behavior")
.SetDescription("Behavior")
.SetExtraInfo(behaviorFullType);
setter.GetParameters().push_back(behaviorParameter);
}
gd::ParameterMetadata valueParameter;
valueParameter.SetType("yesorno")
.SetName("Value")
.SetDescription(capitalizedName)
.SetOptional(true)
.SetDefaultValue("yes");
setter.GetParameters().push_back(valueParameter);
} else {
setter.SetFunctionType(gd::EventsFunction::ActionWithOperator);
setter.SetGetterName(getterName);
}
if (property.GetType() == "Boolean") {
{
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("yes");
} else {
action.AddParameter("yes");
}
event.GetActions().Insert(action, 0);
}
{
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction condition;
condition.SetType("GetArgumentAsBoolean");
condition.AddParameter("\"Value\"");
condition.SetInverted(true);
event.GetConditions().Insert(condition, 0);
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("no");
} else {
action.AddParameter("no");
}
event.GetActions().Insert(action, 0);
}
} else {
auto &event =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard", 0));
gd::Instruction action;
action.SetType(setterType);
action.AddParameter("Object");
gd::String parameterGetterCall =
"GetArgumentAs" + numberOrString + "(\"Value\")";
if (isBehavior) {
action.AddParameter("Behavior");
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
} else {
action.AddParameter("=");
action.AddParameter(parameterGetterCall);
}
event.GetActions().Insert(action, 0);
}
}
}
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") {
return false;
}
auto &functionsContainer = eventsBasedEntity.GetEventsFunctions();
auto getterName = CapitalizeFirstLetter(property.GetName());
auto setterName = "Set" + getterName;
return !functionsContainer.HasEventsFunctionNamed(setterName) &&
!functionsContainer.HasEventsFunctionNamed(getterName);
};
gd::String PropertyFunctionGenerator::GetStringifiedExtraInfo(
const gd::PropertyDescriptor &property) {
if (property.GetType() == "Choice") {
gd::String arrayString;
arrayString += "[";
bool isFirst = true;
for (const gd::String &choice : property.GetExtraInfo()) {
if (!isFirst) {
arrayString += ",";
}
isFirst = false;
arrayString += "\"" + choice + "\"";
}
arrayString += "]";
return arrayString;
}
return "";
}
gd::String
PropertyFunctionGenerator::CapitalizeFirstLetter(const gd::String &string) {
if (string.empty()) {
return string;
}
return string.substr(0, 1).UpperCase() + string.substr(1);
}
gd::String
PropertyFunctionGenerator::UnCapitalizeFirstLetter(const gd::String &string) {
if (string.empty()) {
return string;
}
return string.substr(0, 1).LowerCase() + string.substr(1);
}
} // namespace gd

View File

@@ -0,0 +1,65 @@
/*
* GDevelop Core
* Copyright 2008-2022 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_PROPERTYFUNCTIONGENERATOR_H
#define GDCORE_PROPERTYFUNCTIONGENERATOR_H
namespace gd {
class String;
class Project;
class EventsFunctionsExtension;
class EventsBasedBehavior;
class EventsBasedObject;
class AbstractEventsBasedEntity;
class PropertyDescriptor;
class NamedPropertyDescriptor;
} // namespace gd
namespace gd {
/**
* \brief Generate a getter and a setter functions for properties.
*/
class GD_CORE_API PropertyFunctionGenerator {
public:
/**
* \brief Generate a getter and a setter for the given behavior property.
*/
static void GenerateBehaviorGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::NamedPropertyDescriptor &property, bool isSharedProperties);
/**
* \brief Generate a getter and a setter for the given object property.
*/
static void
GenerateObjectGetterAndSetter(gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property);
static bool CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property);
~PropertyFunctionGenerator();
private:
static void GenerateGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties);
static gd::String CapitalizeFirstLetter(const gd::String &string);
static gd::String UnCapitalizeFirstLetter(const gd::String &string);
static gd::String
GetStringifiedExtraInfo(const gd::PropertyDescriptor &property);
PropertyFunctionGenerator();
};
} // namespace gd
#endif // GDCORE_PROPERTYFUNCTIONGENERATOR_H

View File

@@ -35,40 +35,6 @@
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace {
// These functions are doing the reverse of what is done when adding
// instructions/expression to extension/behaviors. If needed, they could be
// moved to gd::PlatformExtension to colocate the usage of the namespace
// separator?
gd::String GetEventsFunctionFullType(const gd::String& extensionName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String GetBehaviorEventsFunctionFullType(const gd::String& extensionName,
const gd::String& behaviorName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String GetBehaviorFullType(const gd::String& extensionName,
const gd::String& behaviorName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
gd::String GetObjectEventsFunctionFullType(const gd::String& extensionName,
const gd::String& objectName,
const gd::String& functionName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + objectName + separator + functionName;
}
gd::String GetObjectFullType(const gd::String& extensionName,
const gd::String& objectName) {
const auto& separator = gd::PlatformExtension::GetNamespaceSeparator();
return extensionName + separator + objectName;
}
} // namespace
namespace gd {
// By convention, the first parameter of an events based behavior method is
@@ -154,7 +120,11 @@ void WholeProjectRefactorer::ExposeProjectEvents(
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, *eventsFunction, globalObjectsAndGroups, objectsAndGroups);
project,
eventsFunctionsExtension,
*eventsFunction,
globalObjectsAndGroups,
objectsAndGroups);
worker.Launch(eventsFunction->GetEvents(),
globalObjectsAndGroups,
@@ -248,7 +218,7 @@ WholeProjectRefactorer::GetAllObjectTypesUsingEventsBasedBehavior(
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior) {
std::set<gd::String> allTypes;
const gd::String behaviorType = GetBehaviorFullType(
const gd::String behaviorType = gd::PlatformExtension::GetBehaviorFullType(
eventsFunctionsExtension.GetName(), eventsBasedBehavior.GetName());
auto addTypesOfObjectsIn =
@@ -291,7 +261,7 @@ void WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
.SetType("behavior")
.SetName("Behavior")
.SetDescription("Behavior")
.SetExtraInfo(GetBehaviorFullType(eventsFunctionsExtension.GetName(),
.SetExtraInfo(gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()));
}
}
@@ -311,7 +281,7 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
.SetType("object")
.SetName(parentObjectParameterName)
.SetDescription("Object")
.SetExtraInfo(GetObjectFullType(eventsFunctionsExtension.GetName(),
.SetExtraInfo(gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()));
}
}
@@ -326,30 +296,27 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
DoRenameEventsFunction(
project,
eventsFunction,
GetEventsFunctionFullType(oldName, eventsFunction.GetName()),
GetEventsFunctionFullType(newName, eventsFunction.GetName()));
gd::PlatformExtension::GetEventsFunctionFullType(oldName, eventsFunction.GetName()),
gd::PlatformExtension::GetEventsFunctionFullType(newName, eventsFunction.GetName()));
};
auto renameBehaviorEventsFunction =
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the extension name
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(oldName,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(oldName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()),
GetBehaviorEventsFunctionFullType(newName,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(newName,
eventsBasedBehavior.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the extension name
}
};
@@ -359,12 +326,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
property.GetName())),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyActionName(
@@ -374,12 +341,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
property.GetName())),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetPropertyConditionName(
@@ -390,26 +357,60 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// extension name
};
auto renameBehaviorSharedPropertyFunctions =
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
oldName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
newName,
eventsBasedBehavior.GetName(),
gd::EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expressions, expressions are not including the
// extension name
};
auto renameObjectEventsFunction =
[&project, &oldName, &newName](
const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the extension name
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(oldName,
gd::PlatformExtension::GetObjectEventsFunctionFullType(oldName,
eventsBasedObject.GetName(),
eventsFunction.GetName()),
GetObjectEventsFunctionFullType(newName,
gd::PlatformExtension::GetObjectEventsFunctionFullType(newName,
eventsBasedObject.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the extension name
}
};
@@ -419,12 +420,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
oldName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyActionName(
property.GetName())),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
newName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyActionName(
@@ -434,12 +435,12 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
oldName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyConditionName(
property.GetName())),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
newName,
eventsBasedObject.GetName(),
gd::EventsBasedObject::GetPropertyConditionName(
@@ -456,9 +457,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// Free expressions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameEventsFunction(*eventsFunction);
}
}
@@ -467,9 +466,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
@@ -477,8 +474,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// Free instructions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameEventsFunction(*eventsFunction);
}
}
@@ -488,21 +484,26 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
}
// Behavior properties
for (auto&& eventsBasedBehavior :
for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorProperties = eventsBasedBehavior->GetPropertyDescriptors();
for (auto&& propertyDescriptor : behaviorProperties.GetInternalVector()) {
for (auto &&propertyDescriptor :
eventsBasedBehavior->GetPropertyDescriptors().GetInternalVector()) {
renameBehaviorPropertyFunctions(*eventsBasedBehavior,
*propertyDescriptor);
}
for (auto &&propertyDescriptor :
eventsBasedBehavior->GetSharedPropertyDescriptors()
.GetInternalVector()) {
renameBehaviorSharedPropertyFunctions(*eventsBasedBehavior,
*propertyDescriptor);
}
}
// Object instructions
@@ -510,8 +511,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
auto& objectEventsFunctions = eventsBasedObject->GetEventsFunctions();
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameObjectEventsFunction(*eventsBasedObject, *eventsFunction);
}
}
@@ -532,8 +532,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
DoRenameBehavior(
project,
GetBehaviorFullType(oldName, eventsBasedBehavior->GetName()),
GetBehaviorFullType(newName, eventsBasedBehavior->GetName()));
gd::PlatformExtension::GetBehaviorFullType(oldName, eventsBasedBehavior->GetName()),
gd::PlatformExtension::GetBehaviorFullType(newName, eventsBasedBehavior->GetName()));
}
// Finally, rename custom objects type
@@ -541,8 +541,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
DoRenameObject(
project,
GetObjectFullType(oldName, eventsBasedObject->GetName()),
GetObjectFullType(newName, eventsBasedObject->GetName()));
gd::PlatformExtension::GetObjectFullType(oldName, eventsBasedObject->GetName()),
gd::PlatformExtension::GetObjectFullType(newName, eventsBasedObject->GetName()));
}
}
@@ -559,10 +559,20 @@ void WholeProjectRefactorer::RenameEventsFunction(
DoRenameEventsFunction(
project,
eventsFunction,
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
oldFunctionName),
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
newFunctionName));
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsFunctionsExtension.GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
void WholeProjectRefactorer::RenameBehaviorEventsFunction(
@@ -577,30 +587,39 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(oldFunctionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
oldFunctionName),
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
oldFunctionName),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsBasedBehavior.GetEventsFunctions().GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
void WholeProjectRefactorer::RenameObjectEventsFunction(
@@ -615,30 +634,36 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(oldFunctionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
oldFunctionName),
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedObjectExpression(
GetObjectFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
oldFunctionName),
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsBasedObject.GetEventsFunctions().GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
@@ -652,24 +677,25 @@ void WholeProjectRefactorer::MoveEventsFunctionParameter(
const gd::EventsFunction& eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
const gd::String& eventsFunctionType = GetEventsFunctionFullType(
const gd::String& eventsFunctionType = gd::PlatformExtension::GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetFreeExpressionMovedParameter(
eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
@@ -686,29 +712,30 @@ void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetBehaviorExpressionMovedParameter(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
functionName,
oldIndex,
newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
@@ -725,29 +752,30 @@ void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetObjectExpressionMovedParameter(
GetObjectFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
functionName,
oldIndex,
newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
@@ -784,7 +812,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
@@ -792,11 +820,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(oldPropertyName)),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyActionName(newPropertyName)));
@@ -804,11 +832,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(oldPropertyName)),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(newPropertyName)));
@@ -816,6 +844,72 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
}
}
void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldPropertyName,
const gd::String& newPropertyName) {
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(oldPropertyName)) return;
if (properties.Get(oldPropertyName).GetType() == "Behavior") {
// This is a property representing another behavior that must exist on the
// object.
// This other "required behavior" uses the property name, that is about to
// change, as its name.
// So we must change all reference to this name in the events of the
// behavior functions.
gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(),
behaviorObjectParameterName,
oldPropertyName,
newPropertyName);
ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, behaviorRenamer);
} else {
// Properties that represent primitive values will be used through
// their related actions/conditions/expressions. Rename these.
// Order is important: we first rename the expressions then the
// instructions, to avoid being unable to fetch the metadata (the types of
// parameters) of instructions after they are renamed.
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetSharedPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
ExposeProjectEvents(project, expressionRenamer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyActionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyActionName(newPropertyName)));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyConditionName(oldPropertyName)),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetSharedPropertyConditionName(newPropertyName)));
ExposeProjectEvents(project, conditionRenamer);
}
}
void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
@@ -834,7 +928,7 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedObjectExpression(
GetObjectFullType(eventsFunctionsExtension.GetName(),
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
EventsBasedObject::GetPropertyExpressionName(oldPropertyName),
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
@@ -842,11 +936,11 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyActionName(oldPropertyName)),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyActionName(newPropertyName)));
@@ -854,11 +948,11 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyConditionName(oldPropertyName)),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
EventsBasedObject::GetPropertyConditionName(newPropertyName)));
@@ -1088,25 +1182,22 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// behavior
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
eventsFunction.GetName()),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the name of the
// behavior
}
};
@@ -1118,11 +1209,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
@@ -1130,11 +1221,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())),
GetBehaviorEventsFunctionFullType(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
@@ -1144,6 +1235,40 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
// the behavior
};
auto renameBehaviorSharedProperty = [&project,
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](
const gd::NamedPropertyDescriptor&
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(property.GetName())));
ExposeProjectEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(property.GetName())),
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(property.GetName())));
ExposeProjectEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the behavior
};
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
@@ -1151,31 +1276,30 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
// Behavior expressions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
// Behavior instructions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
// Behavior properties
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto&& property : properties.GetInternalVector()) {
for (auto&& property : eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
renameBehaviorProperty(*property);
}
for (auto&& property : eventsBasedBehavior.GetSharedPropertyDescriptors().GetInternalVector()) {
renameBehaviorSharedProperty(*property);
}
DoRenameBehavior(
project,
GetBehaviorFullType(eventsFunctionsExtension.GetName(), oldBehaviorName),
GetBehaviorFullType(eventsFunctionsExtension.GetName(), newBehaviorName));
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(), oldBehaviorName),
gd::PlatformExtension::GetBehaviorFullType(eventsFunctionsExtension.GetName(), newBehaviorName));
}
void WholeProjectRefactorer::RenameEventsBasedObject(
@@ -1197,25 +1321,22 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
&eventsFunctionsExtension,
&oldObjectName,
&newObjectName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// object
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
eventsFunction.GetName()),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the name of the
// object
}
};
@@ -1227,11 +1348,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())));
@@ -1239,11 +1360,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
oldObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())),
GetObjectEventsFunctionFullType(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
newObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())));
@@ -1260,17 +1381,14 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
// Object expressions
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameObjectEventsFunction(*eventsFunction);
}
}
// Object instructions
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameObjectEventsFunction(*eventsFunction);
}
}
@@ -1283,8 +1401,8 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
DoRenameObject(
project,
GetObjectFullType(eventsFunctionsExtension.GetName(), oldObjectName),
GetObjectFullType(eventsFunctionsExtension.GetName(), newObjectName));
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(), oldObjectName),
gd::PlatformExtension::GetObjectFullType(eventsFunctionsExtension.GetName(), newObjectName));
}
void WholeProjectRefactorer::DoRenameEventsFunction(
@@ -1292,20 +1410,20 @@ void WholeProjectRefactorer::DoRenameEventsFunction(
const gd::EventsFunction& eventsFunction,
const gd::String& oldFullType,
const gd::String& newFullType) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer =
gd::InstructionsTypeRenamer(project, oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedFreeExpression(oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer =
gd::InstructionsTypeRenamer(project, oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
}
}
void WholeProjectRefactorer::DoRenameBehavior(

View File

@@ -207,6 +207,21 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **before** a shared property of a behavior is
* renamed.
*
* \warning Do the renaming of the specified shared property after calling
* this. This is because the shared property is expected to have its old name
* for the refactoring.
*/
static void RenameEventsBasedBehaviorSharedProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **before** a property of an object is
* renamed.

View File

@@ -10,8 +10,10 @@
namespace gd {
AbstractEventsBasedEntity::AbstractEventsBasedEntity(const gd::String& _name)
: name(_name), fullName("") {}
AbstractEventsBasedEntity::AbstractEventsBasedEntity(
const gd::String& _name,
gd::EventsFunctionsContainer::FunctionOwner functionContainerSource)
: name(_name), fullName(""), eventsFunctionsContainer(functionContainerSource) {}
void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);

View File

@@ -29,7 +29,9 @@ namespace gd {
*/
class GD_CORE_API AbstractEventsBasedEntity {
public:
AbstractEventsBasedEntity(const gd::String& _name);
AbstractEventsBasedEntity(
const gd::String& _name,
gd::EventsFunctionsContainer::FunctionOwner functionContainerSource);
virtual ~AbstractEventsBasedEntity(){};
/**

View File

@@ -13,7 +13,8 @@ namespace gd {
/**
* \brief Base class used to represents a behavior that can be applied to an
* object. It stores the content (i.e: the properties) of a behavior of an object.
* object. It stores the content (i.e: the properties) of a behavior of an object
* and forward the properties related functions to Javascript with Emscripten.
*
* \see gd::BehaviorsSharedData
* \see gd::Object

View File

@@ -11,6 +11,7 @@
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
#include <map>
@@ -21,52 +22,26 @@ CustomBehavior *CustomBehavior::Clone() const {
return clone;
}
void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
gd::CustomConfigurationHelper::InitializeContent(properties, behaviorContent);
}
std::map<gd::String, gd::PropertyDescriptor> CustomBehavior::GetProperties(
const gd::SerializerElement &behaviorContent) const {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project.HasEventsBasedBehavior(GetTypeName())) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
return behaviorProperties;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = behaviorProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (behaviorContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
newProperty.SetValue(
behaviorContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
newProperty.SetValue(gd::String::From(
behaviorContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
newProperty.SetValue(
behaviorContent.GetChild(propertyName).GetBoolValue() ? "true"
: "false");
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return behaviorProperties;
return gd::CustomConfigurationHelper::GetProperties(properties, behaviorContent);
}
bool CustomBehavior::UpdateProperty(gd::SerializerElement &behaviorContent,
@@ -77,43 +52,10 @@ bool CustomBehavior::UpdateProperty(gd::SerializerElement &behaviorContent,
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(propertyName)) {
return false;
}
const auto &property = properties.Get(propertyName);
auto &element = behaviorContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
}
void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto &&property : properties.GetInternalVector()) {
auto &element = behaviorContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
behaviorContent,
propertyName,
newValue);
}

View File

@@ -17,8 +17,7 @@ using namespace gd;
namespace gd {
/**
* \brief A gd::Behavior that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
* \brief A gd::Behavior that stores its content in JSON.
*/
class CustomBehavior : public gd::Behavior {
public:
@@ -34,13 +33,11 @@ public:
using Behavior::UpdateProperty;
protected:
virtual std::map<gd::String, gd::PropertyDescriptor>
std::map<gd::String, gd::PropertyDescriptor>
GetProperties(const gd::SerializerElement &behaviorContent) const override;
virtual bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name,
const gd::String &value) override;
virtual void
InitializeContent(gd::SerializerElement &behaviorContent) override;
bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name, const gd::String &value) override;
void InitializeContent(gd::SerializerElement &behaviorContent) override;
private:
const Project &project; ///< The project is used to get the

View File

@@ -0,0 +1,61 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomBehaviorsSharedData.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
#include <map>
using namespace gd;
CustomBehaviorsSharedData *CustomBehaviorsSharedData::Clone() const {
CustomBehaviorsSharedData *clone = new CustomBehaviorsSharedData(*this);
return clone;
}
void CustomBehaviorsSharedData::InitializeContent(gd::SerializerElement &behaviorContent) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
gd::CustomConfigurationHelper::InitializeContent(properties, behaviorContent);
}
std::map<gd::String, gd::PropertyDescriptor> CustomBehaviorsSharedData::GetProperties(
const gd::SerializerElement &behaviorContent) const {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
return behaviorProperties;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
return gd::CustomConfigurationHelper::GetProperties(properties, behaviorContent);
}
bool CustomBehaviorsSharedData::UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &propertyName,
const gd::String &newValue) {
if (!project.HasEventsBasedBehavior(GetTypeName())) {
return false;
}
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
behaviorContent,
propertyName,
newValue);
}

View File

@@ -0,0 +1,46 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H
#define GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief A gd::BehaviorsSharedData that stores its content in JSON.
*/
class CustomBehaviorsSharedData : public gd::BehaviorsSharedData {
public:
CustomBehaviorsSharedData(const gd::String &name, const Project &project_,
const gd::String &fullType)
: BehaviorsSharedData(name, fullType), project(project_) {}
CustomBehaviorsSharedData *Clone() const override;
using BehaviorsSharedData::GetProperties;
using BehaviorsSharedData::InitializeContent;
using BehaviorsSharedData::UpdateProperty;
protected:
std::map<gd::String, gd::PropertyDescriptor>
GetProperties(const gd::SerializerElement &behaviorContent) const override;
bool UpdateProperty(gd::SerializerElement &behaviorContent,
const gd::String &name, const gd::String &value) override;
void InitializeContent(gd::SerializerElement &behaviorContent) override;
private:
const Project &project; ///< The project is used to get the
///< EventBasedBehavior from the fullType.
};
} // namespace gd
#endif // GDCORE_CUSTOMBEHAVIORSSHAREDDATA_H

View File

@@ -0,0 +1,104 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomConfigurationHelper.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include <map>
using namespace gd;
void CustomConfigurationHelper::InitializeContent(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
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") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
}
std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetProperties(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
const gd::SerializerElement &configurationContent) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = behaviorProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
newProperty.SetValue(gd::String::From(
configurationContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetBoolValue() ? "true"
: "false");
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return behaviorProperties;
}
bool CustomConfigurationHelper::UpdateProperty(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &configurationContent,
const gd::String &propertyName,
const gd::String &newValue) {
if (!properties.Has(propertyName)) {
return false;
}
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") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
}

View File

@@ -0,0 +1,43 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMCONFIGURATIONHELPER_H
#define GDCORE_CUSTOMCONFIGURATIONHELPER_H
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief Helper functions that gd::CustomBehavior and gd::CustomBehaviorsSharedData use to
* store their content in JSON.
*/
class CustomConfigurationHelper {
public:
CustomConfigurationHelper() {}
static void InitializeContent(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &behaviorContent);
static std::map<gd::String, gd::PropertyDescriptor> GetProperties(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
const gd::SerializerElement &behaviorContent);
static bool UpdateProperty(
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
gd::SerializerElement &behaviorContent,
const gd::String &name,
const gd::String &value);
};
} // namespace gd
#endif // GDCORE_CUSTOMCONFIGURATIONHELPER_H

View File

@@ -12,6 +12,7 @@
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
using namespace gd;
@@ -22,16 +23,14 @@ void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& object
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
childObjectConfigurations.clear();
for (auto& it : objectConfiguration.childObjectConfigurations) {
childObjectConfigurations[it.first] =
gd::make_unique<gd::ObjectConfiguration>(*it.second);
childObjectConfigurations[it.first] = it.second->Clone();
}
}
gd::ObjectConfiguration CustomObjectConfiguration::badObjectConfiguration;
std::unique_ptr<gd::ObjectConfiguration> CustomObjectConfiguration::Clone() const {
CustomObjectConfiguration* clone = new CustomObjectConfiguration(*this);
return std::unique_ptr<gd::ObjectConfiguration>(clone);
return gd::make_unique<gd::CustomObjectConfiguration>(*this);
}
gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(const gd::String &objectName) {
@@ -51,7 +50,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
if (configurationPosition == childObjectConfigurations.end()) {
childObjectConfigurations.insert(std::make_pair(
objectName,
project->CreateObjectConfiguration(childObject.GetType())));
childObject.GetConfiguration().Clone()));
return *(childObjectConfigurations[objectName]);
}
else {
@@ -69,50 +68,7 @@ std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetPrope
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// TODO Move this into a PropertyDescriptor copy method.
auto &newProperty = objectProperties[propertyName]
.SetType(property->GetType())
.SetDescription(property->GetDescription())
.SetGroup(property->GetGroup())
.SetLabel(property->GetLabel())
.SetValue(property->GetValue())
.SetHidden(property->IsHidden());
for (auto &extraInfo : property->GetExtraInfo()) {
newProperty.AddExtraInfo(extraInfo);
}
if (objectContent.HasChild(propertyName)) {
if (
propertyType == "String" ||
propertyType == "Choice" ||
propertyType == "Color"
) {
newProperty.SetValue(
objectContent.GetChild(propertyName).GetStringValue()
);
} else if (propertyType == "Number") {
newProperty.SetValue(
gd::String::From(objectContent.GetChild(propertyName).GetDoubleValue())
);
} else if (propertyType == "Boolean") {
newProperty.SetValue(
objectContent.GetChild(propertyName).GetBoolValue()
? "true"
: "false"
);
}
} else {
// No value was serialized for this property. `newProperty`
// will have the default value coming from `enumeratedProperty`.
}
}
return objectProperties;
return gd::CustomConfigurationHelper::GetProperties(properties, objectContent);
}
bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
@@ -122,27 +78,12 @@ bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
if (!properties.Has(propertyName)) {
return false;
}
const auto &property = properties.Get(propertyName);
auto &element = objectContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (
propertyType == "String" ||
propertyType == "Choice" ||
propertyType == "Color"
) {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
element.SetBoolValue(newValue == "1");
}
return true;
return gd::CustomConfigurationHelper::UpdateProperty(
properties,
objectContent,
propertyName,
newValue);
}
std::map<gd::String, gd::PropertyDescriptor>

View File

@@ -110,6 +110,11 @@ class GD_CORE_API EffectsContainer {
*/
void UnserializeFrom(const SerializerElement& element);
/**
* \brief Clear all effects of the container.
*/
inline void Clear() { effects.clear(); }
private:
std::vector<std::shared_ptr<gd::Effect>> effects;
static Effect badEffect;

View File

@@ -11,17 +11,27 @@
namespace gd {
EventsBasedBehavior::EventsBasedBehavior()
: AbstractEventsBasedEntity("MyBehavior") {}
: AbstractEventsBasedEntity(
"MyBehavior",
gd::EventsFunctionsContainer::FunctionOwner::Behavior) {}
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"));
}
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"));
}
} // namespace gd

View File

@@ -73,6 +73,58 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
return *this;
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
return *this;
}
/**
* \brief Return a reference to the list of shared properties.
*/
SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors() {
return sharedPropertyDescriptors;
}
/**
* \brief Return a const reference to the list of shared properties.
*/
const SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors()
const {
return sharedPropertyDescriptors;
}
/**
* \brief Get the name of the action to change a shared property.
*/
static gd::String GetSharedPropertyActionName(const gd::String &propertyName) {
return "SetSharedProperty" + propertyName;
};
/**
* \brief Get the name of the condition to compare a shared property.
*/
static gd::String GetSharedPropertyConditionName(const gd::String &propertyName) {
return "SharedProperty" + propertyName;
};
/**
* \brief Get the name of the expression to get a shared property.
*/
static gd::String
GetSharedPropertyExpressionName(const gd::String &propertyName) {
return "SharedProperty" + propertyName;
};
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(gd::Project& project,
@@ -80,6 +132,8 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
SerializableWithNameList<NamedPropertyDescriptor> sharedPropertyDescriptors;
};
} // namespace gd

View File

@@ -10,7 +10,10 @@
namespace gd {
EventsBasedObject::EventsBasedObject()
: AbstractEventsBasedEntity("MyObject"), ObjectsContainer() {
: AbstractEventsBasedEntity(
"MyObject",
gd::EventsFunctionsContainer::FunctionOwner::Object),
ObjectsContainer() {
}
EventsBasedObject::~EventsBasedObject() {}
@@ -23,12 +26,16 @@ EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObj
}
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
element.SetAttribute("defaultName", defaultName);
AbstractEventsBasedEntity::SerializeTo(element);
SerializeObjectsTo(element.AddChild("objects"));
}
void EventsBasedObject::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
defaultName = element.GetStringAttribute("defaultName");
AbstractEventsBasedEntity::UnserializeFrom(project, element);
UnserializeObjectsFrom(project, element.GetChild("objects"));
}

View File

@@ -38,6 +38,19 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
*/
EventsBasedObject* Clone() const { return new EventsBasedObject(*this); };
/**
* \brief Get the default name for created objects.
*/
const gd::String& GetDefaultName() const { return defaultName; };
/**
* \brief Set the default name for created objects.
*/
EventsBasedObject& SetDefaultName(const gd::String& defaultName_) {
defaultName = defaultName_;
return *this;
}
EventsBasedObject& SetDescription(const gd::String& description_) override {
AbstractEventsBasedEntity::SetDescription(description_);
return *this;
@@ -65,6 +78,7 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
const SerializerElement& element) override;
private:
gd::String defaultName;
};
} // namespace gd

View File

@@ -7,29 +7,96 @@
#include "EventsFunction.h"
#include <vector>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
namespace gd {
EventsFunction::EventsFunction() : functionType(Action) {}
EventsFunction::EventsFunction() : functionType(Action) {
expressionType.SetName("expression");
}
const std::vector<gd::ParameterMetadata>& EventsFunction::GetParametersForEvents(
const gd::EventsFunctionsContainer& functionsContainer) const {
if (functionType != FunctionType::ActionWithOperator) {
// For most function types, the parameters are specified in the function.
return parameters;
}
// For ActionWithOperator, the parameters are auto generated.
actionWithOperationParameters.clear();
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
return actionWithOperationParameters;
}
const auto& expression = functionsContainer.GetEventsFunction(getterName);
const auto& expressionParameters = expression.parameters;
const auto functionsSource = functionsContainer.GetOwner();
const int expressionValueParameterIndex =
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Behavior ?
2 :
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Object ?
1 :
0;
for (size_t i = 0;
i < expressionValueParameterIndex && i < expressionParameters.size();
i++)
{
actionWithOperationParameters.push_back(expressionParameters[i]);
}
gd::ParameterMetadata parameterMetadata;
parameterMetadata.SetName("Value").SetValueTypeMetadata(expression.expressionType);
actionWithOperationParameters.push_back(parameterMetadata);
for (size_t i = expressionValueParameterIndex;
i < expressionParameters.size();
i++)
{
actionWithOperationParameters.push_back(expressionParameters[i]);
}
return actionWithOperationParameters;
}
void EventsFunction::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
element.SetAttribute("description", description);
if (!description.empty()) {
element.SetAttribute("description", description);
}
element.SetAttribute("sentence", sentence);
element.SetAttribute("group", group);
element.SetBoolAttribute("private", isPrivate);
if (!group.empty()) {
element.SetAttribute("group", group);
}
if (!getterName.empty()) {
element.SetAttribute("getterName", getterName);
}
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
events.SerializeTo(element.AddChild("events"));
gd::String functionTypeStr = "Action";
if (functionType == Condition)
functionTypeStr = "Condition";
else if (functionType == Expression)
else if (functionType == Expression) {
functionTypeStr = "Expression";
else if (functionType == StringExpression)
functionTypeStr = "StringExpression";
// Compatibility code for version 5.1.147 and older.
// There is no longer distinction between number and string in the function
// type directly. The expression type is now used for this.
if (expressionType.IsString()) {
functionTypeStr = "StringExpression";
}
}
else if (functionType == ExpressionAndCondition) {
functionTypeStr = "ExpressionAndCondition";
}
else if (functionType == ActionWithOperator)
functionTypeStr = "ActionWithOperator";
element.SetAttribute("functionType", functionTypeStr);
if (this->IsExpression()) {
expressionType.SerializeTo(element.AddChild("expressionType"));
}
gd::SerializerElement& parametersElement = element.AddChild("parameters");
parametersElement.ConsiderAsArrayOf("parameter");
for (const auto& parameter : parameters) {
@@ -46,16 +113,32 @@ void EventsFunction::UnserializeFrom(gd::Project& project,
description = element.GetStringAttribute("description");
sentence = element.GetStringAttribute("sentence");
group = element.GetStringAttribute("group");
getterName = element.GetStringAttribute("getterName");
isPrivate = element.GetBoolAttribute("private");
events.UnserializeFrom(project, element.GetChild("events"));
gd::String functionTypeStr = element.GetStringAttribute("functionType");
if (functionTypeStr == "Condition")
functionType = Condition;
else if (functionTypeStr == "Expression")
else if (functionTypeStr == "Expression" || functionTypeStr == "StringExpression") {
functionType = Expression;
else if (functionTypeStr == "StringExpression")
functionType = StringExpression;
if (element.HasChild("expressionType")) {
expressionType.UnserializeFrom(element.GetChild("expressionType"));
}
else {
// Compatibility code for version 5.1.147 and older.
// There is no longer distinction between number and string in the function
// type directly. The expression type is now used for this.
expressionType.SetName(functionTypeStr == "StringExpression" ? "string" : "expression");
}
}
else if (functionTypeStr == "ExpressionAndCondition") {
functionType = ExpressionAndCondition;
expressionType.UnserializeFrom(element.GetChild("expressionType"));
}
else if (functionTypeStr == "ActionWithOperator")
functionType = ActionWithOperator;
else
functionType = Action;

View File

@@ -12,6 +12,7 @@
#include "GDCore/Events/EventsList.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/String.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
// TODO: In theory (for separation of concerns between Project and
// extensions/events), this include should be removed and gd::ParameterMetadata
// replaced by a new gd::EventsFunctionParameter class.
@@ -19,6 +20,7 @@
namespace gd {
class SerializerElement;
class Project;
class EventsFunctionsContainer;
} // namespace gd
namespace gd {
@@ -115,7 +117,45 @@ class GD_CORE_API EventsFunction {
return *this;
}
enum FunctionType { Action, Condition, Expression, StringExpression };
/**
* \brief Get the name of the ExpressionAndCondition to use as an operand
* that is defined in the editor.
*/
const gd::String& GetGetterName() const { return getterName; };
/**
* \brief Set the name of the ExpressionAndCondition to use as an operand
* that is defined in the editor.
*/
EventsFunction& SetGetterName(const gd::String& getterName_) {
getterName = getterName_;
return *this;
}
/**
* \brief Set the type of the expression
*/
EventsFunction& SetExpressionType(const gd::ValueTypeMetadata& type) {
expressionType = type;
return *this;
}
/**
* \brief Get the type of the expression
*/
const gd::ValueTypeMetadata& GetExpressionType() const { return expressionType; }
/**
* \brief Get the type of the expression
*/
gd::ValueTypeMetadata& GetExpressionType() { return expressionType; }
enum FunctionType {
Action,
Condition,
Expression,
ExpressionAndCondition,
ActionWithOperator };
/**
* \brief Set the type of the function
@@ -123,12 +163,40 @@ class GD_CORE_API EventsFunction {
EventsFunction& SetFunctionType(FunctionType type) {
functionType = type;
return *this;
};
}
/**
* \brief Get the type of the function
*/
FunctionType GetFunctionType() const { return functionType; };
FunctionType GetFunctionType() const { return functionType; }
/**
* \brief Return true if the function is an action.
*/
bool IsAction() const {
return functionType == gd::EventsFunction::Action ||
functionType == gd::EventsFunction::ActionWithOperator;
}
/**
* \brief Return true if the function is an expression.
*
* Note that a function can be both an expression and a condition.
*/
bool IsExpression() const {
return functionType == gd::EventsFunction::Expression ||
functionType == gd::EventsFunction::ExpressionAndCondition;
}
/**
* \brief Return true if the function is a condition.
*
* Note that a function can be both an expression and a condition.
*/
bool IsCondition() const {
return functionType == gd::EventsFunction::Condition ||
functionType == gd::EventsFunction::ExpressionAndCondition;
}
/**
* \brief Returns true if the function is private.
@@ -154,7 +222,20 @@ class GD_CORE_API EventsFunction {
gd::EventsList& GetEvents() { return events; };
/**
* \brief Return the parameters of the function.
* \brief Return the parameters of the function that are used in the events.
*
* \note During code/extension generation, new parameters are added
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
* This should be transparent to the user.
*/
const std::vector<gd::ParameterMetadata>& GetParametersForEvents(
const gd::EventsFunctionsContainer& functionsContainer) const;
/**
* \brief Return the parameters of the function that are filled in the editor.
*
* \note They won't be used for ActionWithOperator, but they need to be kept
* to avoid to loose them when the function type is changed.
*
* \note During code/extension generation, new parameters are added
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
@@ -202,9 +283,12 @@ class GD_CORE_API EventsFunction {
gd::String description;
gd::String sentence;
gd::String group;
gd::String getterName;
gd::ValueTypeMetadata expressionType;
gd::EventsList events;
FunctionType functionType;
std::vector<gd::ParameterMetadata> parameters;
mutable std::vector<gd::ParameterMetadata> actionWithOperationParameters;
gd::ObjectGroupsContainer objectGroups;
bool isPrivate = false;
};

View File

@@ -25,7 +25,24 @@ namespace gd {
*/
class GD_CORE_API EventsFunctionsContainer
: private SerializableWithNameList<gd::EventsFunction> {
public:
public:
enum FunctionOwner {
Extension,
Object,
Behavior};
EventsFunctionsContainer(FunctionOwner source_) : owner(source_) {}
/**
* \brief Get the source of the function container.
*
* \note For instance, it can be useful to handle specific parameters for
* behaviors.
*/
FunctionOwner GetOwner() const {
return owner;
}
/** \name Events Functions management
*/
///@{
@@ -139,6 +156,9 @@ class GD_CORE_API EventsFunctionsContainer
void Init(const gd::EventsFunctionsContainer& other) {
return SerializableWithNameList<gd::EventsFunction>::Init(other);
};
private:
FunctionOwner owner;
};
} // namespace gd

View File

@@ -13,10 +13,14 @@
namespace gd {
EventsFunctionsExtension::EventsFunctionsExtension() {}
EventsFunctionsExtension::EventsFunctionsExtension() :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {}
EventsFunctionsExtension::EventsFunctionsExtension(
const EventsFunctionsExtension& other) {
const EventsFunctionsExtension& other) :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {
Init(other);
}
@@ -50,7 +54,7 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
element.SetAttribute("version", version);
element.SetAttribute("extensionNamespace", extensionNamespace);
element.SetAttribute("shortDescription", shortDescription);
element.SetAttribute("description", description);
element.AddChild("description").SetMultilineStringValue(description);
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
element.SetAttribute("category", category);
@@ -96,7 +100,7 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
version = element.GetStringAttribute("version");
extensionNamespace = element.GetStringAttribute("extensionNamespace");
shortDescription = element.GetStringAttribute("shortDescription");
description = element.GetStringAttribute("description");
description = element.GetChild("description").GetMultilineStringValue();
name = element.GetStringAttribute("name");
fullName = element.GetStringAttribute("fullName");
category = element.GetStringAttribute("category");

View File

@@ -20,7 +20,9 @@ class Project;
} // namespace gd
namespace gd {
// TODO Remove the EventsFunctionsContainer inheritance and make it an attribute.
// This will allow to get EventsFunctionsContainer the same way for extensions,
// objects and behaviors.
/**
* \brief Hold a list of Events Functions (gd::EventsFunction) and Events Based
* Behaviors.

View File

@@ -244,6 +244,15 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
*/
size_t GetInstancesCount() const { return instancesCount; }
void Reset() {
highestZOrder = 0;
lowestZOrder = 0;
instancesCount = 0;
firstCall = true;
layerRestricted = false;
layerName.clear();
}
private:
int highestZOrder;
int lowestZOrder;

View File

@@ -17,6 +17,7 @@
#include "GDCore/IDE/SceneNameMangler.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/CustomBehaviorsSharedData.h"
#include "GDCore/Project/InitialInstance.h"
#include "GDCore/Project/Layer.h"
#include "GDCore/Project/Object.h"
@@ -26,6 +27,7 @@
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Tools/Log.h"
using namespace std;
@@ -240,11 +242,11 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
}
}
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
// Events based behaviors don't have shared data yet.
auto sharedData =
gd::make_unique<gd::BehaviorsSharedData>(name, behaviorsType);
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
@@ -253,7 +255,14 @@ std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(gd::P
project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
return nullptr;
gd::LogWarning("Tried to create a behavior shared data with an unknown type: " +
behaviorsType + " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// Create a custom behavior shared data to preserve the properties values.
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =

View File

@@ -0,0 +1,27 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementBaseUnit.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include <vector>
namespace gd {
MeasurementBaseUnit::~MeasurementBaseUnit() {}
const gd::MeasurementBaseUnit MeasurementBaseUnit::degreeAngle =
MeasurementBaseUnit("degree", "deg", "");
const gd::MeasurementBaseUnit MeasurementBaseUnit::pixel =
MeasurementBaseUnit("pixel", "px", "distance");
const gd::MeasurementBaseUnit MeasurementBaseUnit::meter =
MeasurementBaseUnit("meter", "m", "distance");
const gd::MeasurementBaseUnit MeasurementBaseUnit::second =
MeasurementBaseUnit("second", "s", "time");
const gd::MeasurementBaseUnit MeasurementBaseUnit::kilogram =
MeasurementBaseUnit("kilogram", "Kg", "mass");
} // namespace gd

View File

@@ -0,0 +1,57 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTBASEUNIT
#define GDCORE_MEASUREMENTBASEUNIT
#include <vector>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief An atomic unit of measurement.
*/
class GD_CORE_API MeasurementBaseUnit {
public:
MeasurementBaseUnit(gd::String name_, gd::String symbol_,
gd::String quantity_)
: name(name_), symbol(symbol_), quantity(quantity_) {}
virtual ~MeasurementBaseUnit();
/**
* \brief Return the unit name.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Return the unit symbol.
*/
const gd::String &GetSymbol() const { return symbol; }
/**
* \brief Return the physical quantity.
*/
const gd::String &GetQuantity() const { return quantity; }
static const gd::MeasurementBaseUnit degreeAngle;
static const gd::MeasurementBaseUnit pixel;
static const gd::MeasurementBaseUnit meter;
static const gd::MeasurementBaseUnit second;
static const gd::MeasurementBaseUnit kilogram;
private:
gd::String name; ///< The unit name
gd::String symbol; ///< The unit symbol
gd::String quantity; ///< The physical quantity
};
} // namespace gd
#endif // GDCORE_MEASUREMENTBASEUNIT

View File

@@ -0,0 +1,38 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementUnit.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include <vector>
namespace gd {
MeasurementUnit::~MeasurementUnit() {}
gd::MeasurementUnit MeasurementUnit::undefined = CreateUndefined();
gd::MeasurementUnit MeasurementUnit::dimensionless = CreateDimensionless();
gd::MeasurementUnit MeasurementUnit::degreeAngle = CreateDegreeAngle();
gd::MeasurementUnit MeasurementUnit::second = CreateSecond();
gd::MeasurementUnit MeasurementUnit::pixel = CreatePixel();
gd::MeasurementUnit MeasurementUnit::pixelSpeed = CreatePixelSpeed();
gd::MeasurementUnit MeasurementUnit::pixelAcceleration =
CreatePixelAcceleration();
gd::MeasurementUnit MeasurementUnit::newton = CreateNewton();
gd::MeasurementUnit MeasurementUnit::angularSpeed = CreateAngularSpeed();
void MeasurementUnit::ApplyTranslation() {
undefined = CreateUndefined();
dimensionless = CreateDimensionless();
degreeAngle = CreateDegreeAngle();
second = CreateSecond();
pixel = CreatePixel();
pixelSpeed = CreatePixelSpeed();
pixelAcceleration = CreatePixelAcceleration();
newton = CreateNewton();
angularSpeed = CreateAngularSpeed();
}
} // namespace gd

View File

@@ -0,0 +1,197 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTUNIT
#define GDCORE_MEASUREMENTUNIT
#include <vector>
#include "GDCore/Project/MeasurementUnitElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
class SerializerElement;
class MeasurementBaseUnit;
} // namespace gd
namespace gd {
/**
* \brief A unit of measurement.
*/
class GD_CORE_API MeasurementUnit {
public:
MeasurementUnit(const std::vector<gd::MeasurementUnitElement> &elements_,
gd::String name_, gd::String label_,
gd::String elementsWithWords_, gd::String description_ = "")
: elements(elements_), name(name_), label(label_),
description(description_), elementsWithWords(elementsWithWords_) {}
MeasurementUnit(gd::String name_, gd::String label_,
gd::String elementsWithWords_, gd::String description_ = "")
: name(name_), label(label_), description(description_),
elementsWithWords(elementsWithWords_) {}
virtual ~MeasurementUnit();
/**
* \brief Return the unit name.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Return the unit label.
*/
const gd::String &GetLabel() const { return label; }
/**
* \brief Return the unit description.
*/
const gd::String &GetDescription() const { return description; }
/**
* \brief Return the unit description.
*/
const gd::String &GetElementsWithWords() const { return elementsWithWords; }
/**
* \brief Return the unit elements.
*/
const std::vector<gd::MeasurementUnitElement> &GetElements() const {
return elements;
}
std::size_t GetElementsCount() const { return elements.size(); }
int GetElementPower(std::size_t elementIndex) const {
return elements.at(elementIndex).GetPower();
}
const gd::MeasurementBaseUnit &
GetElementBaseUnit(std::size_t elementIndex) const {
return elements.at(elementIndex).GetBaseUnit();
}
bool IsUndefined() const { return this == &gd::MeasurementUnit::undefined; }
static void ApplyTranslation();
static gd::MeasurementUnit &GetUndefined() { return undefined; }
static gd::MeasurementUnit &GetDimensionless() { return dimensionless; }
static gd::MeasurementUnit &GetDegreeAngle() { return degreeAngle; }
static gd::MeasurementUnit &GetSecond() { return second; }
static gd::MeasurementUnit &GetPixel() { return pixel; }
static gd::MeasurementUnit &GetPixelSpeed() { return pixelSpeed; }
static gd::MeasurementUnit &GetPixelAcceleration() {
return pixelAcceleration;
}
static gd::MeasurementUnit &GetAngularSpeed() { return angularSpeed; }
static gd::MeasurementUnit &GetNewton() { return newton; }
private:
static gd::MeasurementUnit undefined;
static gd::MeasurementUnit dimensionless;
static gd::MeasurementUnit degreeAngle;
static gd::MeasurementUnit second;
static gd::MeasurementUnit pixel;
static gd::MeasurementUnit pixelSpeed;
static gd::MeasurementUnit pixelAcceleration;
static gd::MeasurementUnit newton;
static gd::MeasurementUnit angularSpeed;
static gd::MeasurementUnit CreateUndefined() {
return MeasurementUnit("Undefined", _("Undefined"), "");
}
static gd::MeasurementUnit CreateDimensionless() {
return MeasurementUnit("Dimensionless", _("Dimensionless"), "");
}
static gd::MeasurementUnit CreateDegreeAngle() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::degreeAngle, 1));
return MeasurementUnit(elements, "DegreeAngle", _("Angle"), _("degree"));
}
static gd::MeasurementUnit CreateSecond() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, 1));
return MeasurementUnit(elements, "Second", _("Duration"), _("second"));
}
static gd::MeasurementUnit CreatePixel() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
return MeasurementUnit(elements, "Pixel", _("Distance"), _("pixel"));
}
static gd::MeasurementUnit CreatePixelSpeed() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -1));
return MeasurementUnit(elements, "PixelSpeed", _("Speed"),
_("pixel per second"),
_("How much distance is covered per second."));
}
static gd::MeasurementUnit CreatePixelAcceleration() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::pixel, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -2));
return MeasurementUnit(elements, "PixelAcceleration", _("Acceleration"),
_("pixel per second, per second"),
_("How much speed is gained (or lost) per second."));
}
static gd::MeasurementUnit CreateNewton() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::meter, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::kilogram, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -2));
return MeasurementUnit(
elements, "Newton",
_("Force (in Newton)"), _("meter kilogram per second, per second"),
_("A unit to measure forces."));
}
static gd::MeasurementUnit CreateAngularSpeed() {
std::vector<gd::MeasurementUnitElement> elements;
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::degreeAngle, 1));
elements.push_back(
MeasurementUnitElement(gd::MeasurementBaseUnit::second, -1));
return MeasurementUnit(elements, "AngularSpeed", _("Angular speed"),
_("degree per second"),
_("How much angle is covered per second."));
}
gd::String name; ///< The unit name.
gd::String label; ///< The unit label.
gd::String description; ///< The unit description.
gd::String elementsWithWords; ///< The unit elements put in words.
std::vector<gd::MeasurementUnitElement> elements; ///< The unit elements.
};
} // namespace gd
#endif // GDCORE_MEASUREMENTUNIT

View File

@@ -0,0 +1,15 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "MeasurementUnitElement.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include <vector>
namespace gd {
MeasurementUnitElement::~MeasurementUnitElement() {}
} // namespace gd

View File

@@ -0,0 +1,46 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_MEASUREMENTUNITELEMENT
#define GDCORE_MEASUREMENTUNITELEMENT
#include <vector>
#include "GDCore/Project/MeasurementBaseUnit.h"
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief A couple of an atomic unit of measurement and its power.
*/
class GD_CORE_API MeasurementUnitElement {
public:
MeasurementUnitElement(const gd::MeasurementBaseUnit &baseUnit_, int power_)
: baseUnit(baseUnit_), power(power_) {}
virtual ~MeasurementUnitElement();
/**
* \brief Return the base unit.
*/
const gd::MeasurementBaseUnit &GetBaseUnit() const { return baseUnit; }
/**
* \brief Return the power on the base unit.
*/
int GetPower() const { return power; }
private:
gd::MeasurementBaseUnit baseUnit; ///< The base unit.
int power; ///< The power on the base unit.
};
} // namespace gd
#endif // GDCORE_MEASUREMENTUNITELEMENT

View File

@@ -114,7 +114,7 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior with an unknown type: " + type
+ " on object " + GetName() + "!");
// It's probably an events-based object that was removed.
// It's probably an events-based behavior that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));

View File

@@ -8,6 +8,8 @@
#include <vector>
#include "GDCore/String.h"
#include "GDCore/Project/MeasurementUnit.h"
namespace gd {
class SerializerElement;
}
@@ -28,12 +30,12 @@ class GD_CORE_API PropertyDescriptor {
* \param propertyValue The value of the property.
*/
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string"), label(""), hidden(false) {}
: currentValue(propertyValue), type("string"), label(""), hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {}
/**
* \brief Empty constructor creating an empty property to be displayed.
*/
PropertyDescriptor() : hidden(false){};
PropertyDescriptor() : hidden(false), measurementUnit(gd::MeasurementUnit::GetUndefined()) {};
/**
* \brief Destructor
@@ -103,12 +105,21 @@ class GD_CORE_API PropertyDescriptor {
extraInformation.push_back(info);
return *this;
}
/**
* \brief Change the unit of measurement of the property value.
*/
PropertyDescriptor& SetMeasurementUnit(const gd::MeasurementUnit &measurementUnit_) {
measurementUnit = measurementUnit_;
return *this;
}
const gd::String& GetValue() const { return currentValue; }
const gd::String& GetType() const { return type; }
const gd::String& GetLabel() const { return label; }
const gd::String& GetDescription() const { return description; }
const gd::String& GetGroup() const { return group; }
const gd::MeasurementUnit& GetMeasurementUnit() const { return measurementUnit; }
const std::vector<gd::String>& GetExtraInfo() const {
return extraInformation;
@@ -168,6 +179,7 @@ class GD_CORE_API PropertyDescriptor {
///< choices, if a property is a displayed as a combo
///< box.
bool hidden;
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
};
} // namespace gd

View File

@@ -208,8 +208,7 @@ SerializerElement& SerializerElement::GetChild(
for (size_t i = 0; i < children.size(); ++i) {
if (children[i].second == std::shared_ptr<SerializerElement>()) continue;
if (children[i].first == name ||
(isArray && children[i].first.empty()) ||
if (children[i].first == name || (isArray && children[i].first.empty()) ||
(!deprecatedName.empty() && children[i].first == deprecatedName)) {
if (index == currentIndex)
return *children[i].second;
@@ -242,8 +241,7 @@ std::size_t SerializerElement::GetChildrenCount(
for (size_t i = 0; i < children.size(); ++i) {
if (children[i].second == std::shared_ptr<SerializerElement>()) continue;
if (children[i].first == name ||
(isArray && children[i].first.empty()) ||
if (children[i].first == name || (isArray && children[i].first.empty()) ||
(!deprecatedName.empty() && children[i].first == deprecatedName))
currentIndex++;
}
@@ -291,4 +289,31 @@ void SerializerElement::Init(const gd::SerializerElement& other) {
deprecatedArrayOf = other.deprecatedArrayOf;
}
void SerializerElement::SetMultilineStringValue(const gd::String& value) {
if (value.find('\n') == gd::String::npos) {
SetStringValue(value);
return;
}
std::vector<gd::String> lines = value.Split('\n');
children.clear();
ConsiderAsArrayOf("");
for (const auto& line : lines) {
AddChild("").SetStringValue(line);
}
}
gd::String SerializerElement::GetMultilineStringValue() {
if (!ConsideredAsArray()) {
return GetValue().GetString();
}
gd::String value;
for (const auto& child : children) {
if (!value.empty()) value += "\n";
value += child.second->GetStringValue();
}
return value;
}
} // namespace gd

View File

@@ -10,6 +10,7 @@
#include <memory>
#include <string>
#include <vector>
#include "GDCore/Serialization/SerializerValue.h"
#include "GDCore/String.h"
@@ -177,6 +178,18 @@ class GD_CORE_API SerializerElement {
* \brief Return true if no value was set for the element.
*/
bool IsValueUndefined() const { return valueUndefined; }
/**
* \brief Save the value either as a string or as an array of strings if it
* has line breaks.
*/
void SetMultilineStringValue(const gd::String &value);
/**
* \brief Read the value, either represented as a string or as an array of strings,
* into a string.
*/
gd::String GetMultilineStringValue();
///@}
/** \name Attributes
@@ -440,7 +453,7 @@ class GD_CORE_API SerializerElement {
* Initialize element using another element. Used by copy-ctor and assign-op.
* Don't forget to update me if members were changed!
*/
void Init(const gd::SerializerElement& other);
void Init(const gd::SerializerElement &other);
bool valueUndefined; ///< If true, the element does not have a value.
SerializerValue elementValue;

View File

@@ -12,6 +12,7 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "catch.hpp"
// TODO Remove these 2 classes and write the test with events based behaviors.
@@ -94,6 +95,13 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
// Don't show extension loading logs for tests (too verbose).
platform.EnableExtensionLoadingLogs(false);
// Required for tests on event generation.
std::shared_ptr<gd::PlatformExtension> commonInstructionsExtension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
commonInstructionsExtension->SetExtensionInformation(
"BuiltinCommonInstructions", "instruction extension", "", "", "");
commonInstructionsExtension->AddEvent("Standard", "Standard event", "", "", "", std::make_shared<gd::StandardEvent>());
std::shared_ptr<gd::PlatformExtension> baseObjectExtension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
@@ -373,6 +381,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddUnsupportedBaseObjectCapability("effect");
}
platform.AddExtension(commonInstructionsExtension);
platform.AddExtension(baseObjectExtension);
platform.AddExtension(extension);
project.AddPlatform(platform);

View File

@@ -10,7 +10,8 @@
TEST_CASE("EventsFunctionsContainer", "[common]") {
SECTION("Sanity checks") {
gd::EventsFunctionsContainer eventsFunctionContainer;
gd::EventsFunctionsContainer eventsFunctionContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer.InsertNewEventsFunction("Function1", 0);
eventsFunctionContainer.InsertNewEventsFunction("Function2", 1);
eventsFunctionContainer.InsertNewEventsFunction("Function3", 2);
@@ -62,7 +63,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
}
SECTION("Serialization") {
gd::Project project;
gd::EventsFunctionsContainer eventsFunctionContainer;
gd::EventsFunctionsContainer eventsFunctionContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer.InsertNewEventsFunction("Function1", 0);
eventsFunctionContainer.InsertNewEventsFunction("Function2", 1);
eventsFunctionContainer.InsertNewEventsFunction("Function3", 2);
@@ -72,7 +74,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
eventsFunctionContainer.RemoveEventsFunction("Function2");
gd::EventsFunctionsContainer eventsFunctionContainer2;
gd::EventsFunctionsContainer eventsFunctionContainer2(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer2.UnserializeEventsFunctionsFrom(project, element);
REQUIRE(eventsFunctionContainer.GetEventsFunctionsCount() == 2);
REQUIRE(eventsFunctionContainer.GetEventsFunction(0).GetName() ==

View File

@@ -0,0 +1,289 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Events/EventsIdentifiersFinder.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "catch.hpp"
namespace {
const void DeclareTimerExtension(gd::Project &project, gd::Platform &platform) {
std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
gd::BuiltinExtensionsImplementer::ImplementsTimeExtension(*(extension.get()));
gd::BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
*(extension.get()));
// Add an instruction to test expressions.
extension
->AddAction("DoSomething", "Do something", "This does something",
"Do something please", "", "", "")
.AddParameter("expression", "Parameter 1 (a number)");
platform.AddExtension(extension);
project.AddPlatform(platform);
}
const gd::StandardEvent UseSceneTimer(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ResetTimer");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression("scene"));
instruction.SetParameter(1, gd::Expression("\"" + name + "\""));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseObjectTimer(const gd::String &objectName,
const gd::String &timerName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ResetObjectTimer");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(objectName));
instruction.SetParameter(1, gd::Expression("\"" + timerName + "\""));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneTimerInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(
0, gd::Expression("1 + TimerElapsedTime(\"" + name + "\")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent
UseObjectTimerInExpression(const gd::String &objectName,
const gd::String &timerName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0, gd::Expression("1 + " + objectName +
".ObjectTimerElapsedTime(\"" + timerName +
"\")"));
event.GetActions().Insert(instruction);
return event;
}
const void UseExternalEvents(gd::Layout &layout,
gd::ExternalEvents &externalEvents) {
gd::LinkEvent linkEvent;
linkEvent.SetTarget(externalEvents.GetName());
layout.GetEvents().InsertEvent(linkEvent);
}
} // namespace
TEST_CASE("EventsIdentifiersFinder (scene timers)", "[common]") {
SECTION("Can find scene timers in scenes") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneTimer("MySceneTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneTimerInExpression("MySceneTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(UseSceneTimer("MySceneTimer"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers the right scene") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.GetEvents().InsertEvent(UseSceneTimer("MySceneTimerInLayout1"));
auto &layout2 = project.InsertNewLayout("Layout2", 0);
layout2.GetEvents().InsertEvent(UseSceneTimer("MySceneTimerInLayout2"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout1, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimerInLayout1\"");
}
SECTION("Can find scene timers in the right external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &externalEvents1 =
project.InsertNewExternalEvents("ExternalEvents1", 0);
externalEvents1.GetEvents().InsertEvent(
UseSceneTimer("MySceneTimerInExternalEvents1"));
UseExternalEvents(layout1, externalEvents1);
auto &layout2 = project.InsertNewLayout("Layout2", 0);
auto &externalEvents2 =
project.InsertNewExternalEvents("ExternalEvents2", 0);
externalEvents2.GetEvents().InsertEvent(
UseSceneTimer("MySceneTimerInExternalEvents2"));
UseExternalEvents(layout2, externalEvents2);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout1, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) ==
"\"MySceneTimerInExternalEvents1\"");
}
}
TEST_CASE("EventsIdentifiersFinder (object timers)", "[common]") {
SECTION("Can find object timers in scenes") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(UseObjectTimer("MyObject", "MyObjectTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in scene expression") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(UseObjectTimerInExpression("MyObject", "MyObjectTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject", "MyObjectTimer"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in scenes for the right object") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
layout.GetEvents().InsertEvent(
UseObjectTimer("MyObject1", "MyObjectTimer1"));
layout.GetEvents().InsertEvent(
UseObjectTimer("MyObject2", "MyObjectTimer2"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object1.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer1\"");
}
SECTION("Can find object timers in external layouts for the right object") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject1", "MyObjectTimer1"));
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject2", "MyObjectTimer2"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object1.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer1\"");
}
}

View File

@@ -0,0 +1,360 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Events/EventsVariablesFinder.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "catch.hpp"
namespace {
const void DeclareVariableExtension(gd::Project &project,
gd::Platform &platform) {
std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
gd::BuiltinExtensionsImplementer::ImplementsVariablesExtension(
*(extension.get()));
gd::BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
*(extension.get()));
// Add an instruction to test expressions.
extension
->AddAction("DoSomething", "Do something", "This does something",
"Do something please", "", "", "")
.AddParameter("expression", "Parameter 1 (a number)");
platform.AddExtension(extension);
project.AddPlatform(platform);
}
const gd::StandardEvent UseGlobalVariable(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarGlobal");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(name));
instruction.SetParameter(1, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneVariable(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarScene");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(name));
instruction.SetParameter(1, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseObjectVariable(const gd::String &objectName,
const gd::String &variableName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarObjet");
instruction.SetParametersCount(3);
instruction.SetParameter(0, gd::Expression(objectName));
instruction.SetParameter(1, gd::Expression(variableName));
instruction.SetParameter(2, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseGlobalVariableInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0,
gd::Expression("1 + GlobalVariable(" + name + ")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneVariableInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0, gd::Expression("1 + Variable(" + name + ")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent
UseObjectVariableInExpression(const gd::String &objectName,
const gd::String &variableName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(
0,
gd::Expression("1 + " + objectName + ".Variable(" + variableName + ")"));
event.GetActions().Insert(instruction);
return event;
}
const void UseExternalEvents(gd::Layout &layout,
gd::ExternalEvents &externalEvents) {
gd::LinkEvent linkEvent;
linkEvent.SetTarget(externalEvents.GetName());
layout.GetEvents().InsertEvent(linkEvent);
}
} // namespace
TEST_CASE("EventsVariablesFinder (FindAllGlobalVariables)", "[common]") {
SECTION("Can find global variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseGlobalVariable("MyGlobalVariable"));
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
SECTION("Can find global variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(
UseGlobalVariableInExpression("MyGlobalVariable"));
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
SECTION("Can find global variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseGlobalVariable("MyGlobalVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
}
TEST_CASE("EventsVariablesFinder (FindAllLayoutVariables)", "[common]") {
SECTION("Can find scene variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneVariable("MySceneVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(
UseSceneVariableInExpression("MySceneVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(UseSceneVariable("MySceneVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables the right scene") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInLayout1"));
auto &layout2 = project.InsertNewLayout("Layout2", 0);
layout2.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInLayout2"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariableInLayout1");
}
SECTION("Can find scene variables in the right external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &externalEvents1 =
project.InsertNewExternalEvents("ExternalEvents1", 0);
externalEvents1.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInExternalEvents1"));
UseExternalEvents(layout1, externalEvents1);
auto &layout2 = project.InsertNewLayout("Layout2", 0);
auto &externalEvents2 =
project.InsertNewExternalEvents("ExternalEvents2", 0);
externalEvents2.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInExternalEvents2"));
UseExternalEvents(layout2, externalEvents2);
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariableInExternalEvents1");
}
}
TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
SECTION("Can find object variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(
UseObjectVariableInExpression("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject", "MyObjectVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in scenes for the right object") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject1", "MyObjectVariable1"));
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject2", "MyObjectVariable2"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");
}
SECTION(
"Can find object variables in external layouts for the right object") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject1", "MyObjectVariable1"));
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject2", "MyObjectVariable2"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");
}
}

View File

@@ -12,11 +12,12 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/CustomObjectConfiguration.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
@@ -29,14 +30,7 @@ using namespace gd;
namespace {
void SetupProject(gd::Project &project, gd::Platform &platform) {
SetupProjectWithDummyPlatform(project, platform);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object =
layout.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
auto &configuration = object.GetConfiguration();
void SetupSpriteConfiguration(gd::ObjectConfiguration &configuration) {
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration != nullptr);
gd::Animation animation;
@@ -44,11 +38,30 @@ void SetupProject(gd::Project &project, gd::Platform &platform) {
spriteConfiguration->AddAnimation(animation);
};
void CheckSpriteConfiguration(
SerializerElement &objectContainerElement) {
gd::Object &SetupProjectWithSprite(gd::Project &project,
gd::Platform &platform) {
SetupProjectWithDummyPlatform(project, platform);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object =
layout.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
SetupSpriteConfiguration(object.GetConfiguration());
return object;
};
void CheckSpriteConfigurationInElement(SerializerElement &projectElement) {
void CheckSpriteConfigurationInObjectElement(SerializerElement &objectElement) {
REQUIRE(objectElement.HasChild("animations"));
auto &animationsElement = objectElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") == "Idle");
};
void CheckSpriteConfigurationInProjectElement(
SerializerElement &projectElement) {
auto &layoutsElement = projectElement.GetChild("layouts");
layoutsElement.ConsiderAsArrayOf("layout");
REQUIRE(layoutsElement.GetChildrenCount() == 1);
@@ -64,24 +77,10 @@ void CheckSpriteConfigurationInElement(SerializerElement &projectElement) {
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") == "MyExtension::Sprite");
REQUIRE(objectElement.HasChild("animations"));
auto &animationsElement = objectElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") ==
"Idle");
CheckSpriteConfigurationInObjectElement(objectElement);
};
void CheckSpriteConfiguration(gd::Project &project) {
auto &layout = project.GetLayout("Scene");
auto &object = layout.GetObject("MyObject");
REQUIRE(object.GetName() == "MyObject");
REQUIRE(object.GetType() == "MyExtension::Sprite");
auto &configuration = object.GetConfiguration();
void CheckSpriteConfiguration(gd::ObjectConfiguration &configuration) {
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration);
REQUIRE(spriteConfiguration->GetAnimationsCount() == 1);
@@ -89,6 +88,88 @@ void CheckSpriteConfiguration(gd::Project &project) {
auto &animation = spriteConfiguration->GetAnimation(0);
REQUIRE(animation.GetName() == "Idle");
};
void CheckSpriteConfiguration(gd::Object &object) {
REQUIRE(object.GetName() == "MyObject");
REQUIRE(object.GetType() == "MyExtension::Sprite");
CheckSpriteConfiguration(object.GetConfiguration());
};
void CheckSpriteConfiguration(gd::Project &project) {
auto &layout = project.GetLayout("Scene");
auto &object = layout.GetObject("MyObject");
CheckSpriteConfiguration(object);
};
gd::Object &SetupProjectWithCustomObject(gd::Project &project,
gd::Platform &platform) {
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
eventsBasedObject.SetFullName("My events based object");
eventsBasedObject.SetDescription("An events based object for test");
eventsBasedObject.InsertNewObject(project, "MyExtension::Sprite", "MyChild",
0);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object = layout.InsertNewObject(
project, "MyEventsExtension::MyEventsBasedObject", "MyObject", 0);
auto &configuration = object.GetConfiguration();
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(&configuration);
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration("MyChild");
SetupSpriteConfiguration(spriteConfiguration);
return object;
};
void CheckCustomObjectConfigurationInProjectElement(
SerializerElement &projectElement) {
auto &layoutsElement = projectElement.GetChild("layouts");
layoutsElement.ConsiderAsArrayOf("layout");
REQUIRE(layoutsElement.GetChildrenCount() == 1);
auto &layoutElement = layoutsElement.GetChild(0);
REQUIRE(layoutElement.GetStringAttribute("name") == "Scene");
REQUIRE(layoutElement.HasChild("objects"));
auto &objectsElement = layoutElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
REQUIRE(objectsElement.GetChildrenCount() == 1);
auto &objectElement = objectsElement.GetChild(0);
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") ==
"MyEventsExtension::MyEventsBasedObject");
auto &childrenContentElement = objectElement.GetChild("childrenContent");
REQUIRE(childrenContentElement.HasChild("MyChild"));
auto &childElement = childrenContentElement.GetChild("MyChild");
CheckSpriteConfigurationInObjectElement(childElement);
};
void CheckCustomObjectConfiguration(gd::Object &object) {
REQUIRE(object.GetName() == "MyObject");
REQUIRE(object.GetType() == "MyEventsExtension::MyEventsBasedObject");
auto &configuration = object.GetConfiguration();
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(&configuration);
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration("MyChild");
CheckSpriteConfiguration(spriteConfiguration);
};
void CheckCustomObjectConfiguration(gd::Project &project) {
auto &layout = project.GetLayout("Scene");
auto &object = layout.GetObject("MyObject");
CheckCustomObjectConfiguration(object);
};
} // namespace
TEST_CASE("ObjectSerialization", "[common]") {
@@ -96,16 +177,52 @@ TEST_CASE("ObjectSerialization", "[common]") {
SECTION("Save and load a project with a sprite configuration") {
gd::Platform platform;
gd::Project writtenProject;
SetupProject(writtenProject, platform);
SetupProjectWithSprite(writtenProject, platform);
CheckSpriteConfiguration(writtenProject);
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
CheckSpriteConfigurationInElement(projectElement);
CheckSpriteConfigurationInProjectElement(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
CheckSpriteConfiguration(readProject);
}
SECTION("Clone a sprite object") {
gd::Platform platform;
gd::Project project;
auto &object = SetupProjectWithSprite(project, platform);
CheckSpriteConfiguration(object);
auto clonedObject = object.Clone();
CheckSpriteConfiguration(*(clonedObject.get()));
}
SECTION("Save and load a project with a custom object configuration") {
gd::Platform platform;
gd::Project writtenProject;
SetupProjectWithCustomObject(writtenProject, platform);
CheckCustomObjectConfiguration(writtenProject);
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
CheckCustomObjectConfigurationInProjectElement(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
CheckCustomObjectConfiguration(readProject);
}
SECTION("Clone a custom object") {
gd::Platform platform;
gd::Project project;
auto &object = SetupProjectWithCustomObject(project, platform);
CheckCustomObjectConfiguration(object);
auto clonedObject = object.Clone();
CheckCustomObjectConfiguration(*(clonedObject.get()));
}
}

View File

@@ -0,0 +1,739 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/PropertyFunctionGenerator.h"
#include "DummyPlatform.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "catch.hpp"
namespace {
gd::EventsBasedBehavior &
CreateBehavior(gd::EventsFunctionsExtension &eventsExtension) {
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().InsertNew(
"MyEventsBasedBehavior", 0);
eventsBasedBehavior.SetFullName("My events based behavior");
eventsBasedBehavior.SetDescription("An events based behavior for test");
eventsBasedBehavior.SetObjectType("");
return eventsBasedBehavior;
};
gd::EventsBasedObject &
CreateObject(gd::EventsFunctionsExtension &eventsExtension) {
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
eventsBasedObject.SetFullName("My events based object");
eventsBasedObject.SetDescription("An events based object for test");
return eventsBasedObject;
};
} // namespace
TEST_CASE("PropertyFunctionGenerator", "[common]") {
SECTION("Can generate functions for a number property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
{
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "Movement angle");
REQUIRE(getter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(getter.GetDescription() ==
"the movement angle of the object. The "
"angle of the trajectory direction.");
REQUIRE(getter.GetSentence() == "the movement angle");
// Object and behavior parameters are added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::PropertyMovementAngle()");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetFunctionType() ==
gd::EventsFunction::ActionWithOperator);
REQUIRE(setter.GetGetterName() == "MovementAngle");
// These fields are deducted from the getter.
REQUIRE(setter.GetFullName() == "");
REQUIRE(setter.GetGroup() == "");
REQUIRE(setter.GetDescription() == "");
REQUIRE(setter.GetSentence() == "");
// Object and behavior parameters are added automatically.
REQUIRE(setter.GetParameters().size() == 0);
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyMovementAngle");
REQUIRE(setterAction.GetParametersCount() == 4);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterAction.GetParameter(2).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(3).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
}
}
SECTION("Can generate functions for a choice property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("CollisionShape", 0);
property.SetType("Choice")
.SetLabel("Collision shape")
.SetLabel("Dot shape")
.SetDescription("The shape is used for collision.")
.SetGroup("Movement");
property.GetExtraInfo().push_back("Dot shape");
property.GetExtraInfo().push_back("Bounding disk");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("CollisionShape"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetCollisionShape"));
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("CollisionShape");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
"[\"Dot shape\",\"Bounding disk\"]");
}
SECTION("Can generate functions for a boolean property in a behavior") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property = behavior.GetPropertyDescriptors().InsertNew("Rotate", 0);
property.SetType("Boolean")
.SetLabel("Rotate object")
.SetDescription(
"The rotation follows movements done by this behavior only.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed("Rotate"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed("SetRotate"));
{
auto &getter = behavior.GetEventsFunctions().GetEventsFunction("Rotate");
REQUIRE(getter.GetFunctionType() == gd::EventsFunction::Condition);
REQUIRE(getter.GetExpressionType().GetName() == "boolean");
REQUIRE(getter.GetFullName() == "Rotate object");
REQUIRE(getter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(getter.GetDescription() ==
"Check if rotate object. The rotation follows movements done by "
"this behavior only.");
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
// Object and behavior parameters are added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 1);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::PropertyRotate");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 2);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
REQUIRE(getterCondition.GetParameter(1).GetPlainString() == "Behavior");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "True");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetRotate");
REQUIRE(setter.GetFunctionType() == gd::EventsFunction::Action);
REQUIRE(setter.GetFullName() == "Rotate object");
REQUIRE(setter.GetGroup() ==
"My events based behavior movement configuration");
REQUIRE(setter.GetDescription() ==
"Change if rotate object. The rotation follows movements done by "
"this behavior only.");
REQUIRE(setter.GetSentence() == "_PARAM0_ rotate object: _PARAM2_");
// To generate the value parameter, object and behavior parameters has to
// be declared too.
REQUIRE(setter.GetParameters().size() == 3);
auto &objectParameter = setter.GetParameters().at(0);
REQUIRE(objectParameter.GetName() == "Object");
REQUIRE(objectParameter.GetType() == "object");
auto &behaviorParameter = setter.GetParameters().at(1);
REQUIRE(behaviorParameter.GetName() == "Behavior");
REQUIRE(behaviorParameter.GetType() == "behavior");
REQUIRE(behaviorParameter.GetExtraInfo() ==
"MyEventsExtension::MyEventsBasedBehavior");
auto &valueParameter = setter.GetParameters().at(2);
REQUIRE(valueParameter.GetName() == "Value");
REQUIRE(valueParameter.GetType() == "yesorno");
REQUIRE(setter.GetEvents().GetEventsCount() == 2);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
REQUIRE(setter.GetEvents().GetEvent(1).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterNoEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterNoEvent.GetConditions().size() == 1);
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterNoAction.GetParametersCount() == 3);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterNoAction.GetParameter(2).GetPlainString() == "no");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
REQUIRE(setterYesEvent.GetConditions().size() == 1);
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::SetPropertyRotate");
REQUIRE(setterYesAction.GetParametersCount() == 3);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "Behavior");
REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "yes");
}
}
SECTION("Can generate functions for a number property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property =
object.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("SetMovementAngle"));
{
auto &getter =
object.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "Movement angle");
REQUIRE(getter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(getter.GetDescription() ==
"the movement angle of the object. The "
"angle of the trajectory direction.");
REQUIRE(getter.GetSentence() == "the movement angle");
// Object parameter is added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.PropertyMovementAngle()");
}
{
auto &setter =
object.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetFunctionType() ==
gd::EventsFunction::ActionWithOperator);
REQUIRE(setter.GetGetterName() == "MovementAngle");
// These fields are deducted from the getter.
REQUIRE(setter.GetFullName() == "");
REQUIRE(setter.GetGroup() == "");
REQUIRE(setter.GetDescription() == "");
REQUIRE(setter.GetSentence() == "");
// Object parameter is added automatically.
REQUIRE(setter.GetParameters().size() == 0);
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(
setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyMovementAngle");
REQUIRE(setterAction.GetParametersCount() == 3);
REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterAction.GetParameter(1).GetPlainString() == "=");
REQUIRE(setterAction.GetParameter(2).GetPlainString() ==
"GetArgumentAsNumber(\"Value\")");
}
}
SECTION("Can generate functions for a choice property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property =
object.GetPropertyDescriptors().InsertNew("CollisionShape", 0);
property.SetType("Choice")
.SetLabel("Collision shape")
.SetLabel("Dot shape")
.SetDescription("The shape is used for collision.")
.SetGroup("Movement");
property.GetExtraInfo().push_back("Dot shape");
property.GetExtraInfo().push_back("Bounding disk");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(
object.GetEventsFunctions().HasEventsFunctionNamed("CollisionShape"));
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed(
"SetCollisionShape"));
auto &getter =
object.GetEventsFunctions().GetEventsFunction("CollisionShape");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
"[\"Dot shape\",\"Bounding disk\"]");
}
SECTION("Can generate functions for a boolean property in an object") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &object = CreateObject(extension);
auto &property = object.GetPropertyDescriptors().InsertNew("Rotate", 0);
property.SetType("Boolean")
.SetLabel("Rotate object")
.SetDescription("The rotation follows movements done by this object.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
project, extension, object, property);
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("Rotate"));
REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("SetRotate"));
{
auto &getter = object.GetEventsFunctions().GetEventsFunction("Rotate");
REQUIRE(getter.GetFunctionType() == gd::EventsFunction::Condition);
REQUIRE(getter.GetExpressionType().GetName() == "boolean");
REQUIRE(getter.GetFullName() == "Rotate object");
REQUIRE(getter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(getter.GetDescription() ==
"Check if rotate object. The rotation follows movements done by "
"this object.");
REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object");
// The Object parameter is added automatically.
REQUIRE(getter.GetParameters().size() == 0);
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 1);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterCondition = getterEvent.GetConditions().at(0);
REQUIRE(getterCondition.GetType() ==
"MyEventsExtension::MyEventsBasedObject::PropertyRotate");
REQUIRE(!getterCondition.IsInverted());
REQUIRE(getterCondition.GetParametersCount() == 1);
REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object");
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnBoolean");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() == "True");
}
{
auto &setter = object.GetEventsFunctions().GetEventsFunction("SetRotate");
REQUIRE(setter.GetFunctionType() == gd::EventsFunction::Action);
REQUIRE(setter.GetFullName() == "Rotate object");
REQUIRE(setter.GetGroup() ==
"My events based object movement configuration");
REQUIRE(setter.GetDescription() ==
"Change if rotate object. The rotation follows movements done by "
"this object.");
REQUIRE(setter.GetSentence() == "_PARAM0_ rotate object: _PARAM1_");
// To generate the value parameter, the object parameter has to
// be declared too.
REQUIRE(setter.GetParameters().size() == 2);
auto &objectParameter = setter.GetParameters().at(0);
REQUIRE(objectParameter.GetName() == "Object");
REQUIRE(objectParameter.GetType() == "object");
REQUIRE(objectParameter.GetExtraInfo() ==
"MyEventsExtension::MyEventsBasedObject");
auto &valueParameter = setter.GetParameters().at(1);
REQUIRE(valueParameter.GetName() == "Value");
REQUIRE(valueParameter.GetType() == "yesorno");
REQUIRE(setter.GetEvents().GetEventsCount() == 2);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
REQUIRE(setter.GetEvents().GetEvent(1).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterNoEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterNoEvent.GetConditions().size() == 1);
REQUIRE(setterNoEvent.GetActions().size() == 1);
auto &setterNoCondition = setterNoEvent.GetConditions().at(0);
REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(setterNoCondition.IsInverted());
REQUIRE(setterNoCondition.GetParametersCount() == 1);
REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterNoAction = setterNoEvent.GetActions().at(0);
REQUIRE(setterNoAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterNoAction.GetParametersCount() == 2);
REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "no");
auto &setterYesEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(1));
REQUIRE(setterYesEvent.GetConditions().size() == 1);
REQUIRE(setterYesEvent.GetActions().size() == 1);
auto &setterYesCondition = setterYesEvent.GetConditions().at(0);
REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean");
REQUIRE(!setterYesCondition.IsInverted());
REQUIRE(setterYesCondition.GetParametersCount() == 1);
REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() ==
"\"Value\"");
auto &setterYesAction = setterYesEvent.GetActions().at(0);
REQUIRE(setterYesAction.GetType() ==
"MyEventsExtension::MyEventsBasedObject::SetPropertyRotate");
REQUIRE(setterYesAction.GetParametersCount() == 2);
REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object");
REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "yes");
}
}
SECTION("Can generate functions for a shared property") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetSharedPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, true);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
{
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetDescription() ==
"the movement angle. The angle of the trajectory direction. "
"While an object is needed, this will apply to all objects using "
"the behavior.");
REQUIRE(getter.GetEvents().GetEventsCount() == 1);
REQUIRE(getter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &getterEvent =
dynamic_cast<gd::StandardEvent &>(getter.GetEvents().GetEvent(0));
REQUIRE(getterEvent.GetConditions().size() == 0);
REQUIRE(getterEvent.GetActions().size() == 1);
auto &getterAction = getterEvent.GetActions().at(0);
REQUIRE(getterAction.GetType() == "SetReturnNumber");
REQUIRE(getterAction.GetParametersCount() == 1);
REQUIRE(getterAction.GetParameter(0).GetPlainString() ==
"Object.Behavior::SharedPropertyMovementAngle()");
}
{
auto &setter =
behavior.GetEventsFunctions().GetEventsFunction("SetMovementAngle");
REQUIRE(setter.GetEvents().GetEventsCount() == 1);
REQUIRE(setter.GetEvents().GetEvent(0).GetType() ==
"BuiltinCommonInstructions::Standard");
auto &setterEvent =
dynamic_cast<gd::StandardEvent &>(setter.GetEvents().GetEvent(0));
REQUIRE(setterEvent.GetConditions().size() == 0);
REQUIRE(setterEvent.GetActions().size() == 1);
auto &setterAction = setterEvent.GetActions().at(0);
REQUIRE(setterAction.GetType() ==
"MyEventsExtension::MyEventsBasedBehavior::"
"SetSharedPropertyMovementAngle");
}
}
SECTION("Allow functions generation when there is no setter") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
REQUIRE(gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when a getter exists") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("MovementAngle", 0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when a setter exists") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("SetMovementAngle",
0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation when both setter and getter exist") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number")
.SetLabel("Movement angle")
.SetDescription("The angle of the trajectory direction.")
.SetGroup("Movement");
behavior.GetEventsFunctions().InsertNewEventsFunction("MovementAngle", 0);
behavior.GetEventsFunctions().InsertNewEventsFunction("SetMovementAngle",
0);
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Forbid functions generation for required behavior properties") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Behavior")
.SetLabel("Pathfinding behavior")
.SetDescription("A required behavior.")
.SetGroup("Movement")
.GetExtraInfo()
.push_back("PlatformBehavior::PlatformerObjectBehavior");
REQUIRE(!gd::PropertyFunctionGenerator::CanGenerateGetterAndSetter(
behavior, property));
}
SECTION("Can generate functions when only the property name is filled") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &extension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &behavior = CreateBehavior(extension);
auto &property =
behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0);
property.SetType("Number");
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
project, extension, behavior, property, false);
REQUIRE(
behavior.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle"));
REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed(
"SetMovementAngle"));
auto &getter =
behavior.GetEventsFunctions().GetEventsFunction("MovementAngle");
REQUIRE(getter.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition);
REQUIRE(getter.GetExpressionType().GetName() == "expression");
REQUIRE(getter.GetFullName() == "MovementAngle");
REQUIRE(getter.GetGroup() == "My events based behavior configuration");
REQUIRE(getter.GetDescription() == "the movementAngle of the object.");
REQUIRE(getter.GetSentence() == "the movementAngle");
}
}

View File

@@ -81,6 +81,30 @@ TEST_CASE("SerializerElement", "[common]") {
REQUIRE(element.GetChild(2).GetDoubleValue() == 45.6);
}
SECTION("Multiline strings") {
SerializerElement element;
// A single line is saved as a string.
element.SetMultilineStringValue("test");
REQUIRE(element.GetMultilineStringValue() == "test");
REQUIRE(element.GetStringValue() == "test");
// A string can be read.
element.SetStringValue("test of\nsomething\nsaved as a string");
REQUIRE(element.GetMultilineStringValue() == "test of\nsomething\nsaved as a string");
// A multi lines string is saved as an array.
element.SetMultilineStringValue("test\nwith\nmultiple lines.");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 3);
REQUIRE(element.GetMultilineStringValue() == "test\nwith\nmultiple lines.");
element.SetMultilineStringValue("test\n\nwith\n\nmultiple lines.\n");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 6);
REQUIRE(element.GetMultilineStringValue() == "test\n\nwith\n\nmultiple lines.\n");
}
SECTION("(Deprecated) attributes") {
SerializerElement element;
element.AddChild("child1").SetStringValue("value123");

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -105,9 +105,9 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
properties[("useLegacyBottomAndRightAnchors")]
.SetLabel(_(
"Stretch object when anchoring right or bottom ledge (deprecated, "
"it's recommended to let this unchecked and anchor both sides if you "
"want Sprite to stretch instead.)"))
"Stretch object when anchoring right or bottom edge (deprecated, "
"it's recommended to leave this unchecked and anchor both sides if "
"you want Sprite to stretch instead.)"))
.SetGroup(_("Deprecated options (advanced)"))
.SetValue(behaviorContent.GetBoolAttribute(
"useLegacyBottomAndRightAnchors", true)

View File

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

View File

@@ -17,8 +17,12 @@ namespace gdjs {
_bottomEdgeDistance: number = 0;
_useLegacyBottomAndRightAnchors: boolean = false;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
behaviorData,
owner: gdjs.RuntimeObject
) {
super(instanceContainer, behaviorData, owner);
this._relativeToOriginalWindowSize = !!behaviorData.relativeToOriginalWindowSize;
this._leftEdgeAnchor = behaviorData.leftEdgeAnchor;
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
@@ -65,11 +69,15 @@ namespace gdjs {
this._invalidDistances = true;
}
doStepPreEvents(runtimeScene) {
const game = runtimeScene.getGame();
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
const workingPoint: FloatPoint = gdjs.staticArray(
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
) as FloatPoint;
// TODO EBO Make it work with event based objects or hide this behavior for them.
const game = instanceContainer.getGame();
let rendererWidth = game.getGameResolutionWidth();
let rendererHeight = game.getGameResolutionHeight();
const layer = runtimeScene.getLayer(this.owner.getLayer());
const layer = instanceContainer.getLayer(this.owner.getLayer());
if (this._invalidDistances) {
if (this._relativeToOriginalWindowSize) {
rendererWidth = game.getOriginalWidth();
@@ -79,7 +87,9 @@ namespace gdjs {
//Calculate the distances from the window's bounds.
const topLeftPixel = layer.convertCoords(
this.owner.getDrawableX(),
this.owner.getDrawableY()
this.owner.getDrawableY(),
0,
workingPoint
);
//Left edge
@@ -125,9 +135,12 @@ namespace gdjs {
}
}
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const bottomRightPixel = layer.convertCoords(
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight()
this.owner.getDrawableY() + this.owner.getHeight(),
0,
workingPoint
);
//Right edge
@@ -268,11 +281,24 @@ namespace gdjs {
}
}
}
const topLeftCoord = layer.convertInverseCoords(leftPixel, topPixel);
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const topLeftCoord = layer.convertInverseCoords(
leftPixel,
topPixel,
0,
workingPoint
);
const left = topLeftCoord[0];
const top = topLeftCoord[1];
const bottomRightCoord = layer.convertInverseCoords(
rightPixel,
bottomPixel
bottomPixel,
0,
workingPoint
);
const right = bottomRightCoord[0];
const bottom = bottomRightCoord[1];
// Compatibility with GD <= 5.0.133
if (this._useLegacyBottomAndRightAnchors) {
@@ -281,25 +307,25 @@ namespace gdjs {
this._rightEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
this.owner.setWidth(right - left);
}
if (
this._bottomEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
this.owner.setHeight(bottom - top);
}
if (
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
left + this.owner.getX() - this.owner.getDrawableX()
);
}
if (
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
top + this.owner.getY() - this.owner.getDrawableY()
);
}
}
@@ -311,15 +337,15 @@ namespace gdjs {
AnchorRuntimeBehavior.HorizontalAnchor.NONE &&
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
this.owner.setX(topLeftCoord[0]);
this.owner.setWidth(right - left);
this.owner.setX(left);
} else {
if (
this._leftEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
left + this.owner.getX() - this.owner.getDrawableX()
);
}
if (
@@ -327,7 +353,7 @@ namespace gdjs {
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
bottomRightCoord[0] +
right +
this.owner.getX() -
this.owner.getDrawableX() -
this.owner.getWidth()
@@ -340,14 +366,14 @@ namespace gdjs {
AnchorRuntimeBehavior.VerticalAnchor.NONE &&
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
this.owner.setY(topLeftCoord[1]);
this.owner.setHeight(bottom - top);
this.owner.setY(top);
} else {
if (
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
top + this.owner.getY() - this.owner.getDrawableY()
);
}
if (
@@ -355,7 +381,7 @@ namespace gdjs {
AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
bottomRightCoord[1] +
bottom +
this.owner.getY() -
this.owner.getDrawableY() -
this.owner.getHeight()
@@ -366,7 +392,7 @@ namespace gdjs {
}
}
doStepPostEvents(runtimeScene) {}
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
static HorizontalAnchor = {
NONE: 0,

View File

@@ -62,16 +62,6 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
return object;
}
function getAnchorBehavior(object) {
const behavior = object.getBehavior(anchorBehaviorName);
if (!(behavior instanceof gdjs.AnchorRuntimeBehavior)) {
throw new Error(
'Expected behavior to be an instance of gdjs.AnchorBehavior'
);
}
return behavior;
}
describe('(anchor horizontal edge)', function () {
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {

View File

@@ -33,7 +33,10 @@ module.exports = {
'Todor Imreorov',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bbtext');
.setExtensionHelpPath('/objects/bbtext')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("BBCode Text Object"))
.setIcon("JsPlatform/Extensions/bbcode32.png");
var objectBBText = new gd.ObjectJsImplementation();
// $FlowExpectedError
@@ -168,7 +171,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd.js'
)
.setCategoryFullName(_('Texts'));
.setCategoryFullName(_('User interface'));
/**
* Utility function to add both a setter and a getter to a property from a list.
@@ -498,7 +501,7 @@ module.exports = {
RenderedBBTextInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/bbcode24.png';
};

View File

@@ -10,11 +10,11 @@ namespace gdjs {
/**
* @param runtimeObject The object to render
* @param runtimeScene The gdjs.RuntimeScene in which the object is
* @param instanceContainer The gdjs.RuntimeInstanceContainer in which the object is
*/
constructor(
runtimeObject: gdjs.BBTextRuntimeObject,
runtimeScene: gdjs.RuntimeScene
instanceContainer: gdjs.RuntimeInstanceContainer
) {
this._object = runtimeObject;
@@ -22,7 +22,7 @@ namespace gdjs {
if (this._pixiObject === undefined) {
this._pixiObject = new MultiStyleText(runtimeObject._text, {
default: {
fontFamily: runtimeScene
fontFamily: instanceContainer
.getGame()
.getFontManager()
.getFontFamily(runtimeObject._fontFamily),
@@ -44,7 +44,7 @@ namespace gdjs {
this.updateFontFamily();
this.updateFontSize();
}
runtimeScene
instanceContainer
.getLayer('')
.getRenderer()
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
@@ -95,7 +95,8 @@ namespace gdjs {
}
updateFontFamily(): void {
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene
this._pixiObject.textStyles.default.fontFamily = this._object
.getInstanceContainer()
.getGame()
.getFontManager()
.getFontFamily(this._object._fontFamily);

View File

@@ -48,11 +48,14 @@ namespace gdjs {
hidden: boolean;
/**
* @param runtimeScene The scene the object belongs to.
* @param instanceContainer The container the object belongs to.
* @param objectData The object data used to initialize the object
*/
constructor(runtimeScene: gdjs.RuntimeScene, objectData: BBTextObjectData) {
super(runtimeScene, objectData);
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: BBTextObjectData
) {
super(instanceContainer, objectData);
// @ts-ignore - parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
this._opacity = parseFloat(objectData.content.opacity);
this._text = objectData.content.text;
@@ -62,7 +65,10 @@ namespace gdjs {
this._fontSize = parseFloat(objectData.content.fontSize);
this._wordWrap = objectData.content.wordWrap;
this._align = objectData.content.align;
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(
this,
instanceContainer
);
this.hidden = !objectData.content.visible;
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
@@ -122,8 +128,8 @@ namespace gdjs {
}
}
onDestroyFromScene(runtimeScene): void {
super.onDestroyFromScene(runtimeScene);
onDestroyFromScene(instanceContainer: gdjs.RuntimeInstanceContainer): void {
super.onDestroyFromScene(instanceContainer);
}
/**
@@ -239,7 +245,7 @@ namespace gdjs {
this._wrappingWidth = width;
this._renderer.updateWrappingWidth();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
/**
@@ -254,7 +260,7 @@ namespace gdjs {
this._wordWrap = wordWrap;
this._renderer.updateWordWrap();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
getWordWrap() {

View File

@@ -35,7 +35,10 @@ module.exports = {
'Aurélien Vivet',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bitmap_text');
.setExtensionHelpPath('/objects/bitmap_text')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Bitmap Text"))
.setIcon("JsPlatform/Extensions/bitmapfont32.png");
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
@@ -171,7 +174,7 @@ module.exports = {
.addIncludeFile(
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
)
.setCategoryFullName(_('Texts'));
.setCategoryFullName(_('User interface'));
object
.addExpressionAndConditionAndAction(
@@ -627,7 +630,7 @@ module.exports = {
RenderedBitmapTextInstance.getThumbnail = function (
project,
resourcesLoader,
object
objectConfiguration
) {
return 'JsPlatform/Extensions/bitmapfont24.png';
};

View File

@@ -10,16 +10,16 @@ namespace gdjs {
/**
* @param runtimeObject The object to render
* @param runtimeScene The gdjs.RuntimeScene in which the object is
* @param instanceContainer The container in which the object is
*/
constructor(
runtimeObject: gdjs.BitmapTextRuntimeObject,
runtimeScene: gdjs.RuntimeScene
instanceContainer: gdjs.RuntimeInstanceContainer
) {
this._object = runtimeObject;
// Obtain the bitmap font to use in the object.
const bitmapFont = runtimeScene
const bitmapFont = instanceContainer
.getGame()
.getBitmapFontManager()
.obtainBitmapFont(
@@ -32,7 +32,7 @@ namespace gdjs {
});
// Set the object on the scene
runtimeScene
instanceContainer
.getLayer('')
.getRenderer()
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
@@ -59,7 +59,8 @@ namespace gdjs {
onDestroy() {
// Mark the font from the object as not used anymore.
this._object._runtimeScene
this._object
.getInstanceContainer()
.getGame()
.getBitmapFontManager()
.releaseBitmapFont(this._pixiObject.fontName);
@@ -73,7 +74,8 @@ namespace gdjs {
updateFont(): void {
// Get the new bitmap font to use
const bitmapFont = this._object._runtimeScene
const bitmapFont = this._object
.getInstanceContainer()
.getGame()
.getBitmapFontManager()
.obtainBitmapFont(
@@ -82,7 +84,8 @@ namespace gdjs {
);
// Mark the old font as not used anymore
this._object._runtimeScene
this._object
.getInstanceContainer()
.getGame()
.getBitmapFontManager()
.releaseBitmapFont(this._pixiObject.fontName);

View File

@@ -50,14 +50,14 @@ namespace gdjs {
_renderer: gdjs.BitmapTextRuntimeObjectPixiRenderer;
/**
* @param runtimeScene The scene the object belongs to.
* @param instanceContainer The container the object belongs to.
* @param objectData The object data used to initialize the object
*/
constructor(
runtimeScene: gdjs.RuntimeScene,
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: BitmapTextObjectData
) {
super(runtimeScene, objectData);
super(instanceContainer, objectData);
this._opacity = objectData.content.opacity;
this._text = objectData.content.text;
@@ -73,7 +73,7 @@ namespace gdjs {
this._renderer = new gdjs.BitmapTextRuntimeObjectRenderer(
this,
runtimeScene
instanceContainer
);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
@@ -137,8 +137,8 @@ namespace gdjs {
}
}
onDestroyFromScene(runtimeScene: gdjs.RuntimeScene): void {
super.onDestroyFromScene(runtimeScene);
onDestroyFromScene(instanceContainer: gdjs.RuntimeInstanceContainer): void {
super.onDestroyFromScene(instanceContainer);
this._renderer.onDestroy();
}
@@ -148,7 +148,7 @@ namespace gdjs {
setText(text: string): void {
this._text = text;
this._renderer.updateTextContent();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
/**
@@ -170,7 +170,7 @@ namespace gdjs {
setScale(scale: float): void {
this._scale = scale;
this._renderer.updateScale();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
getScale(): float {
@@ -276,7 +276,7 @@ namespace gdjs {
setWrappingWidth(width: float): void {
this._wrappingWidth = width;
this._renderer.updateWrappingWidth();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
/**
@@ -289,7 +289,7 @@ namespace gdjs {
setWordWrap(wordWrap: boolean): void {
this._wordWrap = wordWrap;
this._renderer.updateWrappingWidth();
this.hitBoxesDirty = true;
this.invalidateHitboxes();
}
getWordWrap(): boolean {

View File

@@ -7,10 +7,12 @@ namespace gdjs {
export namespace debuggerTools {
/**
* Stop the game execution.
* @param runtimeScene - The current scene.
* @param instanceContainer - The current container.
*/
export const pause = function (runtimeScene: gdjs.RuntimeScene) {
runtimeScene.getGame().pause(true);
export const pause = function (
instanceContainer: gdjs.RuntimeInstanceContainer
) {
instanceContainer.getGame().pause(true);
};
/**
@@ -29,20 +31,20 @@ namespace gdjs {
/**
* Enable or disable the debug draw.
* @param runtimeScene - The current scene.
* @param instanceContainer - The current container.
* @param enableDebugDraw - true to enable the debug draw, false to disable it.
* @param showHiddenInstances - true to apply the debug draw to hidden objects.
* @param showPointsNames - true to show point names.
* @param showCustomPoints - true to show custom points of Sprite objects.
*/
export const enableDebugDraw = function (
runtimeScene: gdjs.RuntimeScene,
instanceContainer: gdjs.RuntimeInstanceContainer,
enableDebugDraw: boolean,
showHiddenInstances: boolean,
showPointsNames: boolean,
showCustomPoints: boolean
) {
runtimeScene.enableDebugDraw(
instanceContainer.enableDebugDraw(
enableDebugDraw,
showHiddenInstances,
showPointsNames,

View File

@@ -25,6 +25,7 @@ DestroyOutsideBehavior::GetProperties(
.SetValue(gd::String::From(
behaviorContent.GetDoubleAttribute("extraBorder", 0)))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetLabel(_("Margin before deleting the object, in pixels"));
return properties;

View File

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

View File

@@ -11,8 +11,12 @@ namespace gdjs {
export class DestroyOutsideRuntimeBehavior extends gdjs.RuntimeBehavior {
_extraBorder: any;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
behaviorData,
owner
) {
super(instanceContainer, behaviorData, owner);
this._extraBorder = behaviorData.extraBorder || 0;
}
@@ -23,14 +27,14 @@ namespace gdjs {
return true;
}
doStepPostEvents(runtimeScene) {
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
// is not necessarily in the middle of the object (for sprites for example).
const ow = this.owner.getWidth();
const oh = this.owner.getHeight();
const ocx = this.owner.getDrawableX() + this.owner.getCenterX();
const ocy = this.owner.getDrawableY() + this.owner.getCenterY();
const layer = runtimeScene.getLayer(this.owner.getLayer());
const layer = instanceContainer.getLayer(this.owner.getLayer());
const boundingCircleRadius = Math.sqrt(ow * ow + oh * oh) / 2.0;
if (
ocx + boundingCircleRadius + this._extraBorder <
@@ -43,7 +47,7 @@ namespace gdjs {
layer.getCameraY() + layer.getCameraHeight() / 2
) {
//We are outside the camera area.
this.owner.deleteFromScene(runtimeScene);
this.owner.deleteFromScene(instanceContainer);
}
}

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