Compare commits

...

180 Commits

Author SHA1 Message Date
Florian Rival
13b62a06eb Add option to allow building Android App Bundles that can be uploaded if you already published APKs with Play App Signing enabled for your game
* If you already opted in for "Play App Signing" in the Play Developer Console and published some APKs like this, you need to enable the new "Old upload key" option, in the Signing Option, before *each* packaging for Android.
* Read this [page to learn more](http://wiki.compilgames.net/doku.php/gdevelop5/publishing/android_and_ios/play-store/upgrading-from-apk-to-aab#done_you_can_now_upload_your_aab) and have step by step details of what to do to update your game.
* If you publish a new game with Android App Bundles, there is nothing to do!
2021-07-22 15:09:58 +01:00
Florian Rival
d27119d8ea Remove deprecated examples upload script from web-app deployment
Don't show in changelog
2021-07-21 17:25:40 +01:00
Florian Rival
dd3ff554d5 Upgrade AppVeyor CI to Node.js 14 (#2826)
[ci skip] [skip ci]
2021-07-20 23:36:56 +01:00
Florian Rival
287a17b634 Add a maximum size for the GDevelop logo so that it's not too big on large resolutions 2021-07-20 18:13:42 +01:00
Florian Rival
7dc477e29e Bump newIDE version 2021-07-20 15:53:20 +01:00
Florian Rival
8174bca3b1 Fix progress bar not shown if GDevelop logo was hidden
Don't show in changelog
2021-07-19 21:41:24 +01:00
Florian Rival
7f6388c6f5 Make the loading screen customizable (#2814)
* Allow to choose the style of the GDevelop logo (light, dark, colored or plain)
* Allow to choose the background color and an optional background image
* Allow to choose the size of the progress bar (with a minimum and maximum size)
* Allow to choose the duration of the fade in animations and the minimum time the loading screen is shown
* Use the **new preview button in the game properties** dialog to run a preview with the loading screen shown
2021-07-19 19:52:25 +01:00
Florian Rival
da3a099ff2 Fix increasing size of the project file whenever changes on a layer were cancelled
* This was because of a duplication of the "cameras" stored in the layer.
* Also add a check to clean project files with the problem.

Fix #2812
2021-07-17 11:56:04 +01:00
Florian Rival
7b2c7b4a00 Bump newIDE version 2021-07-15 18:06:49 +01:00
Florian Rival
f750a356c3 Add missing Flow annotations
Don't show in changelog
2021-07-15 14:50:02 +01:00
Florian Rival
611a72aee1 Fix focus not set when editing a mouse button field in the events sheet
* Also fix various missing Flow annotations
2021-07-14 22:46:18 +01:00
Florian Rival
1b965b65a4 Fix variables not shown properly in the debugger
* This removes toJSON method on gdjs.Variable. This was just introduced in beta 111, if you're using it, please use toJSObject instead and JSON.stringify.
2021-07-14 22:08:00 +01:00
Florian Rival
c077628eb4 Bump newIDE version 2021-07-14 18:12:06 +01:00
Leo_Red
f1a6da0cb2 Remove example issues auto close workflow (#2806)
Don't show in changelog
2021-07-14 14:42:35 +01:00
Aurélien Vivet
df4a780311 Give more consistency to the order of the buttons in the behaviors editor (#2803) 2021-07-13 22:48:58 +01:00
Florian Rival
8c955cf77a Remove useless "Behavior" text in the behaviors editor
Don't show in changelog
2021-07-13 16:15:51 +01:00
Florian Rival
17efae037c Fix crash when analyzing unused variables when a scene is depending on another scene 2021-07-13 16:09:01 +01:00
Florian Rival
f1120238ec Slightly improve rendering scheduling for games 2021-07-13 13:51:32 +01:00
Florian Rival
5d6a2bb3a0 Fix GDJS tests (regression since rework of GDJS build in the editor)
Don't show in changelog
2021-07-13 13:46:32 +01:00
Florian Rival
f3a49ad2cf Speed up GDJS build (including at editor startup) in development
This reduces the number of copied files (let esbuild build directly in the proper output folder).
Also avoid copying the source files after every change (avoiding ~3-4 seconds of copy).

Overall, a change in GDJS/Extensions results in 1-3 seconds of build, instead of almost 10 sometimes due to the large number of file copies.

Only show in developer changelog
2021-07-12 18:11:17 +01:00
Florian Rival
aac3379f59 Remove more deprecated strings from translations
Don't show in changelog
2021-07-12 14:30:28 +01:00
Florian Rival
49949189cf Remove deprecated strings from translations
Don't show in changelog
2021-07-12 11:50:29 +01:00
Florian Rival
977daa50fa Remove gcc from Travis CI matrix as too costly in build minutes
Don't show in changelog
2021-07-11 18:21:48 +01:00
Florian Rival
fd8a7df868 Fix wording 2021-07-11 17:16:04 +01:00
Florian Rival
303110ade5 Fix long description not shown for Yes/No and True/False parameter fields
Fix #2794
2021-07-11 17:16:04 +01:00
Florian Rival
cb15111d03 Display behavior icons in the behaviors editor
* Also better align some icons
* Show button to open the help page for all behaviors
2021-07-11 17:16:04 +01:00
Florian Rival
9db4d603a0 Refactor access to behavior/behavior shared data in the editor
Avoid having getters in the gd::Platform or the gd::Project, as they should not be responsible for this.
Instead, use gd::MetadataProvider to fetch the BehaviorMetadata.

Don't show in changelog
2021-07-11 17:16:04 +01:00
Florian Rival
6c7db9a948 Display extension icons in the project manager 2021-07-11 14:26:57 +01:00
Florian Rival
6b9a8f14e3 Center images in extension generated documentation pages
Don't show in changelog
2021-07-10 18:51:31 +01:00
Florian Rival
2f3a18046a Fix rendering of lists and author names in extensions generated documentation
Don't show in changelog
2021-07-10 18:46:17 +01:00
Florian Rival
812ff43905 Automatically generate pages on the wiki for the community extensions 2021-07-10 18:19:02 +01:00
Florian Rival
1a113004be Update extensions registry loading to use the GDevelop asset API instead of using GitHub directly
Don't show in changelog
2021-07-10 14:18:26 +01:00
Florian Rival
0dd10a7955 Improve extract changelog script
Don't show in changelog
2021-07-09 23:05:27 +01:00
Florian Rival
00b25461ed Fix "X" and "Y" expression for sprite objects (regression)
The default point being passed was incorrectly set as 0 instead of "" (empty string), which was misunderstood by the game engine as the origin point (which would be flipped)

Don't show in changelog
2021-07-09 15:32:48 +01:00
Florian Rival
9e28068e4f Fix Debugger Tools extension not available in the web-app 2021-07-08 20:01:07 +01:00
D8H
6b231f3eb6 Fix: instances editor no longer give unnecessary floating positions (#2790)
* Fix: Moving instances and resizing one instance no longer give floating positions or dimensions on 0° rotated instances.
Don't show in changelog
2021-07-07 20:12:14 +02:00
Florian Rival
2dd2c85f12 Update translations 2021-07-06 17:20:43 +01:00
Florian Rival
c61faa7e05 Clean up some dead code
Don't show in changelog
2021-07-06 17:13:00 +01:00
Florian Rival
1840f6f45a Save a bit of time at startup by avoiding to pre-fetch resources
Don't show in changelog
2021-07-06 15:54:12 +01:00
Florian Rival
b11eccfa22 Clarify in the list of effects that Kawase blur should be preferred over the classic Gaussian blur for performance 2021-07-06 15:53:37 +01:00
Florian Rival
9236608502 Fix broken sortable list of animations (regression)
Fix #2784

Don't show in changelog
2021-07-06 15:32:35 +01:00
Florian Rival
12a6bc23c3 Add an option to package games as Android App Bundles, to publish them on Google Play
* Starting from August, it will be mandatory to upload Android App Bundles to Google Play - APK files won't be accepted anymore.
2021-07-04 19:29:13 +01:00
Florian Rival
23c2b84707 Fix animation scrolling in the Sprite object editor on small screens 2021-07-03 13:00:32 +01:00
Florian Rival
adb4f2a6cf Fix animation editor preview speed being too fast on monitors with high refresh rate
* Also fix scrolling/zooming in the preview

Fix #2779
2021-07-03 12:03:18 +01:00
D8H
aa3ed78eda Improve selection in the scene editor so that multiple instances can be properly rotated and resized (#2751)
* When resizing or rotating multiple instances, they will keep their relative positions.
* When resizing rotated instances, their aspect ratio will be kept to avoid an unintuitive resize effect 
* This makes it easier to edit chunks of levels, scale them, resize them and rotate them.
2021-07-01 23:26:26 +01:00
Aurélien Vivet
efb8668077 Allow popups in the events sheet to be validated after a change using Ctrl+Enter (or Cmd+Enter on macOS) (#2569)
* Also add a preference to choose if Escape should cancel any changes made (like in a dialog) or not (like previously).
2021-06-30 22:03:19 +01:00
Florian Rival
8d763d4600 Put the button to restore the default collision mask in a split menu
This is because its usage is less frequent, so it does not eat the bottom space.
We can also now delete all polygons to restore the default masks, so its usage will decrease.
2021-06-25 14:11:30 +02:00
Aurélien Vivet
0cc1e57450 Put the button "add a behavior to the object" at the bottom of the list of behaviors (#2760)
* Also restore the default collision mask if all polygons are removed in the custom mask
2021-06-25 13:47:01 +02:00
Florian Rival
24c13e1022 Make the buttons at the bottom of the list of animations always visible in the Sprite editor 2021-06-22 11:52:52 +02:00
Florian Rival
965f7aab32 Fix getting the length of an object array always returning 0 2021-06-22 10:16:43 +02:00
Florian Rival
23c1f2423d Update badge to use travis-ci.com
Don't show in changelog
2021-06-17 09:30:49 +02:00
Arthur Pacaud
1d5eb8e529 Move JSON and JS variable conversions to gdjs.Variable (#2740)
Only show in developer changelog
2021-06-16 16:59:12 +01:00
D8H
8968ef34e2 Add Flow typing to the instances editor (#2745)
Only show in developer changelog
2021-06-16 16:50:51 +01:00
Arthur Pacaud
736ba023ff Add various DX improvements (#2741)
Only show in developer changelog
2021-06-16 16:22:30 +01:00
D8H
ade075f076 Reorganize pathfinding behavior actions and conditions in 2 groups (#2738) 2021-06-16 14:58:47 +01:00
Florian Rival
049c36b081 Fix tests 2021-06-16 08:44:46 +02:00
Florian Rival
3977e2b6f2 Fix formatting 2021-06-14 15:12:11 +01:00
Aurélien Vivet
f08f38bc3b Fix "NAN" shown for the cursor position when opening the scene editor (#2743) 2021-06-14 15:10:53 +01:00
D8H
127a881416 Add a condition and expression to check the movement angle for the pathfinding behavior (#2737) 2021-06-12 18:39:31 +01:00
Florian Rival
606143ff36 Fix typings 2021-06-12 15:53:17 +01:00
Florian Rival
05a68cb17f Fix behavior not always set correctly when editing parameters using the inline popover in the events sheet 2021-06-12 15:52:01 +01:00
Florian Rival
280377cc98 Fix RGB colors with a component at 0 not applied to the ambient light color
Fix #2734
2021-06-12 15:02:49 +01:00
Florian Rival
3c88c150d5 Fix effects container of an object not copied properly (and so erased when previewing)
This is a good example of why custom assignment ctor/copy operator should be avoided. In the case of gd::Object, it would have been better to encapsulate the behaviors in a "gd::BehaviorContentContainer", that would have custom assignement ctor/copy operator, so that we could then avoid specifying those for gd::Object.

Don't show in changelog
2021-06-12 02:08:34 +01:00
D8H
13471ad40a Add actions/conditions/expressions to specify an offset for the virtual grid used by the Pathfinding behavior (#2730) 2021-06-12 00:14:24 +01:00
Florian Rival
0d7d574f1b Fix action and condition to change the movement angle offset of the Top Down Movement behavior 2021-06-12 00:05:02 +01:00
D8H
19a10d7dab Add autocompletions for animation and points of Sprite objects (#2722)
* Both for the parameters of actions and inside expressions.
2021-06-11 23:27:44 +01:00
Arthur Pacaud
84e096f85d Update the IDE to Firebase v9 (to reduce the size of the Firebase module) (#2724)
Only show in developer changelog
2021-06-11 23:13:50 +01:00
Aurélien Vivet
40f5eb3e23 Remove unused argument (#2731)
Don't show in changelog
2021-06-11 11:17:00 +01:00
Arthur Pacaud
4948e16dd5 Add disconnection actions to the p2p extension (#2723) 2021-06-09 22:45:20 +01:00
D8H
2b38325d96 Ensure variables referenced in external events are shown in the autocompletions in the variables editor (#2717) 2021-06-09 21:29:20 +01:00
D8H
9a10caeeac Add autocompletion for variable names in expressions (#2692) 2021-06-07 09:16:12 +01:00
Florian Rival
a4be56a24b Add links when creating issues 2021-06-06 17:47:57 +01:00
Arthur Pacaud
fc8b724e3c Export extension dependencies exported when the extension is used (#2671)
* This for example allow to export the vibration extension only if it's used in events.

Only show in developer changelog
2021-06-06 16:45:47 +01:00
Aurélien Vivet
7e61419a36 Clean the examples folder (#2713)
Only show in developer changelog
2021-06-06 15:37:47 +01:00
Florian Rival
473661b8a2 Fix typings 2021-06-06 15:36:01 +01:00
Florian Rival
350fea08bf Enable serialization of effects in gd::Object
Don't show in changelog
2021-06-05 16:55:28 +01:00
D8H
4f9154549b Add autocompletion in expressions for scene names, layer names and parameters with choices (#2702) 2021-06-05 14:27:28 +01:00
Florian Rival
d9172ded36 Replace the list of examples by an "Example Library" (#2697)
* You can contribute to the examples library by submitting your new (or improved) examples on https://github.com/GDevelopApp/GDevelop-examples
2021-06-05 00:57:23 +01:00
Aurélien Vivet
aef8271a4e Specify type of variables in the events sheet sentences (#2701) 2021-06-02 21:33:29 +01:00
Harsimran Singh Virk
83db8ce272 Added Effects Container in gd::Object (#2683)
Only show in developer changelog
2021-05-31 22:22:08 +01:00
Florian Rival
e26c1470c1 Upgrade to Flow 0.131 (#2695)
Don't show in changelog
2021-05-31 22:19:13 +01:00
Florian Rival
4b382cb191 Allow to pass the path to GDJS to the extensions loader (#2694)
Useful for third party/external tools.

Don't show in changelog
2021-05-30 19:21:42 +01:00
Aurélien Vivet
6c631410f2 Fix the opacity value for BBText and Bitmap Text object, value is now clamped between 0 and 255 (#2693) 2021-05-30 18:44:04 +02:00
D8H
516b1d978c Fix platformer objects so that they can always grab a platform ledge even when multiple platforms are overlapping (#2686) 2021-05-28 22:09:50 +01:00
Arthur Pacaud
822146e5f1 Add an action to add a document in Firestore without having to specify a name (#2687) 2021-05-27 21:58:26 +01:00
Florian Rival
7b3987bae4 Upgrade TypeScript to version 4.3.2 for GDJS game engine (#2688)
Only show in developer changelog
2021-05-27 12:37:54 +01:00
Aurélien Vivet
506c029690 Fix events sheet showing "unknown instruction" when the action/condition editor is dismissed with a backdrop click (#2684) 2021-05-27 09:36:09 +01:00
Aurélien Vivet
b28e4e1230 Hide preferences in the web-app when they are not applicable for it (#2681) 2021-05-26 15:44:36 +01:00
Arthur Pacaud
c1edcbfa19 Add support for running queries in Firebase (#2610) 2021-05-26 09:48:16 +01:00
D8H
3125956dfc In the variables editor, add autocompletion for variables that are used in events but not yet defined in the list (#2665)
* This means that if you use a scene, global or object variable in your events, it will be suggested the next time you open the variables editor of the scene, project or of an object, when you enter the name of a new variable.
* Note that this works only for now for variables that are not arrays or structures.
2021-05-25 23:37:25 +01:00
Florian Rival
633c7bbd9d Fix color field warning
Don't show in changelog
2021-05-25 20:51:03 +01:00
Florian Rival
df92ea1ac9 Add a warning when trying to enter a color text with an extra quote
* See as an example: #2675
2021-05-25 20:41:04 +01:00
Aurélien Vivet
3eb571a03a Fix AABB and the positioning of the bitmap text object. 2021-05-25 20:57:41 +02:00
Aurélien Vivet
98303f7f50 Fix BitmapText height when the text is finishing with new lines (#2670) 2021-05-25 08:44:40 +01:00
Arthur Pacaud
34dfbbc294 Remove misleading descriptions of subscriptions (#2544)
Don't show in changelog
2021-05-24 21:49:32 +02:00
Harsimran Singh Virk
afbb316bd2 Refactor gd::Layer to use a gd::EffectsContainer (#2645)
Only show in developer changelog
2021-05-22 00:22:58 +01:00
Arthur Pacaud
b75945869c Add autocompletions for scene, layers and some parameter fields (#2663)
* Both display the scene names (or layer names, or valid choices, according to the field) and the autocompletions like a usual text expression - making it more obvious that you can use an expression here.
2021-05-22 00:07:34 +01:00
Aurélien Vivet
efb3f55885 Add a preference to display the preview window always on top of other windows (#2667) 2021-05-20 23:14:42 +02:00
Aurélien Vivet
82d2a4b5e4 Add padding option for effects on layers (#2658) 2021-05-18 16:25:01 +01:00
aphoenixholland
7748f3c48e Add support for tweening object color using Hue/Saturation/Lightness (HSL) (#2571)
* This is useful to make some effects or have a more natural color change.
2021-05-17 23:23:03 +01:00
D8H
80ddc9c1b7 Fix grid snapping at the object creation when the grid position is not an integer (#2648) 2021-05-15 20:22:50 +01:00
Arthur Pacaud
5d50c31e55 Add code snippets for Visual Studio Code (#2647)
Only show in developer changelog
2021-05-15 19:05:56 +01:00
Florian Rival
61a969fdb1 Bump newIDE version 2021-05-13 19:42:05 +01:00
Florian Rival
a14b8e8bcd Merge branch 'master' of github.com:4ian/GDevelop 2021-05-13 19:41:51 +01:00
D8H
faa37a4fd2 Update Link Tools examples to use the new link action (#2646) 2021-05-13 19:21:00 +01:00
Florian Rival
176efafe43 Increase the size before warning in the service worker
Don't show in changelog
2021-05-12 23:30:49 +01:00
Aurélien Vivet
c242e1747b Fix "debug draw" not taking into account camera zoom and rotation (#2636) 2021-05-12 18:55:37 +01:00
Florian Rival
fe74ec0c49 Fix collision masks overriden if not shared by all animations when opening their editor
Don't show in changelog
2021-05-11 21:07:48 +01:00
Florian Rival
2b5a9b8254 Bump newIDE version 2021-05-11 18:58:47 +01:00
Nilay Majorwar
d352c28003 Fix collision masks and points overriden if not shared by all animations when opening their editor (#2635) 2021-05-11 18:48:06 +01:00
Florian Rival
1991cc634a Change the default behavior when clicking outside a window: it won't be closed anymore.
* This avoids a recurring issue where misclicking outside a window would cancel any changes made in the window.
* You can still press Escape to quickly close a dialog.
* Note that this behavior can be customized in the preferences. You can choose to cancel the changes, apply any changes or do nothing.
2021-05-09 23:10:59 +01:00
Florian Rival
d653f846b6 Update Bitmap Text description/wording
Don't show in changelog
2021-05-09 22:58:32 +01:00
Florian Rival
9a8ec971f2 Update translations 2021-05-09 20:37:45 +01:00
Florian Rival
87c5007a25 Add Bitmap Text example
Don't show in changelog
2021-05-09 19:45:29 +01:00
Florian Rival
666a9c747a Add Bim Bam (Bust-a-Move clone) example (thanks D8H) 2021-05-09 12:48:17 +01:00
Florian Rival
cf59e6fb1e Add Tactical Game Grid Movement example (thanks D8H) 2021-05-09 12:36:01 +01:00
Florian Rival
ab04db387a Add "Menu with tweens" example (thanks Dreamkingmovies) 2021-05-09 12:05:50 +01:00
aphoenixholland
7a7a0d0eec Add an option to Tween behavior scale actions to scale an object from its center (#2611) 2021-05-09 11:44:23 +01:00
Arthur Pacaud
a27d1e97bc Rework GetUsedExtensions (#2588)
Only show in developer changelog
2021-05-07 22:46:02 +02:00
Aurélien Vivet
076732abd9 Automatically disable smoothing of assets from asset store if the project is using the "nearest" scale mode ("pixel-perfect") (#2620) 2021-05-07 15:10:13 +01:00
Aurélien Vivet
9d09ad4dd9 Add Bitmap Text objects, an efficient way to display texts using custom "bitmap fonts" (#2432)
* This allows to display texts on screen that use a "bitmap font", generated with softwares like [BMFont](https://www.angelcode.com/products/bmfont/) or [bmGlyph](http://www.bmglyph.com/).
* Bitmap fonts allow advanced effects and custom design of each character, with complete control over the appearance of the text. This is useful for making a custom score counter, titles, button labels...
* They also render very well in a pixel-perfect, pixel-art or retro-like game.
* Finally, these Bitmap Texts are fast and efficient to render on screen: useful for scores or texts that are updated frequently.
2021-05-07 14:57:00 +01:00
Oxey405
c16d155066 Fix multi-user compatibility on Linux (previews/exports not generated in same folders anymore) (#2590) 2021-05-06 19:31:10 +01:00
Florian Rival
cbdfd344e3 Fix shortcut in preview and text entry not working anymore
Fix #2605

Don't show in changelog
2021-05-06 19:29:18 +01:00
Henrik Hedlund
8cce4ab9f2 Add warning hint about overriding the Peer ID in P2P extension (#2606) 2021-05-04 08:54:38 +01:00
Florian Rival
3266559cb6 Add actions, conditions and expressions to get the position of an object center or set its position using its center (#2593)
* This is useful for making concise actions or conditions.
* This is also useful because for some objects, the center is not located at half the width and the height of the object.
* You can use `Object.CenterX()` and `Object.CenterY()` to get the position of the center of an object in an expression.
2021-05-02 18:43:28 +01:00
Henrik Hedlund
990b04d629 Allow setting the client ID in the peer-to-peer extension (#2598)
* By default, a long unique ID is generated. If you use your own broker server, a game could generate a simple, but unique, ID for each game (e.g. 639239), and then specify that as the client ID. Other players can then connect to that peer using the simpler ID.
2021-05-02 11:04:30 +01:00
Florian Rival
c295456231 Fix sprite objects updating their displayed image one frame too late (#2597)
* This fixes some "flickering" when a sprite is just created, when a scene is changed or when external layout objects are created on a scene.

Fixes #1285
2021-05-01 18:05:25 +01:00
Florian Rival
79e033d57b Increase timeout for GDJS tests and fix a type issue
Don't show in changelog
2021-04-30 11:48:08 +02:00
Florian Rival
f0fc709a84 Fix tests (fix regression due to inexact rad/deg conversion) 2021-04-30 09:57:31 +02:00
D8H
b80ed7e6f5 Improve the type definitions of LinkedObjects (#2594)
Only show in developer changelog
2021-04-30 08:40:16 +01:00
D8H
f23936a6c4 Add tests on moving platforms (#2581)
Only show in developer changelog
2021-04-29 20:53:26 +01:00
Arthur Pacaud
1dff166df0 Add network and persistence configuration to firestore in Firebase (#2582) 2021-04-28 21:35:22 +02:00
Aurélien Vivet
50278b1ab2 Fix page scrolling when keyboard was used in games embedded in web pages (#2589) 2021-04-27 21:38:53 +01:00
Aurélien Vivet
5f4fde7a56 Add new expressions XFromAngleAndDistance and YFromAngleAndDistance (#2558) 2021-04-25 21:08:29 +01:00
Aurélien Vivet
5384225a9b Fix typo and mobile controls on Platformer project example (#2578)
Don't show in changelog
2021-04-24 18:12:05 +02:00
Arthur Pacaud
5ecdfa1c7f Convert Firebase extension to TypeScript (#2417)
Only show in developer changelog
2021-04-24 17:58:36 +02:00
Nilay Majorwar
df6910e062 Rework the behaviors list with collapsible panels (#2553)
* This is useful to hide behaviors when they are too many of them
* This also creates a more visible distinction between each behavior
2021-04-23 12:05:53 +01:00
D8H
5a42d6b94c Add more type definitions and new tests for on moving platforms for the Platformer behavior (#2573)
Only show in developer changelog
2021-04-23 11:55:18 +01:00
Arthur Pacaud
afa9b2a2ae Fix invalid values ("NaN") being potentially stored in variables and corrupting the project files (#2568) 2021-04-20 16:47:20 +01:00
Florian Rival
1dae099be9 Fix a potential crash when instances are selected and an object is deleted (#2557) 2021-04-18 18:37:06 +01:00
Arthur Pacaud
6f64fd3d1c Fix "Sound is playing" condition not consistent when an audio file is being loaded (#2555)
* Because of this, this could create some glitches when playing sounds using the condition to check if a sound was already playing (for example, it could trigger multiple times a sound).
2021-04-18 18:34:05 +01:00
Florian Rival
6c0bd92b24 Fix crash when adding an instance of a global object on the scene 2021-04-17 12:01:05 +01:00
Arthur Pacaud
0bcaa835e6 Improve the file watcher in development mode (#2554)
* Use chokidar for a more reliable and robust change detection
2021-04-17 11:06:26 +01:00
Nilay Majorwar
32ca97650c Rework the collisions editor and points editor (#2481)
* Show the preview and the polygons/points side by side (on large screens), so it's easier to edit.
* The split can be dragged to give more space to the preview or to the tables.
* Show point name in tooltip when hovering on point.
* Improve the dragging of points and polygon vertices so that they continue to follow the cursor even if dragging outside of the image bounds.
* Allow to collapse polygon tables, useful if you have lots of polygons in an object.
* The checkered background in the editors are now also using different colors following the selected theme.
2021-04-14 22:26:31 +01:00
Arthur Pacaud
e19d63ce96 Improve spacing of descriptions for checkboxes in properties (#2542) 2021-04-13 10:25:01 +01:00
Aurélien Vivet
352e5daf1e Addition of the version number under the name of the extension in the presentation panel. (#2540) 2021-04-13 11:08:01 +02:00
Aurélien Vivet
003f56e9bc Remove the rest of the skeleton object (#2539)
Don't show in changelog
2021-04-13 00:22:36 +01:00
D8H
4b3fa9fc39 Fix hitboxes not invalidated in TestRuntimeObject (#2534)
Only show in developer changelog
2021-04-11 20:22:54 +01:00
Florian Rival
e3fa31c4a4 Merge branch 'master' of github.com:4ian/GDevelop 2021-04-11 15:49:51 +01:00
Florian Rival
1ad0ab9881 Add Goose Bomberman example (thanks D8H) 2021-04-11 15:48:33 +01:00
Aurélien Vivet
355c53cb12 Fix Video object wrongly starting to play automatically (#2533) 2021-04-11 14:40:58 +01:00
Florian Rival
2a9b27b503 Add a new Dialogue Tree example (thanks potato-gamer-coder and Midhil457!) 2021-04-11 13:26:32 +01:00
D8H
2fa2824ca2 Add a test for platformer object to fall when a platform is removed (#2529)
Only show in developer changelog
2021-04-11 03:07:33 +01:00
Florian Rival
85e3ed42fa Add experimental configuration for GitHub workspaces
Don't show in changelog
2021-04-11 01:58:12 +00:00
Aurélien Vivet
8a178a85fb Add a preference to choose if clicking outside a dialog is cancelling changes, applying changes or doing nothing (#2484) 2021-04-10 19:36:42 +01:00
Florian Rival
54ef7482cf Fix performance issues on iOS by upgrading internal rendering engine to PixiJS v6 (#2447)
* Thanks @Bouh and @arthuro555 for the help!

Co-authored-by: Bouh <bouh.vivez@gmail.com>
2021-04-09 23:58:18 +01:00
Aurélien Vivet
c846a7a068 Change the wording of the button to "Edit collision masks" (#2525) 2021-04-09 15:46:29 +01:00
Florian Rival
86eff4e408 Fix sounds/musics playing for a very short time at the default volume instead of the specified one
Fix #2490
2021-04-08 23:59:34 +01:00
D8H
916938b4a8 [Top-down] Stick controls now comply with the "allow diagonals" setting (#2518)
* Test cases on stick controls for forbidden diagonals


add tests

* Fix: stick controls now comply with allowDiagonals setting

* Remove only
2021-04-07 19:01:40 +00:00
D8H
49827984ac Allow an object moving vertically to change direction when diagonals are forbidden in the Top-down behavior. (#2516)
* If multiple keys are pressed, the last one is used to move the object.
2021-04-07 00:19:05 +01:00
Aurélien Vivet
02eac6ba5e Small fixes (#2514)
* Remove useless type

* Fix type
2021-04-06 22:43:09 +00:00
Arthur Pacaud
479167638b Update the discord invite URL (#2511) 2021-04-05 22:00:04 +02:00
Florian Rival
fada85d41d Fix export to Android/iOS failing if AdMob id was containing invalid characters (#2510) 2021-04-05 13:31:12 +01:00
Florian Rival
3017798283 Remove deprecated/unmaintained SkeletonObject extension (#2509)
This should probably be restarted from scratch with a well known/battle tested library.

Don't show in changelog
2021-04-03 23:58:02 +01:00
Florian Rival
515308ed9d Add Titan Souls Demo game (Thanks @HelperWesley and @arthuro555) 2021-04-03 22:36:50 +01:00
Tristan Rhodes
7369e05e01 Improve wording on some movement related action sentences (#2501) 2021-04-02 20:09:51 +01:00
Aurélien Vivet
55da9eb2ef Add an action to display collision hitboxes and points of objects during the game (#2394)
* This is useful to check the proper set up of objects and check the position of points of objects during the game.
2021-04-02 00:11:22 +01:00
Arthur Pacaud
01a45a10df Improve the p2p chat example (#2488) 2021-04-01 23:54:38 +01:00
Florian Rival
32d313f538 Bump newIDE version 2021-03-30 00:14:05 +01:00
Arthur Pacaud
1640ca49d7 Fix games hanging when using an empty While event (#2480) 2021-03-28 16:55:22 +01:00
D8H
654b8d0bc1 Change zoom in the scene editor so that it's centered on the cursor (#2478)
* This makes it easier to navigate in the scene by zooming in and out
2021-03-28 15:46:37 +01:00
Florian Rival
2a47d2f630 Auto close issues related to b105 web-app update
Don't show in changelog
2021-03-28 15:40:02 +01:00
Nilay Majorwar
e1423e0b90 Add One Dark theme (for the editor and for JavaScript code blocks) (#2476) 2021-03-28 15:28:56 +01:00
D8H
458430bbfb Fix platformer objects wrongly able to jump after falling (#2477) 2021-03-28 14:37:22 +01:00
Florian Rival
2a5c18474e Fix warning 2021-03-27 17:51:02 +00:00
D8H
827c53e9d5 Add an option to display an isometric grid in the scene editor (#2445) 2021-03-27 17:13:39 +00:00
Florian Rival
019920009a Fix ToJSON wrongly transforming an empty string to a variable containing the number 0 (#2475) 2021-03-27 15:44:45 +00:00
Nilay Majorwar
aaed10a598 Fix bug in theme creation script (#2474)
Only show in developer changelog
2021-03-27 12:20:22 +00:00
Arthur Pacaud
193ce8bcf8 Add VS Code debug configuration (#2466)
This allows to use breakpoints and debug the react app from inside the IDE.

Only show in developer changelog
2021-03-25 21:49:12 +00:00
Florian Rival
fd020fd2a3 Bump newIDE version 2021-03-25 20:16:20 +00:00
2825 changed files with 90400 additions and 439747 deletions

6
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
# This is the Dockerfile to create a "devcontainer".
# This is a way to quickly create a development environment,
# that can be used with GitHub workspaces.
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.162.0/containers/codespaces-linux/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/universal:1-focal

16
.devcontainer/README.md Normal file
View File

@@ -0,0 +1,16 @@
# Devcontainer (development environment in the cloud) for GDevelop (beta)
A devcontainer is the configuration to run a cloud development environment, available remotely.
This is notably used to run a **[GitHub Codespace](https://docs.github.com/en/github/developing-online-with-codespaces/about-codespaces)**. The advantage is that you can quickly experiment and do changes without installing anything on your computer.
> ⚠️ This development environment is in beta, and not everything is supported. This is useful for playing a bit with GDevelop codebase or to make quick changes. **In particular, it's not possible to test your changes on GDJS** because only the web-app can be launched.
It's recommended that you follow the usual [README](../newIDE/README.md) to get started, with development tools installed on your computer.
## Start a GitHub workspace to work on GDevelop (beta)
- Subscribe to the [beta edition of GitHub workspace](https://docs.github.com/en/github/developing-online-with-codespaces/about-codespaces#joining-the-beta).
- In the repository GitHub page, click **Code** and then choose **Open with Codespaces**.
- Wait for the Codespace to load and Visual Studio Code to open.
- When it's ready, open a terminal in Visual Studio Code.
- Follow the usual [README in `newIDE`](../newIDE/README.md) to get started, like you would do on your own computer.

View File

@@ -0,0 +1,51 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.162.0/containers/codespaces-linux
{
"name": "GitHub Codespaces for GDevelop",
"build": {
"dockerfile": "Dockerfile"
},
"settings": {
"terminal.integrated.shell.linux": "/bin/zsh",
"go.toolsManagement.checkForUpdates": "off",
"go.useLanguageServer": true,
"go.gopath": "/go",
"go.goroot": "/usr/local/go",
"python.pythonPath": "/opt/python/latest/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"lldb.executable": "/usr/bin/lldb",
"files.watcherExclude": {
"**/target/**": true
}
},
"remoteUser": "codespace",
"overrideCommand": false,
"workspaceMount": "source=${localWorkspaceFolder},target=/home/codespace/workspace,type=bind,consistency=cached",
"workspaceFolder": "/home/codespace/workspace",
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--privileged", "--init" ],
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"GitHub.vscode-pull-request-github",
"esbenp.prettier-vscode",
"ms-vscode.cpptools",
"xaver.clang-format",
"flowtype.flow-for-vscode"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// "oryx build" will automatically install your dependencies and attempt to build your project
"postCreateCommand": "oryx build -p virtualenv_name=.venv || echo 'Could not auto-build. Skipping.'"
}

17
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: GDevelop Discord
url: https://discord.gg/rjdYHvj
about: Discuss on the forum or on the Discord to get help creating a game or identifying a bug.
- name: GDevelop Forums
url: https://forum.gdevelop-app.com
about: You can also discuss game creation, new feature requests and bugs on the forum.
- name: GDevelop Roadmap
url: https://trello.com/b/qf0lM7k8/gdevelop-roadmap
about: Look at the roadmap and vote on features that you want to see in GDevelop.
- name: Submit a new game example that you created
url: https://github.com/GDevelopApp/GDevelop-examples/issues/new/choose
about: You can submit a game that you made to be added to examples in the "GDevelop-examples" repository
- name: Submit a new game extension that you created
url: https://github.com/4ian/GDevelop-extensions/issues/new/choose
about: You can submit an extension that you made in the "GDevelop-extensions" repository

View File

@@ -4,13 +4,6 @@ jobs:
autoclose:
runs-on: ubuntu-latest
steps:
- name: Autoclose issues about adding a new example without providing anything
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*INSERT the link to your game here, or add it as an attachment.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any example.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose issues about adding a bug without changing the bug report template
uses: arkon/issue-closer-action@v1.1
with:
@@ -18,3 +11,10 @@ jobs:
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose known beta 105 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*_instance.getRawFloatProperty is not a function.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed as this seems to be a known bug. It can be solved by **closing entirely the web-app and opening it again**. This will allow the web-app to auto-update and the problem should be gone."

View File

@@ -1,7 +1,6 @@
language: cpp
sudo: false
compiler:
- gcc
- clang
# Cache .npm folder for faster npm install

129
.vscode/GDevelopExtensions.code-snippets vendored Normal file
View File

@@ -0,0 +1,129 @@
{
"Define extension": {
"scope": "javascript",
"description": "Adds the boilerplate code of a GDevelop extension definition.",
"prefix": "gdext",
"body": [
"// @flow",
"/**",
" * This is a declaration of an extension for GDevelop 5.",
" *",
" * Changes in this file are watched and automatically imported if the editor",
" * is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).",
" *",
" * The file must be named \"JsExtension.js\", otherwise GDevelop won't load it.",
" * ⚠️ If you make a change and the extension is not loaded, open the developer console",
" * and search for any errors.",
" *",
" * More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md",
" */",
"",
"/*::",
"// Import types to allow Flow to do static type checking on this file.",
"// Extensions declaration are typed using Flow (like the editor), but the files",
"// for the game engine are checked with TypeScript annotations.",
"import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'",
"*/",
"",
"module.exports = {",
"\tcreateExtension: function (",
"\t\t/*: (string) => string */,",
"\t\tgd /*: libGDevelop */",
"\t) {",
"\t\tconst extension = new gd.PlatformExtension();",
"\t\textension",
"\t\t\t.setExtensionInformation(",
"\t\t\t\t'${1:${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/$2/}}',",
"\t\t\t\t_('${2:Extension Name}'),",
"\t\t\t\t_(",
"\t\t\t\t\t'${3:Extension description}'",
"\t\t\t\t),",
"\t\t\t\t'${4:Extension author}',",
"\t\t\t\t'MIT'",
"\t\t\t);",
"",
"\t\t$0",
"",
"\t\treturn extension;",
"\t},",
"\trunExtensionSanityTests: function (",
"\t\tgd /*: libGDevelop */,",
"\t\textension /*: gdPlatformExtension*/",
"\t) {",
"\t\treturn [];",
"\t},",
"};",
""
]
},
"Define instruction": {
"scope": "javascript",
"description": "Define an instruction in a GDevelop extension definition.",
"prefix": "gdinstr",
"body": [
"extension",
"\t.add${1|Condition,Action|}(",
"\t\t'${2:InstructionInternalName}',",
"\t\t_('${3:Instruction full name}'),",
"\t\t_(",
"\t\t\t'${4:Instruction description}'",
"\t\t),",
"\t\t_('${5:Event sheet sentence}'),",
"\t\t_('${6:Events group}'),",
"\t\t'JsPlatform/Extensions/${8:icon}.png',",
"\t\t'JsPlatform/Extensions/${8:icon}.png'",
"\t)",
"\t.getCodeExtraInformation()",
"\t.setIncludeFile('Extensions/${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/$2/}/${9:${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/${2:/downcase}/}tools}.js')",
"\t.setFunctionName('gdjs.evtTools.${7:${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/${2:/downcase}/}.}');",
"",
"$0"
]
},
"Define expression": {
"scope": "javascript",
"description": "Define an expression in a GDevelop extension definition.",
"prefix": "gdexp",
"body": [
"extension",
"\t.add${1|Expression,StrExpression|}(",
"\t\t'${2:ExpressionsInternalName}',",
"\t\t_('${3:Expression full name}'),",
"\t\t_(",
"\t\t\t'${4:Expression description}'",
"\t\t),",
"\t\t_('${5:Events group}'),",
"\t\t'JsPlatform/Extensions/${7:icon}.png'",
"\t)",
"\t.getCodeExtraInformation()",
"\t.setIncludeFile('Extensions/${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/$2/}/${8:${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/${2:/downcase}/}tools}.js')",
"\t.setFunctionName('gdjs.evtTools.${6:${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/${2:/downcase}/}.}');",
"",
"$0"
]
},
"Add parameter": {
"scope": "javascript",
"description": "Define a parameter in a GDevelop extension definition.",
"prefix": "gdparam",
"body": [
".addParameter('${1|string,expression,object,behavior,yesorno,stringWithSelector,scenevar,globalvar,objectvar,objectList,objectListWithoutPicking,color,key,sceneName,file,layer,relationalOperator,operator,trueorfalse,musicfile,soundfile,police,mouse,passwordjoyaxis,camera,objectPtr,forceMultiplier|}', '${2:Parameter description}', '${3:Optional parameter data}', /*parameterIsOptional=*/${4|false,true|})"
]
},
"Add code only parameter": {
"scope": "javascript",
"description": "Define a parameter in a GDevelop extension definition.",
"prefix": "gdcoparam",
"body": [
".addCodeOnlyParameter('${1|inlineCode,currentScene,objectsContext,eventsFunctionContext,conditionInverted|}', '${2:Inline code (for inlineCode parameter)}')"
]
},
"Add include": {
"scope": "javascript",
"description": "Define an include file in a GDevelop extension definition.",
"prefix": "gdincl",
"body": [
".addIncludeFile('Extensions/${TM_DIRECTORY/(.*)[\\\\\\/](.*)[\\\\\\/]?$/$2/}/${1:include}.js')"
]
}
}

View File

@@ -1,108 +1,98 @@
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"defines": [
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"macFrameworkPath": [
"/System/Library/Frameworks",
"/Library/Frameworks"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "Linux",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/IDE",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"defines": [
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
},
{
"name": "Win32",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/IDE",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include",
"${workspaceRoot}"
],
"defines": [
"_DEBUG",
"UNICODE",
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "msvc-x64",
"browse": {
"path": [
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
}
],
"version": 4
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"defines": [
"EMSCRIPTEN",
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"macFrameworkPath": ["/System/Library/Frameworks", "/Library/Frameworks"],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "Linux",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"defines": [
"EMSCRIPTEN",
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "clang-x64",
"browse": {
"path": ["/usr/include", "/usr/local/include", "${workspaceRoot}"],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
},
{
"name": "Win32",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/GDCpp",
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"${workspaceRoot}"
],
"defines": [
"_DEBUG",
"UNICODE",
"EMSCRIPTEN",
"GD_IDE_ONLY",
"GD_CORE_API=/* Macro used to export classes on Windows, please ignore */",
"GD_API=/* Macro used to export classes on Windows, please ignore */",
"GD_EXTENSION_API=/* Macro used to export classes on Windows, please ignore */"
],
"intelliSenseMode": "msvc-x64",
"browse": {
"path": ["${workspaceRoot}"],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
}
],
"version": 4
}

8
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"xaver.clang-format",
"ms-vscode.cpptools",
"flowtype.flow-for-vscode"
]
}

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}",
"preLaunchTask": "Start development server"
}
]
}

21
.vscode/settings.json vendored
View File

@@ -90,12 +90,31 @@
"__node_handle": "cpp",
"bit": "cpp",
"optional": "cpp",
"filesystem": "cpp"
"filesystem": "cpp",
"compare": "cpp",
"concepts": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocinfo": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"xlocbuf": "cpp",
"xlocmes": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,
"Binaries/Output": true,
"ExtLibs/SFML": true,
"GDJS/Runtime-dist": true,
"docs": true,
"newIDE/electron-app/dist": true,
"newIDE/app/build": true,

158
.vscode/tasks.json vendored
View File

@@ -1,66 +1,100 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"path": "newIDE/app/",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"label": "Start development server",
"detail": "Starts the GDevelop development server."
},
{
"type": "npm",
"script": "build",
"path": "GDevelop.js/",
"group": "build",
"problemMatcher": [],
"label": "Build GDevelop.js",
"detail": "Builds GDCore for newIDE."
},
{
"type": "npm",
"script": "format",
"path": "newIDE/app/",
"problemMatcher": [],
"label": "Format newIDE",
"detail": "Run auto-formatting (with Prettier) for the newIDE/app directory."
},
{
"type": "npm",
"script": "test",
"path": "newIDE/app/",
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": [],
"label": "Run newIDE tests",
"detail": "Run tests for newIDE."
},
{
"type": "typescript",
"tsconfig": "GDJS/tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": "test",
"label": "GDJS TS Check",
"detail": "Runs a types check on the GDJS Runtime."
},
{
"type": "npm",
"script": "test",
"path": "GDJS/",
"group": "test",
"problemMatcher": [],
"label": "Run GDJS tests",
"detail": "Run tests for GDJS."
}
]
{
"type": "npm",
"script": "start",
"path": "newIDE/app/",
"group": "build",
"label": "Start development server",
"detail": "Starts the GDevelop development server.",
"problemMatcher": [
{
"owner": "cra",
"fileLocation": ["relative", "${workspaceFolder}/newIDE/app"],
"source": "create-react-app",
"applyTo": "allDocuments",
"pattern": [
{
"regexp": "^([^\\s].*?)$",
"file": 1
},
{
"regexp": "^ Line\\s+(\\d+):\\s+(.*)\\s\\s+(.*)$",
"line": 1,
"message": 2,
"code": 3,
"loop": true
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "^(?:Compiled with warnings\\.|Compiled successfully!)$",
"endsPattern": "^(?:Search for the keywords to learn more about each warning\\.|Note that the development build is not optimized\\.)$"
}
}
],
"presentation": {
"reveal": "silent"
},
"isBackground": true,
"runOptions": { "instanceLimit": 1, "runOn": "folderOpen" }
},
{
"type": "npm",
"script": "start",
"path": "newIDE/electron-app/",
"group": "build",
"problemMatcher": [],
"label": "Start electron app",
"detail": "Starts the development local version of GDevelop."
},
{
"type": "npm",
"script": "build",
"path": "GDevelop.js/",
"group": "build",
"problemMatcher": [],
"label": "Build GDevelop.js",
"detail": "Builds GDCore for newIDE."
},
{
"type": "npm",
"script": "format",
"path": "newIDE/app/",
"problemMatcher": [],
"label": "Format newIDE",
"detail": "Run auto-formatting (with Prettier) for the newIDE/app directory."
},
{
"type": "npm",
"script": "test",
"path": "newIDE/app/",
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": [],
"label": "Run newIDE tests",
"detail": "Run tests for newIDE."
},
{
"type": "typescript",
"tsconfig": "GDJS/tsconfig.json",
"option": "watch",
"problemMatcher": ["$tsc-watch"],
"group": "test",
"label": "GDJS TS Check",
"detail": "Runs a types check on the GDJS Runtime."
},
{
"type": "npm",
"script": "test",
"path": "GDJS/",
"group": "test",
"problemMatcher": [],
"label": "Run GDJS tests",
"detail": "Run tests for GDJS."
}
]
}

View File

@@ -1 +1,3 @@
This is the directory where native/WebAssembly binaries from GDCore, GDCpp and GDJS are produced.
This is the directory where native or WebAssembly binaries of the C++ code of GDCore, GDCpp and GDJS are produced.
See GDevelop.js README for the instructions to compile after a change in the C++ source code.

View File

@@ -9,6 +9,7 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Project/Layer.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
@@ -26,10 +27,9 @@ void ExposeProjectEffects(
auto& layout = project.GetLayout(s);
for (std::size_t l = 0; l < layout.GetLayersCount(); ++l) {
auto& layer = layout.GetLayer(l);
for (std::size_t e = 0; e < layer.GetEffectsCount(); ++e) {
auto& effect = layer.GetEffect(e);
auto& effects = layout.GetLayer(l).GetEffects();
for (std::size_t e = 0; e < effects.GetEffectsCount(); ++e) {
auto& effect = effects.GetEffect(e);
worker(effect);
}
}

View File

@@ -360,8 +360,11 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
if (gd::ParameterMetadata::IsObject(type)) {
return codeGenerator.GenerateBadObject();
}
if (gd::ParameterMetadata::IsExpression("string", type)) {
return "\"\"";
}
return (type == "string") ? "\"\"" : "0";
return "0";
}
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {

View File

@@ -66,6 +66,22 @@ class GD_CORE_API ExpressionParser2 {
return Start(type, objectName);
}
/**
* Given an object name (or empty if none) and a behavior name (or empty if none),
* return the index of the first parameter that is inside the parenthesis:
* 0, 1 or 2.
*
* For example, in an expression like `Object.MyBehavior::Method("hello")`, the
* parameter "hello" is the second parameter (the first being by convention Object,
* and the second MyBehavior, also by convention).
*/
static size_t WrittenParametersFirstIndex(const gd::String &objectName,
const gd::String &behaviorName) {
// By convention, object is always the first parameter, and behavior the
// second one.
return !behaviorName.empty() ? 2 : (!objectName.empty() ? 1 : 0);
}
private:
/** \name Grammar
* Each method is a part of the grammar.
@@ -999,13 +1015,6 @@ class GD_CORE_API ExpressionParser2 {
}
///@}
static size_t WrittenParametersFirstIndex(const gd::String &objectName,
const gd::String &behaviorName) {
// By convention, object is always the first parameter, and behavior the
// second one.
return !behaviorName.empty() ? 2 : (!objectName.empty() ? 1 : 0);
}
gd::String expression;
std::size_t currentPosition;

View File

@@ -6,6 +6,7 @@
#include "AllBuiltinExtensions.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
using namespace std;
namespace gd {
@@ -26,7 +27,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
#if defined(GD_IDE_ONLY)
obj.AddCondition("PosX",
_("Compare X position of an object"),
_("X position"),
_("Compare the X position of the object."),
_("the X position"),
_("Position"),
@@ -38,7 +39,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
obj.AddAction("MettreX",
_("X position of an object"),
_("X position"),
_("Change the X position of an object."),
_("the X position"),
_("Position"),
@@ -50,7 +51,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
obj.AddCondition("PosY",
_("Compare Y position of an object"),
_("Y position"),
_("Compare the Y position of an object."),
_("the Y position"),
_("Position"),
@@ -62,7 +63,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
obj.AddAction("MettreY",
_("Y position of an object"),
_("Y position"),
_("Change the Y position of an object."),
_("the Y position"),
_("Position"),
@@ -74,7 +75,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
obj.AddAction("MettreXY",
_("Position of an object"),
_("Position"),
_("Change the position of an object."),
_("Change the position of _PARAM0_: _PARAM1_ _PARAM2_ (x "
"axis), _PARAM3_ _PARAM4_ (y axis)"),
@@ -89,8 +90,41 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Y position"))
.MarkAsSimple();
obj.AddAction("SetCenter",
_("Center position"),
_("Change the position of an object, using its center."),
_("Change the position of the center of _PARAM0_: _PARAM1_ _PARAM2_ (x "
"axis), _PARAM3_ _PARAM4_ (y axis)"),
_("Position/Center"),
"res/actions/position24.png",
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("X position"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Y position"))
.MarkAsSimple();
obj.AddExpressionAndConditionAndAction("number", "CenterX",
_("Center X position"),
_("the X position of the center"),
_("the X position of the center"),
_("Position/Center"),
"res/actions/position24.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
obj.AddExpressionAndConditionAndAction("number", "CenterY",
_("Center Y position"),
_("the Y position of the center"),
_("the Y position of the center"),
_("Position/Center"),
"res/actions/position24.png")
.AddParameter("object", _("Object"))
.UseStandardParameters("number");
obj.AddAction("MettreAutourPos",
_("Put an object around a position"),
_("Put around a position"),
_("Position the center of the given object around a position, "
"using the specified angle "
"and distance."),
@@ -172,7 +206,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"all of the forces it has."),
_("Add to _PARAM0_ _PARAM3_ force of _PARAM1_ p/s on X axis and "
"_PARAM2_ p/s on Y axis"),
_("Movement"),
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
@@ -188,7 +222,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"using the specified angle and length."),
_("Add to _PARAM0_ _PARAM3_ force, angle: _PARAM1_ degrees and "
"length: _PARAM2_ pixels"),
_("Movement"),
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
@@ -202,9 +236,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"AddForceVersPos",
_("Add a force to move toward a position"),
_("Add a force to an object to make it move toward a position."),
_("Move _PARAM0_ to _PARAM1_;_PARAM2_ with _PARAM4_ force of _PARAM3_ "
_("Move _PARAM0_ toward _PARAM1_;_PARAM2_ with _PARAM4_ force of _PARAM3_ "
"pixels"),
_("Movement"),
_("Movement using forces"),
"res/actions/force24.png",
"res/actions/force.png")
@@ -217,30 +251,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction(
"AddForceTournePos",
_("Add a force to move around a position"),
_("Add a force to an object to make it rotate around a "
"Add a force to move around a position",
"Add a force to an object to make it rotate around a "
"position.\nNote that the movement is not precise, especially if "
"the speed is high.\nTo position an object around a position more "
"precisely, use the actions in the category \"Position\"."),
_("Rotate _PARAM0_ around _PARAM1_;_PARAM2_ at _PARAM3_ deg/sec and "
"_PARAM4_ pixels away"),
_("Movement"),
"precisely, use the actions in the category \"Position\".",
"Rotate _PARAM0_ around _PARAM1_;_PARAM2_ at _PARAM3_ deg/sec and "
"_PARAM4_ pixels away",
_("Movement using forces"),
"res/actions/forceTourne24.png",
"res/actions/forceTourne.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position of the center"))
.AddParameter("expression", _("Y position of the center"))
.AddParameter("expression", _("Speed (in Degrees per seconds)"))
.AddParameter("expression", _("Distance (in pixels)"))
.AddParameter("forceMultiplier", _("Force multiplier"))
.AddParameter("expression", "X position of the center")
.AddParameter("expression", "Y position of the center")
.AddParameter("expression", "Speed (in Degrees per seconds)")
.AddParameter("expression", "Distance (in pixels)")
.AddParameter("forceMultiplier", "Force multiplier")
.SetHidden();
obj.AddAction("Arreter",
_("Stop the object"),
_("Stop the object by deleting all of its forces."),
_("Stop _PARAM0_ (remove all forces)"),
_("Movement"),
_("Movement using forces"),
"res/actions/arreter24.png",
"res/actions/arreter.png")
@@ -248,7 +282,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddAction("Delete",
_("Delete an object"),
_("Delete the object"),
_("Delete the specified object."),
_("Delete _PARAM0_"),
_("Objects"),
@@ -383,7 +417,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Montre",
_("Show"),
_("Show the specified object"),
_("Show the specified object."),
_("Show _PARAM0_"),
_("Visibility"),
"res/actions/visibilite24.png",
@@ -406,10 +440,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("Plan",
_("Compare Z order"),
_("Z-order"),
_("Compare the Z-order of the specified object."),
_("the z Order"),
_("Z order"),
_("the Z-order"),
_("Z-order"),
"res/conditions/planicon24.png",
"res/conditions/planicon.png")
@@ -418,7 +452,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("Layer",
_("Compare layer"),
_("Current layer"),
_("Check if the object is on the specified layer."),
_("_PARAM0_ is on layer _PARAM1_"),
_("Layer"),
@@ -430,7 +464,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("Visible",
_("Visibility of an object"),
_("Visibility"),
_("Check if an object is visible."),
_("_PARAM0_ is visible (not marked as hidden)"),
_("Visibility"),
@@ -441,9 +475,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
obj.AddCondition("Invisible",
_("Invisibility of an object"),
_("Check if an object is hidden."),
_("_PARAM0_ is hidden"),
"Invisibility of an object",
"Check if an object is hidden.",
"_PARAM0_ is hidden",
_("Visibility"),
"res/conditions/visibilite24.png",
"res/conditions/visibilite.png")
@@ -452,10 +486,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetHidden(); // Inverted "Visible" condition does the same thing.
obj.AddCondition("Arret",
_("Object is stopped"),
_("Object is stopped (no forces applied on it)"),
_("Check if an object is not moving"),
_("_PARAM0_ is stopped"),
_("Movement"),
_("Movement using forces"),
"res/conditions/arret24.png",
"res/conditions/arret.png")
@@ -463,10 +497,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("Vitesse",
_("Speed"),
_("Speed (from forces)"),
_("Compare the overall speed of an object"),
_("the overall speed"),
_("Movement"),
_("Movement using forces"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
@@ -475,17 +509,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddCondition("AngleOfDisplacement",
_("Angle of movement"),
_("Compare the angle of displacement of an object"),
_("Angle of displacement of _PARAM0_ is _PARAM1_ (tolerance "
_("Angle of movement (using forces)"),
_("Compare the angle of movement of an object according to the forces applied on it."),
_("Angle of movement of _PARAM0_ is _PARAM1_ (tolerance"
": _PARAM2_ degrees)"),
_("Movement"),
_("Movement using forces"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Tolerance"))
.AddParameter("expression", _("Tolerance, in degrees"))
.MarkAsAdvanced();
obj.AddCondition("VarObjet",
@@ -526,9 +560,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetDefaultValue("true");
obj.AddCondition("VarObjetDef",
_("Variable defined"),
_("Check if the variable is defined."),
_("Variable _PARAM1 of _PARAM0_ is defined"),
"Variable defined",
"Check if the variable is defined.",
"Variable _PARAM1 of _PARAM0_ is defined",
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
@@ -598,7 +632,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Removes a variable at the specified index of an object array "
"variable."),
_("Remove variable at index _PARAM2_ from array variable _PARAM1_ of "
"object _PARAM0_"),
"_PARAM0_"),
_("Variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
@@ -636,8 +670,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"AddForceVers",
_("Add a force to move toward an object"),
_("Add a force to an object to make it move toward another."),
_("Move _PARAM0_ to _PARAM1_ with _PARAM3_ force of _PARAM2_ pixels"),
_("Movement"),
_("Move _PARAM0_ toward _PARAM1_ with _PARAM3_ force of _PARAM2_ pixels"),
_("Movement using forces"),
"res/actions/forceVers24.png",
"res/actions/forceVers.png")
@@ -656,7 +690,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"the actions in category \"Position\"."),
_("Rotate _PARAM0_ around _PARAM1_ at _PARAM2_ deg/sec and _PARAM3_ "
"pixels away"),
_("Movement"),
_("Movement using forces"),
"res/actions/forceTourne24.png",
"res/actions/forceTourne.png")
@@ -668,7 +702,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
obj.AddAction("MettreAutour",
_("Put an object around another"),
_("Put the object around another"),
_("Position an object around another, with the specified angle "
"and distance. The center of the objects are used for "
"positioning them."),
@@ -686,32 +720,32 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
// Deprecated action
obj.AddAction("Rebondir",
_("Move an object away from another"),
_("Move an object away from another, using forces."),
_("Move _PARAM0_ away from _PARAM1_ (only _PARAM0_ will move)"),
_("Movement"),
"Move an object away from another",
"Move an object away from another, using forces.",
"Move _PARAM0_ away from _PARAM1_ (only _PARAM0_ will move)",
_("Movement using forces"),
"res/actions/ecarter24.png",
"res/actions/ecarter.png")
.SetHidden()
.AddParameter("object", _("Object"))
.AddParameter("objectList", _("Object 2 (won't move)"));
.AddParameter("objectList", "Object 2 (won't move)");
// Deprecated action
obj.AddAction("Ecarter",
_("Move an object away from another"),
_("Move an object away from another without using forces."),
_("Move _PARAM0_ away from _PARAM2_ (only _PARAM0_ will move)"),
"Move an object away from another",
"Move an object away from another without using forces.",
"Move _PARAM0_ away from _PARAM2_ (only _PARAM0_ will move)",
_("Position"),
"res/actions/ecarter24.png",
"res/actions/ecarter.png")
.SetHidden()
.AddParameter("object", _("Object"))
.AddParameter("objectList", _("Object 2 (won't move)"));
.AddParameter("objectList", "Object 2 (won't move)");
obj.AddAction("SeparateFromObjects",
_("Separate two objects"),
_("Separate objects"),
_("Move an object away from another using their collision "
"masks.\nBe sure to call this action on a reasonable number "
"of objects\nto avoid slowing down the game."),
@@ -848,37 +882,37 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"));
obj.AddExpression("ForceX",
_("Average X coordinates of forces"),
_("Average X coordinates of forces"),
_("Movement"),
_("X coordinate of the sum of forces"),
_("X coordinate of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
obj.AddExpression("ForceY",
_("Average Y coordinates of forces"),
_("Average Y coordinates of forces"),
_("Movement"),
_("Y coordinate of the sum of forces"),
_("Y coordinate of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
obj.AddExpression("ForceAngle",
_("Average angle of the forces"),
_("Average angle of the forces"),
_("Movement"),
_("Angle of the sum of forces"),
_("Angle of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
obj.AddExpression("ForceLength",
_("Average length of the forces"),
_("Average length of the forces"),
_("Movement"),
_("Length of the sum of forces"),
_("Length of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Longueur",
_("Average length of the forces"),
_("Average length of the forces"),
_("Movement"),
_("Length of the sum of forces"),
_("Length of the sum of forces"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"))
.SetHidden();
@@ -914,15 +948,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetHidden();
obj.AddExpression("ZOrder",
_("Z order"),
_("Z order of an object"),
_("Z-order"),
_("Z-order of an object"),
_("Visibility"),
"res/actions/planicon.png")
.AddParameter("object", _("Object"));
obj.AddExpression("Plan",
_("Z order"),
_("Z order of an object"),
_("Z-order"),
_("Z-order of an object"),
_("Visibility"),
"res/actions/planicon.png")
.AddParameter("object", _("Object"))
@@ -998,17 +1032,41 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Angle between two objects"),
_("Compute the angle between two objects. If you need the "
"angle to an arbitrary position, use AngleToPosition."),
_("Position"),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Object"));
obj.AddExpression("XFromAngleAndDistance",
_("X position from angle and distance"),
_("Compute the X position when given an angle and distance "
"relative to the starting object. This is also known as "
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Position"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
obj.AddExpression("YFromAngleAndDistance",
_("Y position from angle and distance"),
_("Compute the Y position when given an angle and distance "
"relative to the starting object. This is also known as "
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Position"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
obj.AddExpression("AngleToPosition",
_("Angle between an object and a position"),
_("Compute the angle between the object center and a "
"\"target\" position. If you need the angle between two "
"objects, use AngleToObject."),
_("Position"),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Target X position"))
@@ -1091,7 +1149,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Moves all objects according to the forces they have. GDevelop "
"calls this action at the end of the events by default."),
_("Apply movement to all objects"),
_("Movement"),
_("Movement using forces"),
"res/actions/doMove24.png",
"res/actions/doMove.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -1099,16 +1157,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension
.AddCondition("SeDirige",
_("An object is moving toward another"),
_("An object is moving toward another (using forces)"),
_("Check if an object moves toward another.\nThe first "
"object must move."),
_("_PARAM0_ is moving toward _PARAM1_"),
_("Movement"),
_("Movement using forces"),
"res/conditions/sedirige24.png",
"res/conditions/sedirige.png")
.AddParameter("objectList", _("Object"))
.AddParameter("objectList", _("Object 2"))
.AddParameter("expression", _("Angle of tolerance"))
.AddParameter("expression", _("Tolerance, in degrees"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();

View File

@@ -379,6 +379,30 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
.AddParameter("expression", _("b (in a+(b-a)*x)"))
.AddParameter("expression", _("x (in a+(b-a)*x)"));
extension
.AddExpression("XFromAngleAndDistance",
_("X position from angle and distance"),
_("Compute the X position when given an angle and distance "
"relative to the origin (0;0). This is also known as "
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
extension
.AddExpression("YFromAngleAndDistance",
_("Y position from angle and distance"),
_("Compute the Y position when given an angle and distance "
"relative to the origin (0;0). This is also known as "
"getting the cartesian coordinates of a 2D vector, using "
"its polar coordinates."),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
#endif
}

View File

@@ -15,7 +15,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
.SetExtensionInformation(
"BuiltinNetwork",
_("Basic internet features"),
_("Features to send web requests, communicate with external \"APIs\" and other network related tasks."),
_("Features to send web requests, communicate with external \"APIs\" "
"and other network related tasks."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/network");
@@ -24,33 +25,33 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
extension
.AddAction(
"SendRequest",
_("Send a request to a web page"),
_("Send a request to the specified web page.\n\nPlease note that for "
"the web games, the game must be hosted on the same host "
"as specified below, except if the server is configured to answer "
"to all requests (cross-domain requests)."),
_("Send _PARAM3_ request to _PARAM0__PARAM1_ with body: _PARAM2_"),
"Send a request to a web page",
"Send a request to the specified web page.\n\nPlease note that for "
"the web games, the game must be hosted on the same host "
"as specified below, except if the server is configured to answer "
"to all requests (cross-domain requests).",
"Send _PARAM3_ request to _PARAM0__PARAM1_ with body: _PARAM2_",
_("Network"),
"res/actions/net24.png",
"res/actions/net.png")
.AddParameter("string", _("Host, with protocol"))
.SetParameterLongDescription(_("Example: \"http://example.com/\"."))
.AddParameter("string", _("Path"))
.AddParameter("string", "Host, with protocol")
.SetParameterLongDescription("Example: \"http://example.com/\".")
.AddParameter("string", "Path")
.SetParameterLongDescription(
_("Example: \"/user/123\" or \"/some-page.php\"."))
.AddParameter("string", _("Request body content"))
.AddParameter("string", _("Method: \"POST\" or \"GET\""), "", true)
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
"Example: \"/user/123\" or \"/some-page.php\".")
.AddParameter("string", "Request body content")
.AddParameter("string", "Method: \"POST\" or \"GET\"", "", true)
.SetParameterLongDescription("If empty, \"GET\" will be used.")
.SetDefaultValue("\"GET\"")
.AddParameter("string", _("Content type"), "", true)
.AddParameter("string", "Content type", "", true)
.SetParameterLongDescription(
_("If empty, \"application/x-www-form-urlencoded\" will be used."))
.AddParameter("scenevar", _("Reponse scene variable"), "", true)
"If empty, \"application/x-www-form-urlencoded\" will be used.")
.AddParameter("scenevar", "Reponse scene variable", "", true)
.SetParameterLongDescription(
_("The response of the server will be stored, as a string, in this "
"variable. If the server returns *JSON*, you may want to use the "
"action \"Convert JSON to a scene variable\" afterwards, to "
"explore the results with a *structure variable*."))
"The response of the server will be stored, as a string, in this "
"variable. If the server returns *JSON*, you may want to use the "
"action \"Convert JSON to a scene variable\" afterwards, to "
"explore the results with a *structure variable*.")
.MarkAsComplex()
.SetHidden();
@@ -74,7 +75,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"highly recommended."))
.AddParameter("string", _("Request body content"))
.AddParameter("stringWithSelector",
_("Resize mode"),
_("Request method"),
"[\"GET\", \"POST\", \"PUT\", \"HEAD\", \"DELETE\", "
"\"PATCH\", \"OPTIONS\"]",
false)

View File

@@ -66,7 +66,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Animation name"))
.AddParameter("objectAnimationName", _("Animation name"))
.MarkAsAdvanced();
obj.AddAction(
@@ -133,9 +133,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.MarkAsSimple();
obj.AddAction("TourneVersPos",
_("Rotate an object toward a position"),
_("Rotate an object towards a position."),
_("Rotate _PARAM0_ towards _PARAM1_;_PARAM2_"),
"Rotate an object toward a position",
"Rotate an object towards a position.",
"Rotate _PARAM0_ towards _PARAM1_;_PARAM2_",
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
@@ -230,7 +230,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Animation name"))
.AddParameter("objectAnimationName", _("Animation name"))
.MarkAsAdvanced();
obj.AddCondition(
@@ -249,7 +249,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddCondition("Sprite",
_("Current frame"),
_("Compare the index of the current frame in the animation displayed by the specified object. The first frame in an animation starts at index 0."),
_("Compare the index of the current frame in the animation "
"displayed by the specified object. The first frame in an "
"animation starts at index 0."),
_("the animation frame"),
_("Animations and images"),
"res/conditions/sprite24.png",
@@ -365,14 +367,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("color", _("Color to make transparent"));
obj.AddAction(
"ChangeColor",
_("Tint color"),
_("Change the tint of an object. The default color is white."),
_("Change tint of _PARAM0_ to _PARAM1_"),
_("Effects"),
"res/actions/color24.png",
"res/actions/color.png")
obj.AddAction("ChangeColor",
_("Tint color"),
_("Change the tint of an object. The default color is white."),
_("Change tint of _PARAM0_ to _PARAM1_"),
_("Effects"),
"res/actions/color24.png",
"res/actions/color.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("color", _("Tint"));
@@ -436,15 +437,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.AddParameter("object", _("Object"), "Sprite");
obj.AddAction("TourneVers",
_("Rotate an object toward another"),
_("Rotate an object towards another."),
_("Rotate _PARAM0_ towards _PARAM1_"),
"Rotate an object toward another",
"Rotate an object towards another.",
"Rotate _PARAM0_ towards _PARAM1_",
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
.AddParameter("object", _("Object to be rotated"), "Sprite")
.AddParameter("objectPtr", _("Rotate toward this object"))
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("objectPtr", "Rotate toward this object")
.AddCodeOnlyParameter("currentScene", "")
.SetHidden(); // Deprecated
@@ -455,7 +456,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Name of the point"), "", true);
.AddParameter("objectPointName", _("Name of the point"), "", true);
obj.AddExpression("Y",
_("Y position of a point"),
@@ -464,7 +465,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Name of the point"), "", true);
.AddParameter("objectPointName", _("Name of the point"), "", true);
obj.AddExpression("PointX",
_("X position of a point"),
@@ -473,7 +474,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Name of the point"));
.AddParameter("objectPointName", _("Name of the point"));
obj.AddExpression("PointY",
_("Y position of a point"),
@@ -482,7 +483,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("string", _("Name of the point"));
.AddParameter("objectPointName", _("Name of the point"));
obj.AddExpression("Direc",
_("Direction"),

View File

@@ -112,9 +112,9 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
extension
.AddExpression("StrRFind",
_("Search in a text from the end"),
_("Search in a text from the end (return the position of "
"the result or -1 if not found)"),
"Search in a text from the end",
"Search in a text from the end (return the position of "
"the result or -1 if not found)",
_("Manipulation of text"),
"res/conditions/toujours24.png")
@@ -152,17 +152,17 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
extension
.AddExpression(
"StrRFindFrom",
_("Search in a text from the end, starting from a position"),
_("Search in a text from the end, starting from a position (return "
"the position of the result or -1 if not found)"),
"Search in a text from the end, starting from a position",
"Search in a text from the end, starting from a position (return "
"the position of the result or -1 if not found)",
_("Manipulation of text"),
"res/conditions/toujours24.png")
.AddParameter("string", _("Text"))
.AddParameter("string", _("Text to search for"))
.AddParameter("expression",
_("Position of the last character in the string to be "
"considered in the search"))
"Position of the last character in the string to be "
"considered in the search")
.SetHidden(); // Deprecated, see StrFindLastFrom instead.
extension

View File

@@ -87,9 +87,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarSceneDef",
_("Test if a scene variable is defined"),
_("Test if the scene variable exists."),
_("Scene variable _PARAM0_ is defined"),
"Test if a scene variable is defined",
"Test if the scene variable exists.",
"Scene variable _PARAM0_ is defined",
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
@@ -136,9 +136,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("VarGlobalDef",
_("Test if a global variable is defined"),
_("Test if a global variable exists"),
_("Global variable _PARAM0_ is defined"),
"Test if a global variable is defined",
"Test if a global variable exists",
"Global variable _PARAM0_ is defined",
_("Variables/Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
@@ -341,7 +341,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"SceneVariableRemoveAt",
_("Remove variable from a scene array (by index)"),
_("Removes a variable at the specified index of a scene array variable."),
_("Remove variable at index _PARAM1_ from array variable _PARAM0_"),
_("Remove variable at index _PARAM1_ from scene array variable _PARAM0_"),
_("Variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")
@@ -367,7 +367,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"GlobalVariableRemoveAt",
_("Remove variable from a global array (by index)"),
_("Removes a variable at the specified index of a global array variable."),
_("Remove variable at index _PARAM1_ from array variable _PARAM0_"),
_("Remove variable at index _PARAM1_ from global array variable _PARAM0_"),
_("Variables/Global variables/Collections/Arrays"),
"res/actions/var24.png",
"res/actions/var.png")

View File

@@ -19,7 +19,7 @@ using namespace std;
namespace gd {
gd::BehaviorMetadata MetadataProvider::badBehaviorInfo;
gd::BehaviorMetadata MetadataProvider::badBehaviorMetadata;
gd::ObjectMetadata MetadataProvider::badObjectInfo;
gd::EffectMetadata MetadataProvider::badEffectMetadata;
gd::InstructionMetadata MetadataProvider::badInstructionMetadata;
@@ -38,7 +38,7 @@ MetadataProvider::GetExtensionAndBehaviorMetadata(const gd::Platform& platform,
}
}
return ExtensionAndMetadata<BehaviorMetadata>(badExtension, badBehaviorInfo);
return ExtensionAndMetadata<BehaviorMetadata>(badExtension, badBehaviorMetadata);
}
const BehaviorMetadata& MetadataProvider::GetBehaviorMetadata(

View File

@@ -245,13 +245,17 @@ class GD_CORE_API MetadataProvider {
return &metadata == &badExpressionMetadata;
}
static bool IsBadBehaviorMetadata(const gd::BehaviorMetadata& metadata) {
return &metadata == &badBehaviorMetadata;
}
virtual ~MetadataProvider();
private:
MetadataProvider();
static PlatformExtension badExtension;
static BehaviorMetadata badBehaviorInfo;
static BehaviorMetadata badBehaviorMetadata;
static ObjectMetadata badObjectInfo;
static EffectMetadata badEffectMetadata;
static gd::InstructionMetadata badInstructionMetadata;

View File

@@ -123,7 +123,10 @@ gd::InstructionMetadata& ObjectMetadata::AddScopedCondition(
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace =
GetName() + gd::PlatformExtension::GetNamespaceSeparator() + name;
GetName().empty()
? name // Don't insert a namespace separator for the base object.
: GetName() + gd::PlatformExtension::GetNamespaceSeparator() + name;
conditionsInfos[nameWithNamespace] = InstructionMetadata(extensionNamespace,
nameWithNamespace,
fullname,
@@ -148,7 +151,10 @@ gd::InstructionMetadata& ObjectMetadata::AddScopedAction(
const gd::String& smallicon) {
#if defined(GD_IDE_ONLY)
gd::String nameWithNamespace =
GetName() + gd::PlatformExtension::GetNamespaceSeparator() + name;
GetName().empty()
? name // Don't insert a namespace separator for the base object.
: GetName() + gd::PlatformExtension::GetNamespaceSeparator() + name;
actionsInfos[nameWithNamespace] = InstructionMetadata(extensionNamespace,
nameWithNamespace,
fullname,

View File

@@ -175,7 +175,8 @@ class GD_CORE_API ParameterMetadata {
* \brief Return true if the type of the parameter is an expression of the
* given type.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor) and in the EventsCodeGenerator.
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsExpression(const gd::String &type,
const gd::String &parameterType) {
@@ -187,7 +188,9 @@ class GD_CORE_API ParameterMetadata {
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName";
parameterType == "sceneName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";

View File

@@ -100,26 +100,6 @@ std::unique_ptr<gd::Object> Platform::CreateObject(
return std::unique_ptr<gd::Object>(std::move(object));
}
gd::Behavior* Platform::GetBehavior(const gd::String& behaviorType) const {
for (std::size_t i = 0; i < extensionsLoaded.size(); ++i) {
gd::Behavior* behavior = extensionsLoaded[i]->GetBehavior(behaviorType);
if (behavior) return behavior;
}
return nullptr;
}
gd::BehaviorsSharedData* Platform::GetBehaviorSharedDatas(
const gd::String& behaviorType) const {
for (std::size_t i = 0; i < extensionsLoaded.size(); ++i) {
gd::BehaviorsSharedData* behaviorSharedData =
extensionsLoaded[i]->GetBehaviorSharedDatas(behaviorType);
if (behaviorSharedData) return behaviorSharedData;
}
return nullptr;
}
#if defined(GD_IDE_ONLY)
std::shared_ptr<gd::BaseEvent> Platform::CreateEvent(
const gd::String& eventType) const {

View File

@@ -126,7 +126,8 @@ class GD_CORE_API Platform {
///@}
/** \name Factory method
* Member functions used to create the platforms objects
* Member functions used to create the platform objects.
* TODO: This could be moved to gd::MetadataProvider.
*/
///@{
@@ -136,18 +137,6 @@ class GD_CORE_API Platform {
std::unique_ptr<gd::Object> CreateObject(gd::String type,
const gd::String& name) const;
/**
* \brief Get the class handling the behavior with the given type, or
* `nullptr` if no behavior with the given type is found.
*/
gd::Behavior* GetBehavior(const gd::String& type) const;
/**
* \brief Get the class handling the behavior shared data with the given type,
* or `nullptr` if no behavior with the given type is found.
*/
gd::BehaviorsSharedData* GetBehaviorSharedDatas(const gd::String& type) const;
#if defined(GD_IDE_ONLY)
/**
* \brief Create an event of given type
@@ -165,11 +154,13 @@ class GD_CORE_API Platform {
/**
* \brief Called when the IDE is about to shut down: Take this opportunity for
* erasing for example any temporary file.
* @deprecated This should be removed.
*/
virtual void OnIDEClosed(){};
/**
* \brief Called when the IDE is initialized and ready to be used.
* @deprecated This should be removed.
*/
virtual void OnIDEInitialized(){};

View File

@@ -512,7 +512,7 @@ PlatformExtension::GetAllStrExpressionsForBehavior(gd::String autoType) {
return badExpressionsMetadata;
}
gd::BaseEventSPtr PlatformExtension::CreateEvent(gd::String eventType) const {
gd::BaseEventSPtr PlatformExtension::CreateEvent(const gd::String& eventType) const {
if (eventsInfos.find(eventType) != eventsInfos.end()) {
if (eventsInfos.find(eventType)->second.instance ==
std::shared_ptr<BaseEvent>()) {
@@ -531,14 +531,14 @@ gd::BaseEventSPtr PlatformExtension::CreateEvent(gd::String eventType) const {
#endif
CreateFunPtr PlatformExtension::GetObjectCreationFunctionPtr(
gd::String objectType) const {
const gd::String& objectType) const {
if (objectsInfos.find(objectType) != objectsInfos.end())
return objectsInfos.find(objectType)->second.createFunPtr;
return NULL;
}
gd::Behavior* PlatformExtension::GetBehavior(gd::String type) const {
gd::Behavior* PlatformExtension::GetBehavior(const gd::String& type) const {
if (behaviorsInfo.find(type) != behaviorsInfo.end())
return &behaviorsInfo.find(type)->second.Get();
@@ -546,7 +546,7 @@ gd::Behavior* PlatformExtension::GetBehavior(gd::String type) const {
}
gd::BehaviorsSharedData* PlatformExtension::GetBehaviorSharedDatas(
gd::String type) const {
const gd::String& type) const {
if (behaviorsInfo.find(type) != behaviorsInfo.end() &&
behaviorsInfo.find(type)->second.GetSharedDataInstance())
return behaviorsInfo.find(type)->second.GetSharedDataInstance();

View File

@@ -400,7 +400,7 @@ class GD_CORE_API PlatformExtension {
* \brief Return a function to create the object if the type is handled by the
* extension
*/
CreateFunPtr GetObjectCreationFunctionPtr(gd::String objectType) const;
CreateFunPtr GetObjectCreationFunctionPtr(const gd::String& objectType) const;
/**
* \brief Return a vector containing all the effect types provided by the
@@ -413,13 +413,13 @@ class GD_CORE_API PlatformExtension {
*
* Return an empty pointer if \a eventType is not provided by the extension.
*/
std::shared_ptr<gd::BaseEvent> CreateEvent(gd::String eventType) const;
std::shared_ptr<gd::BaseEvent> CreateEvent(const gd::String& eventType) const;
/**
* \brief Get the gd::Behavior handling the given behavior type.
*
* Return nullptr if \a behaviorType is not provided by the extension.
*/
gd::Behavior* GetBehavior(gd::String behaviorType) const;
gd::Behavior* GetBehavior(const gd::String& behaviorType) const;
/**
* \brief Get the gd::BehaviorsSharedData handling the given behavior shared
@@ -428,7 +428,7 @@ class GD_CORE_API PlatformExtension {
* Return nullptr if \a behaviorType is not provided by the extension.
*/
gd::BehaviorsSharedData* GetBehaviorSharedDatas(
gd::String behaviorType) const;
const gd::String& behaviorType) const;
/**
* \brief Return a reference to the ObjectMetadata object associated to \a

View File

@@ -15,25 +15,18 @@
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
DependenciesAnalyzer::DependenciesAnalyzer(gd::Project& project_,
gd::Layout& layout_)
DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
const gd::Layout& layout_)
: project(project_), layout(&layout_), externalEvents(NULL) {
parentScenes.push_back(layout->GetName());
}
DependenciesAnalyzer::DependenciesAnalyzer(gd::Project& project_,
gd::ExternalEvents& externalEvents_)
DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
const gd::ExternalEvents& externalEvents_)
: project(project_), layout(NULL), externalEvents(&externalEvents_) {
parentExternalEvents.push_back(externalEvents->GetName());
}
DependenciesAnalyzer::DependenciesAnalyzer(const DependenciesAnalyzer& parent)
: parentScenes(parent.parentScenes),
parentExternalEvents(parent.parentExternalEvents),
project(parent.project),
layout(NULL),
externalEvents(NULL) {}
bool DependenciesAnalyzer::Analyze() {
if (layout)
return Analyze(layout->GetEvents(), true);
@@ -47,9 +40,9 @@ bool DependenciesAnalyzer::Analyze() {
DependenciesAnalyzer::~DependenciesAnalyzer() {}
bool DependenciesAnalyzer::Analyze(gd::EventsList& events, bool isOnTopLevel) {
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLevel) {
for (unsigned int i = 0; i < events.size(); ++i) {
gd::LinkEvent* linkEvent = dynamic_cast<gd::LinkEvent*>(&events[i]);
const gd::LinkEvent* linkEvent = dynamic_cast<const gd::LinkEvent*>(&events[i]);
if (linkEvent) {
DependenciesAnalyzer analyzer(*this);
@@ -113,7 +106,7 @@ bool DependenciesAnalyzer::Analyze(gd::EventsList& events, bool isOnTopLevel) {
sourceFilesDependencies.insert(dependencies.begin(), dependencies.end());
const gd::String& associatedSourceFile =
events[i].GetAssociatedGDManagedSourceFile(project);
events[i].GetAssociatedGDManagedSourceFile(const_cast<gd::Project&>(project));
if (!associatedSourceFile.empty())
sourceFilesDependencies.insert(associatedSourceFile);

View File

@@ -35,7 +35,7 @@ class GD_CORE_API DependenciesAnalyzer {
/**
* \brief Constructor for analyzing the dependencies of a layout
*/
DependenciesAnalyzer(gd::Project& project_, gd::Layout& layout_);
DependenciesAnalyzer(const gd::Project& project_, const gd::Layout& layout_);
/**
* \brief Constructor for analyzing the dependencies of external events.
@@ -45,8 +45,8 @@ class GD_CORE_API DependenciesAnalyzer {
* external events can be compiled separatly and called by a scene. \see
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
*/
DependenciesAnalyzer(gd::Project& project_,
gd::ExternalEvents& externalEvents);
DependenciesAnalyzer(const gd::Project& project_,
const gd::ExternalEvents& externalEvents);
virtual ~DependenciesAnalyzer();
@@ -124,13 +124,7 @@ class GD_CORE_API DependenciesAnalyzer {
* (they have no parents). \return false if a circular dependency exists, true
* otherwise.
*/
bool Analyze(gd::EventsList& events, bool isOnTopLevel);
/**
* \brief Internal constructor used when analyzing a linked layout/external
* events.
*/
DependenciesAnalyzer(const DependenciesAnalyzer& parent);
bool Analyze(const gd::EventsList& events, bool isOnTopLevel);
void AddParentScene(gd::String parentScene) {
parentScenes.push_back(parentScene);
@@ -161,9 +155,9 @@ class GD_CORE_API DependenciesAnalyzer {
std::vector<gd::String>
parentExternalEvents; ///< Used to check for circular dependencies.
gd::Project& project;
gd::Layout* layout;
gd::ExternalEvents* externalEvents;
const gd::Project& project;
const gd::Layout* layout;
const gd::ExternalEvents* externalEvents;
};
#endif // DEPENDENCIESANALYZER_H

View File

@@ -17,6 +17,7 @@ LayoutEditorCanvasOptions::LayoutEditorCanvasOptions()
gridHeight(32),
gridOffsetX(0),
gridOffsetY(0),
gridType("rectangular"),
gridR(158),
gridG(180),
gridB(255),
@@ -30,6 +31,7 @@ void LayoutEditorCanvasOptions::SerializeTo(SerializerElement& element) const {
element.SetAttribute("gridHeight", gridHeight);
element.SetAttribute("gridOffsetX", gridOffsetX);
element.SetAttribute("gridOffsetY", gridOffsetY);
element.SetAttribute("gridType", gridType);
element.SetAttribute("gridR", gridR);
element.SetAttribute("gridG", gridG);
element.SetAttribute("gridB", gridB);
@@ -42,10 +44,11 @@ void LayoutEditorCanvasOptions::UnserializeFrom(
grid = element.GetBoolAttribute("grid");
snap = element.GetBoolAttribute("snap");
windowMask = element.GetBoolAttribute("windowMask");
gridWidth = element.GetIntAttribute("gridWidth", 32);
gridHeight = element.GetIntAttribute("gridHeight", 32);
gridOffsetX = element.GetIntAttribute("gridOffsetX", 0);
gridOffsetY = element.GetIntAttribute("gridOffsetY", 0);
gridWidth = element.GetDoubleAttribute("gridWidth", 32);
gridHeight = element.GetDoubleAttribute("gridHeight", 32);
gridOffsetX = element.GetDoubleAttribute("gridOffsetX", 0);
gridOffsetY = element.GetDoubleAttribute("gridOffsetY", 0);
gridType = element.GetStringAttribute("gridType", "rectangular");
gridR = element.GetIntAttribute("gridR", 158);
gridG = element.GetIntAttribute("gridG", 180);
gridB = element.GetIntAttribute("gridB", 255);

View File

@@ -40,10 +40,11 @@ class GD_CORE_API LayoutEditorCanvasOptions {
bool grid; ///< True if grid activated in editor
bool snap; ///< True if snap to grid activated in editor
int gridWidth; ///< Grid width in editor
int gridHeight; ///< Grid height in editor
int gridOffsetX; ///< Grid X offset
int gridOffsetY; ///< Grid Y offset
double gridWidth; ///< Grid width in editor
double gridHeight; ///< Grid height in editor
double gridOffsetX; ///< Grid X offset
double gridOffsetY; ///< Grid Y offset
gd::String gridType; ///< Grid type: rectangular or isometric
int gridR; ///< Grid red color in editor
int gridG; ///< Grid green color in editor
int gridB; ///< Grid blue color in editor

View File

@@ -17,6 +17,8 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
using namespace std;
@@ -95,11 +97,11 @@ std::set<gd::String> EventsVariablesFinder::FindAllGlobalVariables(
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
std::set<gd::String> results2 =
FindArgumentsInEvents(platform,
project,
project.GetLayout(i),
project.GetLayout(i).GetEvents(),
"globalvar");
FindArgumentsInEventsAndDependencies(
platform,
project,
project.GetLayout(i),
"globalvar");
results.insert(results2.begin(), results2.end());
}
@@ -112,8 +114,8 @@ std::set<gd::String> EventsVariablesFinder::FindAllLayoutVariables(
const gd::Layout& layout) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEvents(
platform, project, layout, layout.GetEvents(), "scenevar");
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
platform, project, layout, "scenevar");
results.insert(results2.begin(), results2.end());
return results;
@@ -126,12 +128,12 @@ std::set<gd::String> EventsVariablesFinder::FindAllObjectVariables(
const gd::Object& object) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEvents(platform,
project,
layout,
layout.GetEvents(),
"objectvar",
object.GetName());
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
platform,
project,
layout,
"objectvar",
object.GetName());
results.insert(results2.begin(), results2.end());
return results;
@@ -203,6 +205,38 @@ std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEvents(
platform, project, layout, layout.GetEvents(), parameterType, objectName);
results.insert(results2.begin(), results2.end());
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, layout, externalEvents.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, dependencyLayout, dependencyLayout.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
}
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEvents(
const gd::Platform& platform,
const gd::Project& project,

View File

@@ -24,11 +24,17 @@ namespace gd {
* global or object variables.
*
* \todo Refactor this class using ArbitraryEventsWorker
* \todo Rework this class to return the shapes (maybe even types?) of the
* variables (in particular for structures and arrays), so we can use this
* for better autocompletions in the variables dialogs in the IDE.
*
* \ingroup IDE
*/
class EventsVariablesFinder {
public:
EventsVariablesFinder(){};
virtual ~EventsVariablesFinder(){};
/**
* Construct a list containing the name of all global variables used in the
* project.
@@ -94,10 +100,34 @@ class EventsVariablesFinder {
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType
* parameterType. It searchs in events dependencies.
*
* \param platform The platform of the project
* \param project The project used
* \param project The layout used
* \param layout The layout used
* \param events The events to be analyzed
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInEventsAndDependencies(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName = "");
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType. It doesn't search in events dependencies.
*
* \param platform The platform of the project
* \param project The project used
* \param layout The layout used
* \param events The events to be analyzed
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account
@@ -113,10 +143,7 @@ class EventsVariablesFinder {
const gd::Layout& layout,
const gd::EventsList& events,
const gd::String& parameterType,
const gd::String& objectName = "");
EventsVariablesFinder(){};
virtual ~EventsVariablesFinder(){};
const gd::String& objectName);
};
} // namespace gd

View File

@@ -0,0 +1,31 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionCompletionFinder.h"
namespace gd {
const gd::ParameterMetadata
ExpressionCompletionDescription::badParameterMetadata;
/**
* \brief Turn an ExpressionCompletionDescription to a string.
*/
std::ostream& operator<<(std::ostream& os,
ExpressionCompletionDescription const& value) {
os << "{ " << value.GetCompletionKind() << ", " << value.GetType() << ", "
<< value.GetPrefix() << ", " << value.GetObjectName() << ", "
<< value.GetBehaviorName() << ", "
<< (value.IsExact() ? "exact" : "non-exact") << ", "
<< (value.IsLastParameter() ? "last parameter" : "not last parameter")
<< ", "
<< (value.HasParameterMetadata()
? gd::String::From(&value.GetParameterMetadata())
: "no parameter metadata")
<< " }";
return os;
}
} // namespace gd

View File

@@ -8,10 +8,14 @@
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
namespace gd {
class Expression;
class ObjectsContainer;
@@ -33,19 +37,21 @@ struct ExpressionCompletionDescription {
/**
* The different kind of completions that can be described.
*/
enum CompletionKind {
Object,
Behavior,
Expression,
Variable,
};
enum CompletionKind { Object, Behavior, Expression, Variable, Text };
/**
* \brief Create a completion for an object with the given prefix
*/
static ExpressionCompletionDescription ForObject(const gd::String& type_,
const gd::String& prefix_) {
return ExpressionCompletionDescription(Object, type_, prefix_);
static ExpressionCompletionDescription ForObject(
const gd::String& type_,
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_) {
return ExpressionCompletionDescription(Object,
type_,
prefix_,
replacementStartPosition_,
replacementEndPosition_);
}
/**
@@ -53,17 +59,58 @@ struct ExpressionCompletionDescription {
* the specified object
*/
static ExpressionCompletionDescription ForBehavior(
const gd::String& prefix_, const gd::String& objectName_) {
return ExpressionCompletionDescription(Behavior, "", prefix_, objectName_);
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_,
const gd::String& objectName_) {
return ExpressionCompletionDescription(Behavior,
"",
prefix_,
replacementStartPosition_,
replacementEndPosition_,
objectName_);
}
/**
* \brief Create a completion for a variable with the given prefix
*/
static ExpressionCompletionDescription ForVariable(
const gd::String& type_, const gd::String& prefix_) {
return ExpressionCompletionDescription(Variable, type_, prefix_);
const gd::String& type_,
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_,
const gd::String& objectName_ = "") {
return ExpressionCompletionDescription(Variable,
type_,
prefix_,
replacementStartPosition_,
replacementEndPosition_,
objectName_);
}
/**
* \brief Create a completion for a text with the given prefix
*/
static ExpressionCompletionDescription ForText(
const gd::String& type_,
const gd::ParameterMetadata& parameterMetadata_,
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_,
const bool isLastParameter_,
const gd::String& objectName_ = "") {
auto description =
ExpressionCompletionDescription(Text,
type_,
prefix_,
replacementStartPosition_,
replacementEndPosition_,
objectName_);
description.SetIsLastParameter(isLastParameter_);
description.SetParameterMetadata(parameterMetadata_);
return description;
}
/**
* \brief Create a completion for an expression (free, object or behavior
* expression) with the given prefix
@@ -71,10 +118,17 @@ struct ExpressionCompletionDescription {
static ExpressionCompletionDescription ForExpression(
const gd::String& type_,
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_,
const gd::String& objectName_ = "",
const gd::String& behaviorName_ = "") {
return ExpressionCompletionDescription(
Expression, type_, prefix_, objectName_, behaviorName_);
return ExpressionCompletionDescription(Expression,
type_,
prefix_,
replacementStartPosition_,
replacementEndPosition_,
objectName_,
behaviorName_);
}
/** Check if two description of completions are equal */
@@ -131,6 +185,56 @@ struct ExpressionCompletionDescription {
*/
bool IsExact() const { return isExact; }
/**
* \brief Return the first character index of the autocompleted part.
*/
size_t GetReplacementStartPosition() const {
return replacementStartPosition;
}
/**
* \brief Return the first character index after the autocompleted part.
*/
size_t GetReplacementEndPosition() const { return replacementEndPosition; }
/**
* \brief Set if the expression is the last child of a function call.
*/
ExpressionCompletionDescription& SetIsLastParameter(bool isLastParameter_) {
isLastParameter = isLastParameter_;
return *this;
}
/**
* \brief Check if the expression is the last child of a function call.
*/
bool IsLastParameter() const { return isLastParameter; }
/**
* \brief Set the parameter metadata, in the case the completion is about
* a parameter of a function call.
*/
ExpressionCompletionDescription& SetParameterMetadata(
const gd::ParameterMetadata& parameterMetadata_) {
parameterMetadata = &parameterMetadata_;
return *this;
}
/**
* \brief Check if the completion is about a parameter of a function call.
*/
bool HasParameterMetadata() const {
return parameterMetadata != &badParameterMetadata;
}
/**
* \brief Return the parameter metadata, if the completion is about a
* parameter of a function call. Returns an empty metadata otherwise.
*/
const gd::ParameterMetadata& GetParameterMetadata() const {
return *parameterMetadata;
}
/** Default constructor, only to be used by Emscripten bindings. */
ExpressionCompletionDescription() : completionKind(Object){};
@@ -138,34 +242,40 @@ struct ExpressionCompletionDescription {
ExpressionCompletionDescription(CompletionKind completionKind_,
const gd::String& type_,
const gd::String& prefix_,
size_t replacementStartPosition_,
size_t replacementEndPosition_,
const gd::String& objectName_ = "",
const gd::String& behaviorName_ = "")
: completionKind(completionKind_),
type(type_),
prefix(prefix_),
replacementStartPosition(replacementStartPosition_),
replacementEndPosition(replacementEndPosition_),
objectName(objectName_),
behaviorName(behaviorName_),
isExact(false) {}
isExact(false),
isLastParameter(false),
parameterMetadata(&badParameterMetadata) {}
CompletionKind completionKind;
gd::String type;
gd::String prefix;
size_t replacementStartPosition;
size_t replacementEndPosition;
gd::String objectName;
gd::String behaviorName;
bool isExact;
bool isLastParameter;
const gd::ParameterMetadata* parameterMetadata;
static const gd::ParameterMetadata badParameterMetadata;
};
/**
* \brief Turn an ExpressionCompletionDescription to a string.
*/
std::ostream& operator<<(std::ostream& os,
ExpressionCompletionDescription const& value) {
os << "{ " << value.GetCompletionKind() << ", " << value.GetType() << ", "
<< value.GetPrefix() << ", " << value.GetObjectName() << ", "
<< value.GetBehaviorName() << ", "
<< (value.IsExact() ? "exact" : "non-exact") << " }";
return os;
}
ExpressionCompletionDescription const& value);
/**
* \brief Returns the list of completion descriptions for an expression node.
@@ -182,16 +292,18 @@ class GD_CORE_API ExpressionCompletionFinder
static std::vector<ExpressionCompletionDescription>
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNode* nodeAtLocation =
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(node,
searchedPosition);
gd::ExpressionNodeLocationFinder finder(searchedPosition);
node.Visit(finder);
gd::ExpressionNode* nodeAtLocation = finder.GetNode();
if (nodeAtLocation == nullptr) {
std::vector<ExpressionCompletionDescription> emptyCompletions;
return emptyCompletions;
}
gd::ExpressionCompletionFinder autocompletionProvider(searchedPosition);
gd::ExpressionNode* maybeParentNodeAtLocation = finder.GetParentNode();
gd::ExpressionCompletionFinder autocompletionProvider(
searchedPosition, maybeParentNodeAtLocation);
nodeAtLocation->Visit(autocompletionProvider);
return autocompletionProvider.GetCompletionDescriptions();
}
@@ -208,32 +320,92 @@ class GD_CORE_API ExpressionCompletionFinder
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.type, ""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitOperatorNode(OperatorNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.type, ""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.type, ""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitNumberNode(NumberNode& node) override {
// No completions
}
void OnVisitTextNode(TextNode& node) override {
// No completions
// Completions are searched in the case the text node is a parameter of a
// function call.
FunctionCallNode* functionCall =
dynamic_cast<FunctionCallNode*>(maybeParentNodeAtLocation);
if (functionCall != nullptr) {
int parameterIndex = -1;
for (int i = 0; i < functionCall->parameters.size(); i++) {
if (functionCall->parameters.at(i).get() == &node) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
// Search the parameter metadata index skipping invisible ones.
size_t visibleParameterIndex = 0;
size_t metadataParameterIndex = ExpressionParser2::WrittenParametersFirstIndex(
functionCall->objectName, functionCall->behaviorName);
const gd::ParameterMetadata* parameterMetadata = nullptr;
while (metadataParameterIndex <
functionCall->expressionMetadata.parameters.size()) {
if (!functionCall->expressionMetadata.parameters[metadataParameterIndex]
.IsCodeOnly()) {
if (visibleParameterIndex == parameterIndex) {
parameterMetadata = &functionCall->expressionMetadata
.parameters[metadataParameterIndex];
}
visibleParameterIndex++;
}
metadataParameterIndex++;
}
const int visibleParameterCount = visibleParameterIndex;
if (parameterMetadata == nullptr) {
// There are too many parameters in the expression, this text node is
// not actually linked to a parameter expected by the function call.
return;
}
const gd::String& type = parameterMetadata->GetType();
if (type == "string") {
// No completions for an arbitrary string.
return;
}
bool isLastParameter = parameterIndex == visibleParameterCount - 1;
completions.push_back(ExpressionCompletionDescription::ForText(
type,
*parameterMetadata,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
isLastParameter,
functionCall->objectName));
}
}
void OnVisitVariableNode(VariableNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForVariable(node.type, node.name));
completions.push_back(ExpressionCompletionDescription::ForVariable(
node.type,
node.name,
node.location.GetStartPosition(),
node.location.GetEndPosition(),
node.objectName));
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
// No completions
@@ -246,13 +418,22 @@ class GD_CORE_API ExpressionCompletionFinder
if (gd::ParameterMetadata::IsObject(node.type)) {
// Only show completions of objects if an object is required
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.identifierName));
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
} else {
// Show completions for expressions and objects otherwise.
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.identifierName));
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.identifierName));
node.type,
node.identifierName,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
@@ -262,16 +443,24 @@ class GD_CORE_API ExpressionCompletionFinder
// function name missing)
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.objectName));
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.objectFunctionOrBehaviorName, node.objectName));
node.objectFunctionOrBehaviorName,
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
node.objectName));
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
IsCaretOn(node.behaviorFunctionNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.behaviorFunctionName,
node.behaviorFunctionNameLocation.GetStartPosition(),
node.behaviorFunctionNameLocation.GetEndPosition(),
node.objectName,
node.objectFunctionOrBehaviorName));
}
@@ -279,13 +468,23 @@ class GD_CORE_API ExpressionCompletionFinder
// Object function or behavior name
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.objectName));
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.objectFunctionOrBehaviorName, node.objectName));
node.objectFunctionOrBehaviorName,
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
node.objectName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.objectFunctionOrBehaviorName, node.objectName));
node.type,
node.objectFunctionOrBehaviorName,
node.objectFunctionOrBehaviorNameLocation.GetStartPosition(),
node.objectFunctionOrBehaviorNameLocation.GetEndPosition(),
node.objectName));
}
}
}
@@ -297,24 +496,35 @@ class GD_CORE_API ExpressionCompletionFinder
// Behavior function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.objectName));
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.behaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.behaviorName, node.objectName));
node.behaviorName,
node.behaviorNameLocation.GetStartPosition(),
node.behaviorNameLocation.GetEndPosition(),
node.objectName));
} else {
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type,
node.functionName,
node.objectName,
node.behaviorName)
.SetIsExact(isCaretOnParenthesis));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
node.objectName,
node.behaviorName)
.SetIsExact(isCaretOnParenthesis));
}
} else if (!node.objectName.empty()) {
// Object function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, node.objectName));
node.type,
node.objectName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition()));
} else {
// Add completions for behaviors, because we could imagine that the user
// wants to move from an object function to a behavior function, and so
@@ -323,25 +533,41 @@ class GD_CORE_API ExpressionCompletionFinder
// function).
if (!isCaretOnParenthesis) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.functionName, node.objectName));
node.functionName,
node.objectNameLocation.GetStartPosition(),
node.objectNameLocation.GetEndPosition(),
node.objectName));
}
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.functionName, node.objectName)
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition(),
node.objectName)
.SetIsExact(isCaretOnParenthesis));
}
} else {
// Free function
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.functionName)
node.type,
node.functionName,
node.functionNameLocation.GetStartPosition(),
node.functionNameLocation.GetEndPosition())
.SetIsExact(isCaretOnParenthesis));
}
}
void OnVisitEmptyNode(EmptyNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.type, node.text));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, node.text));
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.text,
node.location.GetStartPosition(),
node.location.GetEndPosition()));
}
private:
@@ -354,11 +580,14 @@ class GD_CORE_API ExpressionCompletionFinder
(inclusive && searchedPosition <= location.GetEndPosition())));
}
ExpressionCompletionFinder(size_t searchedPosition_)
: searchedPosition(searchedPosition_){};
ExpressionCompletionFinder(size_t searchedPosition_,
gd::ExpressionNode* maybeParentNodeAtLocation_)
: searchedPosition(searchedPosition_),
maybeParentNodeAtLocation(maybeParentNodeAtLocation_){};
std::vector<ExpressionCompletionDescription> completions;
size_t searchedPosition;
gd::ExpressionNode* maybeParentNodeAtLocation;
};
} // namespace gd

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
@@ -46,11 +47,30 @@ class GD_CORE_API ExpressionNodeLocationFinder
return finder.GetNode();
}
/**
* \brief Helper function to find the parent of the deepest node at the search
* position, if any.
*
* \warning Useful for tests. In other cases, prefer using `GetParentNode`.
*/
static ExpressionNode* GetParentNodeAtPosition(gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNodeLocationFinder finder(searchedPosition);
node.Visit(finder);
return finder.GetParentNode();
}
/**
* \brief Return the deepest node found at the search position, if any.
*/
ExpressionNode* GetNode() { return foundNode; };
/**
* \brief Return the parent of deepest node found at the search position, if
* any.
*/
ExpressionNode* GetParentNode() { return parentNode; };
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
CheckSearchPositionInNode(node);
@@ -110,6 +130,7 @@ class GD_CORE_API ExpressionNodeLocationFinder
if (node.location.GetStartPosition() <= searchedPosition &&
((!inclusive && searchedPosition < node.location.GetEndPosition()) ||
(inclusive && searchedPosition <= node.location.GetEndPosition()))) {
parentNode = foundNode;
foundNode = &node;
return true;
}
@@ -119,6 +140,7 @@ class GD_CORE_API ExpressionNodeLocationFinder
size_t searchedPosition;
ExpressionNode* foundNode;
ExpressionNode* parentNode;
};
} // namespace gd

View File

@@ -24,24 +24,6 @@ namespace gd {
InstructionSentenceFormatter *InstructionSentenceFormatter::_singleton = NULL;
gd::String InstructionSentenceFormatter::Translate(
const gd::Instruction &instr, const gd::InstructionMetadata &metadata) {
gd::String out = metadata.GetSentence();
if (out.empty())
out = " "; // Prevent empty sentences that could trigger graphical
// glitches.
// Replace _PARAMx_ placeholders by their values
for (std::size_t i = 0; i < metadata.parameters.size(); ++i) {
gd::String placeholder = "_PARAM" + gd::String::From(i) + "_";
gd::String parameter = instr.GetParameter(i).GetPlainString();
out = out.FindAndReplace(placeholder, parameter);
}
out = out.FindAndReplace("\n", " ");
return out;
}
std::vector<std::pair<gd::String, gd::TextFormatting> >
InstructionSentenceFormatter::GetAsFormattedText(
const Instruction &instr, const gd::InstructionMetadata &metadata) {
@@ -83,8 +65,7 @@ InstructionSentenceFormatter::GetAsFormattedText(
}
// Add the parameter
TextFormatting format =
GetFormattingFromType(metadata.parameters[firstParamIndex].type);
TextFormatting format;
format.userData = firstParamIndex;
gd::String text = instr.GetParameter(firstParamIndex).GetPlainString();
@@ -109,74 +90,6 @@ InstructionSentenceFormatter::GetAsFormattedText(
return formattedStr;
}
TextFormatting InstructionSentenceFormatter::GetFormattingFromType(
const gd::String &type) {
if (gd::ParameterMetadata::IsObject(type)) return typesFormatting["object"];
return typesFormatting[type];
}
gd::String InstructionSentenceFormatter::LabelFromType(const gd::String &type) {
if (type.empty())
return "";
else if (type == "expression")
return _("Expression");
else if (gd::ParameterMetadata::IsObject(type))
return _("Object");
else if (type == "behavior")
return _("Behavior");
else if (type == "operator")
return _("Operator");
else if (type == "relationalOperator")
return _("Relational operator");
else if (type == "file")
return _("File");
else if (type == "key")
return _("Key");
else if (type == "mouse")
return _("Mouse button");
else if (type == "yesorno")
return _("Yes or No");
else if (type == "police")
return _("Font");
else if (type == "color")
return _("Color");
else if (type == "trueorfalse")
return _("True or False");
else if (type == "string")
return _("String");
else if (type == "musicfile")
return _("Music");
else if (type == "soundfile")
return _("Sound");
else if (type == "password")
return _("Password");
else if (type == "layer")
return _("Layer");
else if (type == "joyaxis")
return _("Joystick axis");
else if (type == "objectvar")
return _("Variable of the object");
else if (type == "scenevar")
return _("Scene variable");
else if (type == "globalvar")
return _("Global variable");
return _("Unknown");
}
void InstructionSentenceFormatter::LoadTypesFormattingFromConfig() {
// Load default configuration
typesFormatting.clear();
typesFormatting["expression"].SetColor(27, 143, 1).SetBold();
typesFormatting["object"].SetColor(182, 97, 10).SetBold();
typesFormatting["behavior"].SetColor(119, 119, 119).SetBold();
typesFormatting["operator"].SetColor(55, 131, 211).SetBold();
typesFormatting["objectvar"].SetColor(131, 55, 162).SetBold();
typesFormatting["scenevar"].SetColor(131, 55, 162).SetBold();
typesFormatting["globalvar"].SetColor(131, 55, 162).SetBold();
}
} // namespace gd
#endif

View File

@@ -25,39 +25,12 @@ namespace gd {
*/
class GD_CORE_API InstructionSentenceFormatter {
public:
/**
* \brief Create a sentence from an instruction and its metadata.
*
* Sentence is provided in the gd::InstructionMetadata passed as parameter.
* Parameters placeholders ("_PARAMx_", x being the parameter index) are
* replaced by their values stored in the isntruction passed as parameter.
*/
gd::String Translate(const gd::Instruction &instr,
const gd::InstructionMetadata &metadata);
/**
* \brief Create a formatted sentence from an instruction and its metadata.
*/
std::vector<std::pair<gd::String, gd::TextFormatting> > GetAsFormattedText(
const gd::Instruction &instr, const gd::InstructionMetadata &metadata);
/**
* \brief Return the TextFormatting object associated to the \a type.
*/
TextFormatting GetFormattingFromType(const gd::String &type);
/**
* \brief Return the label of a parameter type
*/
gd::String LabelFromType(const gd::String &type);
/**
* \brief Load the configuration from the default configuration.
*/
void LoadTypesFormattingFromConfig();
std::map<gd::String, gd::TextFormatting> typesFormatting;
static InstructionSentenceFormatter *Get() {
if (NULL == _singleton) {
_singleton = new InstructionSentenceFormatter;

View File

@@ -13,85 +13,20 @@ namespace gd {
/**
* \brief Represents the style of a text displayed in the events editor.
*
* Notably used by EventsRenderingHelper to render Instruction.
*
* \see EventsRenderingHelper
* \ingroup IDEDialogsEventsEditor
*/
class GD_CORE_API TextFormatting {
public:
TextFormatting()
: colorRed(0),
colorGreen(0),
colorBlue(0),
bold(false),
italic(false),
userData(gd::String::npos) {}
TextFormatting() : userData(gd::String::npos) {}
~TextFormatting() {}
/**
* \brief Return true if the bold style must be applied.
*/
bool IsBold() const { return bold; }
/**
* \brief Return true if the italic style must be applied.
*/
bool IsItalic() const { return italic; }
/**
* \brief Return the red component of the color that must be applied to the
* text.
*/
unsigned int GetColorRed() const { return colorRed; }
/**
* \brief Return the green component of the color that must be applied to the
* text.
*/
unsigned int GetColorGreen() const { return colorGreen; }
/**
* \brief Return the blue component of the color that must be applied to the
* text.
*/
unsigned int GetColorBlue() const { return colorBlue; }
/**
* Change the color of the text.
*/
TextFormatting& SetColor(unsigned int r, unsigned int g, unsigned int b) {
colorRed = r;
colorGreen = g;
colorBlue = b;
return *this;
}
/**
* \brief Set if the bold style must be applied.
*/
TextFormatting& SetBold(bool enable = true) {
bold = enable;
return *this;
}
/**
* \brief Set if the italic style must be applied.
*/
TextFormatting& SetItalic(bool enable = true) {
italic = enable;
return *this;
}
/**
* Return the data (an integer) associated with the text formatting.
* Used to store the parameter when rendering instructions.
*/
size_t GetUserData() const { return userData; }
unsigned int colorRed;
unsigned int colorGreen;
unsigned int colorBlue;
bool bold;
bool italic;
size_t userData;
};

View File

@@ -0,0 +1,144 @@
#include "UsedExtensionsFinder.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/Project/BehaviorContent.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
namespace gd {
std::set<gd::String> UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::WholeProjectRefactorer::ExposeProjectObjects(project, worker);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, worker);
return worker.usedExtensions;
};
// Objects scanner
void UsedExtensionsFinder::DoVisitObject(gd::Object& object) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), object.GetType())
.GetExtension()
.GetName());
};
// Behaviors scanner
void UsedExtensionsFinder::DoVisitBehavior(gd::BehaviorContent& behavior) {
usedExtensions.insert(
gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName())
.GetExtension()
.GetName());
};
// Instructions scanner
bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
auto metadata =
isCondition ? gd::MetadataProvider::GetExtensionAndConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
usedExtensions.insert(metadata.GetExtension().GetName());
size_t i = 0;
for (auto expression : instruction.GetParameters()) {
const gd::String& parameterType =
metadata.GetMetadata().GetParameter(i).GetType();
i++;
if (gd::ParameterMetadata::IsExpression("string", parameterType) ||
gd::ParameterMetadata::IsExpression("number", parameterType)) {
gd::ExpressionParser2 parser(project.GetCurrentPlatform(),
GetGlobalObjectsContainer(),
GetObjectsContainer());
parser.ParseExpression(parameterType, expression.GetPlainString())
->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
usedExtensions.insert("BuiltinVariables");
}
return false;
}
// Expressions scanner
// Ignore litterals nodes
void UsedExtensionsFinder::OnVisitNumberNode(NumberNode& node){};
void UsedExtensionsFinder::OnVisitTextNode(TextNode& node){};
// Ignore nodes without valid extensions
void UsedExtensionsFinder::OnVisitEmptyNode(EmptyNode& node){};
void UsedExtensionsFinder::OnVisitObjectFunctionNameNode(
ObjectFunctionNameNode& node){};
// Visit sub-expressions
void UsedExtensionsFinder::OnVisitSubExpressionNode(SubExpressionNode& node) {
node.expression->Visit(*this);
};
void UsedExtensionsFinder::OnVisitOperatorNode(OperatorNode& node) {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
};
void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
node.factor->Visit(*this);
};
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
usedExtensions.insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
usedExtensions.insert("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
// Add extensions bound to Objects/Behaviors/Functions
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode& node) {
if (gd::ParameterMetadata::IsObject(node.type)) {
usedExtensions.insert(gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName)
.GetExtension()
.GetName());
}
};
void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
// Extensions of non-free functions are already found when scanning objects.
if (!(node.objectName.empty() && node.behaviorName.empty())) return;
gd::ExtensionAndMetadata<gd::ExpressionMetadata> metadata;
// Try to find a free number expression
metadata = gd::MetadataProvider::GetExtensionAndExpressionMetadata(
project.GetCurrentPlatform(), node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata.GetMetadata())) {
// Try to find a free str expression
metadata = gd::MetadataProvider::GetExtensionAndStrExpressionMetadata(
project.GetCurrentPlatform(), node.functionName);
// No valid expression found, return.
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata.GetMetadata()))
return;
}
usedExtensions.insert(metadata.GetExtension().GetName());
};
} // namespace gd

View File

@@ -0,0 +1,64 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_USED_EXTENSIONS_FINDER_H
#define GDCORE_USED_EXTENSIONS_FINDER_H
#include <set>
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/String.h"
namespace gd {
class Project;
class Object;
class BehaviorContent;
} // namespace gd
namespace gd {
class GD_CORE_API UsedExtensionsFinder
: public ArbitraryObjectsWorker,
public ArbitraryEventsWorkerWithContext,
public ExpressionParser2NodeWorker {
public:
static std::set<gd::String> ScanProject(gd::Project& project);
private:
UsedExtensionsFinder(gd::Project& project_) : project(project_){};
gd::Project& project;
std::set<gd::String> usedExtensions;
// Object Visitor
void DoVisitObject(gd::Object& object) override;
// Behavior Visitor
void DoVisitBehavior(gd::BehaviorContent& behavior) override;
// Instructions Visitor
bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) override;
// Expression Visitor
void OnVisitSubExpressionNode(SubExpressionNode& node) override;
void OnVisitOperatorNode(OperatorNode& node) override;
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override;
void OnVisitNumberNode(NumberNode& node) override;
void OnVisitTextNode(TextNode& node) override;
void OnVisitVariableNode(VariableNode& node) override;
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override;
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override;
void OnVisitIdentifierNode(IdentifierNode& node) override;
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override;
void OnVisitFunctionCallNode(FunctionCallNode& node) override;
void OnVisitEmptyNode(EmptyNode& node) override;
};
}; // namespace gd
#endif

View File

@@ -14,16 +14,16 @@ namespace gd {
* \note Both objects must be kept alive, as this is keeping a pointer to them.
*/
struct DependencyMetadataAndExtension {
DependencyMetadataAndExtension(gd::DependencyMetadata& dependency_,
gd::PlatformExtension& extension_)
DependencyMetadataAndExtension(gd::DependencyMetadata &dependency_,
gd::PlatformExtension &extension_)
: dependency(&dependency_), extension(&extension_){};
gd::DependencyMetadata& GetDependency() const { return *dependency; };
gd::PlatformExtension& GetExtension() const { return *extension; };
gd::DependencyMetadata &GetDependency() const { return *dependency; };
gd::PlatformExtension &GetExtension() const { return *extension; };
private:
gd::DependencyMetadata* dependency;
gd::PlatformExtension* extension;
gd::DependencyMetadata *dependency;
gd::PlatformExtension *extension;
};
/**
@@ -33,8 +33,8 @@ struct DependencyMetadataAndExtension {
class ExportedDependencyResolver {
public:
/**
* \brief Return the list of dependencies to be exported for the given project
* and dependency type.
* \brief Return the list of dependencies to be exported for the given
* project, used extensions list and dependency type.
*
* Not all dependencies declared by extensions must be exported: some are only
* exported when some settings are filled. Then, some others are only exported
@@ -42,11 +42,13 @@ class ExportedDependencyResolver {
* one level though).
*/
static std::vector<DependencyMetadataAndExtension> GetDependenciesFor(
const gd::Project& project, const gd::String& dependencyType) {
const gd::Project &project,
std::set<gd::String> usedExtensions,
const gd::String &dependencyType) {
std::vector<DependencyMetadataAndExtension> dependenciesWithProperType;
for (std::shared_ptr<gd::PlatformExtension> extension :
project.GetCurrentPlatform().GetAllPlatformExtensions()) {
for (gd::DependencyMetadata& dependency :
for (const gd::String &extensionName : usedExtensions) {
auto extension = project.GetCurrentPlatform().GetExtension(extensionName);
for (gd::DependencyMetadata &dependency :
extension->GetAllDependencies()) {
if (dependency.GetDependencyType() == dependencyType) {
DependencyMetadataAndExtension dependencyMetadataAndExtension(
@@ -60,7 +62,7 @@ class ExportedDependencyResolver {
// and those that don't require extra settings to be filled.
std::vector<DependencyMetadataAndExtension> dependenciesWithFilledSettings;
for (auto dependencyAndExtension : dependenciesWithProperType) {
auto& dependency = dependencyAndExtension.GetDependency();
auto &dependency = dependencyAndExtension.GetDependency();
auto extraSettingValues = GetExtensionDependencyExtraSettingValues(
project, dependencyAndExtension);
@@ -73,15 +75,15 @@ class ExportedDependencyResolver {
// exported (or dependencies that don't require another dependency).
std::vector<DependencyMetadataAndExtension> exportedDependencies;
for (auto dependencyAndExtension : dependenciesWithFilledSettings) {
auto& dependency = dependencyAndExtension.GetDependency();
auto& otherDependencyName =
auto &dependency = dependencyAndExtension.GetDependency();
auto &otherDependencyName =
dependency.GetOtherDependencyThatMustBeExported();
if (otherDependencyName.empty() ||
std::find_if(
dependenciesWithFilledSettings.begin(),
dependenciesWithFilledSettings.end(),
[&otherDependencyName](
DependencyMetadataAndExtension& otherDependencyAndExtension) {
DependencyMetadataAndExtension &otherDependencyAndExtension) {
return otherDependencyAndExtension.GetDependency().GetName() ==
otherDependencyName;
}) != dependenciesWithFilledSettings.end()) {
@@ -98,15 +100,15 @@ class ExportedDependencyResolver {
*/
static std::map<gd::String, gd::String>
GetExtensionDependencyExtraSettingValues(
const gd::Project& project,
const gd::DependencyMetadataAndExtension& dependencyAndExtension) {
const gd::Project &project,
const gd::DependencyMetadataAndExtension &dependencyAndExtension) {
std::map<gd::String, gd::String> values;
auto& dependency = dependencyAndExtension.GetDependency();
const gd::String& extensionName =
auto &dependency = dependencyAndExtension.GetDependency();
const gd::String &extensionName =
dependencyAndExtension.GetExtension().GetName();
for (const auto& extraSetting : dependency.GetAllExtraSettings()) {
const gd::String& type = extraSetting.second.GetType();
for (const auto &extraSetting : dependency.GetAllExtraSettings()) {
const gd::String &type = extraSetting.second.GetType();
const gd::String extraSettingValue =
type == "ExtensionProperty"
? project.GetExtensionProperties().GetValue(

View File

@@ -0,0 +1,43 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ArbitraryObjectsWorker.h"
#include <iostream>
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Project/BehaviorContent.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/String.h"
using namespace std;
namespace gd {
ArbitraryObjectsWorker::~ArbitraryObjectsWorker() {}
void ArbitraryObjectsWorker::VisitObjectContainer(
gd::ObjectsContainer& objects) {
DoVisitObjectContainer(objects);
for (size_t i = 0; i < objects.GetObjectsCount(); i++)
VisitObject(objects.GetObject(i));
}
void ArbitraryObjectsWorker::VisitObject(gd::Object& object) {
DoVisitObject(object);
for (auto behaviorName : object.GetAllBehaviorNames())
VisitBehavior(object.GetBehavior(behaviorName));
}
void ArbitraryObjectsWorker::VisitBehavior(gd::BehaviorContent& behavior) {
DoVisitBehavior(behavior);
}
} // namespace gd

View File

@@ -0,0 +1,63 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef ARBITRARYOBJECTSWORKER_H
#define ARBITRARYOBJECTSWORKER_H
#include <map>
#include <memory>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Object;
class ObjectsContainer;
class BehaviorContent;
} // namespace gd
namespace gd {
/**
* \brief ArbitraryObjectsWorker is an abstract class used to browse objects
* (and behaviors) and do some work on them. Can be used to implement
* refactoring for example.
*
* \ingroup IDE
*/
class GD_CORE_API ArbitraryObjectsWorker {
public:
ArbitraryObjectsWorker(){};
virtual ~ArbitraryObjectsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(gd::ObjectsContainer& objects) { VisitObjectContainer(objects); };
private:
void VisitObjectContainer(gd::ObjectsContainer& objects);
void VisitObject(gd::Object& object);
void VisitBehavior(gd::BehaviorContent& instruction);
/**
* Called to do some work on an object container.
*/
virtual void DoVisitObjectContainer(gd::ObjectsContainer& objects){};
/**
* Called to do some work on an object.
*/
virtual void DoVisitObject(gd::Object& object){};
/**
* Called to do some work on a behavior.
*/
virtual void DoVisitBehavior(gd::BehaviorContent& instruction){};
};
} // namespace gd
#endif // ARBITRARYOBJECTSWORKER_H

View File

@@ -6,8 +6,10 @@
#if defined(GD_IDE_ONLY)
#include "ArbitraryResourceWorker.h"
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
@@ -55,6 +57,21 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
ExposeFile(fontName);
};
void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(bitmapFontName) &&
resources->GetResource(bitmapFontName).GetKind() == "bitmapFont") {
// Nothing to do, the font is a reference to a resource that
// is already exposed.
return;
}
}
ExposeFile(bitmapFontName);
};
void ArbitraryResourceWorker::ExposeResources(
gd::ResourcesManager* resourcesManager) {
if (!resourcesManager) return;
@@ -82,15 +99,8 @@ void LaunchResourceWorkerOnEvents(const gd::Project& project,
gd::EventsList& events,
gd::ArbitraryResourceWorker& worker) {
// Get all extensions used
std::vector<std::shared_ptr<gd::PlatformExtension> > allGameExtensions;
std::vector<gd::String> usedExtensions = project.GetUsedExtensions();
for (std::size_t i = 0; i < usedExtensions.size(); ++i) {
std::shared_ptr<gd::PlatformExtension> extension =
project.GetCurrentPlatform().GetExtension(usedExtensions[i]);
if (extension != std::shared_ptr<gd::PlatformExtension>())
allGameExtensions.push_back(extension);
}
auto allGameExtensions =
project.GetCurrentPlatform().GetAllPlatformExtensions();
for (std::size_t j = 0; j < events.size(); j++) {
vector<gd::InstructionsList*> allActionsVectors =

View File

@@ -70,6 +70,12 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeFont(gd::String &fontName);
/**
* \brief Expose a bitmap font, which is either a reference to a "bitmapFont" resource,
* or a filename if no resource with this name exists.
*/
virtual void ExposeBitmapFont(gd::String &bitmapFontName);
/**
* \brief Expose a shader.
* \warn Currently unsupported.

View File

@@ -38,12 +38,15 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String>& GetAllImages() { return GetAll("image"); };
std::set<gd::String>& GetAllFonts() { return GetAll("font"); };
std::set<gd::String>& GetAllAudios() { return GetAll("audio"); };
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
std::set<gd::String>& GetAll(const gd::String& resourceType) {
return resourceType == "image"
? allImages
: (resourceType == "audio"
? allAudios
: (resourceType == "font") ? allFonts : emptyResources);
: (resourceType == "font")
? allFonts
: (resourceType == "bitmapFont") ? allBitmapFonts : emptyResources);
};
virtual void ExposeFile(gd::String& resource) override{
@@ -58,11 +61,15 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
virtual void ExposeFont(gd::String& fontResourceName) override {
allFonts.insert(fontResourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontResourceName) override {
allBitmapFonts.insert(bitmapFontResourceName);
};
protected:
std::set<gd::String> allImages;
std::set<gd::String> allAudios;
std::set<gd::String> allFonts;
std::set<gd::String> allBitmapFonts;
std::set<gd::String> emptyResources;
};

View File

@@ -4,15 +4,17 @@
* reserved. This project is released under the MIT License.
*/
#include "WholeProjectRefactorer.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
@@ -141,6 +143,13 @@ void WholeProjectRefactorer::ExposeProjectEvents(
}
}
void WholeProjectRefactorer::ExposeProjectObjects(
gd::Project& project, gd::ArbitraryObjectsWorker& worker) {
worker.Launch(project);
for (size_t i = 0; i < project.GetLayoutsCount(); i++)
worker.Launch(project.GetLayout(i));
};
std::set<gd::String>
WholeProjectRefactorer::GetAllObjectTypesUsingEventsBasedBehavior(
const gd::Project& project,

View File

@@ -16,6 +16,7 @@ class EventsFunction;
class ObjectsContainer;
class EventsBasedBehavior;
class ArbitraryEventsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventsWorkerWithContext;
} // namespace gd
@@ -50,10 +51,21 @@ class GD_CORE_API WholeProjectRefactorer {
gd::ArbitraryEventsWorkerWithContext& worker);
/**
* \brief Refactor the project **before** an events function extension is renamed.
* \brief Call the specified worker on all ObjectContainers of the project (global,
* layouts...)
*
* This should be the preferred way to traverse all the objects of a project.
*/
static void ExposeProjectObjects(
gd::Project& project, gd::ArbitraryObjectsWorker& worker);
/**
* \brief Refactor the project **before** an events function extension is
* renamed.
*
* \warning Do the renaming of the specified extension after calling this.
* This is because the extension is expected to have its old name for the refactoring.
* This is because the extension is expected to have its old name for the
* refactoring.
*/
static void RenameEventsFunctionsExtension(
gd::Project& project,
@@ -65,7 +77,8 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Refactor the project **before** an events function is renamed.
*
* \warning Do the renaming of the specified function after calling this.
* This is because the function is expected to have its old name for the refactoring.
* This is because the function is expected to have its old name for the
* refactoring.
*/
static void RenameEventsFunction(
gd::Project& project,
@@ -78,7 +91,8 @@ class GD_CORE_API WholeProjectRefactorer {
* renamed.
*
* \warning Do the renaming of the specified function after calling this.
* This is because the function is expected to have its old name for the refactoring.
* This is because the function is expected to have its old name for the
* refactoring.
*/
static void RenameBehaviorEventsFunction(
gd::Project& project,
@@ -91,8 +105,9 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Refactor the project **before** an events function parameter
* is moved.
*
* \warning Do the move of the specified function parameters after calling this.
* This is because the function is expected to be in its old state for the refactoring.
* \warning Do the move of the specified function parameters after calling
* this. This is because the function is expected to be in its old state for
* the refactoring.
*/
static void MoveEventsFunctionParameter(
gd::Project& project,
@@ -102,11 +117,12 @@ class GD_CORE_API WholeProjectRefactorer {
std::size_t newIndex);
/**
* \brief Refactor the project **before** the parameter of an events function of a
* behavior is moved.
* \brief Refactor the project **before** the parameter of an events function
* of a behavior is moved.
*
* \warning Do the move of the specified function parameters after calling this.
* This is because the function is expected to be in its old state for the refactoring.
* \warning Do the move of the specified function parameters after calling
* this. This is because the function is expected to be in its old state for
* the refactoring.
*/
static void MoveBehaviorEventsFunctionParameter(
gd::Project& project,
@@ -121,7 +137,8 @@ class GD_CORE_API WholeProjectRefactorer {
* renamed.
*
* \warning Do the renaming of the specified property after calling this.
* This is because the property is expected to have its old name for the refactoring.
* This is because the property is expected to have its old name for the
* refactoring.
*/
static void RenameBehaviorProperty(
gd::Project& project,
@@ -134,7 +151,8 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Refactor the project **before** a behavior is renamed.
*
* \warning Do the renaming of the specified behavior after calling this.
* This is because the behavior is expected to have its old name for the refactoring.
* This is because the behavior is expected to have its old name for the
* refactoring.
*/
static void RenameEventsBasedBehavior(
gd::Project& project,

View File

@@ -9,7 +9,6 @@
namespace gd {
#if defined(GD_IDE_ONLY)
void Effect::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("effectType", GetEffectType());
@@ -32,7 +31,6 @@ void Effect::SerializeTo(SerializerElement& element) const {
booleanParametersElement.AddChild(parameter.first)
.SetValue(parameter.second);
}
#endif
void Effect::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name"));

View File

@@ -74,12 +74,10 @@ class GD_CORE_API Effect {
booleanParameters.clear();
}
#if defined(GD_IDE_ONLY)
/**
* \brief Serialize layer.
*/
void SerializeTo(SerializerElement& element) const;
#endif
/**
* \brief Unserialize the layer.

View File

@@ -0,0 +1,140 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EffectsContainer.h"
#include "Effect.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
Effect EffectsContainer::badEffect;
EffectsContainer::EffectsContainer() {}
EffectsContainer::EffectsContainer(const EffectsContainer& other) {
Init(other);
}
EffectsContainer& EffectsContainer::operator=(const EffectsContainer& rhs) {
if (this != &rhs) Init(rhs);
return *this;
}
void EffectsContainer::Init(const EffectsContainer& other) {
effects.clear();
for (auto& it : other.effects) {
effects.push_back(std::make_shared<Effect>(*it));
}
}
bool EffectsContainer::HasEffectNamed(const gd::String& name) const {
return (find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
}) != effects.end());
}
gd::Effect& EffectsContainer::GetEffect(const gd::String& name) {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect != effects.end()) return **effect;
return badEffect;
}
const gd::Effect& EffectsContainer::GetEffect(const gd::String& name) const {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect != effects.end()) return **effect;
return badEffect;
}
gd::Effect& EffectsContainer::GetEffect(std::size_t index) {
return *effects[index];
}
const gd::Effect& EffectsContainer::GetEffect(std::size_t index) const {
return *effects[index];
}
std::size_t EffectsContainer::GetEffectsCount() const { return effects.size(); }
std::size_t EffectsContainer::GetEffectPosition(const gd::String& name) const {
for (std::size_t i = 0; i < effects.size(); ++i) {
if (effects[i]->GetName() == name) return i;
}
return gd::String::npos;
}
gd::Effect& EffectsContainer::InsertNewEffect(const gd::String& name,
std::size_t position) {
auto newEffect = std::make_shared<Effect>();
newEffect->SetName(name);
if (position < effects.size())
effects.insert(effects.begin() + position, newEffect);
else
effects.push_back(newEffect);
return *newEffect;
}
void EffectsContainer::InsertEffect(const gd::Effect& effect,
std::size_t position) {
auto newEffect = std::make_shared<gd::Effect>(effect);
if (position < effects.size())
effects.insert(effects.begin() + position, newEffect);
else
effects.push_back(newEffect);
}
void EffectsContainer::RemoveEffect(const gd::String& name) {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect == effects.end()) return;
effects.erase(effect);
}
void EffectsContainer::SwapEffects(std::size_t firstEffectIndex,
std::size_t secondEffectIndex) {
if (firstEffectIndex >= effects.size() || secondEffectIndex >= effects.size())
return;
auto temp = effects[firstEffectIndex];
effects[firstEffectIndex] = effects[secondEffectIndex];
effects[secondEffectIndex] = temp;
}
void EffectsContainer::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("effect");
for (std::size_t i = 0; i < GetEffectsCount(); ++i) {
SerializerElement& effectElement = element.AddChild("effect");
GetEffect(i).SerializeTo(effectElement);
}
}
void EffectsContainer::UnserializeFrom(const SerializerElement& element) {
effects.clear();
element.ConsiderAsArrayOf("effect");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
const SerializerElement& effectElement = element.GetChild(i);
auto effect = std::make_shared<Effect>();
effect->UnserializeFrom(effectElement);
effects.push_back(effect);
}
}
} // namespace gd

View File

@@ -0,0 +1,114 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EFFECTS_CONTAINER_H
#define GDCORE_EFFECTS_CONTAINER_H
#include <memory>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Effect;
}
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief Contains effects applied to an entity on screen (i.e: a Layer or an
* Object).
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API EffectsContainer {
public:
EffectsContainer();
EffectsContainer(const EffectsContainer& other);
virtual ~EffectsContainer(){};
EffectsContainer& operator=(const EffectsContainer& rhs);
/**
* \brief Return true if the effect called "name" exists.
*/
bool HasEffectNamed(const gd::String& name) const;
/**
* \brief Return a reference to the effect called "name".
*/
Effect& GetEffect(const gd::String& name);
/**
* \brief Return a reference to the effect called "name".
*/
const Effect& GetEffect(const gd::String& name) const;
/**
* Return a reference to the effect at position "index" in the effects list
*/
Effect& GetEffect(std::size_t index);
/**
* Return a reference to the effect at position "index" in the effects list
*/
const Effect& GetEffect(std::size_t index) const;
/**
* Return the position of the effect called "name" in the effects list
*/
std::size_t GetEffectPosition(const gd::String& name) const;
/**
* Return the number of effecst.
*/
std::size_t GetEffectsCount() const;
/**
* Add a new effect at the specified position in the effects list.
*/
gd::Effect& InsertNewEffect(const gd::String& name, std::size_t position);
/**
* \brief Add a copy of the specified effect in the effects list.
*
* \note No pointer or reference must be kept on the effect passed as
* parameter.
*
* \param theEffect The effect that must be copied and inserted
* into the effects list
* \param position Insertion position.
*/
void InsertEffect(const Effect& theEffect, std::size_t position);
/**
* Remove the specified effect.
*/
void RemoveEffect(const gd::String& name);
/**
* Swap the position of two effects.
*/
void SwapEffects(std::size_t firstEffectIndex, std::size_t secondEffectIndex);
/**
* \brief Serialize the effects container.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the effects container.
*/
void UnserializeFrom(const SerializerElement& element);
private:
std::vector<std::shared_ptr<gd::Effect>> effects;
static Effect badEffect;
void Init(const EffectsContainer& other);
};
} // namespace gd
#endif

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/Layer.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -11,9 +12,9 @@
namespace gd {
Camera Layer::badCamera;
Effect Layer::badEffect;
Layer::Layer() : isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
Layer::Layer()
: isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
/**
* Change cameras count, automatically adding/removing them.
@@ -52,11 +53,7 @@ void Layer::SerializeTo(SerializerElement& element) const {
}
SerializerElement& effectsElement = element.AddChild("effects");
effectsElement.ConsiderAsArrayOf("effect");
for (std::size_t i = 0; i < GetEffectsCount(); ++i) {
SerializerElement& effectElement = effectsElement.AddChild("effect");
GetEffect(i).SerializeTo(effectElement);
}
effectsContainer.SerializeTo(effectsElement);
}
#endif
@@ -67,155 +64,53 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name", "", "Name"));
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
SetFollowBaseLayerCamera(element.GetBoolAttribute("followBaseLayerCamera", false));
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
SetFollowBaseLayerCamera(
element.GetBoolAttribute("followBaseLayerCamera", false));
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
element.GetIntAttribute("ambientLightColorG", 200),
element.GetIntAttribute("ambientLightColorB", 200));
// Compatibility with GD <= 3.3
if (element.HasChild("Camera")) {
for (std::size_t i = 0; i < element.GetChildrenCount("Camera"); ++i) {
const SerializerElement& cameraElement = element.GetChild("Camera", i);
SetCameraCount(GetCameraCount() + 1);
Camera& camera = GetCamera(GetCameraCount() - 1);
cameras.clear();
SerializerElement& camerasElement = element.GetChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
for (std::size_t i = 0; i < camerasElement.GetChildrenCount(); ++i) {
const SerializerElement& cameraElement = camerasElement.GetChild(i);
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("DefaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("Width"),
cameraElement.GetDoubleAttribute("Height"));
Camera camera;
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("defaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("width"),
cameraElement.GetDoubleAttribute("height"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("defaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute(
"viewportBottom"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("DefaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("ViewportLeft"),
cameraElement.GetDoubleAttribute("ViewportTop"),
cameraElement.GetDoubleAttribute("ViewportRight"),
cameraElement.GetDoubleAttribute(
"ViewportBottom")); // (sf::Rect used Right and Bottom instead of
// Width and Height before)
}
}
// End of compatibility code
else {
SerializerElement& camerasElement = element.GetChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
for (std::size_t i = 0; i < camerasElement.GetChildrenCount(); ++i) {
const SerializerElement& cameraElement = camerasElement.GetChild(i);
SetCameraCount(GetCameraCount() + 1);
Camera& camera = GetCamera(GetCameraCount() - 1);
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("defaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("width"),
cameraElement.GetDoubleAttribute("height"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("defaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute(
"viewportBottom")); // (sf::Rect used Right and Bottom instead of
// Width and Height before)
}
cameras.push_back(camera);
}
effects.clear();
SerializerElement& effectsElement = element.GetChild("effects");
effectsElement.ConsiderAsArrayOf("effect");
for (std::size_t i = 0; i < effectsElement.GetChildrenCount(); ++i) {
const SerializerElement& effectElement = effectsElement.GetChild(i);
auto effect = std::make_shared<Effect>();
effect->UnserializeFrom(effectElement);
effects.push_back(effect);
if (camerasElement.GetChildrenCount() > 50) {
// Highly unlikely that we want as many cameras, as they were not even exposed in
// the editor nor used in the game engine. Must be because of a bug in the editor that
// duplicated cameras when cancelling changes on a layer.
// Reset to one camera.
SetCameraCount(1);
}
const SerializerElement& effectsElement = element.GetChild("effects");
effectsContainer.UnserializeFrom(effectsElement);
}
gd::Effect& Layer::GetEffect(const gd::String& name) {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect != effects.end()) return **effect;
return badEffect;
}
const gd::Effect& Layer::GetEffect(const gd::String& name) const {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect != effects.end()) return **effect;
return badEffect;
}
gd::Effect& Layer::GetEffect(std::size_t index) { return *effects[index]; }
const gd::Effect& Layer::GetEffect(std::size_t index) const {
return *effects[index];
}
std::size_t Layer::GetEffectsCount() const { return effects.size(); }
bool Layer::HasEffectNamed(const gd::String& name) const {
return (find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
}) != effects.end());
}
std::size_t Layer::GetEffectPosition(const gd::String& name) const {
for (std::size_t i = 0; i < effects.size(); ++i) {
if (effects[i]->GetName() == name) return i;
}
return gd::String::npos;
gd::EffectsContainer& Layer::GetEffects() {
return effectsContainer;
}
gd::Effect& Layer::InsertNewEffect(const gd::String& name,
std::size_t position) {
auto newEffect = std::make_shared<Effect>();
newEffect->SetName(name);
if (position < effects.size())
effects.insert(effects.begin() + position, newEffect);
else
effects.push_back(newEffect);
return *newEffect;
}
void Layer::InsertEffect(const gd::Effect& effect, std::size_t position) {
auto newEffect = std::make_shared<gd::Effect>(effect);
if (position < effects.size())
effects.insert(effects.begin() + position, newEffect);
else
effects.push_back(newEffect);
}
void Layer::RemoveEffect(const gd::String& name) {
auto effect = find_if(effects.begin(),
effects.end(),
[&name](const std::shared_ptr<gd::Effect>& effect) {
return effect->GetName() == name;
});
if (effect == effects.end()) return;
effects.erase(effect);
}
void Layer::SwapEffects(std::size_t firstEffectIndex,
std::size_t secondEffectIndex) {
if (firstEffectIndex >= effects.size() || secondEffectIndex >= effects.size())
return;
auto temp = effects[firstEffectIndex];
effects[firstEffectIndex] = effects[secondEffectIndex];
effects[secondEffectIndex] = temp;
const gd::EffectsContainer& Layer::GetEffects() const {
return effectsContainer;
}
Camera::Camera()

View File

@@ -7,7 +7,10 @@
#define GDCORE_LAYER_H
#include <memory>
#include <vector>
#include "EffectsContainer.h"
#include "GDCore/String.h"
namespace gd {
class Effect;
}
@@ -17,6 +20,9 @@ class Camera;
namespace gd {
class SerializerElement;
}
namespace gd {
class EffectsContainer;
}
namespace gd {
@@ -54,7 +60,9 @@ class GD_CORE_API Layer {
/**
* \brief Set if the layer is a lightining layer or not.
*/
void SetLightingLayer(bool isLightingLayer_) { isLightingLayer = isLightingLayer_; }
void SetLightingLayer(bool isLightingLayer_) {
isLightingLayer = isLightingLayer_;
}
/**
* \brief Return true if the layer is a lighting layer.
@@ -64,7 +72,9 @@ class GD_CORE_API Layer {
/**
* \brief Set if the layer automatically follows the base layer or not.
*/
void SetFollowBaseLayerCamera(bool followBaseLayerCamera_) { followBaseLayerCamera = followBaseLayerCamera_; }
void SetFollowBaseLayerCamera(bool followBaseLayerCamera_) {
followBaseLayerCamera = followBaseLayerCamera_;
}
/**
* \brief Return true if the layer follows the base layer.
@@ -144,66 +154,14 @@ class GD_CORE_API Layer {
*/
///@{
/**
* \brief Return true if the effect called "name" exists.
* \brief Return the effects container.
*/
bool HasEffectNamed(const gd::String& name) const;
EffectsContainer& GetEffects();
/**
* \brief Return a reference to the effect called "name".
* \brief Return a const reference to the effects container.
*/
Effect& GetEffect(const gd::String& name);
/**
* \brief Return a reference to the effect called "name".
*/
const Effect& GetEffect(const gd::String& name) const;
/**
* Return a reference to the effect at position "index" in the effects list
*/
Effect& GetEffect(std::size_t index);
/**
* Return a reference to the effect at position "index" in the effects list
*/
const Effect& GetEffect(std::size_t index) const;
/**
* Return the position of the effect called "name" in the effects list
*/
std::size_t GetEffectPosition(const gd::String& name) const;
/**
* Return the number of effecst.
*/
std::size_t GetEffectsCount() const;
/**
* Add a new effect at the specified position in the effects list.
*/
gd::Effect& InsertNewEffect(const gd::String& name, std::size_t position);
/**
* \brief Add a copy of the specified effect in the effects list.
*
* \note No pointer or reference must be kept on the layer passed as
* parameter.
*
* \param theEffect The effect that must be copied and inserted
* into the effects list
* \param position Insertion position.
*/
void InsertEffect(const Effect& theEffect, std::size_t position);
/**
* Remove the specified effect.
*/
void RemoveEffect(const gd::String& name);
/**
* Swap the position of two effects.
*/
void SwapEffects(std::size_t firstEffectIndex, std::size_t secondEffectIndex);
const EffectsContainer& GetEffects() const;
///@}
#if defined(GD_IDE_ONLY)
@@ -219,19 +177,19 @@ class GD_CORE_API Layer {
void UnserializeFrom(const SerializerElement& element);
private:
gd::String name; ///< The name of the layer
bool isVisible; ///< True if the layer is visible
bool isLightingLayer; ///< True if the layer is used to display lights and renders an ambient light.
bool followBaseLayerCamera; ///< True if the layer automatically follows the base layer
unsigned int ambientLightColorR; ///< Ambient light color Red component
unsigned int ambientLightColorG; ///< Ambient light color Green component
unsigned int ambientLightColorB; ///< Ambient light color Blue component
gd::String name; ///< The name of the layer
bool isVisible; ///< True if the layer is visible
bool isLightingLayer; ///< True if the layer is used to display lights and
///< renders an ambient light.
bool followBaseLayerCamera; ///< True if the layer automatically follows the
///< base layer
unsigned int ambientLightColorR; ///< Ambient light color Red component
unsigned int ambientLightColorG; ///< Ambient light color Green component
unsigned int ambientLightColorB; ///< Ambient light color Blue component
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
std::vector<std::shared_ptr<gd::Effect>>
effects; ///< The effects applied to the layer.
gd::EffectsContainer effectsContainer; ///< The effects applied to the layer.
static gd::Camera badCamera;
static gd::Effect badEffect;
};
/**

View File

@@ -5,10 +5,14 @@
*/
#include "Layout.h"
#include <algorithm>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/SceneNameMangler.h"
#include "GDCore/Project/Behavior.h"
@@ -219,16 +223,21 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
++i) {
const gd::String& name = allBehaviorsNames[i];
if (behaviorsSharedData.find(name) == behaviorsSharedData.end()) {
gd::BehaviorsSharedData* behaviorSharedData =
project.GetBehaviorSharedDatas(allBehaviorsTypes[i]);
if (behaviorSharedData) {
auto behaviorContent =
gd::make_unique<gd::BehaviorContent>(name, allBehaviorsTypes[i]);
behaviorSharedData->InitializeContent(behaviorContent->GetContent());
behaviorsSharedData[name] = std::move(behaviorContent);
}
}
if (behaviorsSharedData.find(name) != behaviorsSharedData.end()) continue;
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
allBehaviorsTypes[i]);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) continue;
gd::BehaviorsSharedData* behaviorSharedData =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorSharedData) continue;
auto behaviorContent =
gd::make_unique<gd::BehaviorContent>(name, allBehaviorsTypes[i]);
behaviorSharedData->InitializeContent(behaviorContent->GetContent());
behaviorsSharedData[name] = std::move(behaviorContent);
}
// Remove useless shared data:

View File

@@ -5,15 +5,63 @@
*/
#include "LoadingScreen.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
LoadingScreen::LoadingScreen()
: showGDevelopSplash(true),
gdevelopLogoStyle("light"),
backgroundImageResourceName(""),
backgroundColor(0),
backgroundFadeInDuration(0.2),
minDuration(1.5),
logoAndProgressFadeInDuration(0.2),
logoAndProgressLogoFadeInDelay(0.2),
showProgressBar(true),
progressBarMinWidth(40),
progressBarMaxWidth(200),
progressBarWidthPercent(30),
progressBarHeight(20),
progressBarColor(0xFFFFFF){};
void LoadingScreen::SerializeTo(SerializerElement& element) const {
element.SetAttribute("showGDevelopSplash", showGDevelopSplash);
element.SetAttribute("gdevelopLogoStyle",
gdevelopLogoStyle);
element.SetAttribute("backgroundImageResourceName",
backgroundImageResourceName);
element.SetAttribute("backgroundColor", backgroundColor);
element.SetAttribute("backgroundFadeInDuration", backgroundFadeInDuration);
element.SetAttribute("minDuration", minDuration);
element.SetAttribute("logoAndProgressFadeInDuration", logoAndProgressFadeInDuration);
element.SetAttribute("logoAndProgressLogoFadeInDelay", logoAndProgressLogoFadeInDelay);
element.SetAttribute("showProgressBar", showProgressBar);
element.SetAttribute("progressBarMinWidth", progressBarMinWidth);
element.SetAttribute("progressBarMaxWidth", progressBarMaxWidth);
element.SetAttribute("progressBarWidthPercent", progressBarWidthPercent);
element.SetAttribute("progressBarHeight", progressBarHeight);
element.SetAttribute("progressBarColor", progressBarColor);
}
void LoadingScreen::UnserializeFrom(const SerializerElement& element) {
showGDevelopSplash = element.GetBoolAttribute("showGDevelopSplash", true);
gdevelopLogoStyle =
element.GetStringAttribute("gdevelopLogoStyle", "light");
backgroundImageResourceName =
element.GetStringAttribute("backgroundImageResourceName");
backgroundColor = element.GetIntAttribute("backgroundColor", 0);
backgroundFadeInDuration =
element.GetDoubleAttribute("backgroundFadeInDuration", 0.2);
minDuration = element.GetDoubleAttribute("minDuration", 1.5);
logoAndProgressFadeInDuration = element.GetDoubleAttribute("logoAndProgressFadeInDuration", 0.2);
logoAndProgressLogoFadeInDelay = element.GetDoubleAttribute("logoAndProgressLogoFadeInDelay", 0.2);
showProgressBar = element.GetBoolAttribute("showProgressBar", true);
progressBarMinWidth = element.GetDoubleAttribute("progressBarMinWidth", 40);
progressBarMaxWidth = element.GetDoubleAttribute("progressBarMaxWidth", 200);
progressBarWidthPercent = element.GetDoubleAttribute("progressBarWidthPercent", 30);
progressBarHeight = element.GetDoubleAttribute("progressBarHeight", 20);
progressBarColor = element.GetIntAttribute("progressBarColor", 0xFFFFFF);
}
} // namespace gd

View File

@@ -22,36 +22,151 @@ namespace gd {
*/
class GD_CORE_API LoadingScreen {
public:
LoadingScreen(){};
LoadingScreen();
virtual ~LoadingScreen(){};
/**
* \brief Set if the GDevelop splash should be shown while loading assets.
*/
void ShowGDevelopSplash(bool show) { showGDevelopSplash = show; };
/**
* \brief Return true if the GDevelop splash should be shown while loading
* \brief Return true if the GDevelop logo should be shown while loading
* assets.
*/
bool IsGDevelopSplashShown() const { return showGDevelopSplash; };
/**
* \brief Set if the GDevelop logo should be shown while loading assets.
*/
LoadingScreen& ShowGDevelopSplash(bool show) {
showGDevelopSplash = show;
return *this;
};
const gd::String& GetGDevelopLogoStyle() const { return gdevelopLogoStyle; };
LoadingScreen& SetGDevelopLogoStyle(const gd::String& value) {
gdevelopLogoStyle = value;
return *this;
}
const gd::String& GetBackgroundImageResourceName() const {
return backgroundImageResourceName;
};
LoadingScreen& SetBackgroundImageResourceName(const gd::String& value) {
backgroundImageResourceName = value;
return *this;
}
int GetBackgroundColor() const { return backgroundColor; };
LoadingScreen& SetBackgroundColor(int value) {
backgroundColor = value;
return *this;
}
double GetBackgroundFadeInDuration() const {
return backgroundFadeInDuration;
};
LoadingScreen& SetBackgroundFadeInDuration(double value) {
backgroundFadeInDuration = value;
return *this;
}
double GetMinDuration() const { return minDuration; };
LoadingScreen& SetMinDuration(double value) {
minDuration = value;
return *this;
}
double GetLogoAndProgressFadeInDuration() const {
return logoAndProgressFadeInDuration;
}
LoadingScreen& SetLogoAndProgressFadeInDuration(double value) {
logoAndProgressFadeInDuration = value;
return *this;
}
double GetLogoAndProgressLogoFadeInDelay() const {
return logoAndProgressLogoFadeInDelay;
}
LoadingScreen& SetLogoAndProgressLogoFadeInDelay(double value) {
logoAndProgressLogoFadeInDelay = value;
return *this;
}
bool GetShowProgressBar() const { return showProgressBar; }
LoadingScreen& SetShowProgressBar(bool value) {
showProgressBar = value;
return *this;
}
double GetProgressBarMinWidth() const { return progressBarMinWidth; }
LoadingScreen& SetProgressBarMinWidth(double value) {
progressBarMinWidth = value;
return *this;
}
double GetProgressBarMaxWidth() const { return progressBarMaxWidth; }
LoadingScreen& SetProgressBarMaxWidth(double value) {
progressBarMaxWidth = value;
return *this;
}
double GetProgressBarWidthPercent() const { return progressBarWidthPercent; }
LoadingScreen& SetProgressBarWidthPercent(double value) {
progressBarWidthPercent = value;
return *this;
}
double GetProgressBarHeight() const { return progressBarHeight; }
LoadingScreen& SetProgressBarHeight(double value) {
progressBarHeight = value;
return *this;
}
int GetProgressBarColor() const { return progressBarColor; }
LoadingScreen& SetProgressBarColor(int value) {
progressBarColor = value;
return *this;
}
/** \name Saving and loading
*/
///@{
/**
* \brief Serialize objects groups container.
* \brief Serialize the loading screen setup.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the objects groups container.
* \brief Unserialize the loading screen setup.
*/
void UnserializeFrom(const SerializerElement& element);
///@}
private:
bool showGDevelopSplash;
gd::String gdevelopLogoStyle;
gd::String backgroundImageResourceName;
int backgroundColor;
double backgroundFadeInDuration; // In seconds.
double minDuration; // In seconds.
double logoAndProgressFadeInDuration; // In seconds.
double logoAndProgressLogoFadeInDelay; // In seconds.
bool showProgressBar;
double progressBarMinWidth; // In pixels.
double progressBarMaxWidth; // In pixels.
double progressBarWidthPercent;
double progressBarHeight; // In pixels.
int progressBarColor;
};
} // namespace gd

View File

@@ -4,6 +4,9 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/Object.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Layout.h"
@@ -24,6 +27,7 @@ void Object::Init(const gd::Object& object) {
type = object.type;
objectVariables = object.objectVariables;
tags = object.tags;
effectsContainer = object.effectsContainer;
behaviors.clear();
for (auto& it : object.behaviors) {
@@ -85,16 +89,17 @@ std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
gd::BehaviorContent* Object::AddNewBehavior(gd::Project& project,
const gd::String& type,
const gd::String& name) {
gd::Behavior* behavior = project.GetCurrentPlatform().GetBehavior(type);
if (behavior) {
auto behaviorContent = gd::make_unique<gd::BehaviorContent>(name, type);
behavior->InitializeContent(behaviorContent->GetContent());
behaviors[name] = std::move(behaviorContent);
return behaviors[name].get();
} else {
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
type);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
return nullptr;
}
auto behaviorContent = gd::make_unique<gd::BehaviorContent>(name, type);
behaviorMetadata.Get().InitializeContent(behaviorContent->GetContent());
behaviors[name] = std::move(behaviorContent);
return behaviors[name].get();
}
std::map<gd::String, gd::PropertyDescriptor>
@@ -116,6 +121,11 @@ void Object::UnserializeFrom(gd::Project& project,
element.GetChild("variables", 0, "Variables"));
behaviors.clear();
if (element.HasChild("effects")) {
const SerializerElement& effectsElement = element.GetChild("effects");
effectsContainer.UnserializeFrom(effectsElement);
}
// Compatibility with GD <= 3.3
if (element.HasChild("Automatism")) {
for (std::size_t i = 0; i < element.GetChildrenCount("Automatism"); ++i) {
@@ -180,6 +190,7 @@ void Object::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", GetType());
element.SetAttribute("tags", GetTags());
objectVariables.SerializeTo(element.AddChild("variables"));
effectsContainer.SerializeTo(element.AddChild("effects"));
SerializerElement& behaviorsElement = element.AddChild("behaviors");
behaviorsElement.ConsiderAsArrayOf("behavior");

View File

@@ -9,7 +9,9 @@
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Project/BehaviorContent.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
@@ -20,6 +22,7 @@ class Layout;
class ArbitraryResourceWorker;
class InitialInstance;
class SerializerElement;
class EffectsContainer;
} // namespace gd
namespace gd {
@@ -146,8 +149,7 @@ class GD_CORE_API Object {
*
* \return false if the new value cannot be set
*/
virtual bool UpdateProperty(const gd::String& name,
const gd::String& value) {
virtual bool UpdateProperty(const gd::String& name, const gd::String& value) {
return false;
};
///@}
@@ -198,12 +200,12 @@ class GD_CORE_API Object {
std::vector<gd::String> GetAllBehaviorNames() const;
/**
* \brief Return a reference to the behavior called \a name.
* \brief Return a reference to the content of the behavior called \a name.
*/
BehaviorContent& GetBehavior(const gd::String& name);
/**
* \brief Return a reference to the behavior called \a name.
* \brief Return a reference to the content of the behavior called \a name.
*/
const BehaviorContent& GetBehavior(const gd::String& name) const;
@@ -270,6 +272,24 @@ class GD_CORE_API Object {
* object variables
*/
gd::VariablesContainer& GetVariables() { return objectVariables; }
///@}
/**
* \name Effects management
* Member functions related to effects management.
*/
///@{
/**
* \brief Provide access to the gd::EffectsContainer member containing the
* effects.
*/
const gd::EffectsContainer& GetEffects() const { return effectsContainer; }
/**
* \brief Provide access to the gd::EffectsContainer member containing the
* effects.
*/
gd::EffectsContainer& GetEffects() { return effectsContainer; }
///@}
/** \name Serialization
@@ -301,7 +321,9 @@ class GD_CORE_API Object {
///< object.
gd::VariablesContainer
objectVariables; ///< List of the variables of the object
gd::String tags; ///< Comma-separated list of tags
gd::String tags; ///< Comma-separated list of tags
gd::EffectsContainer
effectsContainer; ///< The effects container for the object.
/**
* \brief Derived objects can redefine this method to load custom attributes.

View File

@@ -20,6 +20,7 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
@@ -79,28 +80,6 @@ Project::Project()
#endif
{
imageManager->SetResourcesManager(&resourcesManager);
#if defined(GD_IDE_ONLY)
// Game use builtin extensions by default
extensionsUsed.push_back("BuiltinObject");
extensionsUsed.push_back("BuiltinAudio");
extensionsUsed.push_back("BuiltinVariables");
extensionsUsed.push_back("BuiltinTime");
extensionsUsed.push_back("BuiltinMouse");
extensionsUsed.push_back("BuiltinKeyboard");
extensionsUsed.push_back("BuiltinJoystick");
extensionsUsed.push_back("BuiltinCamera");
extensionsUsed.push_back("BuiltinWindow");
extensionsUsed.push_back("BuiltinFile");
extensionsUsed.push_back("BuiltinNetwork");
extensionsUsed.push_back("BuiltinScene");
extensionsUsed.push_back("BuiltinAdvanced");
extensionsUsed.push_back("Sprite");
extensionsUsed.push_back("BuiltinCommonInstructions");
extensionsUsed.push_back("BuiltinCommonConversions");
extensionsUsed.push_back("BuiltinStringInstructions");
extensionsUsed.push_back("BuiltinMathematicalTools");
extensionsUsed.push_back("BuiltinExternalLayouts");
#endif
#if !defined(GD_IDE_ONLY)
platforms.push_back(&CppPlatform::Get());
@@ -130,33 +109,6 @@ std::unique_ptr<gd::Object> Project::CreateObject(
return nullptr;
}
gd::Behavior* Project::GetBehavior(const gd::String& type,
const gd::String& platformName) {
for (std::size_t i = 0; i < platforms.size(); ++i) {
if (!platformName.empty() && platforms[i]->GetName() != platformName)
continue;
gd::Behavior* behavior = platforms[i]->GetBehavior(type);
if (behavior) return behavior;
}
return nullptr;
}
gd::BehaviorsSharedData* Project::GetBehaviorSharedDatas(
const gd::String& type, const gd::String& platformName) {
for (std::size_t i = 0; i < platforms.size(); ++i) {
if (!platformName.empty() && platforms[i]->GetName() != platformName)
continue;
gd::BehaviorsSharedData* behaviorSharedData =
platforms[i]->GetBehaviorSharedDatas(type);
if (behaviorSharedData) return behaviorSharedData;
}
return nullptr;
}
#if defined(GD_IDE_ONLY)
std::shared_ptr<gd::BaseEvent> Project::CreateEvent(
const gd::String& type, const gd::String& platformName) {
@@ -536,8 +488,6 @@ void Project::ClearEventsFunctionsExtensions() {
void Project::UnserializeFrom(const SerializerElement& element) {
// Checking version
#if defined(GD_IDE_ONLY)
gd::String updateText;
const SerializerElement& gdVersionElement =
element.GetChild("gdVersion", 0, "GDVersion");
gdMajorVersion =
@@ -549,10 +499,10 @@ void Project::UnserializeFrom(const SerializerElement& element) {
if (gdMajorVersion > gd::VersionWrapper::Major())
gd::LogWarning(
_("The version of GDevelop used to create this game seems to be a new "
"version.\nGDevelop may fail to open the game, or data may be "
"missing.\nYou should check if a new version of GDevelop is "
"available."));
"The version of GDevelop used to create this game seems to be a new "
"version.\nGDevelop may fail to open the game, or data may be "
"missing.\nYou should check if a new version of GDevelop is "
"available.");
else {
if ((gdMajorVersion == gd::VersionWrapper::Major() &&
gdMinorVersion > gd::VersionWrapper::Minor()) ||
@@ -564,22 +514,12 @@ void Project::UnserializeFrom(const SerializerElement& element) {
gdBuildVersion == gd::VersionWrapper::Build() &&
revision > gd::VersionWrapper::Revision())) {
gd::LogWarning(
_("The version of GDevelop used to create this game seems to be "
"greater.\nGDevelop may fail to open the game, or data may be "
"missing.\nYou should check if a new version of GDevelop is "
"available."));
"The version of GDevelop used to create this game seems to be "
"greater.\nGDevelop may fail to open the game, or data may be "
"missing.\nYou should check if a new version of GDevelop is "
"available.");
}
}
// Compatibility code
if (gdMajorVersion <= 1) {
gd::LogError(_(
"The game was saved with version of GDevelop which is too old. Please "
"open and save the game with one of the first version of GDevelop 2. "
"You will then be able to open your game with this GDevelop version."));
return;
}
// End of Compatibility code
#endif
const SerializerElement& propElement =
@@ -651,18 +591,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
#endif
const SerializerElement& extensionsElement =
propElement.GetChild("extensions", 0, "Extensions");
extensionsElement.ConsiderAsArrayOf("extension", "Extension");
for (std::size_t i = 0; i < extensionsElement.GetChildrenCount(); ++i) {
gd::String extensionName =
extensionsElement.GetChild(i).GetStringAttribute("name");
if (find(GetUsedExtensions().begin(),
GetUsedExtensions().end(),
extensionName) == GetUsedExtensions().end())
GetUsedExtensions().push_back(extensionName);
}
#if defined(GD_IDE_ONLY)
currentPlatform = NULL;
gd::String currentPlatformName =
@@ -715,83 +643,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
currentPlatform = platforms.back();
#endif
// Compatibility code
#if defined(GD_IDE_ONLY)
if (VersionWrapper::IsOlder(gdMajorVersion, 0, 0, 0, 3, 0, 0, 0)) {
updateText +=
_("Sprite scaling has changed since GD 2: The resizing is made so that "
"the origin point of the object won't move whatever the scale of the "
"object.\n");
updateText +=
_("You may have to slightly change the position of some objects if you "
"have changed their size.\n\n");
updateText += _("Thank you for your understanding.\n");
}
#endif
// End of Compatibility code
// Compatibility code
#if defined(GD_IDE_ONLY)
if (VersionWrapper::IsOlderOrEqual(gdMajorVersion,
gdMinorVersion,
revision,
gdBuildVersion,
2,
2,
1,
10822)) {
if (std::find(GetUsedExtensions().begin(),
GetUsedExtensions().end(),
"BuiltinExternalLayouts") == GetUsedExtensions().end())
GetUsedExtensions().push_back("BuiltinExternalLayouts");
}
#endif
// Compatibility code
#if defined(GD_IDE_ONLY)
if (VersionWrapper::IsOlderOrEqual(gdMajorVersion,
gdMinorVersion,
revision,
gdBuildVersion,
3,
3,
3,
0)) {
if (std::find(GetUsedExtensions().begin(),
GetUsedExtensions().end(),
"AStarBehavior") != GetUsedExtensions().end()) {
GetUsedExtensions().erase(std::remove(GetUsedExtensions().begin(),
GetUsedExtensions().end(),
"AStarBehavior"),
GetUsedExtensions().end());
GetUsedExtensions().push_back("PathfindingBehavior");
updateText +=
_("The project is using the pathfinding behavior. This behavior has "
"been replaced by a new one:\n");
updateText +=
_("You must add the new 'Pathfinding' behavior to the objects that "
"need to be moved, and add the 'Pathfinding Obstacle' to the "
"objects that must act as obstacles.");
}
}
#endif
// Compatibility code
#if defined(GD_IDE_ONLY)
if (VersionWrapper::IsOlderOrEqual(gdMajorVersion,
gdMinorVersion,
revision,
gdBuildVersion,
4,
0,
85,
0)) {
for (unsigned int i = 0; i < extensionsUsed.size(); ++i)
extensionsUsed[i] =
extensionsUsed[i].FindAndReplace("Automatism", "Behavior");
}
#endif
#if defined(GD_IDE_ONLY)
GetObjectGroups().UnserializeFrom(
element.GetChild("objectsGroups", 0, "ObjectGroups"));
@@ -917,12 +768,6 @@ void Project::SerializeTo(SerializerElement& element) const {
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
SerializerElement& extensionsElement = propElement.AddChild("extensions");
extensionsElement.ConsiderAsArrayOf("extension");
for (std::size_t i = 0; i < GetUsedExtensions().size(); ++i)
extensionsElement.AddChild("extension")
.SetAttribute("name", GetUsedExtensions()[i]);
SerializerElement& platformsElement = propElement.AddChild("platforms");
platformsElement.ConsiderAsArrayOf("platform");
for (std::size_t i = 0; i < platforms.size(); ++i) {
@@ -1121,7 +966,6 @@ void Project::Init(const gd::Project& game) {
currentPlatform = game.currentPlatform;
#endif
extensionsUsed = game.extensionsUsed;
platforms = game.platforms;
resourcesManager = game.resourcesManager;

View File

@@ -313,20 +313,6 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
void ResetProjectUuid();
/**
* Return a reference to the vector containing the names of extensions used by
* the project.
*/
const std::vector<gd::String>& GetUsedExtensions() const {
return extensionsUsed;
};
/**
* Return a reference to the vector containing the names of extensions used by
* the project.
*/
std::vector<gd::String>& GetUsedExtensions() { return extensionsUsed; };
#if defined(GD_IDE_ONLY)
/**
* \brief Get the properties set by extensions.
@@ -404,37 +390,6 @@ class GD_CORE_API Project : public ObjectsContainer {
const gd::String& name,
const gd::String& platformName = "");
/**
* Get the behavior of the given type.
*
* \note A project can use more than one platform. In this case, the first
* platform supporting the behavior is used, unless \a platformName argument
* is not empty.
* It is assumed that each platform provides an equivalent
* behavior.
*
* \param type The type of the behavior
* \param platformName The name of the platform to be used. If empty, the
* first platform supporting the object is used.
*/
gd::Behavior* GetBehavior(const gd::String& type,
const gd::String& platformName = "");
/**
* Get the behavior shared data of the given type.
*
* \note A project can use more than one platform. In this case, the first
* platform supporting the behavior shared data is used, unless \a
* platformName argument is not empty.
* It is assumed that each platform provides equivalent behavior shared data.
*
* \param type The type of behavior
* \param platformName The name of the platform to be used. If empty, the
* first platform supporting the object is used.
*/
gd::BehaviorsSharedData* GetBehaviorSharedDatas(
const gd::String& type, const gd::String& platformName = "");
#if defined(GD_IDE_ONLY)
/**
* Create an event of the given type.
@@ -1002,7 +957,6 @@ class GD_CORE_API Project : public ObjectsContainer {
std::shared_ptr<gd::ImageManager>
imageManager; ///< Image manager is accessed thanks to a (smart) ptr as
///< it can be shared with GD C++ Platform projects.
std::vector<gd::String> extensionsUsed; ///< List of extensions used
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;

View File

@@ -80,6 +80,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<VideoResource>();
else if (kind == "json")
return std::make_shared<JsonResource>();
else if (kind == "bitmapFont")
return std::make_shared<BitmapFontResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -654,6 +656,26 @@ bool JsonResource::UpdateProperty(const gd::String& name,
#endif
void BitmapFontResource::SetFile(const gd::String& newFile) {
file = newFile;
// Convert all backslash to slashs.
while (file.find('\\') != gd::String::npos)
file.replace(file.find('\\'), 1, "/");
}
void BitmapFontResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
#if defined(GD_IDE_ONLY)
void BitmapFontResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
#endif
#if defined(GD_IDE_ONLY)
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }

View File

@@ -374,6 +374,34 @@ class GD_CORE_API JsonResource : public Resource {
gd::String file;
};
/**
* \brief Describe a bitmap font file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API BitmapFontResource : public Resource {
public:
BitmapFontResource() : Resource() { SetKind("bitmapFont"); };
virtual ~BitmapFontResource(){};
virtual BitmapFontResource* Clone() const override {
return new BitmapFontResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
#if defined(GD_IDE_ONLY)
virtual bool UseFile() override { return true; }
void SerializeTo(SerializerElement& element) const override;
#endif
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*

View File

@@ -7,6 +7,7 @@
#include "GDCore/Project/Variable.h"
#include <sstream>
#include <cmath>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -91,7 +92,9 @@ double Variable::GetValue() const {
if (type == Type::Number) {
return value;
} else if (type == Type::String) {
return str.To<double>();
double retVal = str.To<double>();
if(std::isnan(retVal)) retVal = 0.0;
return retVal;
} else if (type == Type::Boolean) {
return boolVal ? 1.0 : 0.0;
}

View File

@@ -7,6 +7,7 @@
#include "DummyPlatform.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h"
@@ -36,31 +37,38 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Identifier") {
SECTION("Object or expression completions when type is string") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("string", "My"),
gd::ExpressionCompletionDescription::ForExpression("string", "My")};
gd::ExpressionCompletionDescription::ForObject("string", "My", 0, 2),
gd::ExpressionCompletionDescription::ForExpression(
"string", "My", 0, 2)};
REQUIRE(getCompletionsFor("string", "My", 0) == expectedCompletions);
REQUIRE(getCompletionsFor("string", "My", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("string", "My", 2) == expectedEmptyCompletions);
}
SECTION("Object or expression completions when type is number") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("number", "My"),
gd::ExpressionCompletionDescription::ForExpression("number", "My")};
gd::ExpressionCompletionDescription::ForObject("number", "My", 0, 2),
gd::ExpressionCompletionDescription::ForExpression(
"number", "My", 0, 2)};
REQUIRE(getCompletionsFor("number", "My", 0) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "My", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "My", 2) == expectedEmptyCompletions);
}
SECTION("Object or expression completions when type is number|string") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("number|string", "My"),
gd::ExpressionCompletionDescription::ForExpression("number|string", "My")};
REQUIRE(getCompletionsFor("number|string", "My", 0) == expectedCompletions);
REQUIRE(getCompletionsFor("number|string", "My", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("number|string", "My", 2) == expectedEmptyCompletions);
gd::ExpressionCompletionDescription::ForObject(
"number|string", "My", 0, 2),
gd::ExpressionCompletionDescription::ForExpression(
"number|string", "My", 0, 2)};
REQUIRE(getCompletionsFor("number|string", "My", 0) ==
expectedCompletions);
REQUIRE(getCompletionsFor("number|string", "My", 1) ==
expectedCompletions);
REQUIRE(getCompletionsFor("number|string", "My", 2) ==
expectedEmptyCompletions);
}
SECTION("Object when type is an object") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("object", "My")};
gd::ExpressionCompletionDescription::ForObject("object", "My", 0, 2)};
REQUIRE(getCompletionsFor("object", "My", 0) == expectedCompletions);
REQUIRE(getCompletionsFor("object", "My", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("object", "My", 2) == expectedEmptyCompletions);
@@ -70,7 +78,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
// Also test alternate types also considered as objects (but that can
// result in different code generation):
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("objectPtr", "My")};
gd::ExpressionCompletionDescription::ForObject(
"objectPtr", "My", 0, 2)};
REQUIRE(getCompletionsFor("objectPtr", "My", 0) == expectedCompletions);
REQUIRE(getCompletionsFor("objectPtr", "My", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("objectPtr", "My", 2) ==
@@ -78,30 +87,42 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
}
}
SECTION("Operator (number)") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("number", ""),
gd::ExpressionCompletionDescription::ForExpression("number", "")};
REQUIRE(getCompletionsFor("number", "1 + ", 1) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "1 + ", 2) == expectedCompletions);
REQUIRE(getCompletionsFor("number", "1 + ", 3) == expectedCompletions);
std::vector<gd::ExpressionCompletionDescription> expectedCompletions1{
gd::ExpressionCompletionDescription::ForObject("number", "", 1, 1),
gd::ExpressionCompletionDescription::ForExpression("number", "", 1, 1)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions2{
gd::ExpressionCompletionDescription::ForObject("number", "", 2, 2),
gd::ExpressionCompletionDescription::ForExpression("number", "", 2, 2)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions3{
gd::ExpressionCompletionDescription::ForObject("number", "", 3, 3),
gd::ExpressionCompletionDescription::ForExpression("number", "", 3, 3)};
REQUIRE(getCompletionsFor("number", "1 + ", 1) == expectedCompletions1);
REQUIRE(getCompletionsFor("number", "1 + ", 2) == expectedCompletions2);
REQUIRE(getCompletionsFor("number", "1 + ", 3) == expectedCompletions3);
}
SECTION("Operator (string)") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("string", ""),
gd::ExpressionCompletionDescription::ForExpression("string", "")};
REQUIRE(getCompletionsFor("string", "\"a\" + ", 3) == expectedCompletions);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 4) == expectedCompletions);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 5) == expectedCompletions);
std::vector<gd::ExpressionCompletionDescription> expectedCompletions3{
gd::ExpressionCompletionDescription::ForObject("string", "", 3, 3),
gd::ExpressionCompletionDescription::ForExpression("string", "", 3, 3)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions4{
gd::ExpressionCompletionDescription::ForObject("string", "", 4, 4),
gd::ExpressionCompletionDescription::ForExpression("string", "", 4, 4)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions5{
gd::ExpressionCompletionDescription::ForObject("string", "", 5, 5),
gd::ExpressionCompletionDescription::ForExpression("string", "", 5, 5)};
REQUIRE(getCompletionsFor("string", "\"a\" + ", 3) == expectedCompletions3);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 4) == expectedCompletions4);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 5) == expectedCompletions5);
}
SECTION("Free function") {
SECTION("Test 1") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForExpression("unknown",
"Function")};
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Function", 0, 8)};
std::vector<gd::ExpressionCompletionDescription> expectedExactCompletions{
gd::ExpressionCompletionDescription::ForExpression("unknown",
"Function")
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Function", 0, 8)
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "Function(", 0) ==
expectedCompletions);
@@ -123,33 +144,48 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
expectedEmptyCompletions);
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForObject("unknown", "a"),
gd::ExpressionCompletionDescription::ForExpression("unknown", "a")};
gd::ExpressionCompletionDescription::ForObject("unknown", "a", 9, 10),
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "a", 9, 10)};
REQUIRE(getCompletionsFor("string", "Function(a", 9) ==
expectedCompletions);
}
SECTION("Function with a Variable as argument") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForVariable("scenevar",
"myVar")};
gd::ExpressionCompletionDescription::ForVariable(
"scenevar", "myVar", 33, 38)};
REQUIRE(getCompletionsFor("number",
"MyExtension::GetVariableAsNumber(myVar",
33) == expectedCompletions);
}
SECTION("Function with a Layer as argument") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForText(
"layer",
gd::MetadataProvider::GetExpressionMetadata(platform,
"MyExtension::MouseX")
.GetParameter(0),
"",
20,
21,
false)};
REQUIRE(getCompletionsFor("number", "MyExtension::MouseX(\"", 20) ==
expectedCompletions);
}
}
SECTION("Partial object or behavior function") {
SECTION("Test with string type") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("string",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehavior("Func",
"MyObject"),
gd::ExpressionCompletionDescription::ForBehavior(
"Func", 9, 13, "MyObject"),
gd::ExpressionCompletionDescription::ForExpression(
"string", "Func", "MyObject")};
"string", "Func", 9, 13, "MyObject")};
REQUIRE(getCompletionsFor("string", "MyObject.Func", 0) ==
expectedObjectCompletions);
REQUIRE(getCompletionsFor("string", "MyObject.Func", 7) ==
@@ -166,14 +202,14 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test with 'number|string' type") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("number|string",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"number|string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehavior("Func",
"MyObject"),
gd::ExpressionCompletionDescription::ForBehavior(
"Func", 9, 13, "MyObject"),
gd::ExpressionCompletionDescription::ForExpression(
"number|string", "Func", "MyObject")};
"number|string", "Func", 9, 13, "MyObject")};
REQUIRE(getCompletionsFor("number|string", "MyObject.Func", 0) ==
expectedObjectCompletions);
REQUIRE(getCompletionsFor("number|string", "MyObject.Func", 7) ==
@@ -193,18 +229,18 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("unknown",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"unknown", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehavior("Func",
"MyObject"),
gd::ExpressionCompletionDescription::ForBehavior(
"Func", 9, 13, "MyObject"),
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", "MyObject")};
"unknown", "Func", 9, 13, "MyObject")};
std::vector<gd::ExpressionCompletionDescription>
expectedExactFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", "MyObject")
"unknown", "Func", 9, 13, "MyObject")
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 0) ==
expectedObjectCompletions);
@@ -229,16 +265,16 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("string",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
"MyObject")};
gd::ExpressionCompletionDescription::ForBehavior(
"MyBehavior", 9, 19, "MyObject")};
std::vector<gd::ExpressionCompletionDescription>
expectedFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"string", "Func", "MyObject", "MyBehavior")};
"string", "Func", 21, 25, "MyObject", "MyBehavior")};
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 0) ==
expectedObjectCompletions);
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 7) ==
@@ -261,16 +297,16 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 2") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("string",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
"MyObject")};
gd::ExpressionCompletionDescription::ForBehavior(
"MyBehavior", 9, 19, "MyObject")};
std::vector<gd::ExpressionCompletionDescription>
expectedFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"string", "", "MyObject", "MyBehavior")};
"string", "", 21, 21, "MyObject", "MyBehavior")};
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 0) ==
expectedObjectCompletions);
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 7) ==
@@ -292,20 +328,20 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject("unknown",
"MyObject")};
gd::ExpressionCompletionDescription::ForObject(
"unknown", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehavior("MyBehavior",
"MyObject")};
gd::ExpressionCompletionDescription::ForBehavior(
"MyBehavior", 9, 19, "MyObject")};
std::vector<gd::ExpressionCompletionDescription>
expectedFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", "MyObject", "MyBehavior")};
"unknown", "Func", 21, 25, "MyObject", "MyBehavior")};
std::vector<gd::ExpressionCompletionDescription>
expectedExactFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", "MyObject", "MyBehavior")
"unknown", "Func", 21, 25, "MyObject", "MyBehavior")
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 0) ==
expectedObjectCompletions);

View File

@@ -24,6 +24,18 @@ bool CheckNodeAtLocationIs(gd::ExpressionParser2& parser,
*node, searchPosition)) != nullptr;
}
template <class TNode>
bool CheckParentNodeAtLocationIs(gd::ExpressionParser2& parser,
const gd::String& type,
const gd::String& expression,
size_t searchPosition) {
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return dynamic_cast<TNode*>(
gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
*node, searchPosition)) != nullptr;
}
bool CheckNoNodeAtLocation(gd::ExpressionParser2& parser,
const gd::String& type,
const gd::String& expression,
@@ -34,6 +46,16 @@ bool CheckNoNodeAtLocation(gd::ExpressionParser2& parser,
*node, searchPosition) == nullptr;
}
bool CheckNoParentNodeAtLocation(gd::ExpressionParser2& parser,
const gd::String& type,
const gd::String& expression,
size_t searchPosition) {
auto node = parser.ParseExpression(type, expression);
REQUIRE(node != nullptr);
return gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition(
*node, searchPosition) == nullptr;
}
TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
gd::Project project;
gd::Platform platform;
@@ -186,6 +208,13 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "number", "12+\"hello\"", 3) == true);
}
SECTION("Numbers and texts mismatchs (parent node)") {
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 0) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 1) == true);
REQUIRE(CheckNoParentNodeAtLocation(parser, "number", "12+\"hello\"", 2) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "number", "12+\"hello\"", 3) == true);
}
SECTION("Valid objects") {
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "object", "HelloWorld1", 0) == true);
@@ -195,6 +224,10 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
parser, "object", "HelloWorld1", 10) == true);
REQUIRE(CheckNoNodeAtLocation(parser, "object", "HelloWorld1", 11) == true);
}
SECTION("Valid objects (parent node)") {
REQUIRE(CheckNoParentNodeAtLocation(
parser, "object", "HelloWorld1", 0) == true);
}
SECTION("Invalid objects") {
REQUIRE(CheckNodeAtLocationIs<gd::IdentifierNode>(
parser, "object", "a+b", 0) == true);
@@ -296,6 +329,29 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
52) == true);
}
SECTION("Parent node") {
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(
parser, "number", "12 + MyExtension::GetNumber()", 0) ==
true);
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(
parser, "number", "12 + MyExtension::GetNumber()", 6) ==
true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser,
"number",
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
35) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
parser,
"number",
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
35) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
parser,
"number",
"MyExtension::GetNumberWith2Params(12, \"hello world\")",
39) == true);
}
}
SECTION("Invalid function calls") {
@@ -316,6 +372,12 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
REQUIRE(CheckNoNodeAtLocation(parser, "number", "Idontexist(12)", 14) ==
true);
}
SECTION("Invalid function calls (parent node)") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(
parser, "number", "Idontexist(12)", 12) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::FunctionCallNode>(
parser, "number", "Idontexist(12)", 12) == true);
}
SECTION("Unterminated function calls") {
REQUIRE(CheckNodeAtLocationIs<gd::FunctionCallNode>(

View File

@@ -348,7 +348,7 @@ module.exports = {
type: 'stringWithSelector',
paramLabel: _('Alignment'),
options: ['left', 'right', 'center'],
conditionDescription: _('Check the current text alignment'),
conditionDescription: _('Check the current text alignment.'),
conditionSentence: _('The text alignment of _PARAM0_ is _PARAM1_'),
actionDescription: _('Change the alignment of the text.'),
actionSentence: _('Set text alignment of _PARAM0_ to _PARAM1_'),
@@ -360,7 +360,7 @@ module.exports = {
iconPath: 'res/actions/scaleWidth24.png',
type: 'boolean',
paramLabel: _('Word wrap'),
conditionDescription: _('Check if word wrap is enabled'),
conditionDescription: _('Check if word wrap is enabled.'),
conditionSentence: _('Word wrap is enabled'),
actionDescription: _('Set word wrap'),
actionSentence: _('Activate word wrap for _PARAM0_: _PARAM1_'),

View File

@@ -56,7 +56,7 @@ namespace gdjs {
// @ts-ignore - parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
this._opacity = parseFloat(objectData.content.opacity);
this._text = objectData.content.text;
this._color = BBTextRuntimeObject.hexToRGBColor(objectData.content.color);
this._color = gdjs.hexToRGBColor(objectData.content.color);
this._fontFamily = objectData.content.fontFamily;
// @ts-ignore - parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
this._fontSize = parseFloat(objectData.content.fontSize);
@@ -69,11 +69,6 @@ namespace gdjs {
this.onCreated();
}
static hexToRGBColor(hex) {
const hexNumber = parseInt(hex.replace('#', ''), 16);
return [(hexNumber >> 16) & 255, (hexNumber >> 8) & 255, hexNumber & 255];
}
getRendererObject() {
return this._renderer.getRendererObject();
}
@@ -93,9 +88,7 @@ namespace gdjs {
this.setBBText(newObjectData.content.text);
}
if (oldObjectData.content.color !== newObjectData.content.color) {
this._color = BBTextRuntimeObject.hexToRGBColor(
newObjectData.content.color
);
this._color = gdjs.hexToRGBColor(newObjectData.content.color);
this._renderer.updateColor();
}
if (
@@ -226,6 +219,12 @@ namespace gdjs {
* @param opacity The new opacity of the object (0-255).
*/
setOpacity(opacity: float): void {
if (opacity < 0) {
opacity = 0;
}
if (opacity > 255) {
opacity = 255;
}
this._opacity = opacity;
this._renderer.updateOpacity();
}

View File

@@ -0,0 +1,721 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'BitmapText',
_('Bitmap Text'),
_('Displays a text using a "Bitmap Font" (an image representing characters). This is more performant than a traditional Text object and it allows for complete control on the characters aesthetic.'),
'Aurélien Vivet',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bitmap_text');
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
bitmapTextObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
if (propertyName in objectContent) {
if (typeof objectContent[propertyName] === 'boolean')
objectContent[propertyName] = newValue === '1';
else if (typeof objectContent[propertyName] === 'number')
objectContent[propertyName] = parseFloat(newValue);
else objectContent[propertyName] = newValue;
return true;
}
return false;
};
// $FlowExpectedError
bitmapTextObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
objectProperties
.getOrCreate('text')
.setValue(objectContent.text)
.setType('textarea')
.setLabel(_('Text'));
objectProperties
.getOrCreate('opacity')
.setValue(objectContent.opacity.toString())
.setType('number')
.setLabel(_('Opacity (0-255)'));
objectProperties
.getOrCreate('align')
.setValue(objectContent.align)
.setType('choice')
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Alignment, when multiple lines are displayed'));
objectProperties
.getOrCreate('bitmapFontResourceName')
.setValue(objectContent.bitmapFontResourceName)
.setType('resource')
.addExtraInfo('bitmapFont') //fnt or xml files
.setLabel(_('Bitmap Font'));
objectProperties
.getOrCreate('textureAtlasResourceName')
.setValue(objectContent.textureAtlasResourceName)
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Bitmap atlas image'));
objectProperties
.getOrCreate('scale')
.setValue(objectContent.scale.toString())
.setType('number')
.setLabel(_('Text scale'));
objectProperties
.getOrCreate('tint')
.setValue(objectContent.tint)
.setType('color')
.setLabel(_('Font tint'));
objectProperties
.getOrCreate('wordWrap')
.setValue(objectContent.wordWrap ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Word wrapping'));
return objectProperties;
};
bitmapTextObject.setRawJSONContent(
JSON.stringify({
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
tint: '#ffffff',
bitmapFontResourceName: '',
textureAtlasResourceName: '',
align: 'left',
wordWrap: true,
})
);
// $FlowExpectedError
bitmapTextObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue,
project,
layout
) {
return false;
};
// $FlowExpectedError
bitmapTextObject.getInitialInstanceProperties = function (
content,
instance,
project,
layout
) {
var instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
const object = extension
.addObject(
'BitmapTextObject',
_('Bitmap Text'),
_(
'Displays a text using a "Bitmap Font" (an image representing characters). This is more performant than a traditional Text object and it allows for complete control on the characters aesthetic.'
),
'JsPlatform/Extensions/bitmapfont32.png',
bitmapTextObject
)
.setIncludeFile('Extensions/BitmapText/bitmaptextruntimeobject.js')
.addIncludeFile(
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
);
object
.addExpressionAndConditionAndAction(
'string',
'Text',
_('Text'),
_('the text'),
_('the text'),
'',
'res/conditions/text24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('string')
.setFunctionName('setText')
.setGetter('getText');
object
.addExpressionAndConditionAndAction(
'number',
'Opacity',
_('Opacity'),
_('the opacity, between 0 (fully transparent) and 255 (opaque)'),
_('the opacity'),
'',
'res/conditions/opacity24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
.setFunctionName('setOpacity')
.setGetter('getOpacity');
object
.addExpressionAndCondition(
'number',
'FontSize',
_('Font size'),
_('the font size, defined in the Bitmap Font'),
_('the font size'),
'',
'res/conditions/characterSize24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
.setFunctionName('getFontSize');
object
.addExpressionAndConditionAndAction(
'number',
'Scale',
_('Scale'),
_('the scale (1 by default)'),
_('the scale'),
'',
'res/actions/scale24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
.setFunctionName('setScale')
.setGetter('getScale');
object
.addExpressionAndCondition(
'string',
'FontName',
_('Font name'),
_('the font name (defined in the Bitmap font)'),
_('the font name'),
'',
'res/conditions/font24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('string')
.setFunctionName('getFontName');
object
.addAction(
'SetTint',
_('Tint'),
_('Set the tint of the Bitmap Text object.'),
_('Set tint of _PARAM0_ to _PARAM1_'),
'',
'res/actions/color24.png',
'res/actions/color.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter('color', _('Color'), '', false)
.getCodeExtraInformation()
.setFunctionName('setTint');
object
.addAction(
'SetBitmapFontAndTextureAtlasResourceName',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.') +
' ' +
_(
'The resource name can be found in: `Project Manager > Game settings > Resources`.'
),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
'',
'res/actions/font24.png',
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter('bitmapFont', _('Bitmap font resource name'), '', false)
.setParameterLongDescription(
'The resource name of the font file, without quotes.'
)
.addParameter('string', _('Texture atlas resource name'), '', false)
.setParameterLongDescription(
'The resource name of the image exported with the font, with quotes.'
)
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addExpressionAndCondition(
'string',
'Alignment',
_('Alignment'),
_('the text alignment'),
_('the text alignment'),
'',
'res/actions/textAlign24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('string')
.setFunctionName('getAlignment');
object
.addAction(
'SetAlignment',
_('Alignment'),
_('Change the alignment of a Bitmap text object.'),
_('Set the alignment of _PARAM0_ to _PARAM1_'),
'',
'res/actions/textAlign24.png',
'res/actions/textAlign.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter(
'stringWithSelector',
_('Alignment'),
'["left", "center", "right"]',
false
)
.getCodeExtraInformation()
.setFunctionName('setAlignment');
object
.addCondition(
'WordWrap',
_('Word wrap'),
_('Check if word wrap is enabled.'),
_('_PARAM0_ word wrap is enabled'),
'',
'res/conditions/wordWrap24.png',
'res/conditions/wordWrap.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.getCodeExtraInformation()
.setFunctionName('getWordWrap');
object
.addAction(
'SetWordWrap',
_('Word wrap'),
_('De/activate word wrapping.'),
_('Activate word wrap of _PARAM0_: _PARAM1_'),
'',
'res/actions/wordWrap24.png',
'res/actions/wordWrap.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter('yesorno', _('Activate word wrap'), '', false)
.getCodeExtraInformation()
.setFunctionName('setWordWrap');
object
.addExpressionAndConditionAndAction(
'number',
'WrappingWidth',
_('Wrapping width'),
_('the width, in pixels, after which the text is wrapped on next line'),
_('the wrapping width'),
'',
'res/actions/scaleWidth24.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number')
.setFunctionName('setWrappingWidth')
.setGetter('getWrappingWidth');
return extension;
},
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instanciating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array.
*
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
/**
* Register editors for objects.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
objectsEditorService.registerEditorConfiguration(
'BitmapText::BitmapTextObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
helpPagePath: '/objects/bitmaptext',
})
);
},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
/** The bitmap font used in case another font can't be loaded. */
let defaultBitmapFont = null;
const defaultBitmapFontInstallKey = 'GD-DEFAULT-BITMAP-FONT';
/**
* Map counting the number of "reference" to a bitmap font. This is useful
* to uninstall a bitmap font when not used anymore.
*/
const bitmapFontUsageCount = {};
/**
* We patch the installed font to use a name that is unique for each font data and texture,
* to avoid conflicts between different font files using the same font name (by default, the
* font name used by Pixi is the one inside the font data, but this name is not necessarily unique.
* For example, 2 resources can use the same font, or we can have multiple objects with the same
* font data and different textures).
*/
const patchBitmapFont = (bitmapFont, bitmapFontInstallKey) => {
const defaultName = bitmapFont.font;
bitmapFont.font = bitmapFontInstallKey;
PIXI.BitmapFont.available[bitmapFontInstallKey] = bitmapFont;
delete PIXI.BitmapFont.available[defaultName];
return PIXI.BitmapFont.available[bitmapFontInstallKey];
};
/**
* Return a default bitmap font to be used in case another font can't be loaded.
*/
const getDefaultBitmapFont = () => {
if (defaultBitmapFont) return defaultBitmapFont;
const defaultBitmapFontStyle = new PIXI.TextStyle({
fontFamily: 'Arial',
fontSize: 20,
padding: 5,
align: 'left',
fill: '#ffffff',
wordWrap: true,
lineHeight: 20,
});
defaultBitmapFont = patchBitmapFont(
PIXI.BitmapFont.from(
defaultBitmapFontStyle.fontFamily,
defaultBitmapFontStyle,
{
chars: [
[' ', '~'], // All the printable ASCII characters
],
}
),
defaultBitmapFontInstallKey
);
return defaultBitmapFont;
};
/**
* Given a bitmap font resource name and a texture atlas resource name, returns the PIXI.BitmapFont
* for it.
* The font must be released with `releaseBitmapFont` when not used anymore - so that it can be removed
* from memory when not used by any instance.
*
* @param pixiResourcesLoader
* @param project
* @param bitmapFontResourceName
* @param textureAtlasResourceName
*/
const obtainBitmapFont = (
pixiResourcesLoader,
project,
bitmapFontResourceName,
textureAtlasResourceName
) => {
const bitmapFontInstallKey =
bitmapFontResourceName + '@' + textureAtlasResourceName;
if (PIXI.BitmapFont.available[bitmapFontInstallKey]) {
// Return the existing BitmapFont that is already in memory and already installed.
bitmapFontUsageCount[bitmapFontInstallKey] =
(bitmapFontUsageCount[bitmapFontInstallKey] || 0) + 1;
return Promise.resolve(PIXI.BitmapFont.available[bitmapFontInstallKey]);
}
// Get the atlas texture, the bitmap font data and install the font:
const texture = pixiResourcesLoader.getPIXITexture(
project,
textureAtlasResourceName
);
const loadBitmapFont = () =>
pixiResourcesLoader
.getBitmapFontData(project, bitmapFontResourceName)
.then((fontData) => {
if (!texture.valid)
throw new Error(
'Tried to install a BitmapFont with an invalid texture.'
);
const bitmapFont = patchBitmapFont(
PIXI.BitmapFont.install(fontData, texture),
bitmapFontInstallKey
);
bitmapFontUsageCount[bitmapFontInstallKey] =
(bitmapFontUsageCount[bitmapFontInstallKey] || 0) + 1;
return bitmapFont;
})
.catch((err) => {
console.warn('Unable to load font data:', err);
console.info(
'Is the texture atlas properly set for the Bitmap Text object? The default font will be used instead.'
);
const bitmapFont = getDefaultBitmapFont();
return bitmapFont;
});
if (!texture.valid) {
// Post pone texture update if texture is not loaded.
// (otherwise, the bitmap font would not get updated when the
// texture is loaded and udpated).
return new Promise((resolve) => {
texture.once('update', () => {
resolve(loadBitmapFont());
});
});
} else {
// We're ready to load the bitmap font now, as the texture
// is already loaded.
return loadBitmapFont();
}
};
/**
* When a font is not used by an object anymore (object destroyed or font changed),
* call this function to decrease the internal count of objects using the font.
*
* Fonts are unloaded when not used anymore.
*/
const releaseBitmapFont = (bitmapFontInstallKey) => {
if (bitmapFontInstallKey === defaultBitmapFontInstallKey) {
// Never uninstall the default bitmap font.
return;
}
if (!bitmapFontUsageCount[bitmapFontInstallKey]) {
console.error(
'BitmapFont with name ' +
bitmapFontInstallKey +
' was tried to be released but was never marked as used.'
);
return;
}
bitmapFontUsageCount[bitmapFontInstallKey]--;
if (bitmapFontUsageCount[bitmapFontInstallKey] === 0) {
PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
console.info(
'Uninstalled BitmapFont "' + bitmapFontInstallKey + '" from memory.'
);
}
};
/**
* Renderer for instances of BitmapText inside the IDE.
*
* @extends RenderedBitmapTextInstance
* @class RenderedBitmapTextInstance
* @constructor
*/
function RenderedBitmapTextInstance(
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
);
// We'll track changes of the font to trigger the loading of the new font.
this._currentBitmapFontResourceName = '';
this._currentTextureAtlasResourceName = '';
this._pixiObject = new PIXI.BitmapText('', {
// Use a default font. The proper font will be loaded in `update` method.
fontName: getDefaultBitmapFont().font,
});
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBitmapTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBitmapTextInstance.getThumbnail = function (
project,
resourcesLoader,
object
) {
return 'JsPlatform/Extensions/bitmapfont24.png';
};
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObject.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
this._pixiObject.text = rawText;
const opacity = properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const align = properties.get('align').getValue();
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
this._pixiObject.tint = parseInt(color.replace('#', '0x'), 16);
const scale = properties.get('scale').getValue() || 1;
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
this._currentTextureAtlasResourceName !== textureAtlasResourceName
) {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(
this._pixiResourcesLoader,
this._project,
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
});
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this._instance.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + (this._pixiObject.textWidth * scale) / 2;
this._pixiObject.position.y =
this._instance.getY() + (this._pixiObject.textHeight * scale) / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
};
RenderedBitmapTextInstance.prototype.onRemovedFromScene = function () {
RenderedInstance.prototype.onRemovedFromScene.call(this);
releaseBitmapFont(this._pixiObject.fontName);
this._pixiObject.destroy();
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
objectsRenderingService.registerInstanceRenderer(
'BitmapText::BitmapTextObject',
RenderedBitmapTextInstance
);
},
};

View File

@@ -0,0 +1,33 @@
namespace gdjs {
/**
* The Cocos2D-JS renderer for the BitmapTextRuntimeObject.
*
* The implementation is empty as the object is not supported in Cocos2D-JS for now.
*/
export class BitmapTextRuntimeObjectCocosRenderer {
getRendererObject() {}
onDestroy() {}
getFontSize() {}
updateFont() {}
updateTint() {}
getTint() {}
updateScale() {}
getScale() {}
updateWrappingWidth() {}
updateTextContent() {}
updateAlignment() {}
updatePosition() {}
updateAngle() {}
updateOpacity() {}
getWidth() {
return 0;
}
getHeight() {
return 0;
}
}
export const BitmapTextRuntimeObjectRenderer = BitmapTextRuntimeObjectCocosRenderer;
}

View File

@@ -0,0 +1,173 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
/**
* The PIXI.js renderer for the Bitmap Text runtime object.
*/
export class BitmapTextRuntimeObjectPixiRenderer {
_object: gdjs.BitmapTextRuntimeObject;
_pixiObject: PIXI.BitmapText;
/**
* @param runtimeObject The object to render
* @param runtimeScene The gdjs.RuntimeScene in which the object is
*/
constructor(
runtimeObject: gdjs.BitmapTextRuntimeObject,
runtimeScene: gdjs.RuntimeScene
) {
this._object = runtimeObject;
// Obtain the bitmap font to use in the object.
const bitmapFont = runtimeScene
.getGame()
.getBitmapFontManager()
.obtainBitmapFont(
runtimeObject._bitmapFontResourceName,
runtimeObject._textureAtlasResourceName
);
this._pixiObject = new PIXI.BitmapText(runtimeObject._text, {
fontName: bitmapFont.font,
fontSize: bitmapFont.size,
});
// Set the object on the scene
runtimeScene
.getLayer('')
.getRenderer()
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
// Set the anchor in the center, so that the object rotates around
// its center.
// @ts-ignore
this._pixiObject.anchor.x = 0.5;
// @ts-ignore
this._pixiObject.anchor.y = 0.5;
this.updateAlignment();
this.updateTextContent();
this.updateAngle();
this.updateOpacity();
this.updateScale();
this.updateWrappingWidth();
this.updateTint();
}
getRendererObject() {
return this._pixiObject;
}
onDestroy() {
// Mark the font from the object as not used anymore.
this._object._runtimeScene
.getGame()
.getBitmapFontManager()
.releaseBitmapFont(this._pixiObject.fontName);
this._pixiObject.destroy();
}
getFontSize() {
return this._pixiObject.fontSize;
}
updateFont(): void {
// Get the new bitmap font to use
const bitmapFont = this._object._runtimeScene
.getGame()
.getBitmapFontManager()
.obtainBitmapFont(
this._object._bitmapFontResourceName,
this._object._textureAtlasResourceName
);
// Mark the old font as not used anymore
this._object._runtimeScene
.getGame()
.getBitmapFontManager()
.releaseBitmapFont(this._pixiObject.fontName);
// Update the font used by the object:
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this.updatePosition();
}
updateTint(): void {
this._pixiObject.tint = gdjs.rgbToHexNumber(
this._object._tint[0],
this._object._tint[1],
this._object._tint[2]
);
this._pixiObject.dirty = true;
}
/**
* Get the tint of the bitmap object as a "R;G;B" string.
* @returns the tint of bitmap object in "R;G;B" format.
*/
getTint(): string {
return (
this._object._tint[0] +
';' +
this._object._tint[1] +
';' +
this._object._tint[2]
);
}
updateScale(): void {
this._pixiObject.scale.set(Math.max(this._object._scale, 0));
this.updatePosition();
}
getScale() {
return Math.max(this._pixiObject.scale.x, this._pixiObject.scale.y);
}
updateWrappingWidth(): void {
if (this._object._wordWrap) {
this._pixiObject.maxWidth =
this._object._wrappingWidth / this._object._scale;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
}
this.updatePosition();
}
updateTextContent(): void {
this._pixiObject.text = this._object._text;
this.updatePosition();
}
updateAlignment(): void {
// @ts-ignore - assume align is always a valid value.
this._pixiObject.align = this._object._align;
this.updatePosition();
}
updatePosition(): void {
this._pixiObject.position.x = this._object.x + this.getWidth() / 2;
this._pixiObject.position.y = this._object.y + this.getHeight() / 2;
}
updateAngle(): void {
this._pixiObject.rotation = gdjs.toRad(this._object.angle);
}
updateOpacity(): void {
this._pixiObject.alpha = this._object._opacity / 255;
}
getWidth(): float {
return this._pixiObject.textWidth * this.getScale();
}
getHeight(): float {
return this._pixiObject.textHeight * this.getScale();
}
}
export const BitmapTextRuntimeObjectRenderer = BitmapTextRuntimeObjectPixiRenderer;
}

View File

@@ -0,0 +1,323 @@
namespace gdjs {
/** Base parameters for {@link gdjs.BitmapTextRuntimeObject} */
export type BitmapTextObjectDataType = {
/** The base parameters of the Bitmap Text */
content: {
/** The opacity of the text. */
opacity: float;
/** Content of the text. */
text: string;
/** The tint of the text. */
tint: string;
/** The name of the resource containing the bitmap font for the text. */
bitmapFontResourceName: string;
/** The name of the resource containing the atlas image file for the text. */
textureAtlasResourceName: string;
/** The scale of the text. */
scale: float;
/** Activate word wrap if set to true. */
wordWrap: boolean;
/** Wrapping with from custom size properties. */
wrappingWidth: float;
/** Alignment of the text. */
align: 'left' | 'center' | 'right';
};
};
export type BitmapTextObjectData = ObjectData & BitmapTextObjectDataType;
/**
* Displays a text using a "Bitmap Font", generated in a external editor like bmFont.
* This is more efficient/faster to render than a traditional text (which needs
* to have its whole texture re-rendered anytime it changes).
*
* Bitmap Font can be created with softwares like:
* * BMFont (Windows, free): http://www.angelcode.com/products/bmfont/|http://www.angelcode.com/products/bmfont/
* * Glyph Designer (OS X, commercial): http://www.71squared.com/en/glyphdesigner|http://www.71squared.com/en/glyphdesigner
* * Littera (Web-based, free): http://kvazars.com/littera/|http://kvazars.com/littera/
*/
export class BitmapTextRuntimeObject extends gdjs.RuntimeObject {
_opacity: float;
_text: string;
/** color in format [r, g, b], where each component is in the range [0, 255] */
_tint: integer[];
_bitmapFontResourceName: string;
_textureAtlasResourceName: string;
_scale: number;
_wordWrap: boolean;
_wrappingWidth: float;
_align: string;
_renderer: gdjs.BitmapTextRuntimeObjectPixiRenderer;
/**
* @param runtimeScene The scene the object belongs to.
* @param objectData The object data used to initialize the object
*/
constructor(
runtimeScene: gdjs.RuntimeScene,
objectData: BitmapTextObjectData
) {
super(runtimeScene, objectData);
this._opacity = objectData.content.opacity;
this._text = objectData.content.text;
this._tint = gdjs.hexToRGBColor(objectData.content.tint);
this._bitmapFontResourceName = objectData.content.bitmapFontResourceName; // fnt/xml files
this._textureAtlasResourceName =
objectData.content.textureAtlasResourceName; // texture file used with fnt/xml (bitmap font file)
this._scale = objectData.content.scale;
this._wordWrap = objectData.content.wordWrap;
this._wrappingWidth = 0;
this._align = objectData.content.align;
this._renderer = new gdjs.BitmapTextRuntimeObjectRenderer(
this,
runtimeScene
);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
getRendererObject() {
return this._renderer.getRendererObject();
}
// @ts-ignore
updateFromObjectData(
oldObjectData: BitmapTextObjectDataType,
newObjectData: BitmapTextObjectDataType
): boolean {
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
this.setOpacity(newObjectData.content.opacity);
}
if (oldObjectData.content.text !== newObjectData.content.text) {
this.setText(newObjectData.content.text);
}
if (oldObjectData.content.tint !== newObjectData.content.tint) {
this._tint = gdjs.hexToRGBColor(newObjectData.content.tint);
this._renderer.updateTint();
}
if (
oldObjectData.content.bitmapFontResourceName !==
newObjectData.content.bitmapFontResourceName
) {
this.setBitmapFontResourceName(
newObjectData.content.bitmapFontResourceName
);
}
if (
oldObjectData.content.textureAtlasResourceName !==
newObjectData.content.textureAtlasResourceName
) {
this.setTextureAtlasResourceName(
newObjectData.content.textureAtlasResourceName
);
}
if (oldObjectData.content.scale !== newObjectData.content.scale) {
this.setScale(newObjectData.content.scale);
}
if (oldObjectData.content.wordWrap !== newObjectData.content.wordWrap) {
this.setWordWrap(newObjectData.content.wordWrap);
}
if (oldObjectData.content.align !== newObjectData.content.align) {
this.setAlignment(newObjectData.content.align);
}
return true;
}
/**
* Initialize the extra parameters that could be set for an instance.
*/
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
if (initialInstanceData.customSize) {
this.setWrappingWidth(initialInstanceData.width);
}
}
onDestroyFromScene(runtimeScene: gdjs.RuntimeScene): void {
super.onDestroyFromScene(runtimeScene);
this._renderer.onDestroy();
}
/**
* Set the text to display.
*/
setText(text: string): void {
this._text = text;
this._renderer.updateTextContent();
this.hitBoxesDirty = true;
}
/**
* Get the text displayed by the object.
*/
getText(): string {
return this._text;
}
setTint(rgbColorString: string): void {
const splitValue = rgbColorString.split(';');
if (splitValue.length !== 3) return;
this._tint[0] = parseInt(splitValue[0], 10);
this._tint[1] = parseInt(splitValue[1], 10);
this._tint[2] = parseInt(splitValue[2], 10);
this._renderer.updateTint();
}
getTint(): string {
return this._tint[0] + ';' + this._tint[1] + ';' + this._tint[2];
}
setScale(scale: float): void {
this._scale = scale;
this._renderer.updateScale();
this.hitBoxesDirty = true;
}
getScale(): float {
return this._scale;
}
getFontSize(): float {
return this._renderer.getFontSize();
}
setBitmapFontAndTextureAtlasResourceName(
bitmapFontResourceName: string,
textureAtlasResourceName: string
): void {
if (bitmapFontResourceName) {
this.setBitmapFontResourceName(bitmapFontResourceName);
this._renderer.updateFont();
}
if (textureAtlasResourceName) {
this.setTextureAtlasResourceName(textureAtlasResourceName);
this._renderer.updateFont();
}
}
setBitmapFontResourceName(bitmapFontResourceName: string): void {
this._bitmapFontResourceName = bitmapFontResourceName;
}
getBitmapFontResourceName(): string {
return this._bitmapFontResourceName;
}
setTextureAtlasResourceName(textureAtlasResourceName: string): void {
this._textureAtlasResourceName = textureAtlasResourceName;
}
getTextureAtlasResourceName(): string {
return this._textureAtlasResourceName;
}
setAlignment(align: string): void {
this._align = align;
this._renderer.updateAlignment();
}
getAlignment(): string {
return this._align;
}
/**
* Set object position on X axis.
* @param x The new position X of the object.
*/
setX(x: float): void {
super.setX(x);
this._renderer.updatePosition();
}
/**
* Set object position on Y axis.
* @param y The new position Y of the object.
*/
setY(y: float): void {
super.setY(y);
this._renderer.updatePosition();
}
/**
* Set the angle of the object.
* @param angle The new angle of the object.
*/
setAngle(angle: float): void {
super.setAngle(angle);
this._renderer.updateAngle();
}
/**
* Set object opacity.
* @param opacity The new opacity of the object (0-255).
*/
setOpacity(opacity: float): void {
if (opacity < 0) {
opacity = 0;
}
if (opacity > 255) {
opacity = 255;
}
this._opacity = opacity;
this._renderer.updateOpacity();
}
/**
* Get object opacity.
*/
getOpacity(): float {
return this._opacity;
}
/**
* Set the wrapping width.
* @param width The new width in pixels.
*/
setWrappingWidth(width: float): void {
this._wrappingWidth = width;
this._renderer.updateWrappingWidth();
this.hitBoxesDirty = true;
}
/**
* Get the wrapping width of the object.
*/
getWrappingWidth(): float {
return this._wrappingWidth;
}
setWordWrap(wordWrap: boolean): void {
this._wordWrap = wordWrap;
this._renderer.updateWrappingWidth();
this.hitBoxesDirty = true;
}
getWordWrap(): boolean {
return this._wordWrap;
}
/**
* Get the width of the object.
*/
getWidth(): float {
return this._renderer.getWidth();
}
/**
* Get the height of the object.
*/
getHeight(): float {
return this._renderer.getHeight();
}
}
gdjs.registerObject(
'BitmapText::BitmapTextObject',
// @ts-ignore
gdjs.BitmapTextRuntimeObject
);
}

View File

@@ -20,7 +20,6 @@ ADD_SUBDIRECTORY(PhysicsBehavior)
ADD_SUBDIRECTORY(PlatformBehavior)
ADD_SUBDIRECTORY(PrimitiveDrawing)
ADD_SUBDIRECTORY(Shopify)
ADD_SUBDIRECTORY(SkeletonObject)
ADD_SUBDIRECTORY(SystemInfo)
ADD_SUBDIRECTORY(TextEntryObject)
ADD_SUBDIRECTORY(TextObject)

View File

@@ -20,39 +20,75 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
*/
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'DebuggerTools',
_('Debugger Tools'),
_(
'Allow to interact with the editor debugger from the game.'
),
'Arthur Pacaud (arthuro555)',
'MIT'
);
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'DebuggerTools',
_('Debugger Tools'),
_('Allow to interact with the editor debugger from the game.'),
'Arthur Pacaud (arthuro555), Aurélien Vivet (Bouh)',
'MIT'
);
extension
extension
.addAction(
'Pause',
_('Pause game execution'),
_(
'This pauses the game, useful for inspecting the game state through the debugger. ' +
'Note that events will be still executed until the end before the game is paused.'
'Note that events will be still executed until the end before the game is paused.'
),
_('Pause game execution'),
_('Debugger Tools'),
'res/actions/bug32.png',
'res/actions/bug32.png'
)
.addCodeOnlyParameter("currentScene", "")
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
.setFunctionName('gdjs.evtTools.debuggerTools.pause');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
return [];
},
}
extension
.addAction(
'EnableDebugDraw',
_('Draw collisions hitboxes and points'),
_(
'This activates the display of rectangles and information on screen showing the objects bounding boxes (blue), the hitboxes (red) and some points of objects.'
),
_(
'Enable debugging view of bounding boxes/collision masks: _PARAM1_ (include invisible objects: _PARAM2_, point names: _PARAM3_, custom points: _PARAM4_)'
),
_('Debugger Tools'),
'res/actions/planicon24.png',
'res/actions/planicon.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('yesorno', _('Enable debug draw'), '', true)
.setDefaultValue('yes')
.addParameter(
'yesorno',
_('Show collisions for hidden objects'),
'',
true
)
.setDefaultValue('no')
.addParameter('yesorno', _('Show points names'), '', true)
.setDefaultValue('yes')
.addParameter('yesorno', _('Show custom points'), '', true)
.setDefaultValue('yes')
.getCodeExtraInformation()
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
.setFunctionName('gdjs.evtTools.debuggerTools.enableDebugDraw');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

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

View File

@@ -785,7 +785,7 @@ namespace gdjs {
variables: gdjs.dialogueTree.runner.variables.data,
visited: gdjs.dialogueTree.runner.visited,
};
gdjs.evtTools.network._objectToVariable(dialogueState, outputVariable);
outputVariable.fromJSObject(dialogueState);
};
/**
@@ -795,9 +795,7 @@ namespace gdjs {
* @param inputVariable The structured variable where to load the State from.
*/
gdjs.dialogueTree.loadState = function (inputVariable: gdjs.Variable) {
const loadedState = JSON.parse(
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
);
const loadedState = inputVariable.toJSObject();
if (!loadedState) {
console.error('Load state variable is empty:', inputVariable);
return;

View File

@@ -39,12 +39,14 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
type: '',
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
variables: [],
effects: [],
});
var object2 = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
variables: [],
effects: [],
});
runtimeScene.addObject(object);
runtimeScene.addObject(object2);

View File

@@ -125,6 +125,12 @@ module.exports = {
.setValue('7')
.setLabel(_('Quality (between 0 and 20)'))
.setType('number');
advancedBloomProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const asciiEffect = extension
.addEffect('Ascii')
@@ -215,8 +221,8 @@ module.exports = {
const blurEffect = extension
.addEffect('Blur')
.setFullName(_('Blur'))
.setDescription(_('Blur the rendered image.'))
.setFullName(_('Blur (Gaussian, slow - prefer to use Kawase blur)'))
.setDescription(_('Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'))
.addIncludeFile('Extensions/Effects/blur-pixi-filter.js');
const blurProperties = blurEffect.getProperties();
blurProperties
@@ -412,6 +418,12 @@ module.exports = {
.setLabel(_('Noise Frequency'))
.setType('number')
.setDescription('Number of updates per second (0: no updates)');
crtProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const displacementEffect = extension
.addEffect('Displacement')
@@ -511,6 +523,12 @@ module.exports = {
.setValue('false')
.setLabel(_('Shadow only (shows only the shadow when enabled)'))
.setType('boolean');
dropShadowProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const glitchEffect = extension
.addEffect('Glitch')
@@ -680,12 +698,18 @@ module.exports = {
.setValue('100')
.setLabel(_('Center Y (between -1000 and 100)'))
.setType('number');
godrayProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const kawaseBlurEffect = extension
.addEffect('KawaseBlur')
.setFullName(_('Kawase blur'))
.setFullName(_('Blur (Kawase, fast)'))
.setDescription(
_('A much faster blur than Gaussian blur, but more complicated to use.')
_('Blur the rendered image, with much better performance than Gaussian blur.')
)
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
.addIncludeFile('Extensions/Effects/kawase-blur-pixi-filter.js');
@@ -710,6 +734,12 @@ module.exports = {
.setValue('3')
.setLabel(_('Quality (between 1 and 20)'))
.setType('number');
kawaseBlurProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const lightNightEffect = extension
.addEffect('LightNight')
@@ -839,6 +869,12 @@ module.exports = {
.setValue('1')
.setLabel(_('Color of the outline'))
.setType('color');
outlineProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const pixelateEffect = extension
.addEffect('Pixelate')
@@ -891,6 +927,12 @@ module.exports = {
.setValue('0.5')
.setLabel(_('Center Y (between 0 and 1, 0.5 is image middle)'))
.setType('number');
radialBlurProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
const reflectionEffect = extension
.addEffect('Reflection')
@@ -1061,7 +1103,7 @@ module.exports = {
.setValue('20')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for filter area'));
.setDescription(_('Padding for the visual effect area'));
twistProperties
.getOrCreate('offsetX')
.setValue('0.5')
@@ -1100,6 +1142,12 @@ module.exports = {
.setValue('0.3')
.setLabel(_('strength (between 0 and 5)'))
.setType('number');
zoomBlurProperties
.getOrCreate('padding')
.setValue('0')
.setLabel(_('Padding'))
.setType('number')
.setDescription(_('Padding for the visual effect area'));
return extension;
},

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const adjustmentFilter = filter as PIXI.filters.AdjustmentFilter;
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
adjustmentFilter.gamma = value;
} else if (parameterName === 'saturation') {

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const advancedBloomFilter = filter as PIXI.filters.AdvancedBloomFilter;
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
advancedBloomFilter.threshold = value;
} else if (parameterName === 'bloomScale') {
@@ -17,6 +17,8 @@ namespace gdjs {
advancedBloomFilter.blur = value;
} else if (parameterName === 'quality') {
advancedBloomFilter.quality = value;
} else if (parameterName === 'padding') {
advancedBloomFilter.padding = value;
}
},
updateStringParameter: function (filter, parameterName, value) {},

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const asciiFilter = filter as PIXI.filters.AsciiFilter;
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
asciiFilter.size = value;
}

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bevelFilter = filter as PIXI.filters.BevelFilter;
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
} else if (parameterName === 'thickness') {
@@ -21,7 +21,7 @@ namespace gdjs {
}
},
updateStringParameter: function (filter, parameterName, value) {
const bevelFilter = filter as PIXI.filters.BevelFilter;
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value

View File

@@ -1,4 +1,5 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('BlackAndWhite', {
makePIXIFilter: function (layer, effectData) {
const colorMatrix = new PIXI.filters.ColorMatrixFilter();
@@ -7,7 +8,8 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorMatrix = filter as PIXI.filters.ColorMatrixFilter;
// @ts-ignore - unsure why PIXI.filters is not recognised.
const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
}

View File

@@ -1,4 +1,5 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('BlendingMode', {
makePIXIFilter: function (layer, effectData) {
const blendingModeFilter = new PIXI.filters.AlphaFilter();
@@ -6,7 +7,8 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const blendingModeFilter = filter as PIXI.filters.AlphaFilter;
// @ts-ignore - unsure why PIXI.filters is not recognised.
const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter;
if (parameterName === 'alpha') {
blendingModeFilter.alpha = value;
} else if (parameterName === 'blendmode') {

View File

@@ -1,4 +1,5 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Blur', {
makePIXIFilter: function (layer, effectData) {
const blur = new PIXI.filters.BlurFilter();

View File

@@ -1,4 +1,5 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Brightness', {
makePIXIFilter: function (layer, effectData) {
const brightness = new PIXI.filters.ColorMatrixFilter();
@@ -7,7 +8,8 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const brightnessFilter = filter as PIXI.filters.ColorMatrixFilter;
// @ts-ignore - unsure why PIXI.filters is not recognised.
const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'brightness') {
return;
}

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bulgePinchFilter = filter as PIXI.filters.BulgePinchFilter;
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
bulgePinchFilter.center[0] = value;
} else if (parameterName === 'centerY') {

View File

@@ -6,7 +6,6 @@ namespace gdjs {
.getGame()
.getImageManager()
.getPIXITexture(effectData.stringParameters.colorMapTexture);
const colorMapSprite = new PIXI.Sprite(colorMapTexture);
const colorMapFilter = new PIXI.filters.ColorMapFilter(
colorMapTexture,
effectData.booleanParameters.nearest,
@@ -20,7 +19,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorMapFilter = filter as PIXI.filters.ColorMapFilter;
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
value / 100,
@@ -31,7 +30,7 @@ namespace gdjs {
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
const colorMapFilter = filter as PIXI.filters.ColorMapFilter;
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'nearest') {
colorMapFilter.nearest = value;
}

View File

@@ -6,13 +6,13 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorReplaceFilter = filter as PIXI.filters.ColorReplaceFilter;
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'epsilon') {
colorReplaceFilter.epsilon = value;
}
},
updateStringParameter: function (filter, parameterName, value) {
const colorReplaceFilter = filter as PIXI.filters.ColorReplaceFilter;
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value

View File

@@ -42,6 +42,8 @@ namespace gdjs {
filter.animationSpeed = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
} else if (parameterName === 'padding') {
filter.padding = value;
}
},
updateStringParameter: function (filter, parameterName, value) {},

View File

@@ -1,4 +1,5 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Displacement', {
makePIXIFilter: function (layer, effectData) {
const displacementMapTexture = layer
@@ -15,7 +16,8 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const displacementFilter = filter as PIXI.filters.DisplacementFilter;
// @ts-ignore - unsure why PIXI.filters is not recognised.
const displacementFilter = (filter as unknown) as PIXI.filters.DisplacementFilter;
if (parameterName === 'scaleX') {
displacementFilter.scale.x = value;
}

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const dotFilter = filter as PIXI.filters.DotFilter;
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
dotFilter.scale = value;
} else if (parameterName === 'angle') {

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const dropShadowFilter = filter as PIXI.filters.DropShadowFilter;
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
dropShadowFilter.blur = value;
} else if (parameterName === 'quality') {
@@ -17,10 +17,12 @@ namespace gdjs {
dropShadowFilter.distance = value;
} else if (parameterName === 'rotation') {
dropShadowFilter.rotation = value;
} else if (parameterName === 'padding') {
dropShadowFilter.padding = value;
}
},
updateStringParameter: function (filter, parameterName, value) {
const dropShadowFilter = filter as PIXI.filters.DropShadowFilter;
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
@@ -28,7 +30,7 @@ namespace gdjs {
}
},
updateBooleanParameter: function (filter, parameterName, value) {
const dropShadowFilter = filter as PIXI.filters.DropShadowFilter;
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'shadowOnly') {
dropShadowFilter.shadowOnly = value;
}

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const glowFilter = filter as PIXI.filters.GlowFilter;
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'innerStrength') {
glowFilter.innerStrength = value;
} else if (parameterName === 'outerStrength') {
@@ -17,7 +17,7 @@ namespace gdjs {
}
},
updateStringParameter: function (filter, parameterName, value) {
const glowFilter = filter as PIXI.filters.GlowFilter;
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'color') {
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
}

View File

@@ -26,6 +26,8 @@ namespace gdjs {
filter.y = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
} else if (parameterName === 'padding') {
filter.padding = value;
}
},
updateStringParameter: function (filter, parameterName, value) {},

View File

@@ -6,7 +6,7 @@ namespace gdjs {
},
update: function (filter, layer) {},
updateDoubleParameter: function (filter, parameterName, value) {
const kawaseBlurFilter = filter as PIXI.filters.KawaseBlurFilter;
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
// @ts-ignore: fix these wrong parameters
kawaseBlurFilter.pixelizeX = value;

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