Compare commits

...

315 Commits

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

Only show in developer changelog
2024-11-12 17:19:50 +01:00
AlexandreS
aed09d86b3 Improve landscape mode and responsiveness of the UI on small screens (#7142) 2024-11-12 15:40:43 +01:00
Florian Rival
c2d03050b8 Add sort field (creation date, weekly sessions, all time sessions) for the list of games in the Manage tab 2024-11-11 17:50:59 +01:00
D8H
2e941c5afc Add tests on property renaming in expressions (#7146)
- Don't show in changelog
2024-11-11 17:23:18 +01:00
Florian Rival
a3f80f2607 Fix metrics in the new game dashboard wrongly limited to 7 days
Don't show in changelog
2024-11-11 16:42:20 +01:00
github-actions[bot]
3497eb2945 Update translations [skip ci] (#7127)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-11-11 15:18:16 +01:00
MTSyntho
b9a1f50d13 Add mention to Three.js in README (#7144) 2024-11-10 19:22:18 +01:00
Florian Rival
52d239b60c Add a button to ask any question in the Learn page (#7143) 2024-11-10 15:36:49 +01:00
AlexandreS
c549e277a7 Various fixes for the new game dashboard (#7141)
Don't show in changelog
2024-11-08 17:27:04 +01:00
inspace
afed5d57f7 Only add watermark styles in case showWatermark is true (#7140)
* This avoids using a DOM API if not needed.

Only show in developer changelog
2024-11-08 16:27:59 +01:00
D8H
a3f7176c42 [Physics2] Fix a memory leak on object instances (#7136) 2024-11-08 14:53:38 +01:00
Florian Rival
223268554b Fix Dialogue Tree returning sometimes a string instead of a number in expressions reading variables 2024-11-08 13:46:13 +01:00
AlexandreS
43ef037a07 Display game-related data and services in a dashboard (#7114) 2024-11-08 11:55:43 +01:00
Clément Pasteau
20d2e06fc6 Fix login dialog stuck in loading after trying to use a provider (#7138) 2024-11-07 17:52:55 +01:00
Clément Pasteau
9f795c405a Fix destroying an object even if flagged as "DoNothing" in the multiplayer behavior (#7137) 2024-11-07 14:08:02 +01:00
Florian Rival
0155344ec3 Fix dialogue tree crashing the game when syntax errors are present 2024-11-06 20:09:51 +01:00
Clément Pasteau
71d6d6a165 Fix thumbnail height for screenshots in quick customization (#7134)
Don't show in changelog
2024-11-06 16:04:07 +01:00
Clément Pasteau
edd14b5f8b Fix thumbnail condition in marketing package (#7133)
Do not show in changelog
2024-11-06 15:30:44 +01:00
Clément Pasteau
77d60b699b Prevent opening variables dialog for objects & groups if there is no object (#7132) 2024-11-06 14:49:16 +01:00
Florian Rival
5bc80537b7 Fix leaderboards not properly replaced in projects using them in custom objects (#7131) 2024-11-06 12:46:34 +01:00
Clément Pasteau
f93b850382 Improve object collision masks & points dialog button label (#7130) 2024-11-06 10:13:16 +01:00
Clément Pasteau
da7cae08a1 Fix game thumbnail taking too much space on mobile in Quick Customization dialog (#7128) 2024-11-05 14:46:54 +01:00
Florian Rival
0ae68877b7 Fix centering of "UI layers" after a screen orientation change (#7126)
* This happened often on mobile (iOS notably) and sometimes when resizing the window on desktop too.
2024-11-05 13:45:54 +01:00
github-actions[bot]
32c4e040e0 Update translations [skip ci] (#7120)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-11-05 11:44:03 +01:00
D8H
74034a0ac1 Show object effect drop-down list for parameters in extension editor (#7119)
- Shows object thumbnails in custom objects event functions
2024-10-31 17:02:39 +01:00
github-actions[bot]
1b41225822 Update translations [skip ci] (#7113)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-31 11:34:24 +01:00
Clément Pasteau
c620ed75b3 Take automatic game screenshots in Quick Customization previews (#7116) 2024-10-31 11:33:58 +01:00
Florian Rival
edc577067b Add comments to document a risk with ProjectScopedContainers
This is when used with custom objects for editing objects.

Only show in developer changelog
2024-10-31 11:01:41 +01:00
D8H
e00a85909d Replace the "add folder" button by a drop-down menu action (#7117) 2024-10-31 10:51:57 +01:00
D8H
70e6fc7f7f Add a button at the top of the object list to add new objects (#7111) 2024-10-29 16:52:04 +01:00
D8H
b9a899f82e Fix 3D model sizes in the editor when aspect ratio is free (#7115) 2024-10-29 16:46:33 +01:00
Arthur Pacaud (arthuro555)
c38d14ca83 Add gdcore-tools hook (#7112)
Only show in developer changelog
2024-10-29 09:26:48 +01:00
github-actions[bot]
e6b6406a95 Update translations [skip ci] (#7110)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-28 09:39:16 +01:00
Florian Rival
ff7c6de660 Fix issues when reworking a quick customization project (#7109)
* Don't duplicate the project multiple times if already saved
* When the game is exported a second time, the game page was not properly updated
2024-10-25 18:34:26 +02:00
github-actions[bot]
8d78ec6070 Update translations [skip ci] (#7104)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-25 15:43:53 +02:00
Florian Rival
3c2876e08d Fix adding an object from context menu when the Objects panel is closed (#7107) 2024-10-25 10:21:10 +02:00
Clément Pasteau
a68bac6667 Allow uploading gd.games thumbnail directly from the Game Dashboard (#7106) 2024-10-25 09:39:10 +02:00
Florian Rival
53eafe098c Don't show save reminders in quick customization 2024-10-24 17:17:24 +02:00
AlexandreS
ab519d41a1 Fix instances paste from a scene to another (#7105)
Also:
- Fix instance variable editor opening in CustomObject editor
- Fix tilemap painting in CustomObject editor
2024-10-23 17:53:31 +02:00
D8H
5ea03b83f0 Fix a crash at runtime when an animatable custom object has no animation (#7102) 2024-10-23 15:09:06 +02:00
github-actions[bot]
f2d4778459 Update translations [skip ci] (#7098)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-10-23 09:44:39 +02:00
Clément Pasteau
56662fb9b5 Add warnings for variables ownership (#7100)
Do not show in changelog
2024-10-22 18:41:45 +02:00
Clément Pasteau
0788de3d87 UX Improvements across the app (#7099)
* Make New project dialog simpler
* Prevent icons from being always at the end on RTL languages
* Improve number of items displayed on mobile across the app
* Hide tabs in Resources if only 1 is available
2024-10-22 16:29:31 +02:00
AlexandreS
6b7bc361a7 Bump newIDE version (#7097) 2024-10-22 15:05:52 +02:00
github-actions[bot]
32a6e188e7 Update translations [skip ci] (#7088) 2024-10-22 15:03:49 +02:00
D8H
00f67ca7c7 Allow custom objects to use leaderboard properties (#7081) 2024-10-22 14:43:47 +02:00
AlexandreS
bb5291ac6f Improve Max projects callout (#7096)
Don't show in changelog
2024-10-22 11:38:21 +02:00
D8H
a2ea751007 Fix to forbid behavior properties from being used as values in expressions (#7095) 2024-10-21 19:58:32 +02:00
D8H
1a4270195b Fix autocompletion by hiding parameters and properties of type "behavior" (#7094) 2024-10-21 18:46:30 +02:00
D8H
3df42cce3e Fix default parameter of action with operator functions not displaying in the editor (#7093) 2024-10-21 17:48:34 +02:00
D8H
b8de302f7e Optimize custom object layouting in the editor (#7090) 2024-10-21 15:16:06 +02:00
AlexandreS
6a2bc6109c Display Audio Resource parameter field for functions actions (#7092) 2024-10-21 14:54:46 +02:00
D8H
0383f8a7e1 Fix text input visibility when its custom object is hidden (#7089) 2024-10-21 12:41:47 +02:00
D8H
bde7e1896d Fix a crash when an instance with a wrong object name is in a custom object (#7079) 2024-10-21 10:23:46 +02:00
github-actions[bot]
32d855992e Update translations [skip ci] (#7075)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-21 09:14:26 +02:00
Florian Rival
d54c1e2f38 Display context menu using an "action sheet" like drawer on mobile/touchscreens (#7087) 2024-10-20 21:47:22 +02:00
D8H
f09a1dd5b2 Fix wrong custom objects duplication when updating an extension (#7086) 2024-10-20 21:26:45 +02:00
Florian Rival
f4e3f2449a Add larger buttons in quick customization and danger buttons in some interfaces 2024-10-19 19:23:46 +02:00
Florian Rival
a6b2cba281 Update wording [skip ci]
Don't show in changelog
2024-10-19 16:37:24 +02:00
Florian Rival
54237114d9 Ensure GDevelop.js build is done using release mode if not specified [skip ci]
Only show in developer changelog
2024-10-19 16:30:23 +02:00
Florian Rival
3c5bcf2762 Add preview analytics to understand if done by new users
Don't show in changelog
2024-10-19 16:27:54 +02:00
D8H
d66ea06a4c Fix resource used in behavior properties not being exported (#7084)
Only show in developer changelog
2024-10-18 18:45:52 +02:00
AlexandreS
228479c81b Fix check on cloud projects count (#7083)
Don't show in changelog
2024-10-18 16:41:15 +02:00
AlexandreS
1e55c359d8 Fix changing the shape painter colors in the properties panel/object editor (#7082) 2024-10-18 14:24:46 +02:00
Clément Pasteau
451d525b36 Fix objects owned by Multiplayer host not being properly migrated when host changes (#7078) 2024-10-17 15:35:41 +02:00
D8H
c755946d42 Add tutorial bubbles on actions replacing deprecated ones (#7077) 2024-10-17 15:12:24 +02:00
D8H
7a6b6fbf7f Update instance renderers when an event-based object is deleted, renamed or pasted (#7076) 2024-10-17 13:22:18 +02:00
github-actions[bot]
730c8283e5 Update translations [skip ci] (#7070)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-17 11:14:55 +02:00
D8H
7ea250706c Close custom object tabs when they are deleted from their extension (#7074) 2024-10-17 11:14:26 +02:00
Florian Rival
db7a108354 Reduce time before session analytics to improve bounce detection 2024-10-17 11:06:20 +02:00
AlexandreS
2e15d68bce Change max project warning copy and Add callout in Save to storage provider dialog (#7069)
Don't show in changelog
2024-10-17 11:05:33 +02:00
Florian Rival
f6c9e1408c Bump newIDE version 2024-10-17 00:37:34 +02:00
D8H
0b3d4d048a Fix autocompletion of layers and points for custom object children (#7071) 2024-10-17 00:35:54 +02:00
Clément Pasteau
5d625dd497 Always show instant-build page, even if not linked to a game (#7072) 2024-10-17 00:32:46 +02:00
D8H
079eca829a Fix a regression on the capability check in the editor (#7073)
Don't show in changelog
2024-10-17 00:18:42 +02:00
D8H
35b5f92c59 Fix object lists not always up-to-date in the extension events editor after adding an object in the custom object editor (#7068) 2024-10-16 17:33:35 +02:00
github-actions[bot]
70eb95b132 Update translations [skip ci] (#7064)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-16 15:15:57 +02:00
AlexandreS
7c7ee8b7fc Warn not enough cloud projects in quick customization flow (#7065) 2024-10-16 14:37:30 +02:00
Clément Pasteau
3556dd2e3c Improvements across the app (#7067)
* Shorter button label for github star
* More in app tutorials on the same row on mobile
* Smaller icon to get app on mobile
* Reduce spacing in Login & Signup dialog to see more
* Redirect store when in a dead end of game templates
* Improve guided lessons descriptions & re-order them
* Allow closing & deleting an opened cloud project
2024-10-16 14:26:26 +02:00
D8H
f74f77f66a Fix custom object editor not closed when the extension is renamed (#7066) 2024-10-16 13:11:25 +02:00
AlexandreS
712eb4b647 Avoid intermittent game crash, due to an audio issue, when resuming the game (when focused back again) (#7063) 2024-10-16 10:57:02 +02:00
D8H
c4474c766d Fix mouse and key parameters for event-functions (#7052) 2024-10-16 09:43:20 +02:00
github-actions[bot]
ad17a21973 Update translations [skip ci] (#7043)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-16 09:41:29 +02:00
D8H
5001411ccb Fix custom object loading when opening a project (#7060) 2024-10-16 09:38:16 +02:00
Florian Rival
05939f5c3e Fix extraction to custom object sometimes using an already used name (#7059) 2024-10-16 09:08:49 +02:00
Florian Rival
91978d4c6e Fix wait action in custom objects (#7056) 2024-10-15 17:15:07 +02:00
AlexandreS
d6433d89f0 Add icons to help understand to scroll during in app tutorial (#7057) 2024-10-15 14:43:37 +02:00
Clément Pasteau
e652ab9f5a Prevent players from claiming a leaderboard score as an anonymous user (#7055) 2024-10-15 14:36:40 +02:00
Florian Rival
e7decc7b92 Fix silent exceptions when resource loading finished after an instance was reset/removed (#7054)
Only show in developer changelog
2024-10-15 12:39:20 +02:00
AlexandreS
33dcc04112 Add support for free trial notification (#7053)
Don't show in changelog
2024-10-14 17:42:44 +02:00
Florian Rival
58ba2668c2 Add clang-tidy and builds with assertions/memory sanitizers for libGD.js (#7051)
Only show in developer changelog
2024-10-14 12:06:42 +02:00
Florian Rival
b34e802dcb Add a test with diagnostics that used to crash the app
Don't show in changelog
2024-10-14 09:39:07 +02:00
Florian Rival
31dac9cc93 Fix memory corruption/crashes when events in extensions had errors (#7050) 2024-10-13 22:44:36 +02:00
Florian Rival
ab97258832 Fix more tests and uninitialized variable (#7049)
Only show in developer changelog
2024-10-13 21:15:52 +02:00
Florian Rival
2ece223737 Fix wrong argument passed in a test (#7048) 2024-10-13 18:06:22 +02:00
Florian Rival
8342873b6e Fix missing or wrong arguments in GDevelop.js tests (#7047)
* These were "silently succeeding" but would create issues when Emscipten is run with SAFE_HEAP=1.

Only show in developer changelog
2024-10-13 17:39:54 +02:00
github-actions[bot]
0a85fd3814 Update translations [skip ci] (#7038)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-11 15:00:20 +02:00
Clément Pasteau
fe15b6d30b Update CSP (#7042)
Do not show in changelog
2024-10-11 14:59:59 +02:00
Florian Rival
f3c3559518 Fix warning 2024-10-11 14:56:36 +02:00
D8H
a403b1343b Fix extraction of 3D custom objects and prevent extracting 2D+3D custom objects (#7040) 2024-10-11 13:42:06 +02:00
D8H
83dba6c21e Allow to open the custom object editor from objects drop-down menu (#7039) 2024-10-11 12:45:23 +02:00
Florian Rival
7a4aea6557 Fix extension not regenerated after extracting instances to a custom object 2024-10-11 11:12:52 +02:00
AlexandreS
82a6abacb4 Suggest starter tilemaps when creating a tilemap from scratch (#7035) 2024-10-11 10:52:47 +02:00
AlexandreS
f60613cc64 Use user limits to determine if version history feature can be used (#7036)
Don't show in changelog
2024-10-11 10:47:32 +02:00
github-actions[bot]
274aedb3d9 Update translations [skip ci] (#7032)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-11 10:20:02 +02:00
D8H
efb31c3caf Fix text wrapping issue in the editor when used as a custom object child (#7034) 2024-10-11 09:05:36 +02:00
D8H
6781c0fd6e Add a drop-down menu action to extract instances as custom objects (#7030) 2024-10-10 20:13:42 +02:00
D8H
e9781133e1 Fix events refactoring when duplicating custom behaviors or custom objects (#7005) 2024-10-10 19:48:31 +02:00
Florian Rival
8972e0e3a6 Bump version (#7033) 2024-10-10 16:54:55 +02:00
Aurélien Vivet
4c76a5979b Add a new in-app tutorial explaining how to use a Tilemap (#6989) 2024-10-10 16:36:32 +02:00
D8H
484a0daec4 Show the tolerance in the sentence of "Is object turned toward" condition (#7022) 2024-10-10 16:25:38 +02:00
AlexandreS
de432c1bf2 Add a "Tilemap" filter in the Asset Store (#7031) 2024-10-10 16:15:18 +02:00
github-actions[bot]
8c7dcc1c23 Update translations [skip ci] (#7015)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-10 14:56:12 +02:00
AlexandreS
39648af248 Add margin between socials and donate link in profile (#7029)
Don't show in changelog
2024-10-10 10:21:15 +02:00
AlexandreS
5c7bbf5293 Add error callback to tilemap texture parsing (#7027)
To prevent crashes in the preview
2024-10-10 10:19:26 +02:00
Florian Rival
9d97b9d0eb Fix exception 't.bind is not a function' when a resource has an empty file (#7028) 2024-10-09 15:06:28 +02:00
AlexandreS
b39b12864f Add context menu options to duplicate instruction parameter or behavior property (#7026) 2024-10-08 15:50:28 +02:00
D8H
2c3dbbbbde Fix an exception when a touch is ended and then started in the same frame at runtime (#7025) 2024-10-08 11:45:40 +02:00
Clément Pasteau
72cc60bae9 Remove searchbar from Learn page. (#7024)
* As it does not return relevant results, it is thought best to remove it for now.
2024-10-07 18:16:16 +02:00
AlexandreS
c03e94849b Handle line breaks in font resource lines in resource store (#7023)
Don't show in changelog
2024-10-07 18:06:19 +02:00
AlexandreS
80daaf5e5b Fix Teach tab missing education resources button (#7021) 2024-10-07 17:25:04 +02:00
D8H
cb457cfd04 Fix the action to change the animation elapsed time for sprites and 3d models (#7020) 2024-10-07 17:18:40 +02:00
D8H
0a55aa631b Disable the "support us" dialog during an in-app tutorial (#7019) 2024-10-07 15:44:02 +02:00
AlexandreS
8e104f9ae4 Fix text input focus messing with the input manager (#7013) 2024-10-07 12:41:10 +02:00
D8H
a90b9a27e9 [Physics2] Avoid an exception when the object position is not finite (#7017) 2024-10-07 11:11:36 +02:00
D8H
6321e82f63 [Physics2] Forbid negative damping values (#7016) 2024-10-04 18:06:12 +02:00
AlexandreS
05fc63ab1b Fix ghost hitboxes on tilemap when tiles are flipped (#6990) 2024-10-04 17:25:34 +02:00
AlexandreS
85a6a21934 Do not display guided lesson tooltip if an error boundary is displayed (#7014)
Don't show in changelog
2024-10-04 17:23:28 +02:00
Clément Pasteau
a9741e7b42 Handle internal navigation for promotions (#7012)
Do not show in changelog
2024-10-04 17:22:56 +02:00
github-actions[bot]
23afe7b71c Update translations [skip ci] (#7011)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-04 17:22:22 +02:00
AlexandreS
85757e6d98 Rework asset store display when looking for a font or an audio (#7009)
The asset store is not available anymore when looking for other types of resource (image, 3D models, spine).
2024-10-04 15:56:50 +02:00
github-actions[bot]
524ca4dbb3 Update translations [skip ci] (#7002)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-10-03 17:40:13 +02:00
Aurélien Vivet
55cf710ef5 Add GitHub url to community tab (#7008) 2024-10-03 17:39:11 +02:00
Clément Pasteau
eee7e7f04c Remove most titles from home for a cleaner UI (#7010)
Do not show in changelog
2024-10-03 17:38:20 +02:00
Clément Pasteau
ecd984e08b Allow opening any homepage tab from url (#7007)
Do not show in changelog
2024-10-03 17:15:16 +02:00
D8H
d0a7fbbd02 Fix a regression on existing external tile map collision mask (#7004)
- Don't show in changelog
2024-10-02 14:44:07 +02:00
D8H
2a2c930b74 Add vertical alignment property for text object (#6975)
- Fix proportional anchors on custom object children
2024-10-02 11:17:48 +02:00
D8H
f6cb203029 Allow to filter external tile map collision per layer (#6998) 2024-10-02 10:14:52 +02:00
Florian Rival
fe743bbe57 Fix usage of libGD.js in Node 2024-09-30 19:06:24 +02:00
D8H
78c31408d4 Add a comment about anchor calculus (#6976)
- Don't show in changelog
2024-09-30 18:50:13 +02:00
github-actions[bot]
75a038344a Update translations [skip ci] (#7000)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-30 14:58:05 +02:00
Clément Pasteau
f0567b674c Fix example thumbnails (#7001) 2024-09-30 14:43:00 +02:00
Clément Pasteau
9b85f35856 Fix typo (#6999) 2024-09-30 08:40:38 +02:00
github-actions[bot]
f4568febf2 Update translations [skip ci] (#6995)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-30 08:14:52 +02:00
Clément Pasteau
de7f60b693 Bump to 5.4.214 (#6993) 2024-09-28 19:22:46 +02:00
Florian Rival
3a595b200e Update README [skip ci] [ci skip]
Don't show in changelog
2024-09-28 11:47:27 +02:00
AlexandreS
431b5929e8 Fine tune teach section display (#6994)
Don't show in changelog
2024-09-27 15:15:27 +02:00
github-actions[bot]
ec2e82cee5 Update translations [skip ci] (#6992)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-27 12:37:48 +02:00
Florian Rival
76517f1a2a Adapt margins in objects panel 2024-09-27 11:27:19 +02:00
Florian Rival
43f5bd1c0e Allow to edit object properties, behaviors, variables and effects directly from the properties panel (#6898)
* When an object is selected (or when "Edit object" button is clicked in the properties panel after choosing an instance), the properties panel will show the properties of the object.
* This allows for fast edition of most elements of an object: appearance, properties, but also behaviors, variables and effects. Most workflow and iterations on your game should be faster, from adapting the appearance of a text to tweaking behaviors and updating the preview to check the result in realtime.
2024-09-27 10:56:39 +02:00
github-actions[bot]
7198a22ae2 Update translations [skip ci] (#6964)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-09-26 10:43:31 +02:00
AlexandreS
46c52905f6 Add context menu to move Sprite object animation in list (#6987) 2024-09-25 17:28:58 +02:00
AlexandreS
40954bf497 Fix some crashes happening in preview (#6988) 2024-09-25 17:26:01 +02:00
Clément Pasteau
02c06ac6e7 Fix iOS automatic exports when using admob (#6986)
* internal ios deployment-target was conflicting between the plugins used, when admob was used in the project, preventing to build the app
2024-09-25 09:52:19 +02:00
AlexandreS
1fa3f59a77 Add possibility to paint tilemap with a rectangle selection from the tileset (#6977) 2024-09-23 17:37:20 +02:00
Clément Pasteau
cc371273ce Improve Quick Customization flow (#6978)
* Simplify the number of objects suggested for replacing
* Simplify the number of behavior properties suggested for tweaking
* New section to update the in-game title
* Simplify publication at the end of the flow
2024-09-23 15:07:31 +02:00
AlexandreS
364ec2ecfb Fix crashes in Preview due to ill-formed color values (#6980)
- Make color parsing from string more robust (issues when setting colors in Sprite, BBText, Particle emitter and effects with colors)
- Allow use of hex strings and shorthand hex strings in color fields
- Remove UI glitch when switching effect type and both effects have parameters with identical names
2024-09-23 09:21:37 +02:00
D8H
0d36a27b87 Fix anchor behavior when the object has a custom origin (#6970) 2024-09-18 13:54:10 +02:00
D8H
6d597a430b Always show custom objects in the extension editor (#6974) 2024-09-18 13:21:04 +02:00
Clément Pasteau
27b71b08e5 Fix instance selection rectangle not being flipped properly (#6971) 2024-09-18 09:51:08 +02:00
AlexandreS
82158f7073 Fix DismissableTutorialMessage stories typing (#6972)
Do not show in changelog
2024-09-18 09:50:35 +02:00
AlexandreS
8ba352d4ba Fix min width on right container (#6969)
Don't show in changelog
2024-09-17 17:35:48 +02:00
D8H
6cda5d08be Fix missing flippingZ accessors (#6968)
- Don't show in changelog
2024-09-17 16:00:14 +02:00
D8H
1a3a27b73b Fix custom object flipping (#6967)
Do not show in changelog
2024-09-17 15:15:25 +02:00
Clément Pasteau
96d912a6f2 Fix store UI (wrong centering of items) (#6965) 2024-09-17 14:23:28 +02:00
D8H
ed3acd5f0d Layout custom objects children according to their anchors in the editor (#6939) 2024-09-17 12:43:16 +02:00
AlexandreS
3f269206d1 Bump newIDE version (#6963) 2024-09-17 12:37:04 +02:00
github-actions[bot]
76b5aefdbc Update translations [skip ci] (#6961) 2024-09-17 12:36:38 +02:00
AlexandreS
9ef7af803c Change teacher resources list to include upcoming lessons (#6956) 2024-09-17 12:27:45 +02:00
D8H
c77f9b9e0c Fix a crash in the new behavior dialog (#6962)
- The crash happened when the extensions didn't load as expected.
2024-09-17 12:18:26 +02:00
D8H
035ddb8a7a Fix hot reload of object variables in object instances (#6958) 2024-09-17 12:14:10 +02:00
AlexandreS
e7dac1bafc Tilemap improvements (#6957)
- Improve performance display when painting
- Fix painting when tilemap is rotated
- Allow atlas size to be something else than a tile size multiple and ignore last column and row
- Display error message only when the the tile size is greater than the atlas image
- Do not crash preview if tilemap badly configured
- Add object name in actions and conditions
2024-09-17 11:18:30 +02:00
github-actions[bot]
54f00e7c57 Update translations [skip ci] (#6951)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-17 10:06:45 +02:00
Clément Pasteau
0bf9dae2b0 Fix icon of credit packages (#6960)
Do not show in changelog
2024-09-17 10:06:18 +02:00
Florian Rival
428aac8ab0 Fix sprite animation lists and selection checkbox
Don't show in changelog
2024-09-15 17:44:21 +02:00
Florian Rival
9391fc2841 Remove warnings during C++ emscripten compilation 2024-09-13 15:51:55 +02:00
Florian Rival
cea34337c6 Backport improvements made for objects panel (#6955)
Do not show in changelog
2024-09-13 15:26:49 +02:00
Clément Pasteau
dc45f3dae5 Allow changing Opacity & Flip directly in Instance Properties Panel (#6935)
* This allows playing around with instances directly on the canvas instead of relying on the actions to flip or change opacity and needing to start a preview
2024-09-13 13:39:03 +02:00
AlexandreS
0ca26a865e Authenticate plans and pricing systems fetch calls (#6952) 2024-09-13 11:45:23 +02:00
Clément Pasteau
1bce13f326 Bump to 5.4.212 (#6950) 2024-09-12 16:42:52 +02:00
github-actions[bot]
34f8f5750a Update translations [skip ci] (#6943)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-09-12 16:16:06 +02:00
AlexandreS
3b9a612094 Fix tilemap object edge cases causing crashes (#6945) 2024-09-12 15:49:02 +02:00
Clément Pasteau
3a84ed7c89 Fix object images not displaying on quick customization (#6949)
Do not show in changelog
2024-09-12 15:07:22 +02:00
AlexandreS
ef604fd442 Display active pricing systems only (#6948)
Don't show in changelog
2024-09-12 12:09:13 +02:00
Clément Pasteau
d88dc4772f Catch callback functions of Firebase preventing JS crash (#6946)
Only show in developer changelog
2024-09-11 17:32:49 +02:00
AlexandreS
02d40a1d52 Fix: Catch errors when handling electron browser window (#6944) 2024-09-11 09:13:40 +02:00
Clément Pasteau
35082825d4 Fix triggering the onWheel only when an input is focused (#6940) 2024-09-10 17:35:17 +02:00
Clément Pasteau
6a7c3daa8e Fix wheel increment (#6938) 2024-09-09 14:15:38 +02:00
Clément Pasteau
95af02bada Bump to 5.4.211 (#6937) 2024-09-09 13:56:35 +02:00
Clément Pasteau
30516a903e Fix instance number properties which could not be saved to 0 (#6936)
* Also add back the ability to use keyboard arrows and mouse wheel to control the value finely
2024-09-09 13:56:19 +02:00
Florian Rival
762f7ca19c Avoid unnecessary recompilation of many C++ files at each build
Only show in developer changelog
2024-09-09 13:39:34 +02:00
Florian Rival
852bf78c81 Show a loading texture for Panel Sprite instead of a error texture (#6931) 2024-09-09 13:04:15 +02:00
github-actions[bot]
b0da0cee34 Update translations [skip ci] (#6925)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-09 11:13:35 +02:00
Aurélien Vivet
73771f938b Fix checkered background in collision and points editor (#6928) 2024-09-06 16:20:59 +02:00
Florian Rival
b9dbe6dbb5 Fix exception when changing an external tilemap file after it was initially missing (#6929) 2024-09-06 16:05:57 +02:00
Florian Rival
0c2341c6e5 Fix crash in the scene editor when changing the Panel Sprite tiled/stretched option (#6926) 2024-09-05 18:25:13 +02:00
Florian Rival
e9b4de2ca9 Fix tilemap instance error when no image set (#6923)
Only show in developer changelog
2024-09-05 16:08:59 +02:00
Clément Pasteau
75a4114ce8 Fix extension generation for docs (#6924) 2024-09-05 16:04:48 +02:00
Clément Pasteau
20abb9b45a Bump to 5.4.210 (#6922) 2024-09-05 11:54:52 +02:00
github-actions[bot]
56436fd44a Update translations [skip ci] (#6921)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-09-05 11:54:35 +02:00
AlexandreS
0dd5fc55c9 Improve subscriptions dialog to display current plan at the top (#6918)
* Mainly useful for old subscriptions that are not available in the list of available ones anymore.
2024-09-05 09:37:43 +02:00
github-actions[bot]
93db4cb508 Update translations [skip ci] (#6919)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-09-05 08:43:06 +02:00
D8H
f7888abf45 Allow custom objects to use the anchor behavior on their children (#6917) 2024-09-04 19:00:49 +02:00
github-actions[bot]
c8144da704 Update translations [skip ci] (#6909)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-04 15:25:50 +02:00
Giordane Oliveira
832e8cd593 Update AdMob banner so they cover part of the game screen instead of reducing it (#6900)
* In most games, it's better to design the game with the size of the ad banner in mind - which will cover either part of the top or bottom of the screen. Previously, the game size was reduced which was making it difficult to properly design a game and would introduce bad looking black borders.
2024-09-04 09:14:12 +02:00
AlexandreS
ef66a9f1a4 Add support for math formula in instance properties panel fields (#6914)
* For example, you can enter `100 + 50` or `100 + 70/2` or `ToDeg(sin(2.3))` in the instance editor fields
2024-09-03 18:41:38 +02:00
Florian Rival
5efbaa8c58 Add automated crash reporting from previews (#6915)
* This will allow to detect any bugs or crash in the game engine without relying on manual reports from users on GitHub. Note that exceptions and errors in JavaScript code blocks won't be reported. This can be deactivated in preferences if you prefer not to have GDevelop send these crash reports at all.
2024-09-03 16:52:53 +02:00
AlexandreS
ecbf38ccda Remove effects tab (and actions and conditions) wrongly shown for Tilemap objects (#6916) 2024-09-03 16:33:57 +02:00
AlexandreS
b3fcfc3f55 Avoid exponential loop when scrolling out of view on the Scene editor (#6912) 2024-09-03 09:33:45 +02:00
D8H
a515836add Avoid to duplicate custom object data in the project files (#6904)
- Fix hot-reload of sprite object animations
2024-09-02 18:52:27 +02:00
D8H
a7c81b47b2 Fix text input not properly deleted when used in a Custom Object (#6910) 2024-09-02 17:59:39 +02:00
D8H
0f22e462ad Keep clearing object lists before the events too (#6913)
- Don't show in changelog
2024-09-02 17:43:47 +02:00
AlexandreS
e6e4d9048f Add action to set Text object font (#6911) 2024-09-02 16:25:41 +02:00
github-actions[bot]
12f5f95d0c Update translations [skip ci] (#6902)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-09-02 13:53:57 +02:00
D8H
c52168a967 Fix memory clearing/leak when a scene is paused or stopped (#6123) 2024-09-02 13:52:37 +02:00
AlexandreS
1e33a13cc5 Improve tilemap preview rendering (#6908)
- Remove FPS limitation
- Avoid useless computations
2024-09-02 12:10:43 +02:00
Vladyslav Pohorielov
505debd60c Add expressions to get Spine point attachments positions (#6907) 2024-09-02 10:35:30 +02:00
Aurélien Vivet
e3b7109154 Add a default shortcut for adding a comment in the Events Sheet (#6879) 2024-09-02 09:22:13 +02:00
D8H
9e25899d3e Remove an "only" in GDJS tests (#6906)
- Don't show in changelog
2024-08-31 00:28:10 +02:00
AlexandreS
87cb8f0d47 Activate user after creating it (#6901)
Don't show in changelog
2024-08-30 09:57:38 +02:00
github-actions[bot]
481c6da992 Update translations [skip ci] (#6886)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-08-30 09:28:33 +02:00
AlexandreS
7cbebbb82f Fix some tilemap UX paintpoints (#6899)
- When using a mouse, use left click only to paint. Middle click can be used to pan the view.
- Fix issues with undo/redo tile setting
2024-08-29 20:08:05 +02:00
AlexandreS
fcf668788b Add interface for teacher to manage education users (#6891) 2024-08-29 18:42:25 +02:00
D8H
0cc844a77f Make the experimental custom object visual editor accessible (#6897)
* This is an *experimental**, *still work-in-progress*, editor to build objects inside GDevelop - from small, reusable UI objects to larger parts of your game.
2024-08-29 18:33:25 +02:00
Clément Pasteau
a234d9bd35 Show game ads earnings (#6896)
* You can now see the earnings of the games you've published on gd.games, thanks to ads, at the top of your Games dashboard
* You can cash out that amount if you reach the threshold or exchange it with GDevelop credits
2024-08-29 18:27:40 +02:00
D8H
465a6ce2ab Fix custom objects hot-reloading (#6887)
- Don't show in changelog
2024-08-29 18:13:54 +02:00
D8H
7e2e19eb33 Improve tile map collision precision (#6895) 2024-08-29 18:13:08 +02:00
D8H
95101763f7 Hide the drop-down list about quick customization visibility (#6893) 2024-08-28 16:59:24 +02:00
D8H
d4bd5fc671 Hide required behavior properties in the object editor (#6889) 2024-08-28 14:07:36 +02:00
D8H
c7fcf48ba5 Hide a duplicated expression to get the text of a text input (#6892) 2024-08-28 14:07:23 +02:00
D8H
8926d4406f Fix to forbid default parameters from being dragged and dropped (#6888) 2024-08-27 19:20:45 +02:00
Clément Pasteau
9ed2173038 Multiplayer game host migration (#6878)
* Host of a multiplayer game is now automatically migrated to a new one, picked from the list of players, with the lowest ping.
* A new condition is available "Is host migrating" in order to adapt the game while the migration happens. This can take up to a few seconds. If your game has no interactions between players, you can leave it like this. Otherwise, you may want to pause the game while the host is changing!
* A new action is available to configure the lobby game to end instead of migrating when the host leaves. (This is useful if you prefer the host not changing during your game)
2024-08-27 14:30:36 +02:00
github-actions[bot]
2fc3bc337f Update translations [skip ci] (#6885)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-08-27 11:49:01 +02:00
Clément Pasteau
0b7cac79ef Allow changing Sync Rate of objects in a multiplayer game (#6884)
* Default sync rate of objects is now set to 30 times per second (was 60 before, but 30 is enough for most games)
* A new action can be used to update this value depending on the type of your game, at the cost of bandwidth. Fast-paced competitive? go for 60. Slow turn-based game? 10 is probably enough, or even less!
2024-08-26 18:27:27 +02:00
D8H
8721c0099e [Physics2] Merge the 2 world scale properties into one (#6865) 2024-08-26 15:16:01 +02:00
Aurélien Vivet
4453eee3b9 Fix default values for the Adjustement effect (#6883) 2024-08-26 14:11:44 +02:00
github-actions[bot]
0215ab7dbb Update translations [skip ci] (#6874)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-08-26 14:10:00 +02:00
AlexandreS
87f6d5b99f Fix update notifications translations on Desktop app (#6882) 2024-08-26 10:57:39 +02:00
D8H
a440b16f84 Fix a memory issue with default custom object configurations (#6881)
- Don't show in changelog
2024-08-23 20:30:45 +02:00
D8H
f3822ba0df Allow to center objects with the anchor behavior (#6880) 2024-08-23 19:02:31 +02:00
D8H
6c5813affd Fix hot-reload for variables (#6877) 2024-08-23 15:03:40 +02:00
D8H
be4fe62bb6 Allow to override custom objects default property values or to follow them (#6861) 2024-08-23 14:12:13 +02:00
AlexandreS
0a29999894 Add possibility to drop variable after last item (#6875) 2024-08-22 09:59:07 +02:00
1073 changed files with 61000 additions and 25199 deletions

View File

@@ -176,6 +176,7 @@ jobs:
# Build the WebAssembly library only (so that it's cached on a S3 and easy to re-use).
build-gdevelop_js-wasm-only:
resource_class: medium+ # Compilation time decrease linearly with the number of CPUs, but not linking (so "large" does not speedup total build time).
docker:
- image: cimg/node:16.13
@@ -232,10 +233,83 @@ jobs:
name: Deploy to S3 (latest)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the WebAssembly library with clang-tidy and memory sanitizers.
build-gdevelop_js-debug-sanitizers-and-extra-checks:
resource_class: xlarge # Total time decrease linearly with the number of CPUs.
docker:
- image: cimg/node:16.13
working_directory: ~/GDevelop
steps:
- checkout
- aws-cli/setup
# System dependencies (for Emscripten)
- run:
name: Install dependencies for Emscripten
command: sudo apt-get update && sudo apt install cmake
- run:
name: Install dependencies for clang-tidy v19
command: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 19 && sudo apt install clang-tidy-19
- run:
name: Install Python3 dependencies for Emscripten
command: sudo apt install python-is-python3 python3-distutils -y
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
keys:
- gdevelop.js-linux-nodejs-dependencies-{{ checksum "GDevelop.js/package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- gdevelop.js-linux-nodejs-dependencies-
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js
- run:
name: Build GDevelop.js ('debug-sanitizers' variant)
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build -- --variant=debug-sanitizers
- run:
name: Run clang-tidy
command: cd GDevelop.js && npm run lint
- run:
name: Run tests
command: cd GDevelop.js && npm run test -- --maxWorkers=4
# Upload artifacts (CircleCI)
- store_artifacts:
path: Binaries/embuild/GDevelop.js
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/variant/debug-sanitizers/commit/$(git rev-parse HEAD)/
workflows:
builds:
gdevelop_js-wasm:
jobs:
- build-gdevelop_js-wasm-only
gdevelop_js-wasm-extra-checks:
jobs:
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
# Extra checks are resource intensive so don't all run them.
filters:
branches:
only:
- master
- /experimental-build.*/
builds:
jobs:
- build-macos:
filters:
branches:

4
.clang-tidy Normal file
View File

@@ -0,0 +1,4 @@
Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,-cppcoreguidelines-explicit-virtual-functions,-cppcoreguidelines-avoid-const-or-ref-data-members,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-owning-memory,-cppcoreguidelines-virtual-class-destructor,-clang-analyzer-optin.performance.Padding,-cppcoreguidelines-narrowing-conversions'
WarningsAsErrors: 'cppcoreguidelines-pro-type-member-init, clang-analyzer-optin.cplusplus.UninitializedObject'
HeaderFilterRegex: '.*'
FormatStyle: none

22
.github/workflows/gdcore-tools-hook.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
# This worflow notifies arthuro555's gdcore-tools repository when a new release is published.
#
# This is used to allow gdcore-tools, a library to use GDCore outside of GDevelop,
# to attempt to automatically build, test, and publish a release for the new
# GDevelop version.
name: Trigger gdcore-tools pipeline
on:
release:
types: [published]
jobs:
dispatch-event:
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.GDCORE_TOOLS_PAT }}
repository: arthuro555/gdcore-tools
event-type: gdevelop-release
client-payload: '{"release": ${{ toJson(github.event.release) }}}'

View File

@@ -107,7 +107,7 @@
"description": "Define a parameter in a GDevelop extension definition.",
"prefix": "gdparam",
"body": [
".addParameter('${1|string,expression,object,behavior,yesorno,stringWithSelector,scenevar,globalvar,objectvar,objectList,objectListWithoutPicking,color,key,sceneName,file,layer,relationalOperator,operator,trueorfalse,musicfile,soundfile,police,mouse,passwordjoyaxis,camera,objectPtr,forceMultiplier|}', '${2:Parameter description}', '${3:Optional parameter data}', /*parameterIsOptional=*/${4|false,true|})"
".addParameter('${1|string,expression,object,behavior,yesorno,stringWithSelector,scenevar,globalvar,objectvar,objectList,objectListWithoutPicking,color,key,sceneName,file,layer,relationalOperator,operator,trueorfalse,musicfile,soundfile,mouse,passwordjoyaxis,camera,objectPtr,forceMultiplier|}', '${2:Parameter description}', '${3:Optional parameter data}', /*parameterIsOptional=*/${4|false,true|})"
]
},
"Add code only parameter": {

View File

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

View File

@@ -60,7 +60,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_
endif()
#Activate C++11
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 would need to remove usage of bind2nd (should be easy).
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 should be tried.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Mark some warnings as errors
@@ -69,12 +69,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# uninitialized variables or other hard to debug bugs.
add_compile_options(
-Wall
-Wextra
-Wuninitialized
-Wconditional-uninitialized
-Wno-unknown-warning-option
-Wno-reorder-ctor
-Wno-reorder
-Wno-unused-parameter
-Wno-pessimizing-move
-Wno-unused-variable
-Wno-unused-variable # Not a good style, but not a risk
-Wno-unused-private-field
-Wno-ignored-qualifiers # Not a risk
-Wno-sign-compare # Not a big risk
# Make as much warnings considered as errors as possible (only one for now).
-Werror=return-stack-address

View File

@@ -72,8 +72,6 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
///< de/activate infinite loop warning when the
///< user create the event
mutable unsigned int whileConditionsHeight;
int GetConditionsHeight() const;
int GetActionsHeight() const;
int GetWhileConditionsHeight() const;

View File

@@ -14,10 +14,10 @@
namespace gd {
/**
* \brief
* \brief
*/
class GD_CORE_API ProjectDiagnostic {
public:
public:
enum ErrorType {
UndeclaredVariable,
MissingBehavior,
@@ -25,12 +25,17 @@ public:
MismatchedObjectType,
};
ProjectDiagnostic(ErrorType type_, const gd::String &message_,
ProjectDiagnostic(ErrorType type_,
const gd::String &message_,
const gd::String &actualValue_,
const gd::String &expectedValue_, const gd::String &objectName_ = "")
: type(type_), message(message_), actualValue(actualValue_), expectedValue(expectedValue_),
objectName(objectName_){};
virtual ~ProjectDiagnostic(){};
const gd::String &expectedValue_,
const gd::String &objectName_ = "")
: type(type_),
message(message_),
actualValue(actualValue_),
expectedValue(expectedValue_),
objectName(objectName_) {};
virtual ~ProjectDiagnostic() {};
ErrorType GetType() const { return type; };
const gd::String &GetMessage() const { return message; }
@@ -38,7 +43,7 @@ public:
const gd::String &GetActualValue() const { return actualValue; }
const gd::String &GetExpectedValue() const { return expectedValue; }
private:
private:
ErrorType type;
gd::String message;
gd::String objectName;
@@ -47,12 +52,12 @@ private:
};
/**
* \brief
* \brief
*/
class GD_CORE_API DiagnosticReport {
public:
DiagnosticReport(){};
virtual ~DiagnosticReport(){};
public:
DiagnosticReport() {};
virtual ~DiagnosticReport() {};
void Add(const gd::ProjectDiagnostic &projectDiagnostic) {
projectDiagnostics.push_back(
@@ -67,32 +72,39 @@ public:
const gd::String &GetSceneName() const { return sceneName; }
void SetSceneName(const gd::String &sceneName_) {
sceneName = sceneName_;
void SetSceneName(const gd::String &sceneName_) { sceneName = sceneName_; }
void LogAllDiagnostics() {
for (auto &diagnostic : projectDiagnostics) {
std::cout << diagnostic->GetMessage()
<< "(object: " << diagnostic->GetObjectName()
<< ", actual value: " << diagnostic->GetActualValue()
<< ", expected value: " << diagnostic->GetExpectedValue() << ")"
<< std::endl;
}
}
private:
private:
std::vector<std::unique_ptr<gd::ProjectDiagnostic>> projectDiagnostics;
gd::String sceneName;
};
/**
* \brief
* \brief
*/
class GD_CORE_API WholeProjectDiagnosticReport {
public:
WholeProjectDiagnosticReport(){};
virtual ~WholeProjectDiagnosticReport(){};
public:
WholeProjectDiagnosticReport() {};
virtual ~WholeProjectDiagnosticReport() {};
const DiagnosticReport &Get(std::size_t index) const {
return *diagnosticReports[index].get();
};
void Clear() {
diagnosticReports.clear();
};
void Clear() { diagnosticReports.clear(); };
DiagnosticReport& AddNewDiagnosticReportForScene(const gd::String &sceneName) {
DiagnosticReport &AddNewDiagnosticReportForScene(
const gd::String &sceneName) {
auto diagnosticReport = gd::make_unique<gd::DiagnosticReport>();
diagnosticReport->SetSceneName(sceneName);
diagnosticReports.push_back(std::move(diagnosticReport));
@@ -102,7 +114,7 @@ public:
std::size_t Count() const { return diagnosticReports.size(); };
bool HasAnyIssue() {
for (auto& diagnosticReport : diagnosticReports) {
for (auto &diagnosticReport : diagnosticReports) {
if (diagnosticReport->Count() > 0) {
return true;
}
@@ -110,8 +122,8 @@ public:
return false;
}
private:
private:
std::vector<std::unique_ptr<gd::DiagnosticReport>> diagnosticReports;
};
} // namespace gd
} // namespace gd

View File

@@ -77,11 +77,11 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
/**
* @brief Generate a relational operation
*
*
* @param relationalOperator the operator
* @param lhs the left hand operand
* @param rhs the right hand operand
* @return gd::String
* @return gd::String
*/
gd::String EventsCodeGenerator::GenerateRelationalOperation(
const gd::String& relationalOperator,
@@ -345,14 +345,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}
@@ -509,7 +509,7 @@ void EventsCodeGenerator::CheckBehaviorParameters(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
actualBehaviorType, expectedBehaviorType, lastObjectName);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
}
}
});
@@ -567,14 +567,14 @@ gd::String EventsCodeGenerator::GenerateActionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}
@@ -828,7 +828,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
metadata.GetType() == "spineResource" ||
// Deprecated, old parameter names:
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
metadata.GetType() == "soundfile") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.GetType() == "mouse") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
@@ -1007,7 +1007,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
output += "\n" + scopeBegin + "\n" + declarationsCode + "\n" +
eventCoreCode + "\n" + scopeEnd + "\n";
if (event.HasVariables()) {
GetProjectScopedContainers().GetVariablesContainersList().Pop();
}

View File

@@ -528,7 +528,7 @@ protected:
parameter -> string
* - operator : Used to update a value using a setter and a getter -> string
* - key, mouse, objectvar, scenevar, globalvar, password, musicfile,
soundfile, police -> string
soundfile -> string
* - trueorfalse, yesorno -> boolean ( See GenerateTrue/GenerateFalse ).
*
* <br><br>
@@ -849,7 +849,7 @@ protected:
instructionUniqueIds; ///< The unique ids generated for instructions.
size_t eventsListNextUniqueId; ///< The next identifier to use for an events
///< list function name.
gd::DiagnosticReport* diagnosticReport;
};

View File

@@ -36,8 +36,8 @@ struct GD_CORE_API ExpressionParserLocation {
private:
bool isValid;
size_t startPosition;
size_t endPosition;
size_t startPosition = 0;
size_t endPosition = 0;
};
/**

View File

@@ -1768,6 +1768,22 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression",
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
.AddCodeOnlyParameter("conditionInverted", "")
.SetHidden()
.MarkAsAdvanced();
extension
.AddCondition("IsTurnedTowardObject",
_("An object is turned toward another"),
_("Check if an object is turned toward another"),
_("_PARAM0_ is turned toward _PARAM1_ ± _PARAM2_°"),
_("Angle"),
"res/conditions/estTourne24.png",
"res/conditions/estTourne.png")
.AddParameter("objectList", _("Name of the object"))
.AddParameter("objectList", _("Name of the second object"))
.AddParameter("expression",
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
extension

View File

@@ -338,6 +338,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0");
// Deprecated
extension
.AddCondition(
"PopStartedTouch",
@@ -354,6 +355,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
// Deprecated
extension
.AddCondition(
"PopEndedTouch",
@@ -370,6 +372,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
// Deprecated
extension
.AddCondition(
"HasAnyTouchStarted",

View File

@@ -33,6 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"most elements of a game."),
"CppPlatform/Extensions/spriteicon.png")
.SetCategoryFullName(_("General"))
.SetOpenFullEditorLabel(_("Edit animations"))
.AddDefaultBehavior("EffectCapability::EffectBehavior")
.AddDefaultBehavior("ResizableCapability::ResizableBehavior")
.AddDefaultBehavior("ScalableCapability::ScalableBehavior")

View File

@@ -47,19 +47,11 @@ void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Animate even if hidden or far from the screen")]
.SetValue(updateIfNotVisible ? "true" : "false")
.SetType("Boolean");
properties["PLEASE_ALSO_SHOW_EDIT_BUTTON_THANKS"].SetValue("");
return properties;
}
bool SpriteObject::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == _("Animate even if hidden or far from the screen"))
updateIfNotVisible = value == "1";
return true;
}

View File

@@ -37,8 +37,7 @@ BehaviorMetadata::BehaviorMetadata(
className(className_),
iconFilename(icon24x24),
instance(instance_),
sharedDatasInstance(sharedDatasInstance_),
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {
sharedDatasInstance(sharedDatasInstance_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
SetDefaultName(gd::String(defaultName_));
@@ -441,12 +440,18 @@ std::map<gd::String, gd::PropertyDescriptor> BehaviorMetadata::GetSharedProperti
const std::vector<gd::String>& BehaviorMetadata::GetRequiredBehaviorTypes() const {
requiredBehaviors.clear();
for (auto& property : Get().GetProperties()) {
if (!instance) {
return requiredBehaviors;
}
for (auto& property : instance->GetProperties()) {
const String& propertyName = property.first;
const gd::PropertyDescriptor& propertyDescriptor = property.second;
if (propertyDescriptor.GetType() == "Behavior") {
requiredBehaviors.push_back(propertyDescriptor.GetExtraInfo()[0]);
const auto& extraInfos = propertyDescriptor.GetExtraInfo();
if (extraInfos.size() > 0) {
requiredBehaviors.push_back(extraInfos[0]);
}
}
}
return requiredBehaviors;

View File

@@ -307,6 +307,15 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
return *this;
}
BehaviorMetadata &SetOpenFullEditorLabel(const gd::String& label) {
openFullEditorLabel = label;
return *this;
}
const gd::String& GetOpenFullEditorLabel() const {
return openFullEditorLabel;
}
/**
* \brief Return the associated gd::Behavior, handling behavior contents.
*
@@ -384,7 +393,8 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
mutable std::vector<gd::String> requiredBehaviors;
bool isPrivate = false;
bool isHidden = false;
QuickCustomization::Visibility quickCustomizationVisibility;
gd::String openFullEditorLabel;
QuickCustomization::Visibility quickCustomizationVisibility = QuickCustomization::Visibility::Default;
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
std::shared_ptr<gd::Behavior> instance;

View File

@@ -185,10 +185,10 @@ class GD_CORE_API EffectMetadata {
gd::String fullname;
gd::String description;
std::vector<gd::String> includeFiles;
bool isMarkedAsNotWorkingForObjects;
bool isMarkedAsOnlyWorkingFor2D;
bool isMarkedAsOnlyWorkingFor3D;
bool isMarkedAsUnique;
bool isMarkedAsNotWorkingForObjects = false;
bool isMarkedAsOnlyWorkingFor2D = false;
bool isMarkedAsOnlyWorkingFor3D = false;
bool isMarkedAsUnique = false;
std::map<gd::String, gd::PropertyDescriptor> properties;
};

View File

@@ -19,7 +19,8 @@ EventMetadata::EventMetadata(const gd::String &name_,
: fullname(fullname_),
description(description_),
group(group_),
instance(instance_) {
instance(instance_),
hasCustomCodeGenerator(false) {
ClearCodeGenerationAndPreprocessing();
if (instance) instance->SetType(name_);
}

View File

@@ -83,7 +83,7 @@ class GD_CORE_API EventMetadata {
gd::String group;
std::shared_ptr<gd::BaseEvent> instance;
bool hasCustomCodeGenerator;
bool hasCustomCodeGenerator = false;
std::function<gd::String(gd::BaseEvent& event,
gd::EventsCodeGenerator& codeGenerator,
gd::EventsCodeGenerationContext& context)>

View File

@@ -288,8 +288,8 @@ class GD_CORE_API MetadataProvider {
static EffectMetadata badEffectMetadata;
static gd::InstructionMetadata badInstructionMetadata;
static gd::ExpressionMetadata badExpressionMetadata;
int useless; // Useless member to avoid emscripten "must have a positive
// integer typeid pointer" runtime error.
int useless = 0; // Useless member to avoid emscripten "must have a positive
// integer typeid pointer" runtime error.
};
} // namespace gd

View File

@@ -54,7 +54,7 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
gd::LogFatalError(
"Error: Event-based objects don't have blueprint. "
"This method should not never be called.");
"This method should never be called.");
return nullptr;
}) {}

View File

@@ -246,6 +246,11 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
return *this;
}
ObjectMetadata& ResetDefaultBehaviorsJustForTesting() {
defaultBehaviorTypes.clear();
return *this;
}
const gd::String& GetName() const override { return name; }
const gd::String& GetFullName() const override { return fullname; }
const gd::String& GetCategoryFullName() const { return categoryFullName; }
@@ -323,6 +328,15 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
bool IsRenderedIn3D() const { return isRenderedIn3D; }
ObjectMetadata &SetOpenFullEditorLabel(const gd::String& label) {
openFullEditorLabel = label;
return *this;
}
const gd::String& GetOpenFullEditorLabel() const {
return openFullEditorLabel;
}
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
@@ -344,6 +358,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
std::set<gd::String> defaultBehaviorTypes;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new

View File

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

View File

@@ -53,27 +53,32 @@ const gd::String &ValueTypeMetadata::GetExpressionPrimitiveValueType(
const gd::String &
ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "variable" ||
gd::ValueTypeMetadata::IsTypeExpression("variable", parameterType)) {
return ValueTypeMetadata::variableType;
if (parameterType == "number" ||
gd::ValueTypeMetadata::IsTypeValue("number", parameterType)) {
return ValueTypeMetadata::numberType;
}
if (parameterType == "boolean" || parameterType == "yesorno" ||
parameterType == "trueorfalse") {
return ValueTypeMetadata::booleanType;
}
// These 2 types are not strings from the code generator point of view,
// but it is for event-based extensions.
if (parameterType == "key" || parameterType == "mouse") {
if (parameterType == "string" ||
gd::ValueTypeMetadata::IsTypeValue("string", parameterType)) {
return ValueTypeMetadata::stringType;
}
return GetExpressionPrimitiveValueType(parameterType);
if (parameterType == "variable" ||
gd::ValueTypeMetadata::IsTypeValue("variable", parameterType)) {
return ValueTypeMetadata::variableType;
}
if (parameterType == "boolean" ||
gd::ValueTypeMetadata::IsTypeValue("boolean", parameterType)) {
return ValueTypeMetadata::booleanType;
}
return parameterType;
}
const gd::String ValueTypeMetadata::numberValueType = "number";
const gd::String ValueTypeMetadata::booleanValueType = "boolean";
const gd::String ValueTypeMetadata::stringValueType = "string";
const gd::String ValueTypeMetadata::colorValueType = "color";
const gd::String ValueTypeMetadata::choiceValueType = "stringWithSelector";
const gd::String ValueTypeMetadata::stringValueType = "string";
const gd::String ValueTypeMetadata::behaviorValueType = "behavior";
const gd::String ValueTypeMetadata::leaderboardIdValueType = "leaderboardId";
const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
const gd::String &propertyType) {
@@ -85,6 +90,10 @@ const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
return colorValueType;
} else if (propertyType == "Choice") {
return choiceValueType;
} else if (propertyType == "Behavior") {
return behaviorValueType;
} else if (propertyType == "LeaderboardId") {
return leaderboardIdValueType;
}
// For "String" or default
return stringValueType;

View File

@@ -111,21 +111,21 @@ class GD_CORE_API ValueTypeMetadata {
* given type.
*/
bool IsNumber() const {
return gd::ValueTypeMetadata::IsTypeExpression("number", name);
return gd::ValueTypeMetadata::IsTypeValue("number", name);
}
/**
* \brief Return true if the type is a string.
*/
bool IsString() const {
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
return gd::ValueTypeMetadata::IsTypeValue("string", name);
}
/**
* \brief Return true if the type is a boolean.
*/
bool IsBoolean() const {
return gd::ValueTypeMetadata::IsTypeExpression("boolean", name);
return gd::ValueTypeMetadata::IsTypeValue("boolean", name);
}
/**
@@ -135,7 +135,17 @@ class GD_CORE_API ValueTypeMetadata {
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
return gd::ValueTypeMetadata::IsVariable(name);
}
/**
* \brief Return true if the type of the parameter is a variable.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsVariable(const gd::String &type) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(type) == "variable";
}
/**
@@ -175,7 +185,9 @@ class GD_CORE_API ValueTypeMetadata {
}
/**
* \brief Return true if the type is an expression of the given type.
* \brief Return true if the type is an expression of the given type from the
* caller point of view.
*
* \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.
@@ -186,6 +198,7 @@ class GD_CORE_API ValueTypeMetadata {
return parameterType == "number" || parameterType == "expression" ||
parameterType == "camera" || parameterType == "forceMultiplier";
} else if (type == "string") {
// "key" and "mouse" are not mapped her, see GetPrimitiveValueType.
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "stringWithSelector" ||
@@ -227,6 +240,26 @@ class GD_CORE_API ValueTypeMetadata {
return false;
}
/**
* \brief Return true if the type is a value of the given primitive type from
* the function events point of view
*/
static bool IsTypeValue(const gd::String &type,
const gd::String &parameterType) {
if (gd::ValueTypeMetadata::IsTypeExpression(type, parameterType)) {
return true;
}
// These 2 parameter types are not strings from the outside of a function as
// the generator add quote around a text, but from the events inside of the
// function the parameter is a string.
//
// See EventsCodeGenerator::GenerateParameterCodes
if (type == "string") {
return parameterType == "key" || parameterType == "mouse";
}
return false;
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
@@ -278,9 +311,11 @@ class GD_CORE_API ValueTypeMetadata {
static const gd::String numberValueType;
static const gd::String booleanValueType;
static const gd::String stringValueType;
static const gd::String colorValueType;
static const gd::String choiceValueType;
static const gd::String stringValueType;
static const gd::String behaviorValueType;
static const gd::String leaderboardIdValueType;
};
} // namespace gd

View File

@@ -808,6 +808,24 @@ gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
return extensionName + separator + objectName;
}
gd::String PlatformExtension::GetExtensionFromFullObjectType(const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return "";
}
return type.substr(0, separatorIndex);
}
gd::String PlatformExtension::GetObjectNameFromFullObjectType(const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return "";
}
return type.substr(separatorIndex + 2);
}
PlatformExtension::PlatformExtension()
: deprecated(false), category(_("General")) {}

View File

@@ -40,8 +40,7 @@ class Object;
class ObjectConfiguration;
} // namespace gd
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()> CreateFunPtr;
namespace gd {
@@ -51,25 +50,25 @@ namespace gd {
*/
class GD_CORE_API CompilationInfo {
public:
CompilationInfo() : informationCompleted(false){};
virtual ~CompilationInfo(){};
CompilationInfo() {};
virtual ~CompilationInfo() {};
bool informationCompleted;
bool informationCompleted = false;
bool runtimeOnly; ///< True if the extension was compiled for a runtime use
///< only
bool runtimeOnly = false; ///< True if the extension was compiled for a
///< runtime use only
#if defined(__GNUC__)
int gccMajorVersion;
int gccMinorVersion;
int gccPatchLevel;
int gccMajorVersion = 0;
int gccMinorVersion = 0;
int gccPatchLevel = 0;
#endif
int sfmlMajorVersion;
int sfmlMinorVersion;
int sfmlMajorVersion = 0;
int sfmlMinorVersion = 0;
gd::String gdCoreVersion;
int sizeOfpInt;
int sizeOfpInt = 0;
};
struct GD_CORE_API DuplicatedInstructionOptions {
@@ -239,11 +238,12 @@ class GD_CORE_API PlatformExtension {
* \param instance The "blueprint" object to be copied when a new object is
asked for.
*/
gd::ObjectMetadata& AddObject(const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::ObjectConfiguration> instance);
gd::ObjectMetadata& AddObject(
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::ObjectConfiguration> instance);
/**
* \brief Declare a new events based object as being part of the extension.
@@ -253,11 +253,10 @@ class GD_CORE_API PlatformExtension {
* \param description The user friendly description of the object
* \param icon The icon of the object.
*/
gd::ObjectMetadata& AddEventsBasedObject(
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_);
gd::ObjectMetadata& AddEventsBasedObject(const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_);
/**
* \brief Declare a new behavior as being part of the extension.
@@ -420,8 +419,7 @@ class GD_CORE_API PlatformExtension {
PlatformExtension& SetTags(const gd::String& csvTags) {
tags.clear();
tags = csvTags.Split(',');
for (size_t i = 0; i < tags.size(); i++)
{
for (size_t i = 0; i < tags.size(); i++) {
tags[i] = tags[i].Trim().LowerCase();
}
return *this;
@@ -634,26 +632,30 @@ class GD_CORE_API PlatformExtension {
*/
static gd::String GetNamespaceSeparator() { return "::"; }
static gd::String GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName);
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 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 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 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);
static gd::String GetObjectFullType(const gd::String& extensionName,
const gd::String& objectName);
private:
static gd::String GetExtensionFromFullObjectType(const gd::String& type);
static gd::String GetObjectNameFromFullObjectType(const gd::String& type);
private:
/**
* Set the namespace (the string all actions/conditions/expressions start
* with).
@@ -668,10 +670,10 @@ private:
gd::String fullname; ///< Name displayed to users in the editor.
gd::String informations; ///< Description displayed to users in the editor.
gd::String category;
gd::String author; ///< Author displayed to users in the editor.
gd::String license; ///< License name displayed to users in the editor.
bool deprecated; ///< true if the extension is deprecated and shouldn't be
///< shown in IDE.
gd::String author; ///< Author displayed to users in the editor.
gd::String license; ///< License name displayed to users in the editor.
bool deprecated; ///< true if the extension is deprecated and shouldn't be
///< shown in IDE.
gd::String helpPath; ///< The relative path to the help for this extension in
///< the documentation.
gd::String iconUrl; ///< The URL to the icon to be shown for this extension.

View File

@@ -5,6 +5,8 @@
* project is released under the MIT License.
*/
// NOLINTBEGIN
#ifndef GDCORE_PLATFORMEXTENSION_INL
#define GDCORE_PLATFORMEXTENSION_INL
@@ -36,3 +38,5 @@ gd::ObjectMetadata& PlatformExtension::AddObject(const gd::String& name,
} // namespace gd
#endif
// NOLINTEND

View File

@@ -0,0 +1,18 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/CaptureOptions.h"
#include "GDCore/String.h"
using namespace std;
namespace gd {
Screenshot::Screenshot() {}
CaptureOptions::CaptureOptions() {}
} // namespace gd

View File

@@ -0,0 +1,50 @@
#pragma once
#include <memory>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class GD_CORE_API Screenshot {
public:
Screenshot();
virtual ~Screenshot() {};
void SetDelayTimeInSeconds(int delayTimeInMs_) {
delayTimeInMs = delayTimeInMs_;
}
int GetDelayTimeInSeconds() const { return delayTimeInMs; }
void SetSignedUrl(const gd::String& signedUrl_) { signedUrl = signedUrl_; }
const gd::String& GetSignedUrl() const { return signedUrl; }
void SetPublicUrl(const gd::String& publicUrl_) { publicUrl = publicUrl_; }
const gd::String& GetPublicUrl() const { return publicUrl; }
private:
int delayTimeInMs = 0;
gd::String signedUrl;
gd::String publicUrl;
};
class GD_CORE_API CaptureOptions {
public:
CaptureOptions();
virtual ~CaptureOptions() {};
bool IsEmpty() const { return screenshots.empty(); }
void AddScreenshot(const Screenshot& screenshot) {
screenshots.push_back(screenshot);
}
const std::vector<Screenshot>& GetScreenshots() const { return screenshots; }
void ClearScreenshots() { screenshots.clear(); }
private:
std::vector<Screenshot> screenshots;
};
} // namespace gd

View File

@@ -29,9 +29,6 @@ void EventBasedBehaviorBrowser::ExposeEvents(
project, eventsFunctionsExtension, eventsBasedBehavior, worker);
}
void EventBasedBehaviorBrowser::ExposeObjects(
gd::Project &project, gd::ArbitraryObjectsWorker &worker) const {}
void EventBasedBehaviorBrowser::ExposeFunctions(
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
worker.Launch(eventsBasedBehavior.GetEventsFunctions());
@@ -43,7 +40,4 @@ void EventBasedBehaviorBrowser::ExposeEventBasedBehaviors(
worker.Launch(eventsBasedBehavior);
}
void EventBasedBehaviorBrowser::ExposeBehaviorSharedDatas(
gd::Project &project, gd::ArbitraryBehaviorSharedDataWorker &worker) const {}
} // namespace gd

View File

@@ -67,7 +67,7 @@ public:
* \brief Do nothing.
*/
void ExposeObjects(gd::Project &project,
gd::ArbitraryObjectsWorker &worker) const override;
gd::ArbitraryObjectsWorker &worker) const override {};
/**
* \brief Call the specified worker on the event-based behavior.
@@ -80,7 +80,7 @@ public:
* \brief Do nothing.
*/
void ExposeBehaviorSharedDatas(gd::Project &project,
gd::ArbitraryBehaviorSharedDataWorker &worker) const override;
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
private:
const gd::EventsFunctionsExtension &eventsFunctionsExtension;

View File

@@ -0,0 +1,37 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EventBasedObjectBrowser.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
void EventBasedObjectBrowser::ExposeEvents(
gd::Project &project, gd::ArbitraryEventsWorker &worker) const {
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsBasedObject, worker);
}
void EventBasedObjectBrowser::ExposeEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) const {
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject, worker);
}
void EventBasedObjectBrowser::ExposeFunctions(
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
worker.Launch(eventsBasedObject.GetEventsFunctions());
}
} // namespace gd

View File

@@ -0,0 +1,90 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/IDE/ProjectBrowser.h"
namespace gd {
class Project;
class String;
class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
class ArbitraryEventsWorker;
class ArbitraryEventsWorkerWithContext;
class ArbitraryEventsFunctionsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
} // namespace gd
namespace gd {
/**
* \brief Expose event-based object contents to workers.
*/
class GD_CORE_API EventBasedObjectBrowser : public ProjectBrowser {
public:
EventBasedObjectBrowser(
const gd::EventsFunctionsExtension &eventsFunctionsExtension_,
gd::EventsBasedObject &eventsBasedObject_)
: eventsFunctionsExtension(eventsFunctionsExtension_),
eventsBasedObject(eventsBasedObject_) {}
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an event-based object.
*/
void ExposeEvents(gd::Project &project,
gd::ArbitraryEventsWorker &worker) const override;
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an event-based object.
*/
void
ExposeEvents(gd::Project &project,
gd::ArbitraryEventsWorkerWithContext &worker) const override;
/**
* \brief Call the specified worker on all functions of the event-based object
*
* This should be the preferred way to traverse all the function signatures
* of an event-based object.
*/
void ExposeFunctions(gd::Project &project,
gd::ArbitraryEventsFunctionsWorker &worker) const override;
/**
* \brief Do nothing.
*/
void ExposeObjects(gd::Project &project,
gd::ArbitraryObjectsWorker &worker) const override {};
/**
* @brief Do nothing.
*/
void ExposeEventBasedBehaviors(
gd::Project &project,
gd::ArbitraryEventBasedBehaviorsWorker &worker) const override {};
/**
* \brief Do nothing.
*/
void ExposeBehaviorSharedDatas(gd::Project &project,
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
private:
const gd::EventsFunctionsExtension &eventsFunctionsExtension;
gd::EventsBasedObject &eventsBasedObject;
};
} // namespace gd

View File

@@ -1,41 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsLister.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsLister::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i)
if (instrInfo.GetParameter(i).GetType() == "leaderboardId") {
leaderboardIds.insert(instruction.GetParameter(i).GetPlainString());
}
return false;
}
EventsLeaderboardsLister::~EventsLeaderboardsLister() {}
} // namespace gd

View File

@@ -1,48 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsLister_H
#define EventsLeaderboardsLister_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief List the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsLister : public ArbitraryEventsWorker {
public:
EventsLeaderboardsLister(gd::Project& project_) : project(project_){};
virtual ~EventsLeaderboardsLister();
/**
* Return the values of all leaderboardIds found in the events.
*/
const std::set<gd::String>& GetLeaderboardIds() { return leaderboardIds; }
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::set<gd::String> leaderboardIds;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsLister_H

View File

@@ -1,49 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsRenamer.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsRenamer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.GetType() == "leaderboardId") {
const gd::String leaderboardId =
instruction.GetParameter(i).GetPlainString();
if (leaderboardIdMap.find(leaderboardId) != leaderboardIdMap.end()) {
instruction.SetParameter(i, leaderboardIdMap[leaderboardId]);
}
}
}
return false;
}
EventsLeaderboardsRenamer::~EventsLeaderboardsRenamer() {}
} // namespace gd

View File

@@ -1,46 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsRenamer_H
#define EventsLeaderboardsRenamer_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief Replace the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsRenamer : public ArbitraryEventsWorker {
public:
EventsLeaderboardsRenamer(
gd::Project& project_,
const std::map<gd::String, gd::String>& leaderboardIdMap_)
: project(project_), leaderboardIdMap(leaderboardIdMap_){};
virtual ~EventsLeaderboardsRenamer();
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::map<gd::String, gd::String> leaderboardIdMap;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsRenamer_H

View File

@@ -0,0 +1,243 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsParameterReplacer.h"
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/PropertiesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Go through the nodes and rename parameters.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParameterReplacer
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterReplacer(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
bool isParentTypeAVariable_,
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_)
: hasDoneRenaming(false),
platform(platform_),
projectScopedContainers(projectScopedContainers_),
isParentTypeAVariable(isParentTypeAVariable_),
oldToNewPropertyNames(oldToNewPropertyNames_){};
virtual ~ExpressionParameterReplacer(){};
bool HasDoneRenaming() const { return hasDoneRenaming; }
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
return;
}
// The node represents a variable or an object name on which a variable
// will be accessed, or a property with a child.
projectScopedContainers.MatchIdentifierWithName<void>(
// The property name is changed after the refactor operation.
node.name,
[&]() {
// Do nothing, it's an object variable.
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's a property.
if (node.child) node.child->Visit(*this);
}, [&]() {
// This is a parameter
RenameParameter(node.name);
if (node.child) node.child->Visit(*this);
}, [&]() {
// Do nothing, it's something else.
if (node.child) node.child->Visit(*this);
});
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
isParentTypeAVariable = false;
node.expression->Visit(*this);
isParentTypeAVariable = isGrandParentTypeAVariable;
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
return;
}
projectScopedContainers.MatchIdentifierWithName<void>(
// The property name is changed after the refactor operation
node.identifierName,
[&]() {
// Do nothing, it's an object variable.
}, [&]() {
// Do nothing, it's a variable.
}, [&]() {
// Do nothing, it's a property.
}, [&]() {
// This is a parameter.
RenameParameter(node.identifierName);
}, [&]() {
// Do nothing, it's something else.
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
for (auto &parameter : node.parameters) {
const auto &parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform, projectScopedContainers.GetObjectsContainersList(),
node, *parameter);
if (!parameterMetadata) {
continue;
}
const auto &parameterTypeMetadata =
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsParameterReplacer::CanContainParameter(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariable();
parameter->Visit(*this);
}
}
isParentTypeAVariable = isGrandParentTypeAVariable;
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
bool hasDoneRenaming;
bool RenameParameter(
gd::String& name) {
if (oldToNewPropertyNames.count(name) >= 1) {
name = oldToNewPropertyNames.find(name)->second;
hasDoneRenaming = true;
return true;
}
return false; // Nothing was changed or done.
}
// Scope:
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
// Renaming to do
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames;
gd::String objectNameToUseForVariableAccessor;
bool isParentTypeAVariable;
};
bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
if (!gd::EventsParameterReplacer::CanContainParameter(
parameterMetadata.GetValueTypeMetadata())) {
return;
}
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
parameterMetadata.GetValueTypeMetadata().IsVariable(),
oldToNewPropertyNames);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {
instruction.SetParameter(
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
});
return false;
}
bool EventsParameterReplacer::DoVisitEventExpression(
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
if (!gd::EventsParameterReplacer::CanContainParameter(
metadata.GetValueTypeMetadata())) {
return false;
}
auto node = expression.GetRootNode();
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
metadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {
expression = ExpressionParser2NodePrinter::PrintNode(*node);
}
}
return false;
}
bool EventsParameterReplacer::CanContainParameter(
const gd::ValueTypeMetadata &valueTypeMetadata) {
return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() ||
valueTypeMetadata.IsString();
}
EventsParameterReplacer::~EventsParameterReplacer() {}
} // namespace gd

View File

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

View File

@@ -41,6 +41,7 @@ class GD_CORE_API ExpressionPropertyReplacer
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::PropertiesContainer& targetPropertiesContainer_,
bool isParentTypeAVariable_,
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_,
const std::unordered_set<gd::String>& removedPropertyNames_)
: hasDoneRenaming(false),
@@ -48,6 +49,7 @@ class GD_CORE_API ExpressionPropertyReplacer
platform(platform_),
projectScopedContainers(projectScopedContainers_),
targetPropertiesContainer(targetPropertiesContainer_),
isParentTypeAVariable(isParentTypeAVariable_),
oldToNewPropertyNames(oldToNewPropertyNames_),
removedPropertyNames(removedPropertyNames_){};
virtual ~ExpressionPropertyReplacer(){};
@@ -69,16 +71,21 @@ class GD_CORE_API ExpressionPropertyReplacer
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
if (node.child) node.child->Visit(*this);
return;
}
auto& propertiesContainersList =
projectScopedContainers.GetPropertiesContainersList();
// The node represents a variable or an object name on which a variable
// will be accessed, or a property with a child.
// Match the potential *new* name of the property, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.name),
// The property name is changed after the refactor operation.
node.name,
[&]() {
// Do nothing, it's an object variable.
if (node.child) node.child->Visit(*this);
@@ -100,16 +107,7 @@ class GD_CORE_API ExpressionPropertyReplacer
// Do nothing, it's a parameter.
if (node.child) node.child->Visit(*this);
}, [&]() {
// This is something else - potentially a deleted property.
// Check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.name);
}
// Do nothing, it's something else.
if (node.child) node.child->Visit(*this);
});
}
@@ -118,17 +116,24 @@ class GD_CORE_API ExpressionPropertyReplacer
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
isParentTypeAVariable = false;
node.expression->Visit(*this);
isParentTypeAVariable = isGrandParentTypeAVariable;
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (isParentTypeAVariable) {
// Do nothing, it's a variable.
return;
}
auto& propertiesContainersList =
projectScopedContainers.GetPropertiesContainersList();
// Match the potential *new* name of the property, because refactorings are
// done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.identifierName),
// The property name is changed after the refactor operation
node.identifierName,
[&]() {
// Do nothing, it's an object variable.
}, [&]() {
@@ -145,22 +150,29 @@ class GD_CORE_API ExpressionPropertyReplacer
}, [&]() {
// Do nothing, it's a parameter.
}, [&]() {
// This is something else - potentially a deleted property.
// Check if it's coming from the target container with
// properties to replace.
if (propertiesContainersList.HasPropertiesContainer(
targetPropertiesContainer)) {
// The node represents a property, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemovePropertyOfTargetPropertyContainer(node.identifierName);
}
// Do nothing, it's something else.
});
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
bool isGrandParentTypeAVariable = isParentTypeAVariable;
for (auto &parameter : node.parameters) {
const auto &parameterMetadata =
gd::MetadataProvider::GetFunctionCallParameterMetadata(
platform, projectScopedContainers.GetObjectsContainersList(),
node, *parameter);
if (!parameterMetadata) {
continue;
}
const auto &parameterTypeMetadata =
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsPropertyReplacer::CanContainProperty(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariable();
parameter->Visit(*this);
}
}
isParentTypeAVariable = isGrandParentTypeAVariable;
}
void OnVisitEmptyNode(EmptyNode& node) override {}
@@ -168,12 +180,6 @@ class GD_CORE_API ExpressionPropertyReplacer
bool hasDoneRenaming;
bool removedPropertyUsed;
const gd::String& GetPotentialNewName(const gd::String& oldName) {
return oldToNewPropertyNames.count(oldName) >= 1
? oldToNewPropertyNames.find(oldName)->second
: oldName;
}
bool RenameOrRemovePropertyOfTargetPropertyContainer(
gd::String& propertyName) {
if (oldToNewPropertyNames.count(propertyName) >= 1) {
@@ -198,6 +204,7 @@ class GD_CORE_API ExpressionPropertyReplacer
const std::unordered_set<gd::String>& removedPropertyNames;
gd::String objectNameToUseForVariableAccessor;
bool isParentTypeAVariable;
};
bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
@@ -216,20 +223,16 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return; // Not an expression that can contain properties.
if (!gd::EventsPropertyReplacer::CanContainProperty(
parameterMetadata.GetValueTypeMetadata())) {
return;
}
auto node = parameterValue.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
parameterMetadata.GetValueTypeMetadata().IsVariable(),
oldToNewPropertyNames, removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
@@ -246,20 +249,16 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
bool EventsPropertyReplacer::DoVisitEventExpression(
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
const gd::String& type = metadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
!gd::ParameterMetadata::IsExpression("number", type) &&
!gd::ParameterMetadata::IsExpression("string", type))
return false; // Not an expression that can contain properties.
if (!gd::EventsPropertyReplacer::CanContainProperty(
metadata.GetValueTypeMetadata())) {
return false;
}
auto node = expression.GetRootNode();
if (node) {
ExpressionPropertyReplacer renamer(platform,
GetProjectScopedContainers(),
targetPropertiesContainer,
oldToNewPropertyNames,
removedPropertyNames);
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
metadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames,
removedPropertyNames);
node->Visit(renamer);
if (renamer.IsRemovedPropertyUsed()) {
@@ -272,6 +271,12 @@ bool EventsPropertyReplacer::DoVisitEventExpression(
return false;
}
bool EventsPropertyReplacer::CanContainProperty(
const gd::ValueTypeMetadata &valueTypeMetadata) {
return valueTypeMetadata.IsVariable() || valueTypeMetadata.IsNumber() ||
valueTypeMetadata.IsString();
}
EventsPropertyReplacer::~EventsPropertyReplacer() {}
} // namespace gd

View File

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

View File

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

View File

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

View File

@@ -337,19 +337,19 @@ struct GD_CORE_API ExpressionCompletionDescription {
private:
CompletionKind completionKind;
gd::Variable::Type variableType;
gd::VariablesContainer::SourceType variableScope;
gd::Variable::Type variableType = gd::Variable::Unknown;
gd::VariablesContainer::SourceType variableScope = gd::VariablesContainer::Unknown;
gd::String type;
gd::String prefix;
gd::String completion;
size_t replacementStartPosition;
size_t replacementEndPosition;
size_t replacementStartPosition = 0;
size_t replacementEndPosition = 0;
gd::String objectName;
gd::String behaviorName;
bool isExact;
bool isLastParameter;
const gd::ParameterMetadata* parameterMetadata;
const gd::ObjectConfiguration* objectConfiguration;
bool isExact = false;
bool isLastParameter = false;
const gd::ParameterMetadata* parameterMetadata = &badParameterMetadata;
const gd::ObjectConfiguration* objectConfiguration = &badObjectConfiguration;
static const gd::ParameterMetadata badParameterMetadata;
static const gd::ObjectConfiguration badObjectConfiguration;
@@ -1066,8 +1066,8 @@ class GD_CORE_API ExpressionCompletionFinder
bool eagerlyCompleteIfExactMatch = false) {
projectScopedContainers.ForEachIdentifierMatchingSearch(
search,
[&](const gd::String& objectName,
const ObjectConfiguration* objectConfiguration) {
[&](const gd::String &objectName,
const ObjectConfiguration *objectConfiguration) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Object,
location.GetStartPosition(),
@@ -1077,7 +1077,7 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetType(type);
completions.push_back(description);
},
[&](const gd::String& variableName, const gd::Variable& variable) {
[&](const gd::String &variableName, const gd::Variable &variable) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Variable,
location.GetStartPosition(),
@@ -1095,23 +1095,29 @@ class GD_CORE_API ExpressionCompletionFinder
variable, variableName, location);
}
},
[&](const gd::NamedPropertyDescriptor& property) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Property,
location.GetStartPosition(),
location.GetEndPosition());
description.SetCompletion(property.GetName());
description.SetType(property.GetType());
completions.push_back(description);
[&](const gd::NamedPropertyDescriptor &property) {
auto propertyType = gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType());
if (gd::ValueTypeMetadata::IsTypeValue("number", propertyType) ||
gd::ValueTypeMetadata::IsTypeValue("string", propertyType)) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Property,
location.GetStartPosition(), location.GetEndPosition());
description.SetCompletion(property.GetName());
description.SetType(property.GetType());
completions.push_back(description);
}
},
[&](const gd::ParameterMetadata& parameter) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Parameter,
location.GetStartPosition(),
location.GetEndPosition());
description.SetCompletion(parameter.GetName());
description.SetType(parameter.GetType());
completions.push_back(description);
[&](const gd::ParameterMetadata &parameter) {
if (parameter.GetValueTypeMetadata().IsNumber() ||
parameter.GetValueTypeMetadata().IsString()) {
ExpressionCompletionDescription description(
ExpressionCompletionDescription::Parameter,
location.GetStartPosition(), location.GetEndPosition());
description.SetCompletion(parameter.GetName());
description.SetType(parameter.GetType());
completions.push_back(description);
}
});
}

View File

@@ -33,8 +33,10 @@ class GD_CORE_API ExpressionNodeLocationFinder
* \brief Initialize the finder to search at the specified position.
*/
ExpressionNodeLocationFinder(size_t searchedPosition_)
: searchedPosition(searchedPosition_), foundNode(nullptr){};
virtual ~ExpressionNodeLocationFinder(){};
: searchedPosition(searchedPosition_),
foundNode(nullptr),
parentNode(nullptr) {};
virtual ~ExpressionNodeLocationFinder() {};
/**
* \brief Helper function to find the deepest node at the search position, if

View File

@@ -72,7 +72,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
child(nullptr) {};
const gd::String &GetType() {
return gd::ParameterMetadata::GetExpressionValueType(type);
return gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(type);
};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {

View File

@@ -68,19 +68,26 @@ size_t GetMaximumParametersNumber(
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier) {
return ValidateObjectVariableOrVariableOrProperty(identifier.identifierName, identifier.identifierNameLocation, identifier.childIdentifierName, identifier.childIdentifierNameLocation);
}
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::String &identifierName,
const gd::ExpressionParserLocation identifierNameLocation,
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation) {
auto validateVariableTypeForExpression =
[this, &identifier](gd::Variable::Type type) {
[this, &identifierNameLocation](gd::Variable::Type type) {
// Collections type can't be used directly in expressions, a child
// must be accessed.
if (type == Variable::Structure) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable.child`."),
identifier.identifierNameLocation);
identifierNameLocation);
} else if (type == Variable::Array) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable[0]`."),
identifier.identifierNameLocation);
identifierNameLocation);
} else {
// Number, string or boolean variables can be used in expressions.
return;
@@ -96,38 +103,41 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// we consider this node will be of the type required by the parent.
childType = parentType;
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
return projectScopedContainers.MatchIdentifierWithName<bool>(identifierName,
[&]() {
// This represents an object.
if (identifier.childIdentifierName.empty()) {
if (childIdentifierName.empty()) {
RaiseTypeError(_("An object variable or expression should be entered."),
identifier.identifierNameLocation);
identifierNameLocation);
return true; // We should have found a variable.
}
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
auto variableExistence =
objectsContainersList.HasObjectOrGroupWithVariableNamed(
identifierName, childIdentifierName);
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
RaiseUndeclaredVariableError(_("This variable does not exist on this object or group."),
identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
childIdentifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseUndeclaredVariableError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
childIdentifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
RaiseUndeclaredVariableError(_("This group is empty. Add an object to this group first."),
identifier.identifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
identifierNameLocation, childIdentifierName, identifierName);
return true; // We should have found a variable.
}
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(
identifierName, childIdentifierName);
ReadChildTypeFromVariable(variableType);
return true; // We found a variable.
@@ -137,9 +147,9 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// Try to identify a declared variable with the name (and maybe the child
// variable).
const gd::Variable& variable =
variablesContainersList.Get(identifier.identifierName);
variablesContainersList.Get(identifierName);
if (identifier.childIdentifierName.empty()) {
if (childIdentifierName.empty()) {
// Just the root variable is accessed, check it can be used in an
// expression.
validateVariableTypeForExpression(variable.GetType());
@@ -148,33 +158,38 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
return true; // We found a variable.
} else {
// A child variable is accessed, check it can be used in an expression.
if (!variable.HasChild(identifier.childIdentifierName)) {
if (!variable.HasChild(childIdentifierName)) {
RaiseTypeError(_("No child variable with this name found."),
identifier.childIdentifierNameLocation);
childIdentifierNameLocation);
return true; // We should have found a variable.
}
const gd::Variable& childVariable =
variable.GetChild(identifier.childIdentifierName);
variable.GetChild(childIdentifierName);
ReadChildTypeFromVariable(childVariable.GetType());
return true; // We found a variable.
}
}, [&]() {
// This is a property.
if (!identifier.childIdentifierName.empty()) {
if (!childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
identifier.childIdentifierNameLocation);
childIdentifierNameLocation);
return true; // We found a property, even if the child is not allowed.
}
const gd::NamedPropertyDescriptor& property = propertiesContainersList.Get(identifier.identifierName).second;
const gd::NamedPropertyDescriptor &property =
propertiesContainersList.Get(identifierName).second;
if (property.GetType() == "Number") {
childType = Type::Number;
childType = Type::Number;
} else if (property.GetType() == "Boolean") {
// Nothing - we don't know the precise type (this could be used a string or as a number)
// Nothing - we don't know the precise type (this could be used a string
// or as a number)
} else if (property.GetType() == "Behavior") {
RaiseTypeError(_("Behaviors can't be used as a value in expressions."),
identifierNameLocation);
} else {
// Assume type is String or equivalent.
childType = Type::String;
@@ -183,14 +198,14 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
return true; // We found a property.
}, [&]() {
// This is a parameter.
if (!identifier.childIdentifierName.empty()) {
if (!childIdentifierName.empty()) {
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
identifier.childIdentifierNameLocation);
childIdentifierNameLocation);
return true; // We found a parameter, even if the child is not allowed.
}
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifier.identifierName);
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifierName);
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
childType = Type::Number;
@@ -200,7 +215,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
// Nothing - we don't know the precise type (this could be used as a string or as a number).
} else {
RaiseTypeError(_("This parameter is not a string, number or boolean - it can't be used in an expression."),
identifier.identifierNameLocation);
identifierNameLocation);
return true; // We found a parameter, even though the type is incompatible.
}

View File

@@ -14,6 +14,7 @@
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/VariablesContainersList.h"
#include "GDCore/Project/VariablesContainer.h"
namespace gd {
class Expression;
@@ -42,10 +43,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
const gd::String &extraInfo_ = "")
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false),
currentParameterExtraInfo(&extraInfo_) {};
currentParameterExtraInfo(&extraInfo_),
variableObjectName(),
variableObjectNameLocation() {};
virtual ~ExpressionValidator(){};
/**
@@ -225,7 +228,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
[&]() {
// This represents an object.
variableObjectName = node.name;
variableObjectNameLocation = node.nameLocation;
// While understood by the parser, it's forbidden to use the bracket notation just after
// an object name (`MyObject["MyVariable"]`).
forbidsUsageOfBracketsBecauseParentIsObject = true;
@@ -264,7 +268,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
ReportAnyError(node);
// TODO Also check child-variables existence on a path with only VariableAccessor to raise non-fatal errors.
if (!variableObjectName.empty()) {
ValidateObjectVariableOrVariableOrProperty(variableObjectName,
variableObjectNameLocation,
node.name, node.nameLocation);
variableObjectName = "";
}
// In the case we accessed an object variable (`MyObject.MyVariable`),
// brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
forbidsUsageOfBracketsBecauseParentIsObject = false;
@@ -277,6 +287,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
VariableBracketAccessorNode& node) override {
ReportAnyError(node);
variableObjectName = "";
if (forbidsUsageOfBracketsBecauseParentIsObject) {
RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
_("You can't use the brackets to access an object variable. "
@@ -369,6 +380,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, LegacyVariable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function);
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
bool ValidateObjectVariableOrVariableOrProperty(
const gd::String &identifierName,
const gd::ExpressionParserLocation identifierNameLocation,
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation);
void CheckVariableExistence(const ExpressionParserLocation &location, const gd::String& name) {
if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") {
@@ -505,6 +521,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
Type childType; ///< The type "discovered" down the tree and passed up.
Type parentType; ///< The type "required" by the top of the tree.
bool forbidsUsageOfBracketsBecauseParentIsObject;
gd::String variableObjectName;
gd::ExpressionParserLocation variableObjectNameLocation;
const gd::String *currentParameterExtraInfo;
const gd::Platform &platform;
const gd::ProjectScopedContainers &projectScopedContainers;

View File

@@ -74,8 +74,8 @@ class GD_CORE_API ExpressionsParameterMover
const gd::Platform &platform;
gd::String functionName;
std::size_t oldIndex;
std::size_t newIndex;
std::size_t oldIndex = 0;
std::size_t newIndex = 0;
gd::String behaviorType;
gd::String objectType;
};

View File

@@ -0,0 +1,76 @@
#include "LeaderboardIdRenamer.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
namespace gd {
void LeaderboardIdRenamer::DoVisitObject(gd::Object &object) {
for (auto &pair : object.GetConfiguration().GetProperties()) {
auto &propertyName = pair.first;
auto &property = pair.second;
if (property.GetType() == "LeaderboardId") {
auto &leaderboardId = property.GetValue();
allLeaderboardIds.insert(leaderboardId);
if (leaderboardIdMap.find(leaderboardId) != leaderboardIdMap.end()) {
object.GetConfiguration().UpdateProperty(
propertyName, leaderboardIdMap[leaderboardId]);
}
}
}
};
void LeaderboardIdRenamer::DoVisitBehavior(gd::Behavior &behavior) {};
bool LeaderboardIdRenamer::DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) {
const gd::InstructionMetadata &instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.GetType() != "leaderboardId") {
continue;
}
const gd::String leaderboardIdExpression =
instruction.GetParameter(i).GetPlainString();
if (leaderboardIdExpression[0] != '"' ||
leaderboardIdExpression[leaderboardIdExpression.size() - 1] != '"') {
continue;
}
const gd::String leaderboardId =
leaderboardIdExpression.substr(1, leaderboardIdExpression.size() - 2);
allLeaderboardIds.insert(leaderboardId);
if (leaderboardIdMap.find(leaderboardId) != leaderboardIdMap.end()) {
instruction.SetParameter(i,
"\"" + leaderboardIdMap[leaderboardId] + "\"");
}
}
return false;
}
LeaderboardIdRenamer::~LeaderboardIdRenamer() {}
} // namespace gd

View File

@@ -0,0 +1,50 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class Object;
class Behavior;
} // namespace gd
namespace gd {
class GD_CORE_API LeaderboardIdRenamer : public ArbitraryObjectsWorker, public ArbitraryEventsWorker {
public:
LeaderboardIdRenamer(gd::Project& project_): project(project_) {};
virtual ~LeaderboardIdRenamer();
/**
* Set the leaderboard identifiers to be replaced.
*/
void SetLeaderboardIdsToReplace(const std::map<gd::String, gd::String>& leaderboardIdMap_) {
leaderboardIdMap = leaderboardIdMap_;
}
/**
* Return the all the leaderboard identifiers found in the project.
*/
const std::set<gd::String>& GetAllLeaderboardIds() const {
return allLeaderboardIds;
}
private:
bool DoVisitInstruction(gd::Instruction& instruction, bool isCondition) override;
void DoVisitObject(gd::Object& object) override;
void DoVisitBehavior(gd::Behavior& behavior) override;
std::map<gd::String, gd::String> leaderboardIdMap;
std::set<gd::String> allLeaderboardIds;
gd::Project& project;
};
}; // namespace gd

View File

@@ -24,15 +24,16 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputObjectsContainer) {
// Functions scope for objects is defined according
// to parameters
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.GetObjectGroups().Clear();
// to parameters.
auto &parameters = eventsFunction.GetParametersForEvents(functionContainer);
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project,
parameters,
outputObjectsContainer);
// TODO: in theory we should ensure stability of the groups across calls
// to this function. BUT groups in functions should probably have never been
// supported, so we're phasing this out in the UI.
outputObjectsContainer.GetObjectGroups() = eventsFunction.GetObjectGroups();
}
@@ -97,26 +98,6 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
"for the parent. ");
return;
}
gd::EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
eventsBasedObject, outputObjectsContainer);
}
void EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
const gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& outputObjectsContainer) {
auto &children = eventsBasedObject.GetObjects().GetObjects();
for (auto &childObject : children) {
auto child = childObject.get();
outputObjectsContainer.InsertObject(
*child, outputObjectsContainer.GetObjectsCount());
}
auto &childrenGroups = eventsBasedObject.GetObjects().GetObjectGroups();
for (size_t index = 0; index < childrenGroups.Count(); ++index) {
auto &childGroup = childrenGroups.Get(index);
outputObjectsContainer.GetObjectGroups().Insert(
childGroup, outputObjectsContainer.GetObjectGroups().Count());
}
}
} // namespace gd

View File

@@ -68,9 +68,5 @@ class GD_CORE_API EventsFunctionTools {
const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputObjectsContainer);
static void CopyEventsBasedObjectChildrenToObjectsContainer(
const gd::EventsBasedObject &eventsBasedObject,
gd::ObjectsContainer &outputObjectsContainer);
};
} // namespace gd

View File

@@ -4,6 +4,7 @@
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Extensions/Platform.h"
namespace gd {

View File

@@ -225,9 +225,7 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
size_t parameterIndex,
const gd::String& lastObjectName) {
const String& parameterValue = parameterExpression.GetPlainString();
if (parameterMetadata.GetType() ==
"police" || // Should be renamed fontResource
parameterMetadata.GetType() == "fontResource") {
if (parameterMetadata.GetType() == "fontResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeFont(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
@@ -295,7 +293,7 @@ void ResourceWorkerInObjectsWorker::DoVisitObject(gd::Object &object) {
};
void ResourceWorkerInObjectsWorker::DoVisitBehavior(gd::Behavior &behavior){
// TODO Allow behaviors to expose resources
behavior.ExposeResources(worker);
};
gd::ResourceWorkerInObjectsWorker

View File

@@ -170,7 +170,8 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::ArbitraryEventsWorkerWithContext &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainer;
gd::ObjectsContainer parameterObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, *eventsFunction,
@@ -209,7 +210,8 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
for (auto &&eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project, eventsFunctionsExtension, eventsBasedBehavior,
@@ -236,7 +238,8 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsFunctionsExtension, eventsBasedObject,

View File

@@ -247,7 +247,7 @@ bool PropertyFunctionGenerator::CanGenerateGetterAndSetter(
const gd::NamedPropertyDescriptor &property) {
auto &type = property.GetType();
if (type != "Boolean" && type != "Number" && type != "String" &&
type != "Choice" && type != "Color") {
type != "Choice" && type != "Color" && type != "LeaderboardId") {
return false;
}

View File

@@ -6,17 +6,9 @@
#include "ResourceExposer.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Effect.h"
@@ -24,7 +16,6 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
namespace gd {

View File

@@ -13,11 +13,13 @@
#include "GDCore/IDE/DependenciesAnalyzer.h"
#include "GDCore/IDE/GroupVariableHelper.h"
#include "GDCore/IDE/EventBasedBehaviorBrowser.h"
#include "GDCore/IDE/EventBasedObjectBrowser.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Events/BehaviorParametersFiller.h"
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
#include "GDCore/IDE/Events/CustomObjectTypeRenamer.h"
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
#include "GDCore/IDE/Events/EventsParameterReplacer.h"
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h"
@@ -27,12 +29,8 @@
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/Events/LinkEventTargetRenamer.h"
#include "GDCore/IDE/Events/LeaderboardIdRenamer.h"
#include "GDCore/IDE/Events/ProjectElementRenamer.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/BehaviorObjectTypeRenamer.h"
#include "GDCore/IDE/Project/BehaviorsSharedDataBehaviorTypeRenamer.h"
#include "GDCore/IDE/Project/FunctionParameterBehaviorTypeRenamer.h"
@@ -415,20 +413,32 @@ void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceExtensionName) {
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
const EventBasedBehaviorBrowser eventBasedBehaviorBrowser(
eventsFunctionsExtension, eventsBasedBehavior);
WholeProjectRefactorer::RenameEventsFunctionsExtension(
project, eventsFunctionsExtension, sourceExtensionName,
eventsFunctionsExtension.GetName(), eventBasedBehaviorExposer);
eventsFunctionsExtension.GetName(), eventBasedBehaviorBrowser);
}
void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceExtensionName) {
const EventBasedObjectBrowser eventBasedObjectBrowser(
eventsFunctionsExtension, eventsBasedObject);
WholeProjectRefactorer::RenameEventsFunctionsExtension(
project, eventsFunctionsExtension, sourceExtensionName,
eventsFunctionsExtension.GetName(), eventBasedObjectBrowser);
}
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &oldName, const gd::String &newName) {
const WholeProjectBrowser wholeProjectExposer;
const WholeProjectBrowser wholeProjectBrowser;
RenameEventsFunctionsExtension(project, eventsFunctionsExtension, oldName,
newName, wholeProjectExposer);
newName, wholeProjectBrowser);
}
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
@@ -807,6 +817,51 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
}
}
void WholeProjectRefactorer::RenameParameter(
gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &oldParameterName, const gd::String &newParameterName) {
auto &parameters = eventsFunction.GetParameters();
if (!parameters.HasParameterNamed(oldParameterName))
return;
auto &parameter = parameters.GetParameter(oldParameterName);
if (parameter.GetValueTypeMetadata().IsObject()) {
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, eventsFunction,
parameterObjectsContainer, oldParameterName, newParameterName, false);
} else if (parameter.GetValueTypeMetadata().IsBehavior()) {
size_t behaviorParameterIndex = parameters.GetParameterPosition(parameter);
size_t objectParameterIndex =
gd::ParameterMetadataTools::GetObjectParameterIndexFor(
parameters, behaviorParameterIndex);
if (objectParameterIndex == gd::String::npos) {
return;
}
const gd::String &objectName =
parameters.GetParameter(objectParameterIndex).GetName();
gd::EventsBehaviorRenamer behaviorRenamer(project.GetCurrentPlatform(),
objectName, oldParameterName,
newParameterName);
behaviorRenamer.Launch(eventsFunction.GetEvents(), projectScopedContainers);
} else {
// Rename parameter names directly used as an identifier.
std::unordered_map<gd::String, gd::String> oldToNewParameterNames = {
{oldParameterName, newParameterName}};
gd::EventsParameterReplacer eventsParameterReplacer(
project.GetCurrentPlatform(), oldToNewParameterNames);
eventsParameterReplacer.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
// Rename parameter names in legacy expressions and instructions
gd::ProjectElementRenamer projectElementRenamer(
project.GetCurrentPlatform(), "functionParameterName", oldParameterName,
newParameterName);
projectElementRenamer.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
}
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
@@ -941,6 +996,8 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
// 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.
// Rename legacy expressions like: Object.Behavior::PropertyMyPropertyName()
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
@@ -950,14 +1007,16 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
// Rename property names directly used as an identifier.
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension, eventsBasedBehavior,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1012,6 +1071,8 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
// 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.
// Rename legacy expressions like: Object.Behavior::SharedPropertyMyPropertyName()
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
@@ -1021,14 +1082,16 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
// Rename property names directly used as an identifier.
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension, eventsBasedBehavior,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1069,6 +1132,8 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
// 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.
// Rename legacy expressions like: Object.PropertyMyPropertyName()
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedObjectExpression(
@@ -1078,14 +1143,16 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
// Rename property names directly used as an identifier.
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {
{oldPropertyName, newPropertyName}};
std::unordered_set<gd::String> removedPropertyNames;
gd::EventsPropertyReplacer eventsPropertyReplacer(
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
removedPropertyNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsPropertyReplacer);
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject,
eventsPropertyReplacer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1312,6 +1379,19 @@ bool WholeProjectRefactorer::FixInvalidRequiredBehaviorProperties(
return !invalidRequiredBehaviorProblems.empty();
}
void WholeProjectRefactorer::UpdateBehaviorNameInEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceBehaviorName) {
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
eventsFunctionsExtension, eventsBasedBehavior);
WholeProjectRefactorer::RenameEventsBasedBehavior(
project, eventsFunctionsExtension, eventsBasedBehavior,
sourceBehaviorName, eventsBasedBehavior.GetName(),
eventBasedBehaviorExposer);
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
@@ -1324,10 +1404,22 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
return;
}
auto &eventsBasedBehavior = eventsBasedBehaviors.Get(oldBehaviorName);
const WholeProjectBrowser projectBrowser;
WholeProjectRefactorer::RenameEventsBasedBehavior(
project, eventsFunctionsExtension, eventsBasedBehavior, oldBehaviorName,
newBehaviorName, projectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &oldBehaviorName,
const gd::String &newBehaviorName,
const gd::ProjectBrowser &projectBrowser) {
auto renameBehaviorEventsFunction =
[&project, &eventsFunctionsExtension, &oldBehaviorName,
&newBehaviorName](const gd::EventsFunction &eventsFunction) {
&newBehaviorName, &projectBrowser](const gd::EventsFunction &eventsFunction) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// behavior
@@ -1341,12 +1433,12 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
eventsFunction.GetName()));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
projectBrowser.ExposeEvents(project, renamer);
}
};
auto renameBehaviorProperty = [&project, &eventsFunctionsExtension,
&oldBehaviorName, &newBehaviorName](
&oldBehaviorName, &newBehaviorName, &projectBrowser](
const gd::NamedPropertyDescriptor
&property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
@@ -1357,7 +1449,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1367,7 +1459,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the behavior
@@ -1375,7 +1467,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
auto renameBehaviorSharedProperty =
[&project, &eventsFunctionsExtension, &oldBehaviorName,
&newBehaviorName](const gd::NamedPropertyDescriptor &property) {
&newBehaviorName, &projectBrowser](const gd::NamedPropertyDescriptor &property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
@@ -1386,7 +1478,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
@@ -1399,8 +1491,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name
// of the behavior
@@ -1435,13 +1526,25 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
renameBehaviorSharedProperty(*property);
}
const WholeProjectBrowser wholeProjectExposer;
DoRenameBehavior(project,
gd::PlatformExtension::GetBehaviorFullType(
eventsFunctionsExtension.GetName(), oldBehaviorName),
gd::PlatformExtension::GetBehaviorFullType(
eventsFunctionsExtension.GetName(), newBehaviorName),
wholeProjectExposer);
projectBrowser);
}
void WholeProjectRefactorer::UpdateObjectNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceObjectName) {
const EventBasedObjectBrowser eventBasedObjectBrowser(
eventsFunctionsExtension, eventsBasedObject);
WholeProjectRefactorer::RenameEventsBasedObject(
project, eventsFunctionsExtension, eventsBasedObject,
sourceObjectName, eventsBasedObject.GetName(),
eventBasedObjectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedObject(
@@ -1455,10 +1558,21 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
return;
}
auto &eventsBasedObject = eventsBasedObjects.Get(oldObjectName);
const WholeProjectBrowser projectBrowser;
WholeProjectRefactorer::RenameEventsBasedObject(
project, eventsFunctionsExtension, eventsBasedObject, oldObjectName,
newObjectName, projectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &oldObjectName, const gd::String &newObjectName,
const gd::ProjectBrowser &projectBrowser) {
auto renameObjectEventsFunction =
[&project, &eventsFunctionsExtension, &oldObjectName,
&newObjectName](const gd::EventsFunction &eventsFunction) {
[&project, &eventsFunctionsExtension, &oldObjectName, &newObjectName,
&projectBrowser](const gd::EventsFunction &eventsFunction) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// object
@@ -1472,12 +1586,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
eventsFunction.GetName()));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
projectBrowser.ExposeEvents(project, renamer);
}
};
auto renameObjectProperty = [&project, &eventsFunctionsExtension,
&oldObjectName, &newObjectName](
&oldObjectName, &newObjectName, &projectBrowser](
const gd::NamedPropertyDescriptor &property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1487,7 +1601,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1497,7 +1611,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the object
@@ -1528,13 +1642,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
renameObjectProperty(*property);
}
const WholeProjectBrowser wholeProjectExposer;
DoRenameObject(project,
gd::PlatformExtension::GetObjectFullType(
eventsFunctionsExtension.GetName(), oldObjectName),
gd::PlatformExtension::GetObjectFullType(
eventsFunctionsExtension.GetName(), newObjectName),
wholeProjectExposer);
projectBrowser);
}
void WholeProjectRefactorer::DoRenameEventsFunction(
@@ -1638,6 +1751,15 @@ void WholeProjectRefactorer::BehaviorsAddedToObjectInScene(
void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
gd::Project &project, gd::Layout &layout, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) {
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
project, layout, layout.GetObjects(), oldName, newName, isObjectGroup);
}
void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
gd::Project &project, gd::Layout &layout,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName, const gd::String &newName, bool isObjectGroup) {
if (oldName == newName || newName.empty() || oldName.empty())
return;
@@ -1647,7 +1769,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers, layout.GetEvents(),
oldName, newName);
layout.GetObjects(), oldName, newName);
// Object groups can't have instances or be in other groups
if (!isObjectGroup) {
@@ -1664,7 +1786,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
auto &externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
externalEvents.GetEvents(), oldName, newName);
externalEvents.GetEvents(), layout.GetObjects(), oldName, newName);
}
// Rename object in external layouts
@@ -1910,8 +2032,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
auto *function = functionUniquePtr.get();
WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, *function, oldName, newName,
isObjectGroup);
project, projectScopedContainers, *function,
eventsBasedObject.GetObjects(), oldName, newName, isObjectGroup);
}
// Object groups can't have instances or be in other groups
@@ -1928,11 +2050,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
gd::Project &project,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) {
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName, const gd::String &newName, bool isObjectGroup) {
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
eventsFunction.GetEvents(), oldName, newName);
eventsFunction.GetEvents(), targetedObjectsContainer, oldName, newName);
// Object groups can't be in other groups
if (!isObjectGroup) {
@@ -1958,7 +2081,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
if (layout.GetObjects().HasObjectNamed(oldName))
continue;
ObjectOrGroupRenamedInScene(project, layout, oldName, newName,
ObjectOrGroupRenamedInScene(project, layout, project.GetObjects(), oldName, newName,
isObjectGroup);
}
}
@@ -2090,4 +2213,23 @@ std::vector<gd::String> WholeProjectRefactorer::GetAssociatedExternalEvents(
return results;
}
void WholeProjectRefactorer::RenameLeaderboards(
gd::Project &project,
const std::map<gd::String, gd::String> &leaderboardIdMap) {
gd::LeaderboardIdRenamer leaderboardIdRenamer(project);
leaderboardIdRenamer.SetLeaderboardIdsToReplace(leaderboardIdMap);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, leaderboardIdRenamer);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, leaderboardIdRenamer);
}
std::set<gd::String> WholeProjectRefactorer::FindAllLeaderboardIds(gd::Project &project) {
gd::LeaderboardIdRenamer leaderboardIdRenamer(project);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, leaderboardIdRenamer);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, leaderboardIdRenamer);
return leaderboardIdRenamer.GetAllLeaderboardIds();
}
} // namespace gd

View File

@@ -27,14 +27,7 @@ class ObjectsContainer;
class VariablesContainer;
class EventsBasedBehavior;
class EventsBasedObject;
class ArbitraryEventsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventsFunctionsWorker;
class ArbitraryEventsWorkerWithContext;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
class Behavior;
class BehaviorMetadata;
class UnfilledRequiredBehaviorPropertyProblem;
class ProjectBrowser;
class SerializerElement;
@@ -121,14 +114,24 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newName);
/**
* \brief Refactor behavior events after the extension was placed in a new
* \brief Refactor behavior events after the behavior has been placed in a new
* extension.
*/
static void UpdateExtensionNameInEventsBasedBehavior(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& sourceExtensionName);
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceExtensionName);
/**
* \brief Refactor object events after the object has been placed in a new
* extension.
*/
static void UpdateExtensionNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceExtensionName);
/**
* \brief Refactor the project **before** an events function is renamed.
@@ -173,6 +176,21 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldFunctionName,
const gd::String& newFunctionName);
/**
* \brief Refactor the function **before** a parameter is renamed.
*
* \warning Do the renaming of the specified parameter after calling this.
* This is because the function is expected to have its old name for the
* refactoring.
*/
static void
RenameParameter(gd::Project &project,
gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &oldParameterName,
const gd::String &newParameterName);
/**
* \brief Refactor the project **before** an events function parameter
* is moved.
@@ -324,6 +342,16 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldBehaviorName,
const gd::String& newBehaviorName);
/**
* \brief Refactor events-based behavior events after the events-based
* behavior has been duplicated.
*/
static void UpdateBehaviorNameInEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceBehaviorName);
/**
* \brief Refactor the project **before** an object is renamed.
*
@@ -337,6 +365,16 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldObjectName,
const gd::String& newObjectName);
/**
* \brief Refactor events-based object events after the events-based object
* has been duplicated.
*/
static void UpdateObjectNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceObjectName);
/**
* \brief Refactor the project after a layout is renamed.
*/
@@ -506,6 +544,7 @@ class GD_CORE_API WholeProjectRefactorer {
gd::Project& project,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction& eventsFunction,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);
@@ -604,6 +643,18 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String &originLayerName,
const gd::String &targetLayerName);
/**
* \brief Replace the leaderboard ids in the whole project.
*/
static void
RenameLeaderboards(gd::Project &project,
const std::map<gd::String, gd::String> &leaderboardIdMap);
/**
* \brief Find all the leaderboard identifiers in the whole project.
*/
static std::set<gd::String> FindAllLeaderboardIds(gd::Project &project);
/**
* \brief Return the number of instances on the layer named \a layerName and
* all its associated layouts.
@@ -614,6 +665,12 @@ class GD_CORE_API WholeProjectRefactorer {
virtual ~WholeProjectRefactorer(){};
private:
static void ObjectOrGroupRenamedInScene(gd::Project &project,
gd::Layout &scene,
const gd::ObjectsContainer &targetedObjectsContainer,
const gd::String &oldName,
const gd::String &newName,
bool isObjectGroup);
static std::vector<gd::String> GetAssociatedExternalLayouts(
gd::Project& project, gd::Layout& layout);
static std::vector<gd::String>
@@ -654,6 +711,35 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newName,
const gd::ProjectBrowser& projectBrowser);
/**
* \brief Refactor the project **before** a behavior is renamed.
*
* \warning Do the renaming of the specified behavior after calling this.
* This is because the behavior is expected to have its old name for the
* refactoring.
*/
static void RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &oldBehaviorName,
const gd::String &newBehaviorName,
const gd::ProjectBrowser &projectBrowser);
/**
* \brief Refactor the project **before** an object is renamed.
*
* \warning Do the renaming of the specified object after calling this.
* This is because the object is expected to have its old name for the
* refactoring.
*/
static void RenameEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &oldObjectName, const gd::String &newObjectName,
const gd::ProjectBrowser &projectBrowser);
static void FindDependentBehaviorNames(
const gd::Project& project,
const gd::Object& object,

View File

@@ -6,6 +6,7 @@
#include "GDCore/Project/BehaviorConfigurationContainer.h"
#include <iostream>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
namespace gd {
@@ -22,4 +23,48 @@ std::map<gd::String, gd::PropertyDescriptor> BehaviorConfigurationContainer::Get
return nothing;
}
void BehaviorConfigurationContainer::ExposeResources(gd::ArbitraryResourceWorker& worker) {
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
for (auto& property : properties) {
const String& propertyName = property.first;
const gd::PropertyDescriptor& propertyDescriptor = property.second;
if (propertyDescriptor.GetType().LowerCase() == "resource") {
auto& extraInfo = propertyDescriptor.GetExtraInfo();
const gd::String& resourceType = extraInfo.empty() ? "" : extraInfo[0];
const gd::String& oldPropertyValue = propertyDescriptor.GetValue();
gd::String newPropertyValue = oldPropertyValue;
if (resourceType == "image") {
worker.ExposeImage(newPropertyValue);
} else if (resourceType == "audio") {
worker.ExposeAudio(newPropertyValue);
} else if (resourceType == "font") {
worker.ExposeFont(newPropertyValue);
} else if (resourceType == "video") {
worker.ExposeVideo(newPropertyValue);
} else if (resourceType == "json") {
worker.ExposeJson(newPropertyValue);
} else if (resourceType == "tilemap") {
worker.ExposeTilemap(newPropertyValue);
} else if (resourceType == "tileset") {
worker.ExposeTileset(newPropertyValue);
} else if (resourceType == "bitmapFont") {
worker.ExposeBitmapFont(newPropertyValue);
} else if (resourceType == "model3D") {
worker.ExposeModel3D(newPropertyValue);
} else if (resourceType == "atlas") {
worker.ExposeAtlas(newPropertyValue);
} else if (resourceType == "spine") {
worker.ExposeSpine(newPropertyValue);
}
if (newPropertyValue != oldPropertyValue) {
UpdateProperty(propertyName, newPropertyValue);
}
}
}
}
} // namespace gd

View File

@@ -3,13 +3,14 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_BEHAVIORCONFIGURATIONCONTAINER_H
#define GDCORE_BEHAVIORCONFIGURATIONCONTAINER_H
#pragma once
#include <map>
#include <memory>
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/String.h"
namespace gd {
@@ -17,6 +18,7 @@ class PropertyDescriptor;
class SerializerElement;
class Project;
class Layout;
class ArbitraryResourceWorker;
} // namespace gd
namespace gd {
@@ -32,12 +34,21 @@ namespace gd {
*/
class GD_CORE_API BehaviorConfigurationContainer {
public:
BehaviorConfigurationContainer() : folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
BehaviorConfigurationContainer()
: folded(false),
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
propertiesQuickCustomizationVisibilities() {};
BehaviorConfigurationContainer(const gd::String& name_,
const gd::String& type_)
: name(name_), type(type_), folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
: name(name_),
type(type_),
folded(false),
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
propertiesQuickCustomizationVisibilities() {};
virtual ~BehaviorConfigurationContainer();
virtual BehaviorConfigurationContainer* Clone() const { return new BehaviorConfigurationContainer(*this); }
virtual BehaviorConfigurationContainer* Clone() const {
return new BehaviorConfigurationContainer(*this);
}
/**
* \brief Return the name identifying the behavior
@@ -68,7 +79,6 @@ class GD_CORE_API BehaviorConfigurationContainer {
*/
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the
* behavior
@@ -84,9 +94,7 @@ class GD_CORE_API BehaviorConfigurationContainer {
* \brief Called to initialize the content with the default properties
* for the behavior.
*/
virtual void InitializeContent() {
InitializeContent(content);
};
virtual void InitializeContent() { InitializeContent(content); };
/**
* \brief Serialize the behavior content.
@@ -115,15 +123,54 @@ class GD_CORE_API BehaviorConfigurationContainer {
*/
bool IsFolded() const { return folded; }
void SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
/**
* @brief Set if the whole behavior should be visible or not in the Quick
* Customization.
*/
void SetQuickCustomizationVisibility(
QuickCustomization::Visibility visibility) {
quickCustomizationVisibility = visibility;
}
/**
* @brief Get if the whole behavior should be visible or not in the Quick
* Customization.
*/
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
return quickCustomizationVisibility;
}
protected:
/**
* @brief Get the map of properties and their visibility in the Quick
* Customization.
*/
QuickCustomizationVisibilitiesContainer&
GetPropertiesQuickCustomizationVisibilities() {
return propertiesQuickCustomizationVisibilities;
}
/**
* @brief Get the map of properties and their visibility in the Quick
* Customization.
*/
const QuickCustomizationVisibilitiesContainer&
GetPropertiesQuickCustomizationVisibilities() const {
return propertiesQuickCustomizationVisibilities;
}
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources and sometimes update their filename. Implementation example:
* \code
* worker.ExposeImage(myImage);
* worker.ExposeFile(myResourceFile);
* \endcode
*
* \see ArbitraryResourceWorker
*/
void ExposeResources(gd::ArbitraryResourceWorker& worker);
protected:
/**
* \brief Called when the IDE wants to know about the custom properties of the
* behavior.
@@ -159,7 +206,7 @@ protected:
* \brief Called to initialize the content with the default properties
* for the behavior.
*/
virtual void InitializeContent(gd::SerializerElement& behaviorContent){};
virtual void InitializeContent(gd::SerializerElement& behaviorContent) {};
private:
gd::String name; ///< Name of the behavior
@@ -169,8 +216,8 @@ protected:
gd::SerializerElement content; // Storage for the behavior properties
bool folded;
QuickCustomization::Visibility quickCustomizationVisibility;
QuickCustomizationVisibilitiesContainer
propertiesQuickCustomizationVisibilities;
};
} // namespace gd
#endif // GDCORE_BEHAVIORCONFIGURATIONCONTAINER_H

View File

@@ -26,7 +26,7 @@ void CustomConfigurationHelper::InitializeContent(
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource") {
propertyType == "Resource" || propertyType == "LeaderboardId") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
element.SetDoubleValue(property->GetValue().To<double>());
@@ -39,21 +39,21 @@ void CustomConfigurationHelper::InitializeContent(
std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetProperties(
const gd::PropertiesContainer &properties,
const gd::SerializerElement &configurationContent) {
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// Copy the property
behaviorProperties[propertyName] = *property;
objectProperties[propertyName] = *property;
auto &newProperty = behaviorProperties[propertyName];
auto &newProperty = objectProperties[propertyName];
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource") {
propertyType == "Resource" || propertyType == "LeaderboardId") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
@@ -71,7 +71,7 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
}
}
return behaviorProperties;
return objectProperties;
}
bool CustomConfigurationHelper::UpdateProperty(
@@ -89,7 +89,7 @@ bool CustomConfigurationHelper::UpdateProperty(
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource") {
propertyType == "Resource" || propertyType == "LeaderboardId") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());

View File

@@ -21,6 +21,9 @@ void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& object
project = objectConfiguration.project;
objectContent = objectConfiguration.objectContent;
animations = objectConfiguration.animations;
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
objectConfiguration
.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
childObjectConfigurations.clear();
@@ -42,6 +45,26 @@ const gd::EventsBasedObject* CustomObjectConfiguration::GetEventsBasedObject() c
return &project->GetEventsBasedObject(GetType());
}
bool CustomObjectConfiguration::
IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const {
const auto *eventsBasedObject = GetEventsBasedObject();
if (!eventsBasedObject) {
// True is safer because nothing will be lost when serializing.
return true;
}
return eventsBasedObject->GetInitialInstances().GetInstancesCount() == 0;
}
bool CustomObjectConfiguration::
IsOverridingEventsBasedObjectChildrenConfiguration() const {
return isMarkedAsOverridingEventsBasedObjectChildrenConfiguration ||
IsForcedToOverrideEventsBasedObjectChildrenConfiguration();
}
void CustomObjectConfiguration::ClearChildrenConfiguration() {
childObjectConfigurations.clear();
}
gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(const gd::String &objectName) {
const auto *eventsBasedObject = GetEventsBasedObject();
if (!eventsBasedObject) {
@@ -49,12 +72,24 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
}
if (!eventsBasedObject->GetObjects().HasObjectNamed(objectName)) {
gd::LogError("Tried to get the configuration of a child-object:" + objectName
gd::LogError("Tried to get the configuration of a child-object: " + objectName
+ " that doesn't exist in the event-based object: " + GetType());
return badObjectConfiguration;
}
auto &childObject = eventsBasedObject->GetObjects().GetObject(objectName);
if (!IsOverridingEventsBasedObjectChildrenConfiguration()) {
// It should be fine because the editor doesn't allow to edit values when
// the default values from the events-based object is used.
//
// Resource refactor operations may modify it but they will do the same
// thing on the custom object as on the event-based object children so it
// shouldn't have any side effect.
return const_cast<gd::ObjectConfiguration &>(
childObject.GetConfiguration());
}
auto configurationPosition = childObjectConfigurations.find(objectName);
if (configurationPosition == childObjectConfigurations.end()) {
childObjectConfigurations.insert(std::make_pair(
@@ -67,7 +102,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
auto &configuration = pair.second;
return *configuration;
}
}
}
std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetProperties() const {
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
@@ -123,31 +158,21 @@ bool CustomObjectConfiguration::UpdateInitialInstanceProperty(
void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const {
element.AddChild("content") = objectContent;
if (!animations.HasNoAnimations()) {
const auto *eventsBasedObject = GetEventsBasedObject();
if (!animations.HasNoAnimations() ||
(eventsBasedObject && eventsBasedObject->IsAnimatable())) {
auto &animatableElement = element.AddChild("animatable");
animations.SerializeTo(animatableElement);
}
auto &childrenContentElement = element.AddChild("childrenContent");
for (auto &pair : childObjectConfigurations) {
auto &childName = pair.first;
auto &childConfiguration = pair.second;
auto &childElement = childrenContentElement.AddChild(childName);
childConfiguration->SerializeTo(childElement);
}
const auto *eventsBasedObject = GetEventsBasedObject();
if (eventsBasedObject) {
eventsBasedObject->GetInitialInstances().SerializeTo(
element.AddChild("instances"));
eventsBasedObject->GetLayers().SerializeLayersTo(
element.AddChild("layers"));
element.SetIntAttribute("areaMinX", eventsBasedObject->GetAreaMinX());
element.SetIntAttribute("areaMinY", eventsBasedObject->GetAreaMinY());
element.SetIntAttribute("areaMinZ", eventsBasedObject->GetAreaMinZ());
element.SetIntAttribute("areaMaxX", eventsBasedObject->GetAreaMaxX());
element.SetIntAttribute("areaMaxY", eventsBasedObject->GetAreaMaxY());
element.SetIntAttribute("areaMaxZ", eventsBasedObject->GetAreaMaxZ());
if (IsOverridingEventsBasedObjectChildrenConfiguration()) {
auto &childrenContentElement = element.AddChild("childrenContent");
for (auto &pair : childObjectConfigurations) {
auto &childName = pair.first;
auto &childConfiguration = pair.second;
auto &childElement = childrenContentElement.AddChild(childName);
childConfiguration->SerializeTo(childElement);
}
}
}
void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
@@ -159,12 +184,16 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
animations.UnserializeFrom(animatableElement);
}
auto &childrenContentElement = element.GetChild("childrenContent");
for (auto &pair : childrenContentElement.GetAllChildren()) {
auto &childName = pair.first;
auto &childElement = pair.second;
auto &childConfiguration = GetChildObjectConfiguration(childName);
childConfiguration.UnserializeFrom(project, *childElement);
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
element.HasChild("childrenContent");
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
auto &childrenContentElement = element.GetChild("childrenContent");
for (auto &pair : childrenContentElement.GetAllChildren()) {
auto &childName = pair.first;
auto &childElement = pair.second;
auto &childConfiguration = GetChildObjectConfiguration(childName);
childConfiguration.UnserializeFrom(project, *childElement);
}
}
}
@@ -175,8 +204,8 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
for (auto& property : properties) {
const String& propertyName = property.first;
const gd::PropertyDescriptor& propertyDescriptor = property.second;
if (propertyDescriptor.GetType() == "resource") {
const gd::PropertyDescriptor &propertyDescriptor = property.second;
if (propertyDescriptor.GetType().LowerCase() == "resource") {
auto& extraInfo = propertyDescriptor.GetExtraInfo();
const gd::String& resourceType = extraInfo.empty() ? "" : extraInfo[0];
const gd::String& oldPropertyValue = propertyDescriptor.GetValue();
@@ -245,3 +274,16 @@ const SpriteAnimationList& CustomObjectConfiguration::GetAnimations() const {
SpriteAnimationList& CustomObjectConfiguration::GetAnimations() {
return animations;
}
const gd::CustomObjectConfiguration::EdgeAnchor
CustomObjectConfiguration::GetEdgeAnchorFromString(const gd::String &value) {
return (value == _("Window left") || value == _("Window top"))
? gd::CustomObjectConfiguration::EdgeAnchor::MinEdge
: (value == _("Window right") || value == _("Window bottom"))
? gd::CustomObjectConfiguration::EdgeAnchor::MaxEdge
: value == _("Proportional")
? gd::CustomObjectConfiguration::EdgeAnchor::Proportional
: value == _("Window center")
? gd::CustomObjectConfiguration::EdgeAnchor::Center
: gd::CustomObjectConfiguration::EdgeAnchor::NoAnchor;
}

View File

@@ -9,6 +9,7 @@
#include <map>
#include <memory>
#include <unordered_set>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
@@ -30,7 +31,7 @@ namespace gd {
class CustomObjectConfiguration : public gd::ObjectConfiguration {
public:
CustomObjectConfiguration(const Project& project_, const String& type_)
: project(&project_) {
: project(&project_), isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(false) {
SetType(type_);
}
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
@@ -65,7 +66,22 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
gd::ObjectConfiguration &GetChildObjectConfiguration(const gd::String& objectName);
bool IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const;
bool IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() const {
return isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
}
void SetMarkedAsOverridingEventsBasedObjectChildrenConfiguration(
bool isOverridingEventsBasedObjectChildrenConfiguration_) {
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
isOverridingEventsBasedObjectChildrenConfiguration_;
}
void ClearChildrenConfiguration();
gd::ObjectConfiguration &
GetChildObjectConfiguration(const gd::String &objectName);
std::size_t GetAnimationsCount() const override;
@@ -83,6 +99,38 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
*/
SpriteAnimationList& GetAnimations();
enum EdgeAnchor {
NoAnchor = 0,
MinEdge = 1,
MaxEdge = 2,
Proportional = 3,
Center = 4,
};
static const gd::CustomObjectConfiguration::EdgeAnchor
GetEdgeAnchorFromString(const gd::String &value);
/**
* Check if a child object properties must be displayed as folded in the editor.
* This is only useful when the object can override its children configuration (which
* is something being deprecated).
*/
bool IsChildObjectFolded(const gd::String& childName) const {
return unfoldedChildren.find(childName) == unfoldedChildren.end();
}
/**
* Set if a child object properties must be displayed as folded in the editor.
* This is only useful when the object can override its children configuration (which
* is something being deprecated).
*/
void SetChildObjectFolded(const gd::String& childName, bool folded) {
if (!folded)
unfoldedChildren.insert(childName);
else
unfoldedChildren.erase(childName);
}
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
@@ -90,10 +138,15 @@ protected:
private:
const gd::EventsBasedObject* GetEventsBasedObject() const;
const Project* project; ///< The project is used to get the
///< EventBasedObject from the fullType.
bool IsOverridingEventsBasedObjectChildrenConfiguration() const;
const Project* project = nullptr; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
std::unordered_set<gd::String> unfoldedChildren;
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration = false;
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
static gd::ObjectConfiguration badObjectConfiguration;

View File

@@ -12,6 +12,7 @@ namespace gd {
void Effect::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("effectType", GetEffectType());
if (IsFolded()) element.SetBoolAttribute("folded", true);
SerializerElement& doubleParametersElement =
element.AddChild("doubleParameters");
@@ -41,6 +42,7 @@ void Effect::UnserializeFrom(const SerializerElement& element) {
"effectName"
// end of compatibility code
));
SetFolded(element.GetBoolAttribute("folded", false));
doubleParameters.clear();
const SerializerElement& doubleParametersElement =

View File

@@ -21,8 +21,8 @@ namespace gd {
*/
class GD_CORE_API Effect {
public:
Effect(){};
virtual ~Effect(){};
Effect() : folded(false) {};
virtual ~Effect() {};
void SetName(const gd::String& name_) { name = name_; }
const gd::String& GetName() const { return name; }
@@ -32,6 +32,9 @@ class GD_CORE_API Effect {
}
const gd::String& GetEffectType() const { return effectType; }
void SetFolded(bool fold = true) { folded = fold; }
bool IsFolded() const { return folded; }
void SetDoubleParameter(const gd::String& name, double value) {
doubleParameters[name] = value;
}
@@ -85,6 +88,7 @@ class GD_CORE_API Effect {
void UnserializeFrom(const SerializerElement& element);
private:
bool folded;
gd::String name; ///< The name of the layer.
gd::String effectType; ///< The name of the effect to apply.
std::map<gd::String, double> doubleParameters; ///< Values of parameters being doubles, keyed by names.

View File

@@ -16,12 +16,15 @@ EventsBasedObject::EventsBasedObject()
isRenderedIn3D(false),
isAnimatable(false),
isTextContainer(false),
isInnerAreaFollowingParentSize(false),
isUsingLegacyInstancesRenderer(false),
areaMinX(0),
areaMinY(0),
areaMinZ(0),
areaMaxX(64),
areaMaxY(64),
areaMaxZ(64) {
areaMaxZ(64),
objectsContainer(gd::ObjectsContainer::SourceType::Object) {
}
EventsBasedObject::~EventsBasedObject() {}
@@ -37,6 +40,10 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
if (isTextContainer) {
element.SetBoolAttribute("isTextContainer", true);
}
if (isInnerAreaFollowingParentSize) {
element.SetBoolAttribute("isInnerAreaFollowingParentSize", true);
}
element.SetBoolAttribute("isUsingLegacyInstancesRenderer", isUsingLegacyInstancesRenderer);
element.SetIntAttribute("areaMinX", areaMinX);
element.SetIntAttribute("areaMinY", areaMinY);
element.SetIntAttribute("areaMinZ", areaMinZ);
@@ -59,6 +66,8 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
isAnimatable = element.GetBoolAttribute("isAnimatable", false);
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
isInnerAreaFollowingParentSize =
element.GetBoolAttribute("isInnerAreaFollowingParentSize", false);
areaMinX = element.GetIntAttribute("areaMinX", 0);
areaMinY = element.GetIntAttribute("areaMinY", 0);
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
@@ -82,6 +91,15 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
}
initialInstances.UnserializeFrom(element.GetChild("instances"));
if (element.HasChild("isUsingLegacyInstancesRenderer")) {
isUsingLegacyInstancesRenderer =
element.GetBoolAttribute("isUsingLegacyInstancesRenderer", false);
}
else {
// Compatibility with GD <= 5.4.212
isUsingLegacyInstancesRenderer = initialInstances.GetInstancesCount() == 0;
// end of compatibility code
}
}
} // namespace gd

View File

@@ -101,11 +101,53 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
/**
* \brief Declare a TextContainer capability.
*/
EventsBasedObject& MarkAsTextContainer(bool isTextContainer_) {
EventsBasedObject &MarkAsTextContainer(bool isTextContainer_) {
isTextContainer = isTextContainer_;
return *this;
}
/**
* \brief Declare that the parent scale will always be 1 and children will
* adapt there size. This is removing the ScalableCapability.
*/
EventsBasedObject &
MarkAsInnerAreaFollowingParentSize(bool isInnerAreaExpandingWithParent_) {
isInnerAreaFollowingParentSize = isInnerAreaExpandingWithParent_;
return *this;
}
/**
* \brief Return true if objects handle size changes on their own and
* don't have the ScalableCapability.
*
* When the parent dimensions change:
* - if `false`, the object is stretch proportionally while children local
* positions stay the same.
* - if `true`, the children local positions need to be adapted by events
* to follow their parent size.
*/
bool IsInnerAreaFollowingParentSize() const {
return isInnerAreaFollowingParentSize;
}
/**
* \brief Declare that custom object are rendered using their child-objects
* instead of their child-instances.
*/
EventsBasedObject &
MakAsUsingLegacyInstancesRenderer(bool isUsingLegacyInstancesRenderer_) {
isUsingLegacyInstancesRenderer = isUsingLegacyInstancesRenderer_;
return *this;
}
/**
* \brief Return true if custom object are rendered using their child-objects
* instead of their child-instances.
*/
bool IsUsingLegacyInstancesRenderer() const {
return isUsingLegacyInstancesRenderer;
}
/**
* \brief Return true if the object needs a TextContainer capability.
*/
@@ -279,6 +321,8 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
bool isRenderedIn3D;
bool isAnimatable;
bool isTextContainer;
bool isInnerAreaFollowingParentSize;
bool isUsingLegacyInstancesRenderer;
gd::InitialInstancesContainer initialInstances;
gd::LayersContainer layers;
gd::ObjectsContainer objectsContainer;

View File

@@ -10,6 +10,7 @@
#include "EventsFunction.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Extensions/PlatformExtension.h"
namespace gd {
@@ -98,8 +99,11 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
void EventsFunctionsExtension::UnserializeFrom(
gd::Project& project, const SerializerElement& element) {
UnserializeExtensionDeclarationFrom(project, element);
UnserializeExtensionImplementationFrom(project, element);
// Unserialize first the "declaration" (everything but objects content)
// so that objects can be then unserialized in proper order (they can depend
// on each others)
UnserializeExtensionDeclarationFrom(project, element);
UnserializeExtensionImplementationFrom(project, element);
}
void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
@@ -162,6 +166,7 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
// As event based objects can contains objects using CustomBehavior and/or
// CustomObject, this allows them to reference EventBasedBehavior and
// EventBasedObject respectively.
eventsBasedBehaviors.Clear();
auto &behaviorsElement = element.GetChild("eventsBasedBehaviors");
behaviorsElement.ConsiderAsArrayOf("eventsBasedBehavior");
for (std::size_t i = 0; i < behaviorsElement.GetChildrenCount(); ++i) {
@@ -169,6 +174,7 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
behaviorsElement.GetChild(i).GetStringAttribute("name");
eventsBasedBehaviors.InsertNew(behaviorName, eventsBasedBehaviors.GetCount());
}
eventsBasedObjects.Clear();
auto &objectsElement = element.GetChild("eventsBasedObjects");
objectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t i = 0; i < objectsElement.GetChildrenCount(); ++i) {
@@ -185,11 +191,93 @@ void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
eventsBasedBehaviors.UnserializeElementsFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
// It's important to load the objects without erasing them first as each object
// might reference other objects, and so need to know if a custom object exists
// in the project or not.
eventsBasedObjects.ProgressivelyUnserializeElementsFrom(
"eventsBasedObject", project, element.GetChild("eventsBasedObjects"));
auto &eventsBasedObjectsElement = element.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (gd::String &eventsBasedObjectName :
GetUnserializingOrderEventsBasedObjectNames(eventsBasedObjectsElement)) {
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);
eventsBasedObjects.at(extensionIndex)
.UnserializeFrom(project, eventsBasedObjectElement);
}
}
std::vector<gd::String>
EventsFunctionsExtension::GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement) {
// Child-objects need the event-based objects they use to be loaded completely
// before they are unserialized.
// At the beginning, everything is yet to be loaded.
std::vector<gd::String> remainingEventsBasedObjectNames(
eventsBasedObjects.size());
for (std::size_t i = 0; i < eventsBasedObjects.size(); ++i) {
remainingEventsBasedObjectNames[i] = eventsBasedObjects.at(i).GetName();
}
// Helper allowing to find if an object depends on at least one other object from
// the extension that is not loaded yet.
auto &extensionName = name;
auto isDependentFromRemainingEventsBasedObjects =
[&remainingEventsBasedObjectNames,
&extensionName](const gd::SerializerElement &eventsBasedObjectElement) {
auto &objectsElement = eventsBasedObjectElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
gd::String usedExtensionName =
PlatformExtension::GetExtensionFromFullObjectType(objectType);
if (usedExtensionName != extensionName) {
// The object comes from another extension: the project is already responsible
// for loading extensions in the proper order.
continue;
}
gd::String eventsBasedObjectName =
gd::PlatformExtension::GetObjectNameFromFullObjectType(
objectType);
if (std::find(remainingEventsBasedObjectNames.begin(),
remainingEventsBasedObjectNames.end(),
eventsBasedObjectName) !=
remainingEventsBasedObjectNames.end()) {
return true;
}
}
return false;
};
// Find the order of loading so that the objects are loaded when all the objects
// they depend on are already loaded.
std::vector<gd::String> loadOrderEventsBasedObjectNames;
bool foundAnyEventsBasedObject = true;
while (foundAnyEventsBasedObject) {
foundAnyEventsBasedObject = false;
for (std::size_t i = 0; i < remainingEventsBasedObjectNames.size(); ++i) {
auto eventsBasedObjectName = remainingEventsBasedObjectNames[i];
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);
if (!isDependentFromRemainingEventsBasedObjects(
eventsBasedObjectElement)) {
loadOrderEventsBasedObjectNames.push_back(eventsBasedObjectName);
remainingEventsBasedObjectNames.erase(
remainingEventsBasedObjectNames.begin() + i);
i--;
foundAnyEventsBasedObject = true;
}
}
}
return loadOrderEventsBasedObjectNames;
}
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(

View File

@@ -314,6 +314,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
return dependency;
}
std::vector<gd::String> GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement);
gd::String version;
gd::String extensionNamespace;
gd::String shortDescription;

View File

@@ -7,7 +7,7 @@
namespace gd {
ExternalEvents::ExternalEvents() : lastChangeTimeStamp(0) {
ExternalEvents::ExternalEvents() {
// ctor
}
@@ -24,14 +24,12 @@ ExternalEvents& ExternalEvents::operator=(const ExternalEvents& rhs) {
void ExternalEvents::Init(const ExternalEvents& externalEvents) {
name = externalEvents.GetName();
associatedScene = externalEvents.GetAssociatedLayout();
lastChangeTimeStamp = externalEvents.GetLastChangeTimeStamp();
events = externalEvents.events;
}
void ExternalEvents::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
element.SetAttribute("associatedLayout", associatedScene);
element.SetAttribute("lastChangeTimeStamp", (int)lastChangeTimeStamp);
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
@@ -41,8 +39,6 @@ void ExternalEvents::UnserializeFrom(gd::Project& project,
name = element.GetStringAttribute("name", "", "Name");
associatedScene =
element.GetStringAttribute("associatedLayout", "", "AssociatedScene");
lastChangeTimeStamp =
element.GetIntAttribute("lastChangeTimeStamp", 0, "LastChangeTimeStamp");
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}

View File

@@ -67,24 +67,6 @@ class GD_CORE_API ExternalEvents {
associatedScene = name_;
};
/**
* Get the latest time of the build.
* Used when the IDE found that the external events can be compiled separately
* from scene's events.
*
* \todo This is specific to GD C++ Platform
*/
time_t GetLastChangeTimeStamp() const { return lastChangeTimeStamp; };
/**
* Change the latest time of the build of the external events.
*
* \todo This is specific to GD C++ Platform
*/
void SetLastChangeTimeStamp(time_t newTimeStamp) {
lastChangeTimeStamp = newTimeStamp;
};
/**
* \brief Get the events.
*/
@@ -109,7 +91,6 @@ class GD_CORE_API ExternalEvents {
private:
gd::String name;
gd::String associatedScene;
time_t lastChangeTimeStamp; ///< Time of the last build
gd::EventsList events; ///< List of events
/**
@@ -119,19 +100,6 @@ class GD_CORE_API ExternalEvents {
void Init(const ExternalEvents& externalEvents);
};
/**
* \brief Functor testing ExternalEvents' name
*/
struct ExternalEventsHasName
: public std::binary_function<std::unique_ptr<gd::ExternalEvents>,
gd::String,
bool> {
bool operator()(const std::unique_ptr<gd::ExternalEvents>& externalEvents,
gd::String name) const {
return externalEvents->GetName() == name;
}
};
} // namespace gd
#endif // GDCORE_EXTERNALEVENTS_H

View File

@@ -96,19 +96,6 @@ class GD_CORE_API ExternalLayout {
gd::String associatedLayout;
};
/**
* \brief Functor testing ExternalLayout' name
*/
struct ExternalLayoutHasName
: public std::binary_function<std::unique_ptr<gd::ExternalLayout>,
gd::String,
bool> {
bool operator()(const std::unique_ptr<gd::ExternalLayout>& externalLayout,
gd::String name) const {
return externalLayout->GetName() == name;
}
};
} // namespace gd
#endif // GDCORE_EXTERNALLAYOUT_H

View File

@@ -27,7 +27,11 @@ InitialInstance::InitialInstance()
rotationX(0),
rotationY(0),
zOrder(0),
opacity(255),
layer(""),
flippedX(false),
flippedY(false),
flippedZ(false),
customSize(false),
customDepth(false),
width(0),
@@ -57,7 +61,11 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
SetHasCustomDepth(false);
}
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
SetOpacity(element.GetIntAttribute("opacity", 255));
SetLayer(element.GetStringAttribute("layer"));
SetFlippedX(element.GetBoolAttribute("flippedX", false));
SetFlippedY(element.GetBoolAttribute("flippedY", false));
SetFlippedZ(element.GetBoolAttribute("flippedZ", false));
SetLocked(element.GetBoolAttribute("locked", false));
SetSealed(element.GetBoolAttribute("sealed", false));
SetShouldKeepRatio(element.GetBoolAttribute("keepRatio", false));
@@ -113,6 +121,10 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
element.SetAttribute("y", GetY());
if (GetZ() != 0) element.SetAttribute("z", GetZ());
element.SetAttribute("zOrder", GetZOrder());
if (GetOpacity() != 255) element.SetAttribute("opacity", GetOpacity());
if (IsFlippedX()) element.SetAttribute("flippedX", IsFlippedX());
if (IsFlippedY()) element.SetAttribute("flippedY", IsFlippedY());
if (IsFlippedZ()) element.SetAttribute("flippedZ", IsFlippedZ());
element.SetAttribute("layer", GetLayer());
element.SetAttribute("angle", GetAngle());
if (GetRotationX() != 0) element.SetAttribute("rotationX", GetRotationX());
@@ -155,8 +167,8 @@ InitialInstance& InitialInstance::ResetPersistentUuid() {
std::map<gd::String, gd::PropertyDescriptor>
InitialInstance::GetCustomProperties(
gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer) {
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer) {
// Find an object
if (objectsContainer.HasObjectNamed(GetObjectName()))
return objectsContainer.GetObject(GetObjectName())
@@ -172,9 +184,10 @@ InitialInstance::GetCustomProperties(
}
bool InitialInstance::UpdateCustomProperty(
const gd::String &name, const gd::String &value,
gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer) {
const gd::String& name,
const gd::String& value,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer) {
if (objectsContainer.HasObjectNamed(GetObjectName()))
return objectsContainer.GetObject(GetObjectName())
.GetConfiguration()

View File

@@ -29,7 +29,7 @@ class GD_CORE_API InitialInstance {
* \brief Create an initial instance pointing to no object, at position (0,0).
*/
InitialInstance();
virtual ~InitialInstance(){};
virtual ~InitialInstance() {};
/**
* Must return a pointer to a copy of the object. A such method is needed to
@@ -123,6 +123,46 @@ class GD_CORE_API InitialInstance {
*/
void SetZOrder(int zOrder_) { zOrder = zOrder_; }
/**
* \brief Get Opacity.
*/
int GetOpacity() const { return opacity; }
/**
* \brief Set the opacity of the instance.
*/
void SetOpacity(int opacity_) { opacity = opacity_; }
/**
* \brief Return true if the instance is flipped on X axis.
*/
bool IsFlippedX() const { return flippedX; }
/**
* \brief Set whether the instance is flipped on X axis.
*/
void SetFlippedX(bool flippedX_) { flippedX = flippedX_; }
/**
* \brief Return true if the instance is flipped on Y axis.
*/
bool IsFlippedY() const { return flippedY; }
/**
* \brief Set whether the instance is flipped on Y axis.
*/
void SetFlippedY(bool flippedY_) { flippedY = flippedY_; }
/**
* \brief Return true if the instance is flipped on Z axis.
*/
bool IsFlippedZ() const { return flippedZ; }
/**
* \brief Set whether the instance is flipped on Z axis.
*/
void SetFlippedZ(bool flippedZ_) { flippedZ = flippedZ_; }
/**
* \brief Get the layer the instance belongs to.
*/
@@ -134,8 +174,9 @@ class GD_CORE_API InitialInstance {
void SetLayer(const gd::String& layer_) { layer = layer_; }
/**
* \brief Return true if the instance has a width/height which is different from its
* object default width/height. This is independent from `HasCustomDepth`.
* \brief Return true if the instance has a width/height which is different
* from its object default width/height. This is independent from
* `HasCustomDepth`.
*
* \see gd::Object
*/
@@ -150,15 +191,13 @@ class GD_CORE_API InitialInstance {
bool HasCustomDepth() const { return customDepth; }
/**
* \brief Set whether the instance has a width/height which is different from its
* object default width/height or not.
* This is independent from `SetHasCustomDepth`.
* \brief Set whether the instance has a width/height which is different from
* its object default width/height or not. This is independent from
* `SetHasCustomDepth`.
*
* \see gd::Object
*/
void SetHasCustomSize(bool hasCustomSize_) {
customSize = hasCustomSize_;
}
void SetHasCustomSize(bool hasCustomSize_) { customSize = hasCustomSize_; }
/**
* \brief Set whether the instance has a depth which is different from its
@@ -264,18 +303,19 @@ class GD_CORE_API InitialInstance {
* \note Common properties ( name, position... ) do not need to be
* inserted in this map
*/
std::map<gd::String, gd::PropertyDescriptor>
GetCustomProperties(gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer);
std::map<gd::String, gd::PropertyDescriptor> GetCustomProperties(
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer);
/**
* \brief Update the property called \a name with the new \a value.
*
* \return false if the property could not be updated.
*/
bool UpdateCustomProperty(const gd::String &name, const gd::String &value,
gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer);
bool UpdateCustomProperty(const gd::String& name,
const gd::String& value,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer);
/**
* \brief Get the value of a double property stored in the instance.
@@ -343,6 +383,10 @@ class GD_CORE_API InitialInstance {
double rotationX; ///< Instance angle on X axis (for a 3D object)
double rotationY; ///< Instance angle on Y axis (for a 3D object)
int zOrder; ///< Instance Z order (for a 2D object)
int opacity; ///< Instance opacity
bool flippedX; ///< True if the instance is flipped on X axis
bool flippedY; ///< True if the instance is flipped on Y axis
bool flippedZ; ///< True if the instance is flipped on Z axis
gd::String layer; ///< Instance layer
bool customSize; ///< True if object has a custom width and height
bool customDepth; ///< True if object has a custom depth
@@ -352,13 +396,13 @@ class GD_CORE_API InitialInstance {
gd::VariablesContainer initialVariables; ///< Instance specific variables
bool locked; ///< True if the instance is locked
bool sealed; ///< True if the instance is sealed
bool keepRatio; ///< True if the instance's dimensions
/// should keep the same ratio.
bool keepRatio; ///< True if the instance's dimensions
/// should keep the same ratio.
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
/// useful for hot reloading.
static gd::String*
badStringPropertyValue; ///< Empty string returned by GetRawStringProperty
static gd::String* badStringPropertyValue; ///< Empty string returned by
///< GetRawStringProperty
};
} // namespace gd

View File

@@ -163,6 +163,20 @@ bool InitialInstancesContainer::HasInstancesOfObject(
});
}
bool InitialInstancesContainer::IsInstancesCountOfObjectGreaterThan(
const gd::String &objectName, const std::size_t minInstanceCount) const {
std::size_t count = 0;
for (const gd::InitialInstance &instance : initialInstances) {
if (instance.GetObjectName() == objectName) {
count++;
if (count > minInstanceCount) {
return true;
}
}
}
return false;
}
void InitialInstancesContainer::Create(
const InitialInstancesContainer& source) {
try {

View File

@@ -153,6 +153,13 @@ class GD_CORE_API InitialInstancesContainer {
*/
bool HasInstancesOfObject(const gd::String &objectName) const;
/**
* \brief Return true if there is at least N instances of the given object.
*/
bool
IsInstancesCountOfObjectGreaterThan(const gd::String &objectName,
const std::size_t minInstanceCount) const;
/**
* \brief Remove all instances
*/

View File

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

View File

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

View File

@@ -24,10 +24,11 @@
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/PolymorphicClone.h"
using namespace std;
@@ -35,7 +36,10 @@ namespace gd {
gd::BehaviorsSharedData Layout::badBehaviorSharedData("", "");
Layout::Layout(const Layout& other) { Init(other); }
Layout::Layout(const Layout &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Scene) {
Init(other);
}
Layout& Layout::operator=(const Layout& other) {
if (this != &other) Init(other);
@@ -43,7 +47,7 @@ Layout& Layout::operator=(const Layout& other) {
return *this;
}
Layout::~Layout(){};
Layout::~Layout() {};
Layout::Layout()
: backgroundColorR(209),
@@ -52,9 +56,8 @@ Layout::Layout()
stopSoundsOnStartup(true),
standardSortMethod(true),
disableInputWhenNotFocused(true),
variables(gd::VariablesContainer::SourceType::Scene)
{
}
variables(gd::VariablesContainer::SourceType::Scene),
objectsContainer(gd::ObjectsContainer::SourceType::Scene) {}
void Layout::SetName(const gd::String& name_) {
name = name_;
@@ -102,7 +105,9 @@ const gd::Layer& Layout::GetLayer(const gd::String& name) const {
return layers.GetLayer(name);
}
gd::Layer& Layout::GetLayer(std::size_t index) { return layers.GetLayer(index); }
gd::Layer& Layout::GetLayer(std::size_t index) {
return layers.GetLayer(index);
}
const gd::Layer& Layout::GetLayer(std::size_t index) const {
return layers.GetLayer(index);
@@ -125,9 +130,7 @@ void Layout::InsertLayer(const gd::Layer& layer, std::size_t position) {
layers.InsertLayer(layer, position);
}
void Layout::RemoveLayer(const gd::String& name) {
layers.RemoveLayer(name);
}
void Layout::RemoveLayer(const gd::String& name) { layers.RemoveLayer(name); }
void Layout::SwapLayers(std::size_t firstLayerIndex,
std::size_t secondLayerIndex) {
@@ -153,7 +156,7 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
allBehaviorsNames.push_back(behavior.GetName());
}
}
auto &globalObjects = project.GetObjects();
auto& globalObjects = project.GetObjects();
for (std::size_t i = 0; i < globalObjects.GetObjectsCount(); ++i) {
std::vector<gd::String> objectBehaviors =
globalObjects.GetObject(i).GetAllBehaviorNames();
@@ -173,7 +176,8 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
if (behaviorsSharedData.find(name) != behaviorsSharedData.end()) continue;
auto sharedData = CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
auto sharedData =
CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
if (sharedData) {
behaviorsSharedData[name] = std::move(sharedData);
}
@@ -196,37 +200,39 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
}
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior shared data with an unknown type: " +
behaviorsType + " on object " + GetName() + "!");
gd::Project& project,
const gd::String& name,
const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
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 =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) return nullptr;
auto sharedData = behaviorsSharedDataBluePrint->Clone();
sharedData->SetName(name);
sharedData->SetTypeName(behaviorsType);
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
name, project, behaviorsType);
sharedData->InitializeContent();
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
return std::move(sharedData);
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) return nullptr;
auto sharedData = behaviorsSharedDataBluePrint->Clone();
sharedData->SetName(name);
sharedData->SetTypeName(behaviorsType);
sharedData->InitializeContent();
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
}
void Layout::SerializeTo(SerializerElement& element) const {
@@ -243,11 +249,13 @@ void Layout::SerializeTo(SerializerElement& element) const {
editorSettings.SerializeTo(element.AddChild("uiSettings"));
objectsContainer.GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
objectsContainer.GetObjectGroups().SerializeTo(
element.AddChild("objectsGroups"));
GetVariables().SerializeTo(element.AddChild("variables"));
GetInitialInstances().SerializeTo(element.AddChild("instances"));
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
objectsContainer.SerializeFoldersTo(
element.AddChild("objectsFolderStructure"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
@@ -257,15 +265,33 @@ void Layout::SerializeTo(SerializerElement& element) const {
element.AddChild("behaviorsSharedData");
behaviorDatasElement.ConsiderAsArrayOf("behaviorSharedData");
for (const auto& it : behaviorsSharedData) {
const gd::BehaviorsSharedData& sharedData = *it.second;
SerializerElement& dataElement =
behaviorDatasElement.AddChild("behaviorSharedData");
it.second->SerializeTo(dataElement);
sharedData.SerializeTo(dataElement);
dataElement.RemoveChild("type"); // The content can contain type or name
// properties, remove them.
dataElement.RemoveChild("name");
dataElement.SetAttribute("type", it.second->GetTypeName());
dataElement.SetAttribute("name", it.second->GetName());
dataElement.SetAttribute("type", sharedData.GetTypeName());
dataElement.SetAttribute("name", sharedData.GetName());
// Handle Quick Customization info.
dataElement.RemoveChild("propertiesQuickCustomizationVisibilities");
const QuickCustomizationVisibilitiesContainer&
propertiesQuickCustomizationVisibilities =
sharedData.GetPropertiesQuickCustomizationVisibilities();
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
propertiesQuickCustomizationVisibilities.SerializeTo(
dataElement.AddChild("propertiesQuickCustomizationVisibilities"));
}
const QuickCustomization::Visibility visibility =
sharedData.GetQuickCustomizationVisibility();
if (visibility != QuickCustomization::Visibility::Default) {
dataElement.SetAttribute(
"quickCustomizationVisibility",
QuickCustomization::VisibilityAsString(visibility));
}
}
}
@@ -289,9 +315,11 @@ void Layout::UnserializeFrom(gd::Project& project,
gd::EventsListSerialization::UnserializeEventsFrom(
project, GetEvents(), element.GetChild("events", 0, "Events"));
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
objectsContainer.UnserializeObjectsFrom(
project, element.GetChild("objects", 0, "Objets"));
if (element.HasChild("objectsFolderStructure")) {
objectsContainer.UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
objectsContainer.UnserializeFoldersFrom(
project, element.GetChild("objectsFolderStructure", 0));
}
objectsContainer.AddMissingObjectsInRootFolder();
@@ -321,7 +349,6 @@ void Layout::UnserializeFrom(gd::Project& project,
"Behavior"); // Compatibility with GD <= 4
gd::String name = sharedDataElement.GetStringAttribute("name", "", "Name");
auto sharedData = CreateBehaviorsSharedData(project, name, type);
if (sharedData) {
// Compatibility with GD <= 4.0.98
@@ -336,6 +363,21 @@ void Layout::UnserializeFrom(gd::Project& project,
else {
sharedData->UnserializeFrom(sharedDataElement);
}
// Handle Quick Customization info.
if (sharedDataElement.HasChild(
"propertiesQuickCustomizationVisibilities")) {
sharedData->GetPropertiesQuickCustomizationVisibilities()
.UnserializeFrom(sharedDataElement.GetChild(
"propertiesQuickCustomizationVisibilities"));
}
if (sharedDataElement.HasChild("quickCustomizationVisibility")) {
sharedData->SetQuickCustomizationVisibility(
QuickCustomization::StringAsVisibility(
sharedDataElement.GetStringAttribute(
"quickCustomizationVisibility")));
}
behaviorsSharedData[name] = std::move(sharedData);
}
}
@@ -390,8 +432,9 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
type = project.GetObject(name).GetType();
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
else if (searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
if (layout.GetObjectGroups()[i].GetName() == name) {
@@ -448,11 +491,12 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
return type;
}
void GD_CORE_API FilterBehaviorNamesFromObject(
const gd::Object &object, const gd::String &behaviorType,
std::vector<gd::String> &behaviorNames) {
void GD_CORE_API
FilterBehaviorNamesFromObject(const gd::Object& object,
const gd::String& behaviorType,
std::vector<gd::String>& behaviorNames) {
for (size_t i = 0; i < behaviorNames.size();) {
auto &behaviorName = behaviorNames[i];
auto& behaviorName = behaviorNames[i];
if (!object.HasBehaviorNamed(behaviorName) ||
object.GetBehavior(behaviorName).GetTypeName() != behaviorType) {
behaviorNames.erase(behaviorNames.begin() + i);
@@ -462,19 +506,21 @@ void GD_CORE_API FilterBehaviorNamesFromObject(
}
}
std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
const gd::ObjectsContainer &project, const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName, const gd::String &behaviorType,
bool searchInGroups) {
std::vector<gd::String> GD_CORE_API
GetBehaviorNamesInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorType,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
auto& object = layout.GetObject(objectOrGroupName);
auto behaviorNames = object.GetAllBehaviorNames();
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
auto& object = project.GetObject(objectOrGroupName);
auto behaviorNames = object.GetAllBehaviorNames();
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
@@ -486,9 +532,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -497,7 +544,7 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
std::vector<gd::String> behaviorNames;
return behaviorNames;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -510,15 +557,15 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
auto behaviorNames = GetBehaviorNamesInObjectOrGroup(
project, layout, groupsObjects[0], behaviorType, false);
for (size_t i = 1; i < groupsObjects.size(); i++) {
auto &objectName = groupsObjects[i];
auto& objectName = groupsObjects[i];
if (layout.HasObjectNamed(objectName)) {
auto &object = layout.GetObject(objectName);
auto& object = layout.GetObject(objectName);
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
if (project.HasObjectNamed(objectName)) {
auto &object = project.GetObject(objectName);
auto& object = project.GetObject(objectName);
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
@@ -529,10 +576,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
return behaviorNames;
}
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName,
const gd::String &behaviorName,
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
@@ -547,9 +594,10 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -557,7 +605,7 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
} else {
return false;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -566,9 +614,9 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
// Check that all objects have the behavior.
for (auto &&object : groupsObjects) {
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
false)) {
for (auto&& object : groupsObjects) {
if (!HasBehaviorInObjectOrGroup(
project, layout, object, behaviorName, false)) {
return false;
}
}
@@ -576,18 +624,18 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::String objectOrGroupName,
gd::String behaviorName,
bool searchInGroups) {
const gd::ObjectsContainer& layout,
gd::String objectOrGroupName,
gd::String behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
auto& object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) &&
object.GetBehavior(behaviorName).IsDefaultBehavior();
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
auto& object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) &&
object.GetBehavior(behaviorName).IsDefaultBehavior();
}
@@ -597,9 +645,10 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -607,7 +656,7 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
} else {
return false;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -616,30 +665,32 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
}
// Check that all objects have the same type.
for (auto &&object : groupsObjects) {
if (!IsDefaultBehavior(project, layout, object, behaviorName,
false)) {
for (auto&& object : groupsObjects) {
if (!IsDefaultBehavior(project, layout, object, behaviorName, false)) {
return false;
}
}
return true;
}
gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
gd::String GD_CORE_API
GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) ?
object.GetBehavior(behaviorName).GetTypeName() : "";
auto& object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName)
? object.GetBehavior(behaviorName).GetTypeName()
: "";
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) ?
object.GetBehavior(behaviorName).GetTypeName() : "";
auto& object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName)
? object.GetBehavior(behaviorName).GetTypeName()
: "";
}
if (!searchInGroups) {
@@ -647,9 +698,10 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -657,7 +709,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
} else {
return "";
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -668,9 +720,9 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
// Check that all objects have the behavior with the same type.
auto behaviorType = GetTypeOfBehaviorInObjectOrGroup(
project, layout, groupsObjects[0], behaviorName, false);
for (auto &&object : groupsObjects) {
if (GetTypeOfBehaviorInObjectOrGroup(project, layout, object, behaviorName,
false) != behaviorType) {
for (auto&& object : groupsObjects) {
if (GetTypeOfBehaviorInObjectOrGroup(
project, layout, object, behaviorName, false) != behaviorType) {
return "";
}
}
@@ -682,14 +734,14 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
gd::String name,
bool searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectsCount(); ++i) {
const auto &object = layout.GetObject(i);
const auto& object = layout.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
}
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
const auto &object = project.GetObject(i);
const auto& object = project.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
@@ -726,8 +778,9 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
}
// Search in groups
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
if (searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
if (layout.GetObjectGroups()[i].GetName() == name) {

View File

@@ -369,9 +369,9 @@ class GD_CORE_API Layout {
private:
gd::String name; ///< Scene name
gd::String mangledName; ///< The scene name mangled by SceneNameMangler
unsigned int backgroundColorR; ///< Background color Red component
unsigned int backgroundColorG; ///< Background color Green component
unsigned int backgroundColorB; ///< Background color Blue component
unsigned int backgroundColorR = 0; ///< Background color Red component
unsigned int backgroundColorG = 0; ///< Background color Green component
unsigned int backgroundColorB = 0; ///< Background color Blue component
gd::String title; ///< Title displayed in the window
gd::VariablesContainer variables; ///< Variables list
gd::ObjectsContainer objectsContainer;
@@ -379,12 +379,12 @@ class GD_CORE_API Layout {
gd::LayersContainer layers;
std::map<gd::String, std::unique_ptr<gd::BehaviorsSharedData>>
behaviorsSharedData; ///< Initial shared datas of behaviors
bool stopSoundsOnStartup; ///< True to make the scene stop all sounds at
///< startup.
bool standardSortMethod; ///< True to sort objects using standard sort.
bool disableInputWhenNotFocused; /// If set to true, the input must be
/// disabled when the window do not have the
/// focus.
bool stopSoundsOnStartup = true; ///< True to make the scene stop all sounds at
///< startup.
bool standardSortMethod = true; ///< True to sort objects using standard sort.
bool disableInputWhenNotFocused = true; /// If set to true, the input must be
/// disabled when the window do not have the
/// focus.
static gd::BehaviorsSharedData
badBehaviorSharedData; ///< Null object, returned when
///< GetBehaviorSharedData can not find the
@@ -405,18 +405,6 @@ class GD_CORE_API Layout {
const gd::String& behaviorsType);
};
/**
* \brief Functor testing layout name.
* \see gd::Layout
*/
struct LayoutHasName
: public std::binary_function<std::unique_ptr<Layout>, gd::String, bool> {
bool operator()(const std::unique_ptr<Layout>& layout,
gd::String name) const {
return layout->GetName() == name;
}
};
/**
* \brief Get the names of all layers from the given layout
* that are invisible.

View File

@@ -12,8 +12,9 @@
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/UUID/UUID.h"
@@ -27,8 +28,8 @@ Object::Object(const gd::String& name_,
: name(name_),
configuration(std::move(configuration_)),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_);
}
SetType(type_);
}
Object::Object(const gd::String& name_,
const gd::String& type_,
@@ -36,8 +37,8 @@ Object::Object(const gd::String& name_,
: name(name_),
configuration(configuration_),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_);
}
SetType(type_);
}
void Object::Init(const gd::Object& object) {
persistentUuid = object.persistentUuid;
@@ -54,9 +55,7 @@ void Object::Init(const gd::Object& object) {
configuration = object.configuration->Clone();
}
gd::ObjectConfiguration& Object::GetConfiguration() {
return *configuration;
}
gd::ObjectConfiguration& Object::GetConfiguration() { return *configuration; }
const gd::ObjectConfiguration& Object::GetConfiguration() const {
return *configuration;
@@ -77,8 +76,7 @@ bool Object::RenameBehavior(const gd::String& name, const gd::String& newName) {
behaviors.find(newName) != behaviors.end())
return false;
std::unique_ptr<Behavior> aut =
std::move(behaviors.find(name)->second);
std::unique_ptr<Behavior> aut = std::move(behaviors.find(name)->second);
behaviors.erase(name);
behaviors[newName] = std::move(aut);
behaviors[newName]->SetName(newName);
@@ -99,10 +97,10 @@ bool Object::HasBehaviorNamed(const gd::String& name) const {
}
gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name) {
auto initializeAndAdd =
[this, &name](std::unique_ptr<gd::Behavior> behavior) {
const gd::String& type,
const gd::String& name) {
auto initializeAndAdd = [this,
&name](std::unique_ptr<gd::Behavior> behavior) {
behavior->InitializeContent();
this->behaviors[name] = std::move(behavior);
return this->behaviors[name].get();
@@ -111,18 +109,17 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
if (project.HasEventsBasedBehavior(type)) {
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
}
else {
} else {
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
type);
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 behavior that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
gd::LogWarning("Tried to create a behavior with an unknown type: " +
type + " on object " + GetName() + "!");
// 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));
}
std::unique_ptr<gd::Behavior> behavior(behaviorMetadata.Get().Clone());
behavior->SetName(name);
@@ -196,6 +193,23 @@ void Object::UnserializeFrom(gd::Project& project,
else {
behavior->UnserializeFrom(behaviorElement);
}
bool isFolded = behaviorElement.GetBoolAttribute("isFolded", false);
behavior->SetFolded(isFolded);
// Handle Quick Customization info.
if (behaviorElement.HasChild(
"propertiesQuickCustomizationVisibilities")) {
behavior->GetPropertiesQuickCustomizationVisibilities().UnserializeFrom(
behaviorElement.GetChild(
"propertiesQuickCustomizationVisibilities"));
}
if (behaviorElement.HasChild("quickCustomizationVisibility")) {
behavior->SetQuickCustomizationVisibility(
QuickCustomization::StringAsVisibility(
behaviorElement.GetStringAttribute(
"quickCustomizationVisibility")));
}
}
}
@@ -217,8 +231,8 @@ void Object::SerializeTo(SerializerElement& element) const {
std::vector<gd::String> allBehaviors = GetAllBehaviorNames();
for (std::size_t i = 0; i < allBehaviors.size(); ++i) {
const gd::Behavior& behavior = GetBehavior(allBehaviors[i]);
// Default behaviors are added at the object creation according to metadata.
// They don't need to be serialized.
// Default behaviors are added at the object creation according to
// metadata. They don't need to be serialized.
if (behavior.IsDefaultBehavior()) {
continue;
}
@@ -228,8 +242,27 @@ void Object::SerializeTo(SerializerElement& element) const {
behaviorElement.RemoveChild("type"); // The content can contain type or
// name properties, remove them.
behaviorElement.RemoveChild("name");
behaviorElement.RemoveChild("isFolded");
behaviorElement.SetAttribute("type", behavior.GetTypeName());
behaviorElement.SetAttribute("name", behavior.GetName());
if (behavior.IsFolded()) behaviorElement.SetAttribute("isFolded", true);
// Handle Quick Customization info.
behaviorElement.RemoveChild("propertiesQuickCustomizationVisibilities");
const QuickCustomizationVisibilitiesContainer&
propertiesQuickCustomizationVisibilities =
behavior.GetPropertiesQuickCustomizationVisibilities();
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
propertiesQuickCustomizationVisibilities.SerializeTo(
behaviorElement.AddChild("propertiesQuickCustomizationVisibilities"));
}
const QuickCustomization::Visibility visibility =
behavior.GetQuickCustomizationVisibility();
if (visibility != QuickCustomization::Visibility::Default) {
behaviorElement.SetAttribute(
"quickCustomizationVisibility",
QuickCustomization::VisibilityAsString(visibility));
}
}
configuration->SerializeTo(element);

View File

@@ -193,7 +193,7 @@ class GD_CORE_API ObjectFolderOrObject {
static gd::ObjectFolderOrObject badObjectFolderOrObject;
gd::ObjectFolderOrObject*
parent; // nullptr if root folder, points to the parent folder otherwise.
parent = nullptr; // nullptr if root folder, points to the parent folder otherwise.
QuickCustomization::Visibility quickCustomizationVisibility;
// Representing an object:

View File

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

View File

@@ -16,7 +16,9 @@
namespace gd {
ObjectsContainer::ObjectsContainer() {
ObjectsContainer::ObjectsContainer(
const ObjectsContainer::SourceType sourceType_)
: sourceType(sourceType_) {
rootFolder = gd::make_unique<gd::ObjectFolderOrObject>("__ROOT");
}
@@ -34,6 +36,7 @@ ObjectsContainer& ObjectsContainer::operator=(
}
void ObjectsContainer::Init(const gd::ObjectsContainer& other) {
sourceType = other.sourceType;
initialObjects = gd::Clone(other.initialObjects);
objectGroups = other.objectGroups;
// The objects folders are not copied.
@@ -205,6 +208,14 @@ void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
objectFolderOrObject, newParentFolder, newPosition);
}
std::set<gd::String> ObjectsContainer::GetAllObjectNames() const {
std::set<gd::String> names;
for (const auto& object : initialObjects) {
names.insert(object->GetName());
}
return names;
}
std::vector<const ObjectFolderOrObject*>
ObjectsContainer::GetAllObjectFolderOrObjects() const {
std::vector<const ObjectFolderOrObject*> results;

View File

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

View File

@@ -416,7 +416,8 @@ gd::String ObjectsContainersList::GetTypeOfObject(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfObject(emptyObjectsContainer, *objectsContainers[0],
objectName, true);
}
@@ -439,7 +440,8 @@ bool ObjectsContainersList::HasBehaviorInObjectOrGroup(
return false;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::HasBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, true);
@@ -468,7 +470,8 @@ gd::String ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, searchInGroups);
@@ -495,7 +498,8 @@ gd::String ObjectsContainersList::GetTypeOfBehavior(
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetTypeOfBehavior(emptyObjectsContainer, *objectsContainers[0],
behaviorName, searchInGroups);
}
@@ -521,7 +525,8 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetBehaviorsOfObject(emptyObjectsContainer,
*objectsContainers[0], objectName,
searchInGroups);
@@ -530,6 +535,33 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
*objectsContainers[0], *objectsContainers[1], objectName, searchInGroups);
}
std::vector<gd::String> ObjectsContainersList::GetBehaviorNamesInObjectOrGroup(
const gd::String &objectOrGroupName, const gd::String &behaviorType, bool searchInGroups) const {
if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of
// containers.
gd::LogFatalError(
"ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called with objectsContainers "
"not being exactly 2. This is a logical error and will crash.");
}
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called without any "
"objectsContainer");
std::vector<gd::String> behaviors;
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetBehaviorNamesInObjectOrGroup(emptyObjectsContainer,
*objectsContainers[0], objectOrGroupName, behaviorType,
searchInGroups);
}
return gd::GetBehaviorNamesInObjectOrGroup(
*objectsContainers[0], *objectsContainers[1], objectOrGroupName, behaviorType, searchInGroups);
}
std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
const gd::String &objectOrGroupName) const {
std::vector<gd::String> animationNames;
@@ -589,4 +621,35 @@ std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
return animationNames;
}
const ObjectsContainer *
ObjectsContainersList::GetObjectsContainerFromObjectName(
const gd::String &objectOrGroupName) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
++it) {
if ((*it)->HasObjectNamed(objectOrGroupName) ||
(*it)->GetObjectGroups().Has(objectOrGroupName)) {
return *it;
}
}
return nullptr;
}
const gd::ObjectsContainer::SourceType
ObjectsContainersList::GetObjectsContainerSourceType(
const gd::String &objectOrGroupName) const {
const auto *objectsContainer =
GetObjectsContainerFromObjectName(objectOrGroupName);
return objectsContainer ? objectsContainer->GetSourceType()
: gd::ObjectsContainer::SourceType::Unknown;
}
const gd::ObjectsContainer &
ObjectsContainersList::GetObjectsContainer(std::size_t index) const {
return *objectsContainers[index];
}
std::size_t ObjectsContainersList::GetObjectsContainersCount() const {
return objectsContainers.size();
}
} // namespace gd

View File

@@ -2,12 +2,12 @@
#include <vector>
#include "Variable.h"
#include "GDCore/Project/ObjectsContainer.h"
namespace gd {
class String;
class Project;
class Layout;
class ObjectsContainer;
class VariablesContainer;
class Object;
class ObjectConfiguration;
@@ -50,6 +50,11 @@ class GD_CORE_API ObjectsContainersList {
*/
bool HasObjectOrGroupNamed(const gd::String& name) const;
/**
* \brief Check if the specified object exists ignoring groups.
*/
bool HasObjectNamed(const gd::String& name) const;
enum VariableExistence {
DoesNotExist,
Exists,
@@ -128,6 +133,18 @@ class GD_CORE_API ObjectsContainersList {
std::vector<gd::String> GetBehaviorsOfObject(
const gd::String& objectName, bool searchInGroups = true) const;
/**
* \brief Get behaviors of an object/group of a given behavior type.
* \note The behaviors of a group are the behaviors which are found in common
* when looking all the objects of the group.
*
* @return Vector containing names of behaviors
*/
std::vector<gd::String>
GetBehaviorNamesInObjectOrGroup(const gd::String &objectOrGroupName,
const gd::String &behaviorType,
bool searchInGroups = true) const;
/**
* \brief Get the animation names of an object/group.
* \note The animation names of a group are the animation names common to
@@ -173,13 +190,41 @@ class GD_CORE_API ObjectsContainersList {
std::function<void(const gd::String& variableName,
const gd::Variable& variable)> fn) const;
/**
* \brief Return the source type of the container for the specified object or
* group of objects.
*/
const gd::ObjectsContainer::SourceType GetObjectsContainerSourceType(
const gd::String& objectOrGroupName) const;
/**
* Get the objects container for for the specified object or group of objects.
*/
const ObjectsContainer *
GetObjectsContainerFromObjectName(const gd::String &objectOrGroupName) const;
/**
* \brief Return a the objects container at position \a index.
*
* \warning The returned `ObjectsContainer` may contain cloned objects (in the case of
* `ProjectScopedContainers::MakeNewProjectScopedContainersForEventsBasedObject`)
* or "fake" objects used by events like parameters. They must not be used to
* edit the objects.
* Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
* in the codebase.
*/
const gd::ObjectsContainer &GetObjectsContainer(std::size_t index) const;
/**
* \brief Return the number of objects containers.
*/
std::size_t GetObjectsContainersCount() const;
/** Do not use - should be private but accessible to let Emscripten create a
* temporary. */
ObjectsContainersList(){};
private:
bool HasObjectNamed(const gd::String& name) const;
const gd::Object* GetObject(const gd::String& name) const;
bool HasObjectWithVariableNamed(const gd::String& objectName,

View File

@@ -71,23 +71,22 @@ Project::Project()
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
currentPlatform(NULL),
currentPlatform(nullptr),
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()),
variables(gd::VariablesContainer::SourceType::Global) {}
variables(gd::VariablesContainer::SourceType::Global),
objectsContainer(gd::ObjectsContainer::SourceType::Global) {}
Project::~Project() {}
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));
void Project::EnsureObjectDefaultBehaviors(gd::Object& object) const {
auto& platform = GetCurrentPlatform();
auto& project = *this;
auto& objectType = object.GetType();
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
const gd::String& behaviorType) {
auto& behaviorMetadata =
@@ -97,17 +96,47 @@ std::unique_ptr<gd::Object> Project::CreateObject(
" has an unknown default behavior: " + behaviorType);
return;
}
auto* behavior = object->AddNewBehavior(
project, behaviorType, behaviorMetadata.GetDefaultName());
behavior->SetDefaultBehavior(true);
const gd::String& behaviorName = behaviorMetadata.GetDefaultName();
// Check if we can keep a behavior that would have been already set up on the object.
if (object.HasBehaviorNamed(behaviorName)) {
const auto& behavior = object.GetBehavior(behaviorName);
if (!behavior.IsDefaultBehavior() || behavior.GetTypeName() != behaviorType) {
// Behavior type has changed, remove it so it is re-created.
object.RemoveBehavior(behaviorName);
}
}
if (!object.HasBehaviorNamed(behaviorName)) {
auto* behavior = object.AddNewBehavior(
project, behaviorType, behaviorName);
behavior->SetDefaultBehavior(true);
}
};
auto &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (!MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
// Add all default behaviors.
const auto& defaultBehaviorTypes = objectMetadata.GetDefaultBehaviors();
for (auto &behaviorType : defaultBehaviorTypes) {
addDefaultBehavior(behaviorType);
}
// Ensure there are no default behaviors that would not be required left on the object.
for (const auto& behaviorName : object.GetAllBehaviorNames()) {
auto& behavior = object.GetBehavior(behaviorName);
if (!behavior.IsDefaultBehavior()) {
// Non default behaviors are not handled by this function.
continue;
}
if (defaultBehaviorTypes.find(behavior.GetTypeName()) == defaultBehaviorTypes.end()) {
object.RemoveBehavior(behaviorName);
}
}
}
// During project deserialization, event-based object metadata are not yet
// generated. Default behaviors will be added by
@@ -115,6 +144,14 @@ std::unique_ptr<gd::Object> Project::CreateObject(
else if (!project.HasEventsBasedObject(objectType)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
}
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));
EnsureObjectDefaultBehaviors(*object);
return std::move(object);
}
@@ -264,15 +301,21 @@ bool Project::RemovePlatform(const gd::String& platformName) {
bool Project::HasLayoutNamed(const gd::String& name) const {
return (find_if(scenes.begin(),
scenes.end(),
bind2nd(gd::LayoutHasName(), name)) != scenes.end());
[&name](const std::unique_ptr<gd::Layout>& layout) {
return layout->GetName() == name;
}) != scenes.end());
}
gd::Layout& Project::GetLayout(const gd::String& name) {
return *(*find_if(
scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name)));
scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
return layout->GetName() == name;
}));
}
const gd::Layout& Project::GetLayout(const gd::String& name) const {
return *(*find_if(
scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name)));
scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
return layout->GetName() == name;
}));
}
gd::Layout& Project::GetLayout(std::size_t index) { return *scenes[index]; }
const gd::Layout& Project::GetLayout(std::size_t index) const {
@@ -317,7 +360,9 @@ gd::Layout& Project::InsertLayout(const gd::Layout& layout,
void Project::RemoveLayout(const gd::String& name) {
std::vector<std::unique_ptr<gd::Layout> >::iterator scene =
find_if(scenes.begin(), scenes.end(), bind2nd(gd::LayoutHasName(), name));
find_if(scenes.begin(), scenes.end(), [&name](const std::unique_ptr<gd::Layout>& layout) {
return layout->GetName() == name;
});
if (scene == scenes.end()) return;
scenes.erase(scene);
@@ -326,19 +371,24 @@ void Project::RemoveLayout(const gd::String& name) {
bool Project::HasExternalEventsNamed(const gd::String& name) const {
return (find_if(externalEvents.begin(),
externalEvents.end(),
bind2nd(gd::ExternalEventsHasName(), name)) !=
externalEvents.end());
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
return externalEvents->GetName() == name;
}) != externalEvents.end());
}
gd::ExternalEvents& Project::GetExternalEvents(const gd::String& name) {
return *(*find_if(externalEvents.begin(),
externalEvents.end(),
bind2nd(gd::ExternalEventsHasName(), name)));
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
return externalEvents->GetName() == name;
}));
}
const gd::ExternalEvents& Project::GetExternalEvents(
const gd::String& name) const {
return *(*find_if(externalEvents.begin(),
externalEvents.end(),
bind2nd(gd::ExternalEventsHasName(), name)));
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
return externalEvents->GetName() == name;
}));
}
gd::ExternalEvents& Project::GetExternalEvents(std::size_t index) {
return *externalEvents[index];
@@ -382,7 +432,9 @@ void Project::RemoveExternalEvents(const gd::String& name) {
std::vector<std::unique_ptr<gd::ExternalEvents> >::iterator events =
find_if(externalEvents.begin(),
externalEvents.end(),
bind2nd(gd::ExternalEventsHasName(), name));
[&name](const std::unique_ptr<gd::ExternalEvents>& externalEvents) {
return externalEvents->GetName() == name;
});
if (events == externalEvents.end()) return;
externalEvents.erase(events);
@@ -448,19 +500,24 @@ void Project::SwapExternalLayouts(std::size_t first, std::size_t second) {
bool Project::HasExternalLayoutNamed(const gd::String& name) const {
return (find_if(externalLayouts.begin(),
externalLayouts.end(),
bind2nd(gd::ExternalLayoutHasName(), name)) !=
externalLayouts.end());
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
return externalLayout->GetName() == name;
}) != externalLayouts.end());
}
gd::ExternalLayout& Project::GetExternalLayout(const gd::String& name) {
return *(*find_if(externalLayouts.begin(),
externalLayouts.end(),
bind2nd(gd::ExternalLayoutHasName(), name)));
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
return externalLayout->GetName() == name;
}));
}
const gd::ExternalLayout& Project::GetExternalLayout(
const gd::String& name) const {
return *(*find_if(externalLayouts.begin(),
externalLayouts.end(),
bind2nd(gd::ExternalLayoutHasName(), name)));
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
return externalLayout->GetName() == name;
}));
}
gd::ExternalLayout& Project::GetExternalLayout(std::size_t index) {
return *externalLayouts[index];
@@ -504,7 +561,9 @@ void Project::RemoveExternalLayout(const gd::String& name) {
std::vector<std::unique_ptr<gd::ExternalLayout> >::iterator externalLayout =
find_if(externalLayouts.begin(),
externalLayouts.end(),
bind2nd(gd::ExternalLayoutHasName(), name));
[&name](const std::unique_ptr<gd::ExternalLayout>& externalLayout) {
return externalLayout->GetName() == name;
});
if (externalLayout == externalLayouts.end()) return;
externalLayouts.erase(externalLayout);
@@ -805,34 +864,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
eventsFunctionsExtensions.clear();
const SerializerElement& eventsFunctionsExtensionsElement =
element.GetChild("eventsFunctionsExtensions");
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// First, only unserialize behaviors and objects names.
// As event based objects can contains CustomObject and Custom Object,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
gd::EventsFunctionsExtension& newEventsFunctionsExtension =
InsertNewEventsFunctionsExtension("",
GetEventsFunctionsExtensionsCount());
newEventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
eventsFunctionsExtensions.at(i)->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
UnserializeAndInsertExtensionsFrom(eventsFunctionsExtensionsElement);
objectsContainer.GetObjectGroups().UnserializeFrom(
element.GetChild("objectsGroups", 0, "ObjectGroups"));
@@ -900,6 +932,120 @@ void Project::UnserializeFrom(const SerializerElement& element) {
}
}
void Project::UnserializeAndInsertExtensionsFrom(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// First, only unserialize behaviors and objects names.
// As event based objects can contains custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
gd::EventsFunctionsExtension& eventsFunctionsExtension =
HasEventsFunctionsExtensionNamed(name)
? GetEventsFunctionsExtension(name)
: InsertNewEventsFunctionsExtension(
name, GetEventsFunctionsExtensionsCount());
eventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensions.at(extensionIndex)
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
}
std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
// Some extension have custom objects, which have child objects coming from other extension.
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
// This implies: an order on the extension unserialization (and no cycles).
// At the beginning, everything is yet to be loaded.
std::vector<gd::String> remainingExtensionNames(
eventsFunctionsExtensions.size());
for (std::size_t i = 0; i < eventsFunctionsExtensions.size(); ++i) {
remainingExtensionNames[i] = eventsFunctionsExtensions.at(i)->GetName();
}
// Helper allowing to find if an extension has an object that depends on
// at least one other object from another extension that is not loaded yet.
auto isDependentFromRemainingExtensions =
[&remainingExtensionNames](
const gd::SerializerElement &eventsFunctionsExtensionElement) {
auto &eventsBasedObjectsElement =
eventsFunctionsExtensionElement.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t eventsBasedObjectsIndex = 0;
eventsBasedObjectsIndex <
eventsBasedObjectsElement.GetChildrenCount();
++eventsBasedObjectsIndex) {
auto &objectsElement =
eventsBasedObjectsElement.GetChild(eventsBasedObjectsIndex)
.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
gd::String extensionName =
eventsFunctionsExtensionElement.GetStringAttribute("name");
gd::String usedExtensionName =
gd::PlatformExtension::GetExtensionFromFullObjectType(objectType);
if (usedExtensionName != extensionName &&
std::find(remainingExtensionNames.begin(),
remainingExtensionNames.end(),
usedExtensionName) != remainingExtensionNames.end()) {
return true;
}
}
}
return false;
};
// Find the order of loading so that the extensions are loaded when all the other
// extensions they depend on are already loaded.
std::vector<gd::String> loadOrderExtensionNames;
bool foundAnyExtension = true;
while (foundAnyExtension) {
foundAnyExtension = false;
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
auto extensionName = remainingExtensionNames[i];
size_t extensionIndex =
GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
if (!isDependentFromRemainingExtensions(
eventsFunctionsExtensionElement)) {
loadOrderExtensionNames.push_back(extensionName);
remainingExtensionNames.erase(remainingExtensionNames.begin() + i);
i--;
foundAnyExtension = true;
}
}
}
return loadOrderExtensionNames;
}
void Project::SerializeTo(SerializerElement& element) const {
SerializerElement& versionElement = element.AddChild("gdVersion");
versionElement.SetAttribute("major", gd::VersionWrapper::Major());
@@ -1076,7 +1222,9 @@ bool Project::HasSourceFile(gd::String name, gd::String language) const {
vector<std::unique_ptr<SourceFile> >::const_iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
bind2nd(gd::ExternalSourceFileHasName(), name));
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return false;
@@ -1086,20 +1234,26 @@ bool Project::HasSourceFile(gd::String name, gd::String language) const {
gd::SourceFile& Project::GetSourceFile(const gd::String& name) {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
bind2nd(gd::ExternalSourceFileHasName(), name)));
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
const gd::SourceFile& Project::GetSourceFile(const gd::String& name) const {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
bind2nd(gd::ExternalSourceFileHasName(), name)));
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
void Project::RemoveSourceFile(const gd::String& name) {
std::vector<std::unique_ptr<gd::SourceFile> >::iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
bind2nd(gd::ExternalSourceFileHasName(), name));
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return;
externalSourceFiles.erase(sourceFile);
@@ -1121,7 +1275,10 @@ gd::SourceFile& Project::InsertNewSourceFile(const gd::String& name,
return newlyInsertedSourceFile;
}
Project::Project(const Project& other) { Init(other); }
Project::Project(const Project &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Global) {
Init(other);
}
Project& Project::operator=(const Project& other) {
if (this != &other) Init(other);

View File

@@ -523,13 +523,7 @@ class GD_CORE_API Project {
std::unique_ptr<gd::Object> CreateObject(const gd::String& type,
const gd::String& name) const;
/**
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
void EnsureObjectDefaultBehaviors(gd::Object& object) const;
/**
* Create an event of the given type.
@@ -899,6 +893,16 @@ class GD_CORE_API Project {
const EventsFunctionsExtension& eventsFunctionExtension,
std::size_t position);
/**
* \brief Unserialize and insert in the project the extensions.
*
* Unserialization is done in two passe to allow dependencies between extensions.
*
* \note If an extension with the same name already exists, it will be overwritten.
*/
void UnserializeAndInsertExtensionsFrom(
const gd::SerializerElement& eventsFunctionsExtensionsElement);
/**
* \brief Delete the events functions extension named "name".
*/
@@ -990,16 +994,12 @@ class GD_CORE_API Project {
/**
* \brief return the objects of the project.
*/
gd::ObjectsContainer& GetObjects() {
return objectsContainer;
}
gd::ObjectsContainer& GetObjects() { return objectsContainer; }
/**
* \brief Return the objects of the project.
*/
const gd::ObjectsContainer& GetObjects() const {
return objectsContainer;
}
const gd::ObjectsContainer& GetObjects() const { return objectsContainer; }
///@}
/** \name Identifier names
@@ -1080,32 +1080,53 @@ class GD_CORE_API Project {
*/
void Init(const gd::Project& project);
gd::String name; ///< Game name
gd::String description; ///< Game description
gd::String version; ///< Game version number (used for some exports)
unsigned int windowWidth; ///< Window default width
unsigned int windowHeight; ///< Window default height
int maxFPS; ///< Maximum Frame Per Seconds, -1 for unlimited
unsigned int minFPS; ///< Minimum Frame Per Seconds ( slow down game if FPS
///< are below this number )
bool verticalSync; ///< If true, must activate vertical synchronization.
/**
* @brief Get the project extensions names in the order they have to be
* unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
*/
std::vector<gd::String> GetUnserializingOrderExtensionNames(
const gd::SerializerElement& eventsFunctionsExtensionsElement);
/**
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
gd::String name; ///< Game name
gd::String description; ///< Game description
gd::String version; ///< Game version number (used for some exports)
unsigned int windowWidth = 0; ///< Window default width
unsigned int windowHeight = 0; ///< Window default height
int maxFPS = 0; ///< Maximum Frame Per Seconds, -1 for unlimited
unsigned int minFPS = 0; ///< Minimum Frame Per Seconds ( slow down game if
///< FPS are below this number )
bool verticalSync =
false; ///< If true, must activate vertical synchronization.
gd::String scaleMode;
bool pixelsRounding; ///< If true, the rendering should stop pixel
///< interpolation of rendered objects.
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
bool pixelsRounding = false; ///< If true, the rendering should stop pixel
///< interpolation of rendered objects.
bool adaptGameResolutionAtRuntime =
true; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
gd::String antialiasingMode;
bool isAntialisingEnabledOnMobile;
bool isAntialisingEnabledOnMobile = false;
gd::String projectUuid; ///< UUID useful to identify the game in online
///< services or database that would require it.
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from
///< events will have 0 as Z order,
///< instead of the highest Z order
///< found on the layer at the scene
///< startup.
bool useDeprecatedZeroAsDefaultZOrder =
false; ///< If true, objects created from
///< events will have 0 as Z order,
///< instead of the highest Z order
///< found on the layer at the scene
///< startup.
std::vector<std::unique_ptr<gd::Layout> > scenes; ///< List of all scenes
gd::VariablesContainer variables; ///< Initial global variables
gd::ObjectsContainer objectsContainer;
@@ -1118,7 +1139,8 @@ class GD_CORE_API Project {
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
bool useExternalSourceFiles; ///< True if game used external source files.
bool useExternalSourceFiles =
false; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
@@ -1127,35 +1149,40 @@ class GD_CORE_API Project {
std::vector<gd::String>
authorUsernames; ///< Game author usernames, from GDevelop users DB.
std::vector<gd::String> categories; ///< Game categories
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
bool isPlayableWithMobile; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
bool isPlayableWithKeyboard =
false; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad =
false; ///< The project is playable with a gamepad.
bool isPlayableWithMobile = false; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
gd::String templateSlug; ///< The slug of the template from which the game is
///< created.
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
bool
folderProject; ///< True if folder project, false if single file project.
bool folderProject =
false; ///< True if folder project, false if single file project.
gd::String
projectFile; ///< Path to the project file - when editing a local file.
gd::String latestCompilationDirectory;
gd::Platform*
currentPlatform; ///< The platform being used to edit the project.
gd::Platform* currentPlatform =
nullptr; ///< The platform being used to edit the project.
gd::PlatformSpecificAssets platformSpecificAssets;
gd::LoadingScreen loadingScreen;
gd::Watermark watermark;
std::vector<std::unique_ptr<gd::ExternalEvents> >
externalEvents; ///< List of all externals events
ExtensionProperties
extensionProperties; ///< The properties of the extensions.
extensionProperties; ///< The properties of the extensions.
gd::WholeProjectDiagnosticReport wholeProjectDiagnosticReport;
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
///< time the project was saved.
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
///< time the project was saved.
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
///< time the project was saved.
mutable unsigned int gdMajorVersion =
0; ///< The GD major version used the last
///< time the project was saved.
mutable unsigned int gdMinorVersion =
0; ///< The GD minor version used the last
///< time the project was saved.
mutable unsigned int gdBuildVersion =
0; ///< The GD build version used the last
///< time the project was saved.
};
} // namespace gd

View File

@@ -4,12 +4,55 @@
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/PlatformExtension.h"
namespace gd {
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(
const gd::Project &project, const gd::Layout &layout) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
project, layout),
VariablesContainersList::
MakeNewVariablesContainersListForProjectAndLayout(project, layout),
&project.GetVariables(), &layout.GetVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForProject(
const gd::Project &project) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProject(project),
VariablesContainersList::MakeNewVariablesContainersListForProject(
project),
&project.GetVariables(), nullptr,
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersFor(
const gd::ObjectsContainer &globalObjectsContainers,
const gd::ObjectsContainer &objectsContainers) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
globalObjectsContainers, objectsContainers),
VariablesContainersList::MakeNewEmptyVariablesContainersList(),
nullptr, nullptr,
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
};
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtension(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension) {
@@ -18,6 +61,8 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtensi
ObjectsContainersList::MakeNewEmptyObjectsContainersList(),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
@@ -37,6 +82,8 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForFreeEventsFunction(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddParameters(
@@ -63,6 +110,8 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
@@ -87,11 +136,14 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsBasedObject, eventsFunction, parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
eventsBasedObject.GetObjects(),
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
@@ -109,22 +161,34 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsBasedObject(
const gd::EventsBasedObject &eventsBasedObject,
gd::ObjectsContainer &outputObjectsContainer) {
// TODO: We should avoid to use a single `outputObjectsContainer` and instead
// use multiple, stable objects container pointed by the `ProjectScopedContainers`
// created below.
// Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
// in the codebase.
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.GetObjectGroups().Clear();
// This object named "Object" represents the parent and is used by events.
// TODO: Use a dedicated `ObjectsContainer` with only this "Object" and check in
// the codebase that this container is not assumed as a
// "globalObjectsContainer".
// Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
// in the codebase.
outputObjectsContainer.InsertNewObject(
project,
gd::PlatformExtension::GetObjectFullType(
eventsFunctionsExtension.GetName(), eventsBasedObject.GetName()),
"Object", outputObjectsContainer.GetObjectsCount());
gd::EventsFunctionTools::CopyEventsBasedObjectChildrenToObjectsContainer(
eventsBasedObject, outputObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
outputObjectsContainer),
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
eventsBasedObject.GetObjects(), outputObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(

View File

@@ -5,13 +5,11 @@
#include "ObjectsContainersList.h"
#include "PropertiesContainersList.h"
#include "VariablesContainersList.h"
#include "VariablesContainer.h"
namespace gd {
class Project;
class ObjectsContainer;
class ObjectsContainersList;
class VariablesContainersList;
class PropertiesContainersList;
class NamedPropertyDescriptor;
class ParameterMetadataContainer;
class BaseEvent;
@@ -19,7 +17,7 @@ class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
} // namespace gd
} // namespace gd
namespace gd {
@@ -38,51 +36,29 @@ class ProjectScopedContainers {
ProjectScopedContainers(
const gd::ObjectsContainersList &objectsContainersList_,
const gd::VariablesContainersList &variablesContainersList_,
const gd::VariablesContainer *legacyGlobalVariables_,
const gd::VariablesContainer *legacySceneVariables_,
const gd::PropertiesContainersList &propertiesContainersList_)
: objectsContainersList(objectsContainersList_),
variablesContainersList(variablesContainersList_),
legacyGlobalVariables(legacyGlobalVariables_),
legacySceneVariables(legacySceneVariables_),
propertiesContainersList(propertiesContainersList_){};
virtual ~ProjectScopedContainers(){};
static ProjectScopedContainers
MakeNewProjectScopedContainersForProjectAndLayout(const gd::Project &project,
const gd::Layout &layout) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
project, layout),
VariablesContainersList::
MakeNewVariablesContainersListForProjectAndLayout(project, layout),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
const gd::Layout &layout);
static ProjectScopedContainers
MakeNewProjectScopedContainersForProject(const gd::Project &project) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProject(
project),
VariablesContainersList::
MakeNewVariablesContainersListForProject(project),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
MakeNewProjectScopedContainersForProject(const gd::Project &project);
/**
* @deprecated Use another method for an explicit context instead.
*/
static ProjectScopedContainers MakeNewProjectScopedContainersFor(
const gd::ObjectsContainer &globalObjectsContainers,
const gd::ObjectsContainer &objectsContainers) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
globalObjectsContainers, objectsContainers),
VariablesContainersList::MakeNewEmptyVariablesContainersList(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
};
const gd::ObjectsContainer &objectsContainers);
static ProjectScopedContainers
MakeNewProjectScopedContainersForEventsFunctionsExtension(
@@ -221,6 +197,24 @@ class ProjectScopedContainers {
return variablesContainersList;
};
/**
* @brief Return the global variables of the current scene or the current
* extension. It allows legacy "globalvar" parameters to accept extension
* variables.
*/
const gd::VariablesContainer *GetLegacyGlobalVariables() const {
return legacyGlobalVariables;
};
/**
* @brief Return the scene variables of the current scene or the current
* extension. It allows legacy "scenevar" parameters to accept extension
* variables.
*/
const gd::VariablesContainer *GetLegacySceneVariables() const {
return legacySceneVariables;
};
const gd::PropertiesContainersList &GetPropertiesContainersList() const {
return propertiesContainersList;
};
@@ -231,11 +225,14 @@ class ProjectScopedContainers {
/** Do not use - should be private but accessible to let Emscripten create a
* temporary. */
ProjectScopedContainers(){};
ProjectScopedContainers()
: legacyGlobalVariables(nullptr), legacySceneVariables(nullptr){};
private:
private:
gd::ObjectsContainersList objectsContainersList;
gd::VariablesContainersList variablesContainersList;
const gd::VariablesContainer *legacyGlobalVariables;
const gd::VariablesContainer *legacySceneVariables;
gd::PropertiesContainersList propertiesContainersList;
std::vector<const ParameterMetadataContainer *> parametersVectorsList;
};

View File

@@ -33,6 +33,7 @@ class GD_CORE_API PropertyDescriptor {
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string"), label(""), hidden(false),
deprecated(false), advanced(false),
hasImpactOnOtherProperties(false),
measurementUnit(gd::MeasurementUnit::GetUndefined()),
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
@@ -41,6 +42,7 @@ class GD_CORE_API PropertyDescriptor {
*/
PropertyDescriptor()
: hidden(false), deprecated(false), advanced(false),
hasImpactOnOtherProperties(false),
measurementUnit(gd::MeasurementUnit::GetUndefined()),
quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
@@ -175,6 +177,21 @@ class GD_CORE_API PropertyDescriptor {
*/
bool IsAdvanced() const { return advanced; }
/**
* \brief Check if the property has impact on other properties - which means a change
* must re-render other properties.
*/
bool HasImpactOnOtherProperties() const { return hasImpactOnOtherProperties; }
/**
* \brief Set if the property has impact on other properties - which means a change
* must re-render other properties.
*/
PropertyDescriptor& SetHasImpactOnOtherProperties(bool enable) {
hasImpactOnOtherProperties = enable;
return *this;
}
QuickCustomization::Visibility GetQuickCustomizationVisibility() const { return quickCustomizationVisibility; }
PropertyDescriptor& SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
@@ -221,6 +238,7 @@ class GD_CORE_API PropertyDescriptor {
bool hidden;
bool deprecated;
bool advanced;
bool hasImpactOnOtherProperties;
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
QuickCustomization::Visibility quickCustomizationVisibility;
};

View File

@@ -1,16 +1,37 @@
#pragma once
#include "GDCore/String.h"
namespace gd {
class QuickCustomization {
public:
enum Visibility {
/** Visibility based on the parent or editor heuristics (probably visible). */
/** Visibility based on the parent or editor heuristics (probably visible).
*/
Default,
/** Visible in the quick customization editor. */
Visible,
/** Not visible in the quick customization editor. */
Hidden
};
static Visibility StringAsVisibility(const gd::String& str) {
if (str == "visible")
return Visibility::Visible;
else if (str == "hidden")
return Visibility::Hidden;
return Visibility::Default;
}
static gd::String VisibilityAsString(Visibility visibility) {
if (visibility == Visibility::Visible)
return "visible";
else if (visibility == Visibility::Hidden)
return "hidden";
return "default";
}
};
} // namespace gd

View File

@@ -0,0 +1,58 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
#include <algorithm>
#include <iostream>
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/UUID/UUID.h"
using namespace std;
namespace gd {
QuickCustomizationVisibilitiesContainer::
QuickCustomizationVisibilitiesContainer() {}
bool QuickCustomizationVisibilitiesContainer::IsEmpty() const {
return visibilities.empty();
}
void QuickCustomizationVisibilitiesContainer::Set(
const gd::String& name, QuickCustomization::Visibility visibility) {
visibilities[name] = visibility;
}
QuickCustomization::Visibility QuickCustomizationVisibilitiesContainer::Get(
const gd::String& name) const {
auto it = visibilities.find(name);
if (it != visibilities.end()) return it->second;
return QuickCustomization::Visibility::Default;
}
void QuickCustomizationVisibilitiesContainer::SerializeTo(
SerializerElement& element) const {
for (auto& visibility : visibilities) {
element.SetStringAttribute(
visibility.first,
QuickCustomization::VisibilityAsString(visibility.second));
}
}
void QuickCustomizationVisibilitiesContainer::UnserializeFrom(
const SerializerElement& element) {
visibilities.clear();
for (auto& child : element.GetAllChildren()) {
visibilities[child.first] =
QuickCustomization::StringAsVisibility(child.second->GetStringValue());
}
}
} // namespace gd

View File

@@ -0,0 +1,29 @@
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
namespace gd {
class QuickCustomizationVisibilitiesContainer {
public:
QuickCustomizationVisibilitiesContainer();
void Set(const gd::String& name, QuickCustomization::Visibility visibility);
QuickCustomization::Visibility Get(const gd::String& name) const;
bool IsEmpty() const;
void SerializeTo(SerializerElement& element) const;
void UnserializeFrom(const SerializerElement& element);
private:
std::map<gd::String, QuickCustomization::Visibility> visibilities;
};
} // namespace gd

View File

@@ -173,9 +173,6 @@ std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties()
properties[_("Smooth the image")]
.SetValue(smooth ? "true" : "false")
.SetType("Boolean");
properties[_("Always loaded in memory")]
.SetValue(alwaysLoaded ? "true" : "false")
.SetType("Boolean");
return properties;
}
@@ -184,8 +181,6 @@ bool ImageResource::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == _("Smooth the image"))
smooth = value == "1";
else if (name == _("Always loaded in memory"))
alwaysLoaded = value == "1";
return true;
}
@@ -569,14 +564,12 @@ void ImageResource::SetFile(const gd::String& newFile) {
}
void ImageResource::UnserializeFrom(const SerializerElement& element) {
alwaysLoaded = element.GetBoolAttribute("alwaysLoaded");
smooth = element.GetBoolAttribute("smoothed");
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void ImageResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("alwaysLoaded", alwaysLoaded);
element.SetAttribute("smoothed", smooth);
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());

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