Compare commits

...

210 Commits

Author SHA1 Message Date
Neyl
153b2d5034 Support of BokehShader in effects 2025-01-10 17:06:12 +01:00
Neyl
fa86d81db7 added BokehShader2.ts that link runtime and packaged shader code. Removed the shader from Extensions/3D as it was not the way to go 2025-01-10 13:21:34 +01:00
Neyl
77a0c2e6ad first implementation test of DoF shader from ThreeJS 2025-01-10 11:25:23 +01:00
Neyl
98152b191f added a guard for object 2025-01-09 13:32:25 +01:00
Neyl
197440497d Update yarn.lock 2025-01-09 12:55:30 +01:00
Neyl
892f5e6e86 Merge branch 'feat/animation-name-behaviors' of https://github.com/4ian/GDevelop into feat/animation-name-behaviors 2025-01-09 12:07:36 +01:00
Neyl
f92ab329e3 add filter support for unnamed animations 2025-01-09 12:06:35 +01:00
Neyl
728a82761a Update EventsBasedBehaviorPropertiesEditor.js 2025-01-09 11:29:31 +01:00
Neyl
2bb236d591 removed an empty line 2025-01-09 11:25:00 +01:00
Neyl
45a53f614f Update newIDE/app/src/EventsBasedBehaviorEditor/EventsBasedBehaviorPropertiesEditor.js
Co-authored-by: D8H <Davy.Helard@gmail.com>
2025-01-09 11:22:34 +01:00
Neyl
dee21a203e changed getChoice syntax 2025-01-09 11:15:34 +01:00
Neyl
3663eef67c fixed stylistic issues 2025-01-09 10:56:59 +01:00
Neyl
17d38a04e3 fixed stylistic issues 2025-01-09 10:55:28 +01:00
Neyl
23df4f2a92 removed a console.log 2025-01-09 10:50:18 +01:00
Neyl
4858ccbbea Update BehaviorPropertiesEditor.js 2025-01-09 10:46:01 +01:00
Neyl
025516a95e removed a console.log 2025-01-09 10:45:29 +01:00
Neyl
1d81ab954f Update CompactBehaviorPropertiesEditor.js 2025-01-09 10:41:18 +01:00
Neyl
a11d08d36c Update CompactBehaviorPropertiesEditor.js 2025-01-09 10:40:45 +01:00
Neyl
96ae5ec458 Update String.cpp 2025-01-09 10:39:08 +01:00
Neyl
4157b4fdba Update ObjectJsImplementation.cpp 2025-01-09 10:38:39 +01:00
Neyl
124da1e850 Update ObjectJsImplementation.cpp 2025-01-09 10:38:20 +01:00
Neyl
ccc9992a97 Update String.cpp 2025-01-09 10:37:50 +01:00
Neyl
0487fa8bb9 Update String.cpp 2025-01-09 10:37:05 +01:00
Neyl
8c811139fd label correction for no value 2025-01-09 10:27:01 +01:00
Neyl
4a7f8de196 Update EventsBasedBehaviorPropertiesEditor.js 2025-01-09 10:15:10 +01:00
Neyl
a7f5ef1d28 Update index.js 2025-01-09 10:13:22 +01:00
Neyl
c6aca52b11 Add support of animationName selector on compact behavior properties editor 2025-01-09 10:11:21 +01:00
Neyl
2ec5f51576 correction for PR 2025-01-08 15:31:59 +01:00
Neyl
ae32021120 Cleaning the code for PR 2025-01-08 12:16:12 +01:00
Neyl
37e43c5f33 Functional feature for object editing dialog window 2025-01-08 11:59:59 +01:00
Neyl
ba65a1bc56 Started to add proper animation name support in editor, WIP 2025-01-07 15:13:25 +01:00
Neyl
2500ac930f WIP 2025-01-06 17:40:45 +01:00
D8H
18892f97f3 Remove unused import and fix a typo (#7277) 2025-01-04 11:19:42 +01:00
D8H
0ec0654032 Allow to make custom objects private to an extension (#7275) 2025-01-03 19:36:38 +01:00
D8H
8e29c723e8 [3D physics character] Fix a 1-frame delay on the "is falling" condition (#7276) 2025-01-03 19:33:33 +01:00
Clément Pasteau
6acde2865a Fix opening windows in foreground on windows everywhere in app (#7274)
* Ensure electron/remote is used
2025-01-02 16:36:43 +01:00
Clément Pasteau
49e176a98f Scroll to projects when opening from dashboard (#7273)
Do not show in changelog
2025-01-02 15:16:30 +01:00
D8H
71c2a0be01 Add a property to choose the maximum stair height a 3D character can walk (#7265) 2025-01-02 13:18:46 +01:00
Clément Pasteau
4ad1a0dd68 Bump 221 (#7271) 2025-01-02 11:20:00 +01:00
github-actions[bot]
a556690307 Update translations [skip ci] (#7269)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-02 11:17:46 +01:00
Clément Pasteau
6950f323b3 Disable type checking in JS Events until all autocompletions are properly handled (#7270) 2025-01-02 11:09:59 +01:00
github-actions[bot]
de129f6cc4 Update translations [skip ci] (#7252)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-02 10:57:33 +01:00
Clément Pasteau
b3b88a0445 Fix wrong prop name & add translations (#7268)
Do not show in changelog
2025-01-02 10:57:08 +01:00
D8H
8e8f3a7d55 Fix the collision conditions for 3D physics characters (#7264)
* Migrate to Jolt 0.31.0
2024-12-31 16:58:08 +01:00
D8H
cc6b4a283a Enable type checking on the Dialog Tree implementation (#7263)
- Don't show in changelog
2024-12-31 13:03:16 +01:00
D8H
f393f36bad Add a tooltip on some physics properties (#7262) 2024-12-30 18:23:20 +01:00
Florian Rival
5261f5a431 Remove logs for audio events when manipulating a sound/music on an unused channel (#7260) 2024-12-29 15:50:05 +01:00
AlexandreS
1a6cf8d69a Paginate feedbacks (#7259)
Also:
* Display 3 last projects only in context menu
2024-12-27 14:09:45 +01:00
AlexandreS
e93b38fee4 Fix asset preview being too zoomed in (#7255) 2024-12-27 14:08:06 +01:00
Florian Rival
22c7215071 Increase bottom drawer bottom margin on mobile
Don't show in changelog
2024-12-22 13:06:01 +01:00
Florian Rival
a221990c57 Ensure Home tab still shown when project opened on small screens
* Also smoothly scroll to the active tab
2024-12-22 12:43:33 +01:00
AlexandreS
85f6e74a5c Fix mobile horizontal scroll (#7254)
Don't show in changelog
2024-12-20 10:57:46 +01:00
Florian Rival
6577432b27 Bump newIDE version 2024-12-19 16:59:15 +01:00
AlexandreS
16d94b5e38 When duplicating a project, ask for the new name and if link with game should be kept (#7253)
Don't show in changelog
2024-12-19 16:50:38 +01:00
Florian Rival
88a2060364 Fix warning 2024-12-19 16:12:59 +01:00
Florian Rival
2d0ffee102 Improve subscription dialog and profile 2024-12-19 15:39:35 +01:00
D8H
aa30f3c465 Fix a regression on parameter editor lock (#7249)
Do not show in changelog
2024-12-19 11:43:41 +01:00
github-actions[bot]
cfcb4b557f Update translations [skip ci] (#7248)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-12-19 11:42:58 +01:00
Clément Pasteau
d8db679a1d Show "Get Premium" button with enhanced colors (#7251) 2024-12-19 11:33:03 +01:00
Florian Rival
01503d46c1 Bump newIDE version 2024-12-18 18:16:59 +01:00
Florian Rival
02d44bbba4 Remove unused API method to initialize game rendering [skip ci] (#7235) 2024-12-18 18:16:13 +01:00
D8H
b6d8170a00 [3D character] Allow to set higher speed than the maximum speed (#7245)
Don't show in changelog
2024-12-18 18:15:50 +01:00
github-actions[bot]
554c4c8f58 Update translations [skip ci] (#7239)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-12-18 18:14:59 +01:00
D8H
9e29146841 Fix a regression on custom object life-cycle parameters not being locked (#7247) 2024-12-18 18:03:41 +01:00
AlexandreS
189e971cd2 Fix: Highlight the currently opened project if multiple projects with same uuid (#7244)
Don't show in changelog
2024-12-18 17:54:46 +01:00
Florian Rival
deab962081 Fix regression in extensions loading (#7242) 2024-12-18 12:26:56 +01:00
D8H
e2281dfd82 Add 3D physics and 3D character behaviors (#7149)
* The 3D physics engine is powered by [Jolt Physics](https://github.com/jrouwe/JoltPhysics) a modern, performant, battle-tested, fully featured 3D physics engine used in AAA games, like Horizon: Forbidden West. The new 3D physics engine is perfect for making FPS, TPS, 3D platformer and in the future 3D racing games or any 3D game.
* Similar to the 2D physics engine, you can add the 3D Physics behavior to your object. For example, add the behavior to platforms with the type set to "Static".
* You can choose the collider shape: box, sphere, cylinder or capsule and modify physics properties of the objects having this behavior - much like the 2D physics engine.
* For characters, a dedicated "3D Physics Character" behavior is available. Pair it with one or more additional behaviors to get controls working out of the box: "3D Shooter keyboard mapper", "3D Platformer keyboard mapper", etc...
* Take a look at the new examples to give it a try! New examples and behaviors will be progressively added.
2024-12-17 18:00:48 +01:00
AlexandreS
44daf709e4 Fix asset store display speed and reduce the burst of requests (#7241) 2024-12-17 17:37:02 +01:00
Florian Rival
c9e5272367 Allow to browse marketing boosts from analytics screen
Also fix a warning

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

1
.gitattributes vendored
View File

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

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

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

View File

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

View File

@@ -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; }
@@ -295,6 +300,22 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
ObjectMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
/**
* \brief Set the object to be hidden in the IDE.
*
@@ -351,6 +372,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
gd::String iconFilename;
gd::String categoryFullName;
std::set<gd::String> defaultBehaviorTypes;
bool isPrivate = false;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;

View File

@@ -6,6 +6,8 @@
#include "ParameterMetadataTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
@@ -13,8 +15,6 @@
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "InstructionMetadata.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
namespace gd {
const ParameterMetadata ParameterMetadataTools::badParameterMetadata;
@@ -23,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

@@ -74,9 +74,11 @@ ValueTypeMetadata::GetPrimitiveValueType(const gd::String &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) {
@@ -88,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

@@ -135,7 +135,17 @@ class GD_CORE_API ValueTypeMetadata {
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::GetPrimitiveValueType(name) == "variable";
return gd::ValueTypeMetadata::IsVariable(name);
}
/**
* \brief Return true if the type of the parameter is a variable.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsVariable(const gd::String &type) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(type) == "variable";
}
/**
@@ -301,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

@@ -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

@@ -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

@@ -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

@@ -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;
@@ -45,7 +46,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false),
currentParameterExtraInfo(&extraInfo_) {};
currentParameterExtraInfo(&extraInfo_),
variableObjectName(),
variableObjectNameLocation() {};
virtual ~ExpressionValidator(){};
/**
@@ -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

@@ -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

@@ -293,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

@@ -19,6 +19,7 @@
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
#include "GDCore/IDE/Events/CustomObjectTypeRenamer.h"
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
#include "GDCore/IDE/Events/EventsParameterReplacer.h"
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h"
@@ -28,6 +29,7 @@
#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/Project/BehaviorObjectTypeRenamer.h"
#include "GDCore/IDE/Project/BehaviorsSharedDataBehaviorTypeRenamer.h"
@@ -815,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,
@@ -949,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(
@@ -958,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,
@@ -1020,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(
@@ -1029,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,
@@ -1077,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(
@@ -1086,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,
@@ -1692,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;
@@ -1701,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) {
@@ -1718,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
@@ -1964,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
@@ -1982,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) {
@@ -2012,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);
}
}
@@ -2144,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

@@ -176,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.
@@ -529,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);
@@ -627,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.
@@ -637,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>

View File

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

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#define GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#pragma once
#include <vector>
#include "GDCore/Project/NamedPropertyDescriptor.h"
@@ -40,6 +39,21 @@ class GD_CORE_API AbstractEventsBasedEntity {
*/
AbstractEventsBasedEntity* Clone() const { return new AbstractEventsBasedEntity(*this); };
/**
* \brief Check if the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
AbstractEventsBasedEntity& SetPrivate(bool isPrivate_) {
isPrivate = isPrivate_;
return *this;
}
/**
* \brief Get the description of the behavior or object, that is displayed in the
* editor.
@@ -151,8 +165,7 @@ class GD_CORE_API AbstractEventsBasedEntity {
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::PropertiesContainer propertyDescriptors;
gd::String extensionName;
bool isPrivate = false;
};
} // namespace gd
#endif // GDCORE_ABSTRACTEVENTSBASEDENTITY_H

View File

@@ -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,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_BEHAVIORCONFIGURATIONCONTAINER_H
#define GDCORE_BEHAVIORCONFIGURATIONCONTAINER_H
#pragma once
#include <map>
#include <memory>
@@ -19,6 +18,7 @@ class PropertyDescriptor;
class SerializerElement;
class Project;
class Layout;
class ArbitraryResourceWorker;
} // namespace gd
namespace gd {
@@ -158,6 +158,18 @@ class GD_CORE_API BehaviorConfigurationContainer {
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
@@ -209,5 +221,3 @@ class GD_CORE_API BehaviorConfigurationContainer {
};
} // 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>());
@@ -53,7 +53,8 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource") {
propertyType == "Resource" || propertyType == "LeaderboardId" ||
propertyType == "AnimationName") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
@@ -89,7 +90,8 @@ bool CustomConfigurationHelper::UpdateProperty(
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource") {
propertyType == "Resource" || propertyType == "LeaderboardId" ||
propertyType == "AnimationName") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());

View File

@@ -158,7 +158,9 @@ 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);
}
@@ -202,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();

View File

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

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EVENTSBASEDBEHAVIOR_H
#define GDCORE_EVENTSBASEDBEHAVIOR_H
#pragma once
#include <vector>
#include "GDCore/Project/AbstractEventsBasedEntity.h"
@@ -75,17 +74,11 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
EventsBasedBehavior& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
@@ -149,11 +142,8 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
gd::PropertiesContainer sharedPropertyDescriptors;
QuickCustomization::Visibility quickCustomizationVisibility;
};
} // namespace gd
#endif // GDCORE_EVENTSBASEDBEHAVIOR_H

View File

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

View File

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

View File

@@ -166,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) {
@@ -173,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) {

View File

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

View File

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

View File

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

View File

@@ -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);
@@ -546,7 +551,8 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorNamesInObjectOrGroup(
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
gd::ObjectsContainer emptyObjectsContainer(
gd::ObjectsContainer::SourceType::Unknown);
return gd::GetBehaviorNamesInObjectOrGroup(emptyObjectsContainer,
*objectsContainers[0], objectOrGroupName, behaviorType,
searchInGroups);
@@ -615,6 +621,28 @@ std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
return animationNames;
}
const ObjectsContainer *
ObjectsContainersList::GetObjectsContainerFromObjectName(
const gd::String &objectOrGroupName) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
++it) {
if ((*it)->HasObjectNamed(objectOrGroupName) ||
(*it)->GetObjectGroups().Has(objectOrGroupName)) {
return *it;
}
}
return nullptr;
}
const gd::ObjectsContainer::SourceType
ObjectsContainersList::GetObjectsContainerSourceType(
const gd::String &objectOrGroupName) const {
const auto *objectsContainer =
GetObjectsContainerFromObjectName(objectOrGroupName);
return objectsContainer ? objectsContainer->GetSourceType()
: gd::ObjectsContainer::SourceType::Unknown;
}
const gd::ObjectsContainer &
ObjectsContainersList::GetObjectsContainer(std::size_t index) const {
return *objectsContainers[index];

View File

@@ -2,12 +2,12 @@
#include <vector>
#include "Variable.h"
#include "GDCore/Project/ObjectsContainer.h"
namespace gd {
class String;
class Project;
class Layout;
class ObjectsContainer;
class VariablesContainer;
class Object;
class ObjectConfiguration;
@@ -190,8 +190,28 @@ 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;

View File

@@ -75,19 +75,18 @@ Project::Project()
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()),
variables(gd::VariablesContainer::SourceType::Global) {}
variables(gd::VariablesContainer::SourceType::Global),
objectsContainer(gd::ObjectsContainer::SourceType::Global) {}
Project::~Project() {}
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& objectType, const gd::String& name) const {
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
name, objectType, CreateObjectConfiguration(objectType));
void Project::EnsureObjectDefaultBehaviors(gd::Object& object) const {
auto& platform = GetCurrentPlatform();
auto& project = *this;
auto& objectType = object.GetType();
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
const gd::String& behaviorType) {
auto& behaviorMetadata =
@@ -97,17 +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);
}
@@ -827,36 +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 custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
gd::EventsFunctionsExtension& newEventsFunctionsExtension =
InsertNewEventsFunctionsExtension("",
GetEventsFunctionsExtensionsCount());
newEventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensions.at(extensionIndex)
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
UnserializeAndInsertExtensionsFrom(eventsFunctionsExtensionsElement);
objectsContainer.GetObjectGroups().UnserializeFrom(
element.GetChild("objectsGroups", 0, "ObjectGroups"));
@@ -924,21 +932,84 @@ void Project::UnserializeFrom(const SerializerElement& element) {
}
}
void Project::UnserializeAndInsertExtensionsFrom(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
std::map<gd::String, size_t> extensionNameToElementIndex;
// First, only unserialize behaviors and objects names.
// As event based objects can contains custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
extensionNameToElementIndex[name] = i;
gd::EventsFunctionsExtension& eventsFunctionsExtension =
HasEventsFunctionsExtensionNamed(name)
? GetEventsFunctionsExtension(name)
: InsertNewEventsFunctionsExtension(
name, GetEventsFunctionsExtensionsCount());
eventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
if (extensionIndex == gd::String::npos) {
// Should never happen because the extension was added in the first pass.
gd::LogError("Can't find extension " + extensionName + " in the list of extensions in second pass of unserialization.");
continue;
}
auto& partiallyLoadedExtension = eventsFunctionsExtensions.at(extensionIndex);
if (extensionNameToElementIndex.find(extensionName) == extensionNameToElementIndex.end()) {
// Should never happen because the extension element is present.
gd::LogError("Can't find extension element to unserialize for " + extensionName + " in second pass of unserialization.");
continue;
}
size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);
partiallyLoadedExtension
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
}
std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
// Some extension have custom objects, which have child objects coming from other extension.
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// Some extension have custom objects, which have child objects coming from other extension.
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
// This implies: an order on the extension unserialization (and no cycles).
// At the beginning, everything is yet to be loaded.
std::map<gd::String, size_t> extensionNameToElementIndex;
std::vector<gd::String> remainingExtensionNames(
eventsFunctionsExtensions.size());
for (std::size_t i = 0; i < eventsFunctionsExtensions.size(); ++i) {
remainingExtensionNames[i] = eventsFunctionsExtensions.at(i)->GetName();
eventsFunctionsExtensionsElement.GetChildrenCount());
for (std::size_t i = 0; i < eventsFunctionsExtensionsElement.GetChildrenCount(); ++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
remainingExtensionNames[i] = name;
extensionNameToElementIndex[name] = i;
}
// Helper allowing to find if an extension has an object that depends on
// Helper allowing to find if an extension has an object that depends on
// at least one other object from another extension that is not loaded yet.
auto isDependentFromRemainingExtensions =
[&remainingExtensionNames](
@@ -984,10 +1055,10 @@ std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
foundAnyExtension = false;
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
auto extensionName = remainingExtensionNames[i];
size_t extensionIndex =
GetEventsFunctionsExtensionPosition(extensionName);
size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensionsElement.GetChild(elementIndex);
if (!isDependentFromRemainingExtensions(
eventsFunctionsExtensionElement)) {
@@ -1230,7 +1301,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".
*/
@@ -1069,6 +1073,18 @@ class GD_CORE_API Project {
return wholeProjectDiagnosticReport;
}
/**
* @brief Get the project extensions names in the order they have to be
* unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
*
* \warning This is only public to allow testing - don't use it in the editor.
*/
static std::vector<gd::String> GetUnserializingOrderExtensionNames(
const gd::SerializerElement& eventsFunctionsExtensionsElement);
private:
/**
* Initialize from another game. Used by copy-ctor and assign-op.
@@ -1077,12 +1093,12 @@ class GD_CORE_API Project {
void Init(const gd::Project& project);
/**
* @brief Get the project extensions names in the order they have to be unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::vector<gd::String> GetUnserializingOrderExtensionNames(const gd::SerializerElement &eventsFunctionsExtensionsElement);
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
gd::String name; ///< Game name
gd::String description; ///< Game description

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

@@ -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());

View File

@@ -166,7 +166,7 @@ class GD_CORE_API Resource {
*/
class GD_CORE_API ImageResource : public Resource {
public:
ImageResource() : Resource(), smooth(true), alwaysLoaded(false) {
ImageResource() : Resource(), smooth(true) {
SetKind("image");
};
virtual ~ImageResource(){};
@@ -210,7 +210,6 @@ class GD_CORE_API ImageResource : public Resource {
void SetSmooth(bool enable = true) { smooth = enable; }
bool smooth; ///< True if smoothing filter is applied
bool alwaysLoaded; ///< True if the image must always be loaded in memory.
private:
gd::String file;
};

View File

@@ -151,6 +151,37 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
REQUIRE(worker.images[0] == "res1");
}
SECTION("Can find resource usages in behavior configurations") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
project.GetResourcesManager().AddResource(
"res1", "path/to/file1.png", "image");
project.GetResourcesManager().AddResource(
"res2", "path/to/file2.png", "image");
project.GetResourcesManager().AddResource(
"res3", "path/to/file3.png", "image");
project.GetResourcesManager().AddResource(
"res4", "path/to/file4.png", "audio");
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
auto &layout = project.InsertNewLayout("Scene", 0);
auto &object = layout.GetObjects().InsertNewObject(
project, "MyExtension::Sprite", "MyObject", 0);
auto *behavior = object.AddNewBehavior(
project, "MyExtension::BehaviorWithRequiredBehaviorProperty",
"BehaviorWithResource");
behavior->UpdateProperty("resourceProperty", "res1");
worker.files.clear();
worker.images.clear();
gd::ResourceExposer::ExposeWholeProjectResources(project, worker);
REQUIRE(worker.files.size() == 4);
REQUIRE(worker.images.size() == 1);
REQUIRE(worker.images[0] == "res1");
}
SECTION("Can find resource usages in layout events") {
gd::Platform platform;
gd::Project project;

View File

@@ -39,20 +39,31 @@ class BehaviorWithRequiredBehaviorProperty : public gd::Behavior {
behaviorContent.GetStringAttribute("requiredBehaviorProperty"))
.SetType("Behavior")
.AddExtraInfo("MyExtension::MyBehavior");
properties["resourceProperty"]
.SetLabel("A resource")
.SetValue(
behaviorContent.GetStringAttribute("resourceProperty"))
.SetType("Resource")
.AddExtraInfo("image");
return properties;
}
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) override {
if (name == _("requiredBehaviorProperty")) {
if (name == "requiredBehaviorProperty") {
behaviorContent.SetAttribute("requiredBehaviorProperty", value);
return true;
}
if (name == "resourceProperty") {
behaviorContent.SetAttribute("resourceProperty", value);
return true;
}
return false;
}
virtual void InitializeContent(
gd::SerializerElement& behaviorContent) override {
behaviorContent.SetAttribute("requiredBehaviorProperty", "");
behaviorContent.SetAttribute("resourceProperty", "");
}
};
@@ -254,9 +265,9 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
extension
->AddAction("SetNumberVariable",
"Do something with number variables",
"This does something with variables",
"Do something with variables",
"Change variable value",
"Modify the number value of a variable.",
"the variable _PARAM0_",
"",
"",
"")
@@ -267,9 +278,9 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
extension
->AddAction("SetStringVariable",
"Do something with string variables",
"This does something with variables",
"Do something with variables",
"Change text variable",
"Modify the text (string) of a variable.",
"the variable _PARAM0_",
"",
"",
"")
@@ -280,9 +291,9 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
extension
->AddAction("SetBooleanVariable",
"Do something with boolean variables",
"This does something with variables",
"Do something with variables",
"Change boolean variable",
"Modify the boolean value of a variable.",
"Change the variable _PARAM0_: _PARAM1_",
"",
"",
"")
@@ -329,8 +340,8 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("object", _("Object 2 parameter"))
.AddParameter("object", "Object 1 parameter")
.AddParameter("object", "Object 2 parameter")
.SetFunctionName("doSomethingWithObjects");
extension
@@ -347,6 +358,17 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("soundfile", "Parameter 3 (an audio resource)")
.SetFunctionName("doSomethingWithResources");
extension
->AddAction("DoSomethingWithAnyVariable",
"Do something with variables",
"This does something with variables",
"Do something with variables please",
"",
"",
"")
.AddParameter("variable", "Any variable")
.SetFunctionName("doSomethingWithAnyVariable");
extension
->AddAction("DoSomethingWithLegacyPreScopedVariables",
"Do something with variables",
@@ -687,6 +709,14 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.SetDefaultValue("\"\"")
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Property name"));
extension
->AddAction("DisplayLeaderboard",
"Display leaderboard",
"Display the specified leaderboard on top of the game.",
"Display leaderboard _PARAM1_",
"Display leaderboard", "", "")
.AddParameter("leaderboardId", "Leaderboard", "", false);
}
{

View File

@@ -1737,10 +1737,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors().size() == 3);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"An object variable or expression should be entered.");
REQUIRE(validator.GetFatalErrors()[2]->GetMessage() ==
"A name should be entered after the dot.");
}
}
@@ -1817,6 +1819,19 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid object variables (non existing variable with child)") {
{
auto node =
parser.ParseExpression("MySpriteObject.MyNonExistingVariable.MyNonExistingChild");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"This variable does not exist on this object or group.");
}
}
SECTION("Invalid object (object entered without any variable)") {
{
auto node =
@@ -1962,6 +1977,25 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid property (required behavior)") {
{
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
propertiesContainer.InsertNew("MyRequiredBehavior").SetType("Behavior");
auto node =
parser.ParseExpression("\"abc\" + MyRequiredBehavior");
gd::ExpressionValidator validator(platform, projectScopedContainersWithProperties, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "Behaviors can't be used as a value in expressions.");
}
}
SECTION("Invalid property (unsupported child syntax, 1 level)") {
{
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
@@ -2357,10 +2391,12 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 2);
REQUIRE(validator.GetFatalErrors().size() == 3);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"A name should be entered after the dot.");
REQUIRE(validator.GetFatalErrors()[1]->GetMessage() ==
"An object variable or expression should be entered.");
REQUIRE(validator.GetFatalErrors()[2]->GetMessage() ==
"A name should be entered after the dot.");
}

View File

@@ -376,8 +376,7 @@ TEST_CASE("ObjectSerialization", "[common]") {
REQUIRE(readProject.GetEventsFunctionsExtensionsCount() == 1);
auto &eventsExtension =
readProject.GetEventsFunctionsExtension(0);
REQUIRE(eventsExtension.GetEventsBasedObjects().GetCount() ==
2);
REQUIRE(eventsExtension.GetEventsBasedObjects().GetCount() == 2);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get(0);
REQUIRE(eventsBasedObject.GetObjects().GetObjectsCount() == 1);
@@ -392,4 +391,24 @@ TEST_CASE("ObjectSerialization", "[common]") {
customObjectConfiguration->GetChildObjectConfiguration("MyChildSprite");
CheckSpriteConfiguration(spriteConfiguration);
}
SECTION("Can unserialize over an existing extension without duplicating its event-based objects") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
{
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
}
SerializerElement extensionElement;
eventsExtension.SerializeTo(extensionElement);
eventsExtension.UnserializeFrom(project, extensionElement);
REQUIRE(eventsExtension.GetEventsBasedObjects().GetCount() == 1);
}
}

View File

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

View File

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

View File

@@ -77,6 +77,65 @@ const gd::String &GetEventFirstActionType(const gd::BaseEvent &event) {
return actions.Get(0).GetType();
}
const gd::Instruction &
CreateInstructionWithNumberParameter(gd::Project &project,
gd::EventsList &events,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0, expression);
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateInstructionWithObjectParameter(gd::Project &project,
gd::EventsList &events,
const gd::String &objectName) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithObjects");
instruction.SetParametersCount(2);
instruction.SetParameter(0, objectName);
instruction.SetParameter(1, "");
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateInstructionWithBehaviorParameter(gd::Project &project,
gd::EventsList &events,
const gd::String &objectName,
const gd::String &behaviorName) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("MyExtension::BehaviorDoSomething");
instruction.SetParametersCount(2);
instruction.SetParameter(0, objectName);
instruction.SetParameter(1, behaviorName);
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateInstructionWithVariableParameter(gd::Project &project,
gd::EventsList &events,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithAnyVariable");
instruction.SetParametersCount(1);
instruction.SetParameter(0, expression);
return event.GetActions().Insert(instruction);
}
enum TestEvent {
FreeFunctionAction,
FreeFunctionWithExpression,
@@ -1433,7 +1492,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after the renaming of an object
// Trigger the refactoring before the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
project, layout, "ObjectWithMyBehavior",
"RenamedObjectWithMyBehavior",
@@ -1461,7 +1520,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after the renaming of a group
// Trigger the refactoring before the renaming of a group
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInScene(
project, layout, "GroupWithMyBehavior", "RenamedGroupWithMyBehavior",
/* isObjectGroup=*/true);
@@ -1533,16 +1592,17 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// events in this test.
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
// Trigger the refactoring after the renaming of an object
// Trigger the refactoring before the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, eventsFunction,
"Object1", "RenamedObject1",
parametersObjectsContainer, "Object1", "RenamedObject1",
/* isObjectGroup=*/false);
REQUIRE(objectGroup.Find("Object1") == false);
@@ -1561,7 +1621,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsExtension.GetEventsFunction("MyOtherEventsFunction");
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
@@ -1573,10 +1634,11 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
objectWithMyBehavior.GetVariables().InsertNew("MyVariable");
objectWithMyBehavior.GetVariables().InsertNew("MyStructureVariable").CastTo(gd::Variable::Structure);
// Trigger the refactoring after the renaming of an object
// Trigger the refactoring before the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, projectScopedContainers, eventsFunction,
"ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
parametersObjectsContainer, "ObjectWithMyBehavior",
"RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
// Check object name has been renamed in action parameters.
@@ -1616,7 +1678,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
project, "MyExtension::Sprite", "Object2", 0);
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForEventsBasedObject(
project, eventsExtension, eventsBasedObject,
@@ -1657,7 +1720,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance2);
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForEventsBasedObject(
project, eventsExtension, eventsBasedObject,
@@ -1683,13 +1747,14 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
.Get("MyOtherEventsBasedObject");
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForEventsBasedObject(
project, eventsExtension, eventsBasedObject,
parametersObjectsContainer);
// Trigger the refactoring after the renaming of an object
// Trigger the refactoring before the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
project, projectScopedContainers, eventsBasedObject,
"ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
@@ -1738,7 +1803,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
project, "MyExtension::Sprite", "Object2", 0);
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForEventsBasedObject(
project, eventsExtension, eventsBasedObject,
@@ -1778,7 +1844,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance2);
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer;
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForEventsBasedObject(
project, eventsExtension, eventsBasedObject,
@@ -2206,7 +2273,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
.GetObjectType() == "MyRenamedExtension::MyEventsBasedObject");
}
SECTION("(Free) events action renamed") {
SECTION("(Free function) events action renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2224,7 +2291,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression renamed") {
SECTION("(Free function) events expression renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2242,7 +2309,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression and condition renamed") {
SECTION("(Free function) events expression and condition renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2271,7 +2338,239 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events action parameter moved") {
SECTION("(Free function) number parameter renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
auto &instruction = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(), "MyParameter");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyParameter])");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedParameter");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedParameter])");
}
SECTION("(Free function) number parameter not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
// Parameters can't actually be used in "variable" parameters.
auto &instruction = CreateInstructionWithVariableParameter(
project, eventsFunction.GetEvents(), "MyParameter");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyParameter)");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
// "variable" parameters are left untouched.
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyParameter");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyParameter)");
}
SECTION("(Free function) object parameter renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
.SetName("objectList")
.SetExtraInfo("MyExtension::Sprite");
auto &instruction = CreateInstructionWithObjectParameter(
project, eventsFunction.GetEvents(), "MyObject");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(), "MyObject.GetObjectStringWith1Param(0)");
auto &instruction3 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.GetObjectStringWith1Param(0)])");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyObject", "MyRenamedObject");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedObject");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyRenamedObject.GetObjectStringWith1Param(0)");
REQUIRE(instruction3.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedObject.GetObjectStringWith1Param(0)])");
}
SECTION("(Free function) object parameter not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
.SetName("objectList")
.SetExtraInfo("MyExtension::Sprite");
// Parameters can't actually be used in "variable" parameters.
auto &instruction = CreateInstructionWithVariableParameter(
project, eventsFunction.GetEvents(), "MyObject");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyObject)");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyObject", "MyRenamedObject");
// "variable" parameters are left untouched.
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyObject");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyObject)");
}
SECTION("(Free function) behavior parameter renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
.SetName("objectList")
.SetExtraInfo("MyExtension::Sprite");
eventsFunction.GetParameters()
.AddNewParameter("MyBehavior")
.GetValueTypeMetadata()
.SetName("behavior")
.SetExtraInfo("MyExtension::MyBehavior");
auto &instruction = CreateInstructionWithBehaviorParameter(
project, eventsFunction.GetEvents(), "MyObject", "MyBehavior");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(), "MyObject.MyBehavior::GetBehaviorStringWith1Param(0)");
auto &instruction3 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.MyBehavior::GetBehaviorStringWith1Param(0)])");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior");
REQUIRE(instruction.GetParameter(1).GetPlainString() ==
"MyRenamedBehavior");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyObject.MyRenamedBehavior::GetBehaviorStringWith1Param(0)");
REQUIRE(instruction3.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyObject.MyRenamedBehavior::GetBehaviorStringWith1Param(0)])");
}
SECTION("(Free function) behavior parameter not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
.SetName("objectList")
.SetExtraInfo("MyExtension::Sprite");
eventsFunction.GetParameters()
.AddNewParameter("MyBehavior")
.GetValueTypeMetadata()
.SetName("behavior")
.SetExtraInfo("MyExtension::MyBehavior");
// Parameters can't actually be used in "variable" parameters.
auto &instruction = CreateInstructionWithVariableParameter(
project, eventsFunction.GetEvents(), "MyBehavior");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, eventsFunction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyBehavior)");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior");
// "variable" parameters are left untouched.
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyBehavior");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyBehavior)");
}
SECTION("(Free function) events action parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2293,7 +2592,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression parameter moved") {
SECTION("(Free function) events expression parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -2311,7 +2610,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression and condition parameter moved") {
SECTION("(Free function) events expression and condition parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
@@ -3038,6 +3337,66 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Events based behavior) property renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
auto &instruction = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(), "MyProperty");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyProperty])");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
project, eventsExtension, eventsBasedBehavior, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedProperty])");
}
SECTION("(Events based behavior) property not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
// Properties can't actually be used in "variable" parameters.
auto &instruction = CreateInstructionWithVariableParameter(
project, behaviorAction.GetEvents(), "MyProperty");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyProperty)");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
project, eventsExtension, eventsBasedBehavior, "MyProperty",
"MyRenamedProperty");
// "variable" parameters are left untouched.
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyProperty");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyProperty)");
}
SECTION("(Events based behavior) shared property renamed") {
gd::Project project;
gd::Platform platform;
@@ -3089,6 +3448,35 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Events based behavior) shared property renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
auto &instruction = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(), "MySharedProperty");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MySharedProperty])");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
project, eventsExtension, eventsBasedBehavior, "MySharedProperty",
"MyRenamedSharedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedSharedProperty");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedSharedProperty])");
}
SECTION("(Events based object) property renamed") {
gd::Project project;
gd::Platform platform;
@@ -3118,6 +3506,66 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyCustomObject.PropertyMyRenamedProperty()");
}
}
SECTION("(Events based object) property renamed (in expressions)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
auto &instruction = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(), "MyProperty");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyProperty])");
gd::WholeProjectRefactorer::RenameEventsBasedObjectProperty(
project, eventsExtension, eventsBasedObject, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedProperty])");
}
SECTION("(Events based object) property not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
// Properties can't actually be used in "variable" parameters.
auto &instruction = CreateInstructionWithVariableParameter(
project, behaviorAction.GetEvents(), "MyProperty");
auto &instruction2 = CreateInstructionWithNumberParameter(
project, behaviorAction.GetEvents(),
"MyExtension::GetVariableAsNumber(MyProperty)");
gd::WholeProjectRefactorer::RenameEventsBasedObjectProperty(
project, eventsExtension, eventsBasedObject, "MyProperty",
"MyRenamedProperty");
// "variable" parameters are left untouched.
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyProperty");
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyProperty)");
}
}
// TODO: Check that this works when behaviors are attached to a child-object.
TEST_CASE("WholeProjectRefactorer (FindInvalidRequiredBehaviorProperties)",
@@ -4451,4 +4899,33 @@ TEST_CASE("MergeLayers", "[common]") {
// Other layers from the same layout are untouched.
REQUIRE(initialInstances.GetLayerInstancesCount("My other layer") == 1);
}
// TODO: ideally, a test should also cover objects having `leaderboardId` as property.
SECTION("Can rename a leaderboard in scene events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &layout = project.InsertNewLayout("My layout", 0);
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
layout.GetEvents().InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction action;
action.SetType("MyExtension::DisplayLeaderboard");
action.SetParametersCount(1);
action.SetParameter(0, gd::Expression("\"12345678-9abc-def0-1234-56789abcdef0\""));
event.GetActions().Insert(action);
std::set<gd::String> allLeaderboardIds = gd::WholeProjectRefactorer::FindAllLeaderboardIds(project);
REQUIRE(allLeaderboardIds.size() == 1);
REQUIRE(allLeaderboardIds.count("12345678-9abc-def0-1234-56789abcdef0") == 1);
std::map<gd::String, gd::String> leaderboardIdMap;
leaderboardIdMap["12345678-9abc-def0-1234-56789abcdef0"] = "87654321-9abc-def0-1234-56789abcdef0";
gd::WholeProjectRefactorer::RenameLeaderboards(project, leaderboardIdMap);
REQUIRE(GetEventFirstActionFirstParameterString(layout.GetEvents().GetEvent(0)) ==
"\"87654321-9abc-def0-1234-56789abcdef0\"");
}
}

View File

@@ -0,0 +1,97 @@
namespace gdjs {
interface DepthOfFieldFilterNetworkSyncData {
b: number;
c: number;
}
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::DepthOfField',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
if (typeof THREE === 'undefined') {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
console.log("Donne moi une info !!");
return new (class implements gdjs.PixiFiltersTools.Filter {
shaderPass: THREE_ADDONS.ShaderPass;
_isEnabled: boolean;
constructor() {
this.shaderPass = new THREE_ADDONS.ShaderPass(
THREE_ADDONS.BokehDepthShader
);
console.log(THREE_ADDONS);
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().addPostProcessingPass(this.shaderPass);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
if (!(target instanceof gdjs.Layer)) {
return false;
}
target.getRenderer().removePostProcessingPass(this.shaderPass);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'brightness') {
this.shaderPass.uniforms[parameterName].value = value;
}
if (parameterName === 'contrast') {
this.shaderPass.uniforms[parameterName].value = value;
}
}
getDoubleParameter(parameterName: string): number {
if (parameterName === 'brightness') {
return this.shaderPass.uniforms[parameterName].value;
}
if (parameterName === 'contrast') {
return this.shaderPass.uniforms[parameterName].value;
}
return 0;
}
updateStringParameter(parameterName: string, value: string): void {}
updateColorParameter(parameterName: string, value: number): void {}
getColorParameter(parameterName: string): number {
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): DepthOfFieldFilterNetworkSyncData {
return {
b: this.shaderPass.uniforms.brightness.value,
c: this.shaderPass.uniforms.contrast.value,
};
}
updateFromNetworkSyncData(
data: DepthOfFieldFilterNetworkSyncData
) {
}
})();
}
})()
);
}

View File

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

View File

@@ -1983,6 +1983,16 @@ module.exports = {
.setLabel(_('Threshold (between 0 and 1)'))
.setType('number');
}
{
const effect = extension
.addEffect('DepthOfField')
.setFullName(_('DepthOfField'))
.setDescription(_('Apply a depth-of-field effect.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/BokehShader2.js');
const properties = effect.getProperties();
}
{
const effect = extension
.addEffect('BrightnessAndContrast')
@@ -3231,9 +3241,9 @@ module.exports = {
: this._originalDepth / modelDepth;
const minScaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(minScaleRatio)) {
this._defaultWidth = modelWidth;
this._defaultHeight = modelHeight;
this._defaultDepth = modelDepth;
this._defaultWidth = this._originalWidth;
this._defaultHeight = this._originalHeight;
this._defaultDepth = this._originalDepth;
} else {
if (widthRatio === minScaleRatio) {
this._defaultWidth = this._originalWidth;
@@ -3274,6 +3284,10 @@ module.exports = {
this._defaultDepth = this._originalDepth;
}
}
} else {
this._defaultWidth = this._originalWidth;
this._defaultHeight = this._originalHeight;
this._defaultDepth = this._originalDepth;
}
this._threeObject.add(this._threeModelGroup);

View File

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

View File

@@ -34,18 +34,18 @@ module.exports = {
extension
.addAction(
'LoadDialogueFromSceneVariable',
_('Load dialogue Tree from a scene variable'),
_('Load dialogue tree from a scene variable'),
_(
'Load a dialogue data object - Yarn json format, stored in a scene variable. Use this command to load all the Dialogue data at the beginning of the game.'
'Load a dialogue data object - Yarn JSON format, stored in a scene variable. Use this command to load all the Dialogue data at the beginning of the game.'
),
_('Load dialogue data from Scene variable _PARAM0_'),
_('Load dialogue data from scene variable _PARAM0_'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter(
'scenevar',
_('Scene variable that holds the Yarn Json data'),
_('Scene variable that holds the Yarn JSON data'),
'',
false
)
@@ -57,11 +57,11 @@ module.exports = {
extension
.addAction(
'LoadDialogueFromJsonFile',
_('Load dialogue Tree from a Json File'),
_('Load dialogue tree from a JSON file'),
_(
'Load a dialogue data object - Yarn json format, stored in a Json file. Use this command to load all the Dialogue data at the beginning of the game.'
'Load a dialogue data object - Yarn JSON format, stored in a JSON file. Use this command to load all the Dialogue data at the beginning of the game.'
),
_('Load dialogue data from json file _PARAM1_'),
_('Load dialogue data from JSON file _PARAM1_'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
@@ -69,7 +69,7 @@ module.exports = {
.addCodeOnlyParameter('currentScene', '')
.addParameter(
'jsonResource',
_('Json file that holds the Yarn Json data'),
_('JSON file that holds the Yarn JSON data'),
'',
false
)
@@ -125,11 +125,11 @@ module.exports = {
extension
.addAction(
'ConfirmSelectOption',
_('Confirm selected Option'),
_('Confirm selected option'),
_(
'Set the selected option as confirmed, which will validate it and go forward to the next node. Use other actions to select options (see "select next option" and "Select previous option").'
),
_('Confirm selected Option'),
_('Confirm selected option'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
@@ -140,11 +140,11 @@ module.exports = {
extension
.addAction(
'SelectNextOption',
_('Select next Option'),
_('Select next option'),
_(
'Select next Option (add 1 to selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
'Select next option (add 1 to selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
),
_('Select next Option'),
_('Select next option'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
@@ -155,11 +155,11 @@ module.exports = {
extension
.addAction(
'SelectPreviousOption',
_('Select previous Option'),
_('Select previous option'),
_(
'Select previous Option (subtract 1 from selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
'Select previous option (subtract 1 from selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
),
_('Select previous Option'),
_('Select previous option'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
@@ -225,8 +225,8 @@ module.exports = {
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('State Variable Name'), '', false)
.addParameter('string', _('Variable string value'), '', false)
.addParameter('string', _('State variable name'), '', false)
.addParameter('string', _('New value'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.setVariable');
@@ -242,8 +242,8 @@ module.exports = {
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('State Variable Name'), '', false)
.addParameter('expression', _('Variable number value'), '', true)
.addParameter('string', _('State variable name'), '', false)
.addParameter('expression', _('New value'), '', true)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.setVariable');
@@ -259,8 +259,8 @@ module.exports = {
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('State Variable Name'), '', false)
.addParameter('trueorfalse', _('Variable boolean value'), '', false)
.addParameter('string', _('State variable name'), '', false)
.addParameter('trueorfalse', _('New value'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.setVariable');
@@ -336,9 +336,9 @@ module.exports = {
extension
.addStrExpression(
'Option',
_('Get the text of an option from an Options line type'),
_('Get the text of an option from an options line type'),
_(
"Get the text of an option from an Options line type, using the option's Number. The numbers start from 0."
"Get the text of an option from an options line type, using the option's Number. The numbers start from 0."
),
'',
'JsPlatform/Extensions/yarn32.png'
@@ -350,9 +350,9 @@ module.exports = {
extension
.addStrExpression(
'HorizontalOptionsList',
_('Get a Horizontal list of options from the Options line type'),
_('Get a Horizontal list of options from the options line type'),
_(
"Get the text of all available options from an Options line type as a horizontal list. You can also pass the selected option's cursor string, which by default is ->"
"Get the text of all available options from an options line type as a horizontal list. You can also pass the selected option's cursor string, which by default is ->"
),
'',
'JsPlatform/Extensions/yarn32.png'
@@ -365,9 +365,9 @@ module.exports = {
extension
.addStrExpression(
'VerticalOptionsList',
_('Get a Vertical list of options from the Options line type'),
_('Get a Vertical list of options from the options line type'),
_(
"Get the text of all available options from an Options line type as a vertical list. You can also pass the selected option's cursor string, which by default is ->"
"Get the text of all available options from an options line type as a vertical list. You can also pass the selected option's cursor string, which by default is ->"
),
'',
'JsPlatform/Extensions/yarn32.png'
@@ -507,14 +507,26 @@ module.exports = {
extension
.addExpression(
'Variable',
_('Get dialogue state value'),
_('Get dialogue state value'),
_('Get the number stored in a dialogue state variable'),
_('Get the number stored in a dialogue state variable'),
'',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Variable Name'), '', false)
.addParameter('string', _('Dialogue state variable name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getVariable');
.setFunctionName('gdjs.dialogueTree.getVariableAsNumber');
extension
.addStrExpression(
'VariableString',
_('Get the string stored in a dialogue state variable'),
_('Get the string stored in a dialogue state variable'),
'',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Dialogue state variable name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getVariableAsString');
extension
.addCondition(

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -882,6 +882,11 @@ module.exports = {
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('variable', _('Variable'), '', false)
.setParameterLongDescription(
_(
'Only root variables can change ownership. Arrays and structures children are synchronized with their parent.'
)
)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(_('Player number'))
@@ -916,6 +921,11 @@ module.exports = {
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('variable', _('Variable'), '', false)
.setParameterLongDescription(
_(
'Only root variables can change ownership. Arrays and structures children are synchronized with their parent.'
)
)
.setHelpPath('/all-features/multiplayer')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')
@@ -947,6 +957,11 @@ module.exports = {
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('variable', _('Variable'), '', false)
.setParameterLongDescription(
_(
'Only root variables can change ownership. Arrays and structures children are synchronized with their parent.'
)
)
.setHelpPath('/all-features/multiplayer')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Multiplayer/peer.js')

View File

@@ -917,12 +917,12 @@ namespace gdjs {
// As we are the host, we do not cancel the message if it times out.
shouldCancelMessageIfTimesOut: false,
});
for (const peerId of otherPeerIds) {
debugLogger.info(
`Relaying ownership change of variable with Id ${variableNetworkId} to ${peerId}.`
);
sendDataTo(otherPeerIds, messageName, messageData);
}
debugLogger.info(
`Relaying ownership change of variable with Id ${variableNetworkId} to ${otherPeerIds.join(
', '
)}.`
);
sendDataTo(otherPeerIds, messageName, messageData);
}
});
});
@@ -1361,6 +1361,11 @@ namespace gdjs {
// As we are the host, we do not cancel the message if it times out.
shouldCancelMessageIfTimesOut: false,
});
debugLogger.info(
`Relaying instance destroyed message for object ${objectName} with instance network ID ${instanceNetworkId} to ${otherPeerIds.join(
', '
)}.`
);
sendDataTo(otherPeerIds, messageName, messageData);
}
});

View File

@@ -254,6 +254,7 @@ namespace gdjs {
// If game is running and the object belongs to a player who is not connected, destroy the object.
// As the game may create objects before the lobby game starts, we don't want to destroy them if it's not running.
if (
this.actionOnPlayerDisconnect !== 'DoNothing' && // Should not delete if flagged as such.
this.playerNumber !== 0 && // Host is always connected.
!gdjs.multiplayerMessageManager.isPlayerConnected(this.playerNumber)
) {

View File

@@ -340,7 +340,8 @@ module.exports = {
_(
'The friction applied when touching other objects. The higher the value, the more friction.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('restitution')
.setValue(
@@ -352,7 +353,8 @@ module.exports = {
_(
'The "bounciness" of the object. The higher the value, the more other objects will bounce against it.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('linearDamping')
.setValue(
@@ -695,7 +697,7 @@ module.exports = {
.addCondition(
'IsDynamic',
_('Is dynamic'),
_('Test if an object is dynamic.'),
_('Check if an object is dynamic.'),
_('_PARAM0_ is dynamic'),
_('Dynamics'),
'res/physics32.png',
@@ -727,7 +729,7 @@ module.exports = {
.addCondition(
'IsStatic',
_('Is static'),
_('Test if an object is static.'),
_('Check if an object is static.'),
_('_PARAM0_ is static'),
_('Dynamics'),
'res/physics32.png',
@@ -759,7 +761,7 @@ module.exports = {
.addCondition(
'IsKinematic',
_('Is kinematic'),
_('Test if an object is kinematic.'),
_('Check if an object is kinematic.'),
_('_PARAM0_ is kinematic'),
_('Dynamics'),
'res/physics32.png',
@@ -790,9 +792,9 @@ module.exports = {
aut
.addCondition(
'IsBullet',
_('Is treat as bullet'),
_('Test if an object is being treat as a bullet.'),
_('_PARAM0_ is bullet'),
_('Is treated as a bullet'),
_('Check if the object is being treated as a bullet.'),
_('_PARAM0_ is treated as a bullet'),
_('Dynamics'),
'res/physics32.png',
'res/physics32.png'
@@ -825,7 +827,7 @@ module.exports = {
.addCondition(
'HasFixedRotation',
_('Has fixed rotation'),
_('Test if an object has fixed rotation.'),
_('Check if an object has fixed rotation.'),
_('_PARAM0_ has fixed rotation'),
_('Dynamics'),
'res/physics32.png',
@@ -859,7 +861,7 @@ module.exports = {
.addCondition(
'IsSleepingAllowed',
_('Is sleeping allowed'),
_('Test if an object can sleep.'),
_('Check if an object can sleep.'),
_('_PARAM0_ can sleep'),
_('Dynamics'),
'res/physics32.png',
@@ -898,7 +900,7 @@ module.exports = {
.addCondition(
'IsSleeping',
_('Is sleeping'),
_('Test if an object is sleeping.'),
_('Check if an object is sleeping.'),
_('_PARAM0_ is sleeping'),
_('Dynamics'),
'res/physics32.png',
@@ -1267,7 +1269,7 @@ module.exports = {
.addCondition(
'LayerEnabled',
_('Layer enabled'),
_('Test if an object has a specific layer enabled.'),
_('Check if an object has a specific layer enabled.'),
_('_PARAM0_ has layer _PARAM2_ enabled'),
_('Filtering'),
'res/physics32.png',
@@ -1303,7 +1305,7 @@ module.exports = {
.addCondition(
'MaskEnabled',
_('Mask enabled'),
_('Test if an object has a specific mask enabled.'),
_('Check if an object has a specific mask enabled.'),
_('_PARAM0_ has mask _PARAM2_ enabled'),
_('Filtering'),
'res/physics32.png',
@@ -1897,7 +1899,7 @@ module.exports = {
.addCondition(
'JointFirstObject',
_('Joint first object'),
_('Test if an object is the first object on a joint.'),
_('Check if an object is the first object on a joint.'),
_('_PARAM0_ is the first object for joint _PARAM2_'),
_('Joints'),
'res/physics32.png',
@@ -1913,7 +1915,7 @@ module.exports = {
.addCondition(
'JointSecondObject',
_('Joint second object'),
_('Test if an object is the second object on a joint.'),
_('Check if an object is the second object on a joint.'),
_('_PARAM0_ is the second object for joint _PARAM2_'),
_('Joints'),
'res/physics32.png',
@@ -2382,7 +2384,7 @@ module.exports = {
.addCondition(
'RevoluteJointLimitsEnabled',
_('Revolute joint limits enabled'),
_('Test if a revolute joint limits are enabled.'),
_('Check if a revolute joint limits are enabled.'),
_('Limits for revolute joint _PARAM2_ are enabled'),
_('Joints/Revolute'),
'JsPlatform/Extensions/revolute_joint24.png',
@@ -2461,7 +2463,7 @@ module.exports = {
.addCondition(
'RevoluteJointMotorEnabled',
_('Revolute joint motor enabled'),
_('Test if a revolute joint motor is enabled.'),
_('Check if a revolute joint motor is enabled.'),
_('Motor of revolute joint _PARAM2_ is enabled'),
_('Joints/Revolute'),
'JsPlatform/Extensions/revolute_joint24.png',
@@ -2700,7 +2702,7 @@ module.exports = {
.addCondition(
'PrismaticJointLimitsEnabled',
_('Prismatic joint limits enabled'),
_('Test if a prismatic joint limits are enabled.'),
_('Check if a prismatic joint limits are enabled.'),
_('Limits for prismatic joint _PARAM2_ are enabled'),
_('Joints/Prismatic'),
'JsPlatform/Extensions/prismatic_joint24.png',
@@ -2779,7 +2781,7 @@ module.exports = {
.addCondition(
'PrismaticJointMotorEnabled',
_('Prismatic joint motor enabled'),
_('Test if a prismatic joint motor is enabled.'),
_('Check if a prismatic joint motor is enabled.'),
_('Motor for prismatic joint _PARAM2_ is enabled'),
_('Joints/Prismatic'),
'JsPlatform/Extensions/prismatic_joint24.png',
@@ -3459,7 +3461,7 @@ module.exports = {
.addCondition(
'WheelJointMotorEnabled',
_('Wheel joint motor enabled'),
_('Test if a wheel joint motor is enabled.'),
_('Check if a wheel joint motor is enabled.'),
_('Motor for wheel joint _PARAM2_ is enabled'),
_('Joints/Wheel'),
'JsPlatform/Extensions/wheel_joint24.png',
@@ -4203,7 +4205,7 @@ module.exports = {
.getCodeExtraInformation()
.addIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.setFunctionName('gdjs.physics2.objectsCollide');
.setFunctionName('gdjs.physics2.areObjectsColliding');
extension
.addCondition(

View File

@@ -35,19 +35,23 @@ namespace gdjs {
invScaleX: float;
/** @deprecated Use `worldInvScale` instead */
invScaleY: float;
timeStep: float;
frameTime: float = 0;
stepped: boolean = false;
timeScale: float = 1;
world: Box2D.b2World;
staticBody: Box2D.b2Body;
/** Contact listener to keep track of current collisions */
contactListener: Box2D.JSContactListener;
_nextJointId: number = 1;
/** Start with 1 so the user is safe from default variables value (0) */
joints: { [key: string]: Box2D.b2Joint } = {};
/** Avoid creating new vectors all the time */
_tempb2Vec2 = new Box2D.b2Vec2(0, 0);
/** Sometimes two vectors are needed on the same function call */
_tempb2Vec2Sec = new Box2D.b2Vec2(0, 0);
/**
* List of physics behavior in the runtimeScene. It should be updated
@@ -68,9 +72,7 @@ namespace gdjs {
sharedData.worldScale || Math.sqrt(this.scaleX * this.scaleY);
this.worldInvScale = 1 / this.worldScale;
this.timeStep = 1 / 60;
this.world = new Box2D.b2World(
new Box2D.b2Vec2(this.gravityX, this.gravityY)
);
this.world = new Box2D.b2World(this.b2Vec2(this.gravityX, this.gravityY));
this.world.SetAutoClearForces(false);
this.staticBody = this.world.CreateBody(new Box2D.b2BodyDef());
this.contactListener = new Box2D.JSContactListener();
@@ -135,6 +137,13 @@ namespace gdjs {
this.world.SetContactListener(this.contactListener);
}
b2Vec2(x: float, y: float): Box2D.b2Vec2 {
const tempb2Vec2 = this._tempb2Vec2;
tempb2Vec2.set_x(x);
tempb2Vec2.set_y(y);
return tempb2Vec2;
}
// (string)jointId -> (b2Joint)b2Joint
static getSharedData(
runtimeScene: gdjs.RuntimeScene,
@@ -169,27 +178,16 @@ namespace gdjs {
this._registeredBehaviors.delete(physicsBehavior);
}
/**
* Reset all contactsStartedThisFrame and contactsEndedThisFrame of all
* registered physics behavior.
*/
resetStartedAndEndedCollisions(): void {
step(deltaTime: float): void {
// Reset started and ended contacts array for all physics instances.
for (const physicsBehavior of this._registeredBehaviors) {
physicsBehavior.contactsStartedThisFrame.length = 0;
physicsBehavior.contactsEndedThisFrame.length = 0;
}
}
/**
* Update all registered body.
*/
updateBodiesFromObjects(): void {
for (const physicsBehavior of this._registeredBehaviors) {
physicsBehavior.updateBodyFromObject();
}
}
step(deltaTime: float): void {
this.frameTime += deltaTime;
// `frameTime` can take negative values.
// It's better to be a bit early rather than skipping a frame and being
@@ -207,6 +205,13 @@ namespace gdjs {
}
this.world.ClearForces();
this.stepped = true;
// It's important that updateBodyFromObject and updateObjectFromBody are
// called at the same time because other behavior may move the object in
// their doStepPreEvents.
for (const physicsBehavior of this._registeredBehaviors) {
physicsBehavior.updateObjectFromBody();
}
}
clearBodyJoints(body: Box2D.b2Body): void {
@@ -300,14 +305,11 @@ namespace gdjs {
}
}
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
if (
// @ts-ignore
runtimeScene.physics2SharedData &&
// @ts-ignore
runtimeScene.physics2SharedData.world
) {
// @ts-ignore
Box2D.destroy(runtimeScene.physics2SharedData.world);
const physics2SharedData = runtimeScene.physics2SharedData;
if (physics2SharedData && physics2SharedData.world) {
Box2D.destroy(physics2SharedData.world);
Box2D.destroy(physics2SharedData._tempb2Vec2);
Box2D.destroy(physics2SharedData._tempb2Vec2Sec);
}
});
@@ -358,8 +360,6 @@ namespace gdjs {
currentContacts: Array<Physics2RuntimeBehavior>;
destroyedDuringFrameLogic: boolean;
_body: Box2D.b2Body | null = null;
/** Avoid creating new vectors all the time */
_tempb2Vec2: Box2D.b2Vec2;
/**
* sharedData is a reference to the shared data of the scene, that registers
@@ -367,8 +367,6 @@ namespace gdjs {
* before stepping the world.
*/
_sharedData: Physics2SharedData;
/** Sometimes two vectors are needed on the same function call */
_tempb2Vec2Sec: Box2D.b2Vec2;
_objectOldX: number = 0;
_objectOldY: number = 0;
@@ -414,22 +412,22 @@ namespace gdjs {
instanceContainer.getScene(),
behaviorData.name
);
this._tempb2Vec2 = new Box2D.b2Vec2();
this._tempb2Vec2Sec = new Box2D.b2Vec2();
this._sharedData.addToBehaviorsList(this);
}
// Stores a Box2D pointer of created vertices
b2Vec2(x: float, y: float): Box2D.b2Vec2 {
this._tempb2Vec2.set_x(x);
this._tempb2Vec2.set_y(y);
return this._tempb2Vec2;
const tempb2Vec2 = this._sharedData._tempb2Vec2;
tempb2Vec2.set_x(x);
tempb2Vec2.set_y(y);
return tempb2Vec2;
}
b2Vec2Sec(x: float, y: float): Box2D.b2Vec2 {
this._tempb2Vec2Sec.set_x(x);
this._tempb2Vec2Sec.set_y(y);
return this._tempb2Vec2Sec;
const tempb2Vec2Sec = this._sharedData._tempb2Vec2Sec;
tempb2Vec2Sec.set_x(x);
tempb2Vec2Sec.set_y(y);
return tempb2Vec2Sec;
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
@@ -678,16 +676,18 @@ namespace gdjs {
// Average radius from width and height
if (this.shapeDimensionA > 0) {
shape.set_m_radius(
this.shapeDimensionA *
Math.max(0.1, this.shapeDimensionA) *
this.shapeScale *
this._sharedData.worldInvScale
);
} else {
const radius =
(this.owner.getWidth() * this._sharedData.worldInvScale +
this.owner.getHeight() * this._sharedData.worldInvScale) /
(Math.max(0.1, this.owner.getWidth()) *
this._sharedData.worldInvScale +
Math.max(0.1, this.owner.getHeight()) *
this._sharedData.worldInvScale) /
4;
shape.set_m_radius(radius > 0 ? radius : 1);
shape.set_m_radius(radius);
}
// Set the offset
@@ -701,11 +701,11 @@ namespace gdjs {
!this.polygon ||
!Physics2RuntimeBehavior.isPolygonConvex(this.polygon)
) {
let width =
(this.owner.getWidth() > 0 ? this.owner.getWidth() : 1) *
const width =
Math.max(0.1, this.owner.getWidth()) *
this._sharedData.worldInvScale;
let height =
(this.owner.getHeight() > 0 ? this.owner.getHeight() : 1) *
const height =
Math.max(0.1, this.owner.getHeight()) *
this._sharedData.worldInvScale;
// Set the shape box
@@ -775,14 +775,12 @@ namespace gdjs {
// Length from the custom dimension or from the object width
const length =
(this.shapeDimensionA > 0
? this.shapeDimensionA * this.shapeScale
: this.owner.getWidth() > 0
? this.owner.getWidth()
: 1) * this._sharedData.worldInvScale;
let height =
this.owner.getHeight() > 0
? this.owner.getHeight() * this._sharedData.worldInvScale
: 0;
? Math.max(0.1, this.shapeDimensionA * this.shapeScale)
: Math.max(0.1, this.owner.getWidth())) *
this._sharedData.worldInvScale;
const height =
Math.max(0.1, this.owner.getHeight()) *
this._sharedData.worldInvScale;
// Angle from custom dimension, otherwise is 0
const angle = this.shapeDimensionB
@@ -805,18 +803,16 @@ namespace gdjs {
shape = new Box2D.b2PolygonShape();
// Width and height from custom dimensions or object size
let width =
const width =
(this.shapeDimensionA > 0
? this.shapeDimensionA * this.shapeScale
: this.owner.getWidth() > 0
? this.owner.getWidth()
: 1) * this._sharedData.worldInvScale;
let height =
? Math.max(0.1, this.shapeDimensionA * this.shapeScale)
: Math.max(0.1, this.owner.getWidth())) *
this._sharedData.worldInvScale;
const height =
(this.shapeDimensionB > 0
? this.shapeDimensionB * this.shapeScale
: this.owner.getHeight() > 0
? this.owner.getHeight()
: 1) * this._sharedData.worldInvScale;
? Math.max(0.1, this.shapeDimensionB * this.shapeScale)
: Math.max(0.1, this.owner.getHeight())) *
this._sharedData.worldInvScale;
// Set the shape box, the offset must be added here too
shape.SetAsBox(
@@ -946,20 +942,28 @@ namespace gdjs {
!this._sharedData.stepped &&
!instanceContainer.getScene().getTimeManager().isFirstFrame()
) {
// Reset started and ended contacts array for all physics instances.
this._sharedData.resetStartedAndEndedCollisions();
this._sharedData.updateBodiesFromObjects();
this._sharedData.step(
instanceContainer.getScene().getTimeManager().getElapsedTime() /
1000.0
);
}
}
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// Reset world step to update next frame
this._sharedData.stepped = false;
}
onObjectHotReloaded() {
this.updateBodyFromObject();
}
updateObjectFromBody() {
// Copy transform from body to the GD object.
// It's possible the behavior was either deactivated or the object deleted
// just before this doStepPreEvents (for example, another behavior deleted
// the object during its own doStepPreEvents). If the body is null, we just
// don't do anything (but still run the physics simulation - this is independent).
// The body is null when the behavior was either deactivated or the object deleted.
// It would be useless to try to recreate it as updateBodyFromObject already does it.
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (this._body !== null) {
this.owner.setX(
this._body.GetPosition().get_x() * this._sharedData.worldScale -
@@ -982,15 +986,6 @@ namespace gdjs {
this._objectOldAngle = this.owner.getAngle();
}
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// Reset world step to update next frame
this._sharedData.stepped = false;
}
onObjectHotReloaded() {
this.updateBodyFromObject();
}
updateBodyFromObject() {
// If there is no body, set a new one
if (this._body === null) {

View File

@@ -1,6 +1,6 @@
namespace gdjs {
export namespace physics2 {
export const objectsCollide = function (
export const areObjectsColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
namespace gdjs {
export namespace physics3d {
export const areObjectsColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName2: string,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics3DRuntimeBehavior.areObjectsColliding,
objectsLists1,
objectsLists2,
inverted,
behaviorName
);
};
export const haveObjectsStartedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName2: string,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics3DRuntimeBehavior.hasCollisionStartedBetween,
objectsLists1,
objectsLists2,
inverted,
behaviorName
);
};
export const haveObjectsStoppedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName2: string,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics3DRuntimeBehavior.hasCollisionStoppedBetween,
objectsLists1,
objectsLists2,
inverted,
behaviorName
);
};
type BehaviorNamePair = { character: string; physics: string };
const isOnPlatformAdapter = (
characterObject: gdjs.RuntimeObject,
physicsObject: gdjs.RuntimeObject,
behaviorNamePair: BehaviorNamePair
): boolean => {
const characterBehavior = characterObject.getBehavior(
behaviorNamePair.character
) as gdjs.PhysicsCharacter3DRuntimeBehavior;
const physicsBehavior = physicsObject.getBehavior(
behaviorNamePair.physics
) as gdjs.Physics3DRuntimeBehavior;
if (!characterBehavior || !physicsBehavior) {
return false;
}
return characterBehavior.isOnFloorObject(physicsBehavior);
};
const behaviorNamePair: BehaviorNamePair = { character: '', physics: '' };
export const isOnPlatform = (
characterObjectsLists: Hashtable<Array<gdjs.RuntimeObject>>,
characterBehaviorName: string,
physicsObjectsLists: Hashtable<Array<gdjs.RuntimeObject>>,
physicsBehaviorName: string,
inverted: boolean
) => {
behaviorNamePair.character = characterBehaviorName;
behaviorNamePair.physics = physicsBehaviorName;
return gdjs.evtTools.object.twoListsTest(
isOnPlatformAdapter,
characterObjectsLists,
physicsObjectsLists,
inverted,
behaviorNamePair
);
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
## Physics 3D Behaviors for GDevelop
This is the 3D physics engine for GDevelop, based on [Jolt Physics](https://github.com/jrouwe/JoltPhysics.js/) (WebAssembly, version 0.30.0).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1 @@
describe('Physics3DRuntimeBehavior', () => {});

View File

@@ -231,9 +231,13 @@ namespace gdjs {
...super.getNetworkSyncData(),
props: {
cs: this._currentSpeed,
// TODO Try to remove these 3 fields from the synch
// They are reset every frame and are not part of the state.
rdx: this._requestedDeltaX,
rdy: this._requestedDeltaY,
ldy: this._lastDeltaY,
cfs: this._currentFallSpeed,
cj: this._canJump,
ldl: this._lastDirectionIsLeft,

View File

@@ -11,16 +11,18 @@ This project is released under the MIT License.
#include "GDCore/Project/InitialInstance.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Project/PropertyDescriptor.h"
using namespace std;
ShapePainterObjectBase::ShapePainterObjectBase()
: fillOpacity(255),
fillColor("255;255;255"),
outlineSize(1),
outlineOpacity(255),
outlineColor("0;0;0"),
clearBetweenFrames(true),
absoluteCoordinates(false),
antialiasing("none") {}
@@ -37,7 +39,6 @@ void ShapePainterObjectBase::DoUnserializeFrom(
.GetValue()
.GetInt();
const auto& fillColorElement = element.GetChild("fillColor", 0, "FillColor");
if (fillColorElement.GetValue().IsString()) {
fillColor = fillColorElement.GetStringValue();
@@ -46,11 +47,14 @@ void ShapePainterObjectBase::DoUnserializeFrom(
int fillColorR = fillColorElement.GetIntAttribute("r");
int fillColorG = fillColorElement.GetIntAttribute("g");
int fillColorB = fillColorElement.GetIntAttribute("b");
fillColor = gd::String::From(fillColorR) + ";" + gd::String::From(fillColorG) + ";" + gd::String::From(fillColorB);
fillColor = gd::String::From(fillColorR) + ";" +
gd::String::From(fillColorG) + ";" +
gd::String::From(fillColorB);
// end of compatibility code
}
const auto& outlineColorElement = element.GetChild("outlineColor", 0, "OutlineColor");
const auto& outlineColorElement =
element.GetChild("outlineColor", 0, "OutlineColor");
if (outlineColorElement.GetValue().IsString()) {
outlineColor = outlineColorElement.GetStringValue();
} else {
@@ -58,7 +62,9 @@ void ShapePainterObjectBase::DoUnserializeFrom(
int outlineColorR = outlineColorElement.GetIntAttribute("r");
int outlineColorG = outlineColorElement.GetIntAttribute("g");
int outlineColorB = outlineColorElement.GetIntAttribute("b");
outlineColor = gd::String::From(outlineColorR) + ";" + gd::String::From(outlineColorG) + ";" + gd::String::From(outlineColorB);
outlineColor = gd::String::From(outlineColorR) + ";" +
gd::String::From(outlineColorG) + ";" +
gd::String::From(outlineColorB);
// end of compatibility code
}
@@ -94,8 +100,9 @@ void ShapePainterObjectBase::DoSerializeTo(
auto rgb = fillColor.Split(';');
auto& fillColorElement = element.AddChild("fillColor");
if (rgb.size() == 3) {
// Still serialize the old particle color components for compatibility with GDevelop <= 5.4.212.
// Remove this in a few releases (or when hex strings are accepted for the color).
// Still serialize the old particle color components for compatibility
// with GDevelop <= 5.4.212. Remove this in a few releases (or when hex
// strings are accepted for the color).
fillColorElement.AddChild("r").SetValue(rgb[0].To<double>());
fillColorElement.AddChild("g").SetValue(rgb[1].To<double>());
fillColorElement.AddChild("b").SetValue(rgb[2].To<double>());
@@ -108,8 +115,9 @@ void ShapePainterObjectBase::DoSerializeTo(
auto rgb = outlineColor.Split(';');
auto& outlineColorElement = element.AddChild("outlineColor");
if (rgb.size() == 3) {
// Still serialize the old particle color components for compatibility with GDevelop <= 5.4.212.
// Remove this in a few releases (or when hex strings are accepted for the color).
// Still serialize the old particle color components for compatibility
// with GDevelop <= 5.4.212. Remove this in a few releases (or when hex
// strings are accepted for the color).
outlineColorElement.AddChild("r").SetValue(rgb[0].To<double>());
outlineColorElement.AddChild("g").SetValue(rgb[1].To<double>());
outlineColorElement.AddChild("b").SetValue(rgb[2].To<double>());
@@ -143,7 +151,7 @@ void ShapePainterObjectBase::SetOutlineOpacity(double val) {
}
bool ShapePainterObject::UpdateProperty(const gd::String& propertyName,
const gd::String& newValue) {
const gd::String& newValue) {
if (propertyName == "fillOpacity") {
SetFillOpacity(newValue.To<double>());
return true;
@@ -186,8 +194,8 @@ bool ShapePainterObject::UpdateProperty(const gd::String& propertyName,
return false;
}
std::map<gd::String, gd::PropertyDescriptor>
ShapePainterObject::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> ShapePainterObject::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> objectProperties;
objectProperties["fillColor"]
@@ -231,7 +239,9 @@ ShapePainterObject::GetProperties() const {
.SetType("boolean")
.SetLabel(_("Clear drawing at each frame"))
.SetGroup(_("Drawing"))
.SetDescription(_("When activated, clear the previous render at each frame. Otherwise, shapes are staying on the screen until you clear manually the object in events."));
.SetDescription(_("When activated, clear the previous render at each "
"frame. Otherwise, shapes are staying on the screen "
"until you clear manually the object in events."));
objectProperties["antialiasing"]
.SetValue(GetAntialiasing())

View File

@@ -32,13 +32,13 @@ class GD_EXTENSION_API ShapePainterObjectBase {
void SetOutlineOpacity(double val);
inline double GetOutlineOpacity() const { return outlineOpacity; };
void SetOutlineColor(const gd::String& color);
void SetOutlineColor(const gd::String& color) { outlineColor = color; };
const gd::String& GetOutlineColor() const { return outlineColor; };
void SetFillOpacity(double val);
inline double GetFillOpacity() const { return fillOpacity; };
void SetFillColor(const gd::String& color);
void SetFillColor(const gd::String& color) { fillColor = color; };
const gd::String& GetFillColor() const { return fillColor; };
inline void SetCoordinatesAbsolute() { absoluteCoordinates = true; }

View File

@@ -194,5 +194,13 @@ namespace gdjs {
? resource
: null;
}
/**
* To be called when the game is disposed.
* Clear the Spine Atlases loaded in this manager.
*/
dispose(): void {
this._loadedSpineAtlases.clear();
this._loadingSpineAtlases.clear();
}
}
}

View File

@@ -115,5 +115,13 @@ namespace gdjs {
? resource
: null;
}
/**
* To be called when the game is disposed.
* Clear the Spine skeleton data loaded in this manager.
*/
dispose(): void {
this._loadedSpines.clear();
}
}
}

View File

@@ -129,10 +129,27 @@ namespace gdjs {
// Hide the input entirely if the layer is not visible.
// Because this object is rendered as a DOM element (and not part of the PixiJS
// scene graph), we have to do this manually.
const layer = this._instanceContainer.getLayer(this._object.getLayer());
if (!layer.isVisible()) {
this._input.style.display = 'none';
return;
{
let instanceContainer = this._instanceContainer;
let object: gdjs.RuntimeObject = this._object;
let hasParent = true;
do {
const layer = instanceContainer.getLayer(object.getLayer());
if (!layer.isVisible() || !object.isVisible()) {
this._input.style.display = 'none';
return;
}
// TODO Declare an interface to move up in the object tree.
if (
instanceContainer instanceof
gdjs.CustomRuntimeObjectInstanceContainer
) {
object = instanceContainer.getOwner();
instanceContainer = object.getInstanceContainer();
} else {
hasParent = false;
}
} while (hasParent);
}
const workingPoint: FloatPoint = gdjs.staticArray(
@@ -141,6 +158,7 @@ namespace gdjs {
const runtimeGame = this._instanceContainer.getGame();
const runtimeGameRenderer = runtimeGame.getRenderer();
const layer = this._instanceContainer.getLayer(this._object.getLayer());
const topLeftCanvasCoordinates = layer.convertInverseCoords(
this._object.x,
this._object.y,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -36,7 +36,7 @@ namespace gdjs {
*/
private static readonly workingPoint: FloatPoint = [0, 0];
_opacity: float;
_opacity: float = 255;
_atlasImage: string;
_tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
_renderer: gdjs.TileMapRuntimeObjectPixiRenderer;
@@ -45,7 +45,7 @@ namespace gdjs {
readonly _tileSize: number;
_displayMode = 'all';
_layerIndex = 0;
_initialTileMapAsJsObject: TileMapHelper.EditableTileMapAsJsObject | null = null;
_initialTileMapAsJsObject: TileMapHelper.EditableTileMapAsJsObject;
readonly _initialTilesWithHitBox: number[];
_isTileMapDirty: boolean = false;
_sceneToTileMapTransformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
@@ -66,6 +66,13 @@ namespace gdjs {
this._rowCount = objectData.content.rowCount;
this._columnCount = objectData.content.columnCount;
this._tileSize = objectData.content.tileSize;
this._initialTileMapAsJsObject = {
tileWidth: this._tileSize,
tileHeight: this._tileSize,
dimX: 1,
dimY: 1,
layers: [{ id: 0, alpha: this._opacity / 255, tiles: [] }],
};
this._initialTilesWithHitBox = (objectData.content
.tilesWithHitBox as string)
.split(',')
@@ -79,6 +86,17 @@ namespace gdjs {
instanceContainer
);
this._loadInitialTileMap((tileMap: TileMapHelper.EditableTileMap) => {
this._renderer.updatePosition();
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
tileMap,
this._hitBoxTag
);
this.updateTransformation();
});
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}

View File

@@ -10,7 +10,6 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
metadata: '',
name: 'SmallTiledMap.json',
userAdded: true,
alwaysLoaded: true,
},
{
file: 'base/tests-utils/simple-tiled-map/FlippingTiledMap.json',
@@ -18,7 +17,6 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
metadata: '',
name: 'FlippingTiledMap.json',
userAdded: true,
alwaysLoaded: true,
},
{
file: 'base/tests-utils/simple-tiled-map/MiniTiledSet.json',
@@ -26,7 +24,6 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
metadata: '',
name: 'MiniTiledSet.json',
userAdded: true,
alwaysLoaded: true,
},
],
},

View File

@@ -129,12 +129,11 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsAndGroups;
gd::ObjectsContainer parameterObjectsAndGroups(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project,
eventsFunctionsExtension,
eventsFunction,
project, eventsFunctionsExtension, eventsFunction,
parameterObjectsAndGroups);
EventsCodeGenerator codeGenerator(projectScopedContainers);
@@ -184,14 +183,12 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
const gd::String& preludeCode,
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project,
eventsFunctionsExtension,
eventsBasedBehavior,
eventsFunction,
parameterObjectsContainers);
project, eventsFunctionsExtension, eventsBasedBehavior,
eventsFunction, parameterObjectsContainers);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
@@ -266,13 +263,11 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
const gd::String& endingCode,
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers;
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project,
eventsFunctionsExtension,
eventsBasedObject,
eventsFunction,
project, eventsFunctionsExtension, eventsBasedObject, eventsFunction,
parameterObjectsContainers);
EventsCodeGenerator codeGenerator(projectScopedContainers);
@@ -1390,14 +1385,22 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
} else if (scope == LAYOUT_VARIABLE) {
output = "runtimeScene.getScene().getVariables()";
const auto *legacySceneVariables = GetProjectScopedContainers().GetLegacySceneVariables();
if (HasProjectAndLayout()) {
variables = &GetLayout().GetVariables();
} else if (legacySceneVariables && legacySceneVariables->Has(variableName)) {
variables = legacySceneVariables;
output = "eventsFunctionContext.sceneVariablesForExtension";
}
} else if (scope == PROJECT_VARIABLE) {
output = "runtimeScene.getGame().getVariables()";
const auto *legacyGlobalVariables = GetProjectScopedContainers().GetLegacyGlobalVariables();
if (HasProjectAndLayout()) {
variables = &GetProject().GetVariables();
} else if (legacyGlobalVariables && legacyGlobalVariables->Has(variableName)) {
variables = legacyGlobalVariables;
output = "eventsFunctionContext.globalVariablesForExtension";
}
} else {
std::vector<gd::String> realObjects =

View File

@@ -154,6 +154,9 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
.AddDefaultBehavior("TextContainerCapability::TextContainerBehavior");
}
if (eventsBasedObject.IsPrivate())
objectMetadata.SetPrivate();
// TODO EBO Use full type to identify object to avoid collision.
// Objects are identified by their name alone.
const gd::String &objectType = eventsBasedObject.GetName();

View File

@@ -102,6 +102,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
/*includeWebsocketDebuggerClient=*/false,
/*includeWindowMessageDebuggerClient=*/false,
/*includeMinimalDebuggerClient=*/false,
/*includeCaptureManager*/ false,
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
@@ -119,8 +120,11 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
helper.ExportEffectIncludes(exportedProject, includesFiles);
// Export events
if (!helper.ExportEventsCode(exportedProject, codeOutputDir, includesFiles,
wholeProjectDiagnosticReport, false)) {
if (!helper.ExportEventsCode(exportedProject,
codeOutputDir,
includesFiles,
wholeProjectDiagnosticReport,
false)) {
gd::LogError(_("Error during exporting! Unable to export events:\n") +
lastError);
return false;
@@ -139,11 +143,11 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
for (std::size_t layoutIndex = 0;
layoutIndex < exportedProject.GetLayoutsCount(); layoutIndex++) {
layoutIndex < exportedProject.GetLayoutsCount();
layoutIndex++) {
auto &layout = exportedProject.GetLayout(layoutIndex);
scenesUsedResources[layout.GetName()] =
gd::SceneResourcesFinder::FindSceneResources(exportedProject,
layout);
gd::SceneResourcesFinder::FindSceneResources(exportedProject, layout);
}
// Strip the project (*after* generating events as the events may use
@@ -152,8 +156,11 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
//...and export it
gd::SerializerElement noRuntimeGameOptions;
helper.ExportProjectData(fs, exportedProject, codeOutputDir + "/data.js",
noRuntimeGameOptions, projectUsedResources,
helper.ExportProjectData(fs,
exportedProject,
codeOutputDir + "/data.js",
noRuntimeGameOptions,
projectUsedResources,
scenesUsedResources);
includesFiles.push_back(codeOutputDir + "/data.js");
@@ -196,6 +203,13 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
if (!helper.ExportElectronFiles(
exportedProject, options.exportPath, usedExtensions))
return false;
if (!helper.ExportBuildResourcesElectronFiles(
// It's important to use the original project here, as the exported
// project can have its resources modified.
options.project,
options.exportPath))
return false;
} else if (options.target == "facebookInstantGames") {
if (!exportProject(options.exportPath)) return false;

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