Compare commits

...

384 Commits

Author SHA1 Message Date
Florian Rival
c471a0af6d Bump newIDE version 2020-03-04 08:37:28 +00:00
Florian Rival
b1ea60e1d2 Fix Box2D.js in the global namespace conflicting with Facebook Instant Games runtime
Tested on Facebook Instant Games Android, iOS
Tested on Desktop Chrome, Firefox, Safari
2020-03-03 22:59:16 +00:00
Florian Rival
f745907f9d Merge branch 'master' of github.com:4ian/GDevelop 2020-03-03 21:54:34 +00:00
Florian Rival
92df124a92 Update translations 2020-03-03 21:54:25 +00:00
Geetesh Gupta
96f26c89ab Fix renaming not cancelled when object name is not changed (#1479) 2020-03-03 20:07:41 +00:00
Florian Rival
3f6428dfcc Merge pull request #1486 from 4ian/feature/more-expression-parser-node-locations
Improver expression parser with more location information
2020-03-02 22:11:57 +00:00
Florian Rival
22c6a57394 Improve completions from ExpressionCompletionFinder 2020-03-02 22:11:31 +00:00
Harsimran Singh Virk
dc942e6abc Fix unscrollable configuration/parameters of Event Functions (#1483) 2020-03-02 18:26:54 +00:00
Quarkstar
f9430a0da1 Fix hash of piskel-editor (#1485)
Fix #1482
2020-03-02 08:56:58 +00:00
Florian Rival
94ac7166ed Add more tests for ExpressionParser2 2020-03-01 19:00:18 +00:00
Florian Rival
88b20240ff Fix location of identifier (function names, etc...) with trailing whitespaces 2020-03-01 18:40:30 +00:00
Florian Rival
af93149f6a Add more location information in ExpressionParser nodes
Also fix some whitespace sensitivity of the parser
2020-03-01 17:37:21 +00:00
Florian Rival
732a716be4 Refactor error display in GenericExpressionField 2020-02-29 17:51:39 +00:00
gautamv95
11b660e05d Fix change of focused tab after a tab is closed (#1480)
Fix #986
2020-02-29 13:36:47 +00:00
Florian Rival
6155606d20 Refactor EnumerateExpressions 2020-02-27 21:00:27 +00:00
Florian Rival
89e3853296 Enforce TypeScript "strictNullChecks" and other strict compiler options 2020-02-25 21:06:04 +00:00
Arthur Pacaud
df655f2269 Removed non-required use of Hashtable.items (#1458) 2020-02-25 20:51:55 +00:00
Florian Rival
6b6ec6f06f Upgrade another package-lock.json file to latest format 2020-02-25 20:46:03 +00:00
Florian Rival
d80a47e569 Upgrade package-lock.json files to latest format 2020-02-25 20:22:01 +00:00
Florian Rival
9cf9d09f3a Refactor GenericExpressionField highlighting styling 2020-02-25 19:24:50 +00:00
Florian Rival
8fa23c5463 Move some files to InstructionOrExpression folder in newIDE 2020-02-25 19:24:50 +00:00
Aurélien Vivet
1c65e3c655 Fix casing in camera actions/conditions (#1467) 2020-02-25 18:21:22 +00:00
Florian Rival
b1e292e04e Update TimeDelta name/descriptions 2020-02-24 23:09:30 +00:00
Arthur Pacaud
e2f8f70d54 Improve JSDoc in gd.js (#1459) 2020-02-23 20:34:37 +00:00
Aurélien Vivet
d04faa039b Add Discord button on StartPage (#1462) 2020-02-23 20:27:57 +00:00
Florian Rival
4e59573042 Speed up identifiers and other character parsing in ExpressionParser2 2020-02-23 19:41:53 +00:00
Florian Rival
07fce517d6 Add ExpressionParser2Benchmarks 2020-02-23 14:52:09 +00:00
Florian Rival
ac90b982ac Hide verbose extension loading logs in tests 2020-02-23 14:49:35 +00:00
Florian Rival
4df974a4d7 Fix error highlighting in GenericExpressionField when shown inline 2020-02-22 18:28:56 +00:00
Florian Rival
cebf1e2a84 Add sanity check for libGD.js size 2020-02-22 16:35:57 +00:00
Florian Rival
650676cc6e Update Breakout example 2020-02-22 15:21:58 +00:00
Florian Rival
a3614a85b8 Add script to automatically upload example resources for the web-app 2020-02-22 15:15:58 +00:00
Florian Rival
ef52fec3ca Add Flappy bird example 2020-02-22 15:00:22 +00:00
Florian Rival
ca6f11b55a Support for describing completions to display for an expression (#1447) 2020-02-22 14:27:43 +00:00
Arthur Pacaud
7015962aa0 Update README-extensions.md (#1450)
As the majority of extensions use JavaScript now.
2020-02-22 12:55:57 +00:00
Florian Rival
4df7f0d10f Add links to "good first issues"/"not too hard" cards 2020-02-22 11:57:58 +00:00
Florian Rival
8c7ffe319a Merge pull request #1443 from 4ian/fix/visit-function-node
Fix WholeProjectRefactorer potential wrong renaming/moving of parameters
2020-02-19 08:36:12 +00:00
Florian Rival
710c2f0304 Fix renaming a property not renaming property expression used in a property action/condition 2020-02-19 08:33:32 +00:00
Florian Rival
727fa8a538 Fix WholeProjectRefactorer renaming/moving parameters of wrong functions for behaviors 2020-02-18 23:27:19 +00:00
Aurélien Vivet
f45e4c2049 Fix whitespace not preserved in comments (#1442) 2020-02-18 20:54:04 +00:00
Florian Rival
3bdf612f8e Fix ExpressionsRenamer when renaming an object function (#1438) 2020-02-18 08:55:25 +00:00
Florian Rival
4f7c91190e Fix formatting 2020-02-17 21:19:45 +00:00
Aurélien Vivet
5ed0c57e48 Improve pixel perfect rendering of games (#1432)
Can help with the issue of "bleeding" tiles.
2020-02-17 21:34:27 +01:00
Florian Rival
3955612e3b Run autoformatting on runtimegame-pixi-renderer.js 2020-02-17 20:27:58 +00:00
Arthur Pacaud
8eede20b07 Fix warning shown after dismissing the rename of an object (#1437) 2020-02-17 21:25:53 +01:00
Aurélien Vivet
3df9b29c3e Improve description of behavior methods and add "Extension" in tab names (#1431) 2020-02-16 23:48:13 +01:00
Florian Rival
280906dd3a Add "once" in description of onFirstSceneLoaded 2020-02-14 16:58:05 +00:00
Florian Rival
94e81ddf40 Add field for GDevelop version compatibility in extension headers and alert in IDE 2020-02-14 16:58:05 +00:00
Florian Rival
392e602651 Add message about export of lifecycle functions only if extension used 2020-02-14 16:58:05 +00:00
Florian Rival
0d60a54fa7 Add support for extension lifecycle events function 2020-02-14 16:58:05 +00:00
Florian Rival
e95a336dd2 Add new GDJS callbacks: first scene loaded, pre/post events 2020-02-12 20:47:43 +00:00
Florian Rival
b8ccf02f70 Refactor callbacks registration in GDJS 2020-02-11 23:33:30 +00:00
Florian Rival
a564a484a7 Rework codeNamespace to be generated by LayoutCodeGenerator instead of EventsCodeGenerator 2020-02-11 21:54:49 +00:00
Florian Rival
b9c1f5f6a7 Add LayoutCodeGenerator 2020-02-11 21:26:00 +00:00
Florian Rival
243bc93fe5 Add EventsFunctionsExtensionCodeGenerator 2020-02-11 20:45:20 +00:00
Florian Rival
d544319302 Add location in ExpressionParser2 and ExpressionNodeLocationFinder 2020-02-11 08:20:09 +00:00
Florian Rival
0fa5988995 Fix broken scripts since update to Emscripten 1.39.6 2020-02-10 23:28:54 +00:00
Arthur Pacaud
746b2f5480 Add more JSDoc/typing to gdjs.RuntimeObject and Hashtable (#1376) 2020-02-10 20:24:11 +00:00
Florian Rival
2cc7c8740e Fix Storybook and add story for NewInstructionEditorMenu 2020-02-09 22:16:09 +00:00
Florian Rival
aa30052dca Fix Events Extractor dialog showing empty extension names 2020-02-09 11:50:45 +00:00
Florian Rival
beb1cf0631 Hide horizontal scrollbar in InlinePopover 2020-02-09 11:22:31 +00:00
Todor Imreorov
78640d74c8 Add InstructionEditor showing as popover when right click on "Add action/condition" (#1416) 2020-02-09 11:17:38 +00:00
Todor Imreorov
7becb0be4b Fix multiple issues in Dialogue Tree (Yarn) extension (#1418)
* Add protection against misusage of the extension
* Fix json loading resource reading at the beginning of scene (thanks @4ian)
* Fix getClippedLineText sometimes clips the last letter
2020-02-08 18:25:09 +00:00
Florian Rival
6c789b7eb0 Remove remote connection for AppVeyor 2020-02-08 10:43:45 +00:00
Florian Rival
f743a785ad Update tests run on Windows on AppVeyor 2020-02-08 10:43:45 +00:00
Florian Rival
bac8aa14fa Fix Windows tests and add AppVeyor CI 2020-02-08 10:43:45 +00:00
Florian Rival
3ebb483e32 Update GDevelop.js Windows build to use embedded Ninja instead of MinGW make 2020-02-08 00:29:19 +00:00
Florian Rival
4fbbd34d40 Fix typo 2020-02-07 17:10:30 +00:00
Florian Rival
24091db88b Add deploy step to upload GDJS (and extensions) Runtime for web-app 2020-02-05 22:09:16 +00:00
Florian Rival
4ec3f1f082 Merge pull request #1406 from 4ian/refactor/emscripten-1.39
Upgrade to Emscripten 1.39.6
2020-02-05 21:58:31 +00:00
Florian Rival
991378e004 Remove debug message 2020-02-05 21:42:28 +00:00
Florian Rival
dfaee92d24 Fix GDevelop.js tests on Windows 2020-02-05 21:35:28 +00:00
Florian Rival
b07c71cb9c Exclude emsdk from tests in GDevelop.js 2020-02-05 21:27:41 +00:00
Florian Rival
cb0c0f903f Avoid full rebuild when changing a CMake parameter in GDevelop.js 2020-02-05 20:52:03 +00:00
Florian Rival
a81a121a8d Fix test coverage not running with Node environment 2020-02-02 20:24:31 +00:00
Florian Rival
7bf892c7eb Upgrade to Emscripten 1.39.6 2020-02-02 19:32:09 +00:00
Aurélien Vivet
ce4fdbe4f8 Add option to search in event texts (comments, group names) (#1398) 2020-02-01 18:45:15 +00:00
Florian Rival
d82bbdc1af Create CNAME for hosting on editor.gdevelop-app.com
This file is required by GitHub when using a custom domain with GitHub pages.
Now required because of using Cloudflare directly instead of Cloudfront.
2020-02-01 14:01:33 +00:00
Florian Rival
62dca91637 Fix formatting 2020-02-01 13:00:51 +00:00
Florian Rival
1e1c5f7206 Fix drag'n'drop of selected event invalidating the event in memory (potential crash) 2020-02-01 12:33:50 +00:00
Florian Rival
7c9e6ee6f1 Prevent larger texts in landscape mode on Safari iOS 2020-01-30 21:29:55 +00:00
Florian Rival
5690de71c5 Remove useless define in GDevelop.js 2020-01-30 08:24:48 +00:00
Florian Rival
eae2a06a4e Improve export speed (~20%) by memoizing mangled names 2020-01-28 23:57:37 +00:00
Florian Rival
4affa25672 Fix compilation on recent compilers
Fix compilation with latest Emscripten
2020-01-28 22:49:54 +00:00
Florian Rival
7b30f63cb1 Remove unused variables 2020-01-27 23:15:27 +00:00
Arthur Pacaud
574bdaaf41 Refactor SpriteRuntimeObject types to add prefix "Sprite" (#1378)
Also remove useless parseFloat
2020-01-27 22:54:28 +00:00
Florian Rival
bff43ee771 Remove dead code related to old ExpressionParser 2020-01-27 22:08:48 +00:00
Aurélien Vivet
9b9b6c5996 Fix usage of getFontFamily in BBText instance renderer (#1399) 2020-01-27 20:51:07 +00:00
Florian Rival
721872e45b Fix race condition between extension loading and GDJS development watcher 2020-01-27 20:18:35 +00:00
Arthur Pacaud
edd9ec94cf Add @arthuro555 to credits (#1396) 2020-01-27 18:21:18 +00:00
Florian Rival
c986c52409 Add document explaining platforms and exporters 2020-01-26 20:43:39 +00:00
Aurélien Vivet
168b8d3fea Simplify the Loop checkbox in Sprite editor (#1397) 2020-01-26 17:20:47 +00:00
Florian Rival
47025329dd Remove remaining old functions to register objects/behaviors 2020-01-26 10:53:49 +00:00
Aurélien Vivet
9f05a65ed2 Add font selector for BBText object (#1394) 2020-01-25 19:44:13 +00:00
Florian Rival
370c6d03ad Update estimated build time for electron builds 2020-01-20 21:32:30 +00:00
Aurélien Vivet
b650ff3aa5 Improve credits (#1391) 2020-01-20 20:34:56 +00:00
Florian Rival
285ff6f5f5 Update the script to extract expressions reference 2020-01-19 15:29:59 +00:00
Florian Rival
81f292941f Add help button in ExpressionSelector to open expressions reference 2020-01-19 14:12:25 +00:00
Florian Rival
eb27ba7c86 Improve script to extract expressions reference documentation
Still need to add parameters
2020-01-19 12:27:51 +00:00
Florian Rival
01a844c356 Refactor import-GDJS-Runtime script to use Node.js 2020-01-18 14:27:36 +00:00
Arthur Pacaud
054e48227c Make object and behavior registering more explicit (#1379) 2020-01-17 22:49:24 +00:00
Florian Rival
9adc40a55d Add script to extract all the expressions available in GDevelop 2020-01-16 23:44:26 +00:00
Bouh
034c734568 Fix unaligned text for "Unknown behavior" (#1382)
- Remove useless span
- Remove useless style related to span
- Use `Text` for Unknown behavior to ensure alignement and correct font.
2020-01-16 20:19:15 +00:00
Florian Rival
b65aed4c02 Merge pull request #1380 from 4ian/feature/notarization-macos
Enable notarization on macos
2020-01-16 19:35:20 +00:00
Florian Rival
adbcef8f59 Build zip for macOS on CircleCI 2020-01-16 08:56:06 +00:00
Florian Rival
44dbbd7138 Add notarization for macOS 2020-01-15 23:34:25 +00:00
Florian Rival
3e63e34d61 Upgrade electron-builder to 20.44.4 and electron-updater to 4.2.0 2020-01-15 20:53:49 +00:00
Florian Rival
dc976003b7 Add action to center the window on screen 2020-01-14 22:03:10 +00:00
Florian Rival
fb3002cd77 Add version metadata in analytics 2020-01-14 21:41:05 +00:00
Florian Rival
b25752907f Fix effects not applied in exported games 2020-01-13 22:31:54 +00:00
Florian Rival
72da63afcb Fix broken Text and TiledSprite objects 2020-01-13 21:51:35 +00:00
Florian Rival
ab5a593ab3 Use resources.gdevelop-app.com for static content serving 2020-01-13 21:23:24 +00:00
Florian Rival
71dcb20b7b Fix date not removed from generated GDJS HTML documentation 2020-01-13 19:35:23 +00:00
Florian Rival
61ffb40dee Remove date from generated documentation HTML 2020-01-13 08:56:08 +00:00
Florian Rival
07152639d1 Ensure npm install is run before running jsdoc with GenerateAllDocs 2020-01-12 23:51:31 +00:00
Florian Rival
6655a949ec Fix GDJS doc generation (incompatible TypeScript/JSDoc notations) 2020-01-12 23:33:39 +00:00
Arthur Pacaud
99312c71e0 Improve overall type annotations (#1361)
* Add type annotation for ObjectData and related types.
* Add missing type annotations.
* Add // @ts-check to some files.
2020-01-12 20:14:00 +00:00
Bouh
3abb5a393d Set object names in bold in dark theme (like in light theme) (#1366) 2020-01-12 20:12:51 +00:00
Florian Rival
010b52ced6 Update documentation urls to docs.gdevelop-app.com/... 2020-01-12 18:45:39 +00:00
Florian Rival
7a1ce790d4 Make "Allow again jumping" action description clearer 2020-01-10 23:40:20 +00:00
Florian Rival
fbdf530e00 Refactor upload, removing dependency on AWS S3 2020-01-10 23:29:15 +00:00
Florian Rival
ee1e67a367 Bump newIDE version 2020-01-07 20:52:26 +00:00
Florian Rival
5eded536b8 Rework builds/previews 2020-01-07 20:51:20 +00:00
Florian Rival
fa511d5faa Bump newIDE version 2020-01-07 18:09:30 +00:00
Florian Rival
a0887a1982 Fix rendering of particles not using a texture 2020-01-06 23:41:15 +00:00
Florian Rival
975a5a44cc Bump newIDE version 2020-01-06 22:55:20 +00:00
Florian Rival
252315d0d1 Fix ExpressionParametersEditorDialog being too small for expression selectors 2020-01-06 21:52:00 +00:00
Bouh
cbd7a70059 Fix help page link in Effects List dialog (#1352) 2020-01-06 14:11:56 +00:00
Florian Rival
d9a8f79926 Fix "Number of objects" condition not showing the object name anymore 2020-01-06 08:22:50 +00:00
Florian Rival
a417e3c9db Bump newIDE version 2020-01-06 08:19:38 +00:00
Florian Rival
7530b4e137 Don't open devtools by default 2020-01-05 20:37:31 +00:00
Florian Rival
e0adc2a924 Update translations 2020-01-05 18:36:43 +00:00
Florian Rival
a4227dac78 Normalize naming of addExpression parameters in BBText extension 2020-01-03 22:43:41 +00:00
Florian Rival
4e76f28fd3 Fix BBText sentences and help page path 2020-01-03 22:36:57 +00:00
Florian Rival
3b89284fd2 Normalize/fix some action/condition sentences 2020-01-03 22:29:08 +00:00
Florian Rival
dd8a0c7158 Add additional descriptions to anchor behavior properties 2020-01-02 21:33:36 +00:00
Florian Rival
641d87b097 Update Platformer example to adapt its width dynamically
This is good testbed for anchor behavior + dynamic game resolution adaptation to the window
2020-01-02 21:33:36 +00:00
Florian Rival
64dbab1bce Fix layer center position after resolution update 2020-01-02 21:33:36 +00:00
Florian Rival
dc4e68dd21 Add actions to change game resolution resize mode 2020-01-02 21:33:36 +00:00
Florian Rival
9d3cd9f110 Add option to automatically resize game resolution to window size
* Also refactor:
  * method names related to window/game resolution
  * runtime game renderer size handling

* Update ProjectPropertiesDialog
2020-01-02 21:33:36 +00:00
Todor Imreorov
eb5687dc7b Add RGBSplit, Outline, Glow, DropShadow, Bevel, Ascii & Adjustment effects 2019-12-29 19:18:03 +00:00
Florian Rival
9eb548452a Add MoveEventsFunctionParameter and MoveBehaviorEventsFunctionParameter in WholeProjectRefactorer 2019-12-27 22:21:24 +01:00
Florian Rival
4ac3516336 Improve type annotations for gdjs.Layer 2019-12-27 18:34:03 +01:00
Florian Rival
e3aed61390 Add type annotations and auto-format gdjs.Layer 2019-12-27 18:21:31 +01:00
Florian Rival
479584c3ef Also show parameter names and descriptions for checkbox in EffectsList 2019-12-27 17:33:26 +01:00
Florian Rival
3fc3d1e235 Add menu item to show parameter names in EffectsList 2019-12-27 17:09:57 +01:00
Florian Rival
448e543658 Display effect descriptions in EffectsList 2019-12-27 14:44:45 +01:00
Florian Rival
eb1abbd539 Rework Preferences dialog and show all hints to enable/disable 2019-12-27 14:44:41 +01:00
Florian Rival
48420c6b79 Add preference to display assignment operators in EventsSheet 2019-12-27 13:46:57 +01:00
Florian Rival
a6d7c032ac Remove redundant "current" word in animation related actions/conditions 2019-12-27 13:46:57 +01:00
Florian Rival
2f8840b2cf [codemod] Adapt sentences of action/conditions 2019-12-27 13:46:57 +01:00
Florian Rival
5df3afdb67 Adapt extension properties to new sentences + various changes 2019-12-27 13:46:57 +01:00
Florian Rival
df2185a505 Capitalize sentences displayed by Instruction 2019-12-27 13:46:57 +01:00
Florian Rival
1d67453454 Adapt OperatorField to new sentences 2019-12-27 13:46:57 +01:00
Florian Rival
217cd9639f Add methods to use default (relational) operator parameters 2019-12-27 13:46:57 +01:00
Florian Rival
9f91654628 Add script to refactor actions and conditions sentences 2019-12-27 13:46:57 +01:00
Florian Rival
e0602d347b Fix effects 2019-12-26 19:54:37 +01:00
Todor Imreorov
f14e02e739 Add Godray, Tilt Shift, Kawase Blur, Zoom Blur, Advanced Blur and CRT effects (#1325)
* add crt filter

* add godray,tiltshift, kawaseblur,zoomblur, advBloom filters

* refactor filters to fit api updates

* use else ifs

* Run Prettier on JsExtension.js

* Update names and descriptions

* Update crt-pixi-filter.js

* Fix godray

Co-authored-by: Florian Rival <Florian.rival@gmail.com>
2019-12-26 19:43:49 +01:00
Florian Rival
f3ef26dae5 Fix div used in p when using MarkdownText with TextField 2019-12-26 19:15:54 +01:00
Florian Rival
eda1f9b770 Ensure buttons next to text fields are not shrinked 2019-12-26 19:04:05 +01:00
Florian Rival
10e296bff7 Update package-lock.json 2019-12-26 18:49:50 +01:00
Florian Rival
73dc3a93ea Update displacement template image and fix displacement label/description 2019-12-26 18:08:32 +01:00
Bouh
42ddf9b41a Add Displacement, ColorMap, Pixelate and Reflection effects (#1329) 2019-12-26 17:38:28 +01:00
Florian Rival
17ac7cf091 Fix links color in embedded Markdown text 2019-12-26 16:35:31 +01:00
Florian Rival
3f04f0ea92 Add .prettierrc for Extensions 2019-12-26 11:36:03 +01:00
Florian Rival
7c57881359 Add support for markdown in helper texts (for effects/action/condition descriptions) 2019-12-25 23:44:41 +01:00
Florian Rival
e29c2d033c Ensure no line break between icon and name in EventsSheet 2019-12-25 21:05:53 +01:00
Florian Rival
8bae458b65 Fix events sheet (actions, conditions...) not refreshed after language change 2019-12-25 19:59:11 +01:00
Florian Rival
854d03ff0e Fix GDevelop.js tests 2019-12-25 19:27:02 +01:00
Florian Rival
5243ae0d5b Simplify/rework sentences of a few actions 2019-12-25 19:13:59 +01:00
Florian Rival
73f6636163 Use getter and normalize declaration of "ChangeScale" 2019-12-25 15:36:39 +01:00
Florian Rival
276f262fe7 Fix "Mouse joint target" condition sentence 2019-12-24 16:05:34 +01:00
Florian Rival
7abff84cc8 Fix particles flow condition sentence 2019-12-24 16:04:19 +01:00
Florian Rival
07c4f6f485 Normalize a condition sentence 2019-12-24 16:03:13 +01:00
Florian Rival
942d6f8413 Remove useless commented code 2019-12-24 12:19:32 +01:00
Florian Rival
20f10efc3d Add menu option to add a long description to functions parameters 2019-12-23 16:46:49 +01:00
Bouh
c75c6f3df1 Make warning/error icons color + other fixes (#1330) 2019-12-23 13:59:19 +01:00
Florian Rival
e75db3a626 Allow to display long descriptions of some parameters as helper text 2019-12-22 12:07:59 +01:00
Florian Rival
04f248b2d2 Add new examples by @ddabrahim 2019-12-21 16:26:02 +01:00
Florian Rival
50adbfaae9 Add support for booleans in effects parameters 2019-12-21 15:16:15 +01:00
Florian Rival
ec9a629124 Add optional description to effect properties 2019-12-21 12:04:08 +01:00
Florian Rival
977dc27bd0 Add update for effects + colors and resources for effect parameters 2019-12-21 12:04:08 +01:00
Florian Rival
6108088979 Fix compilation of EffectMetadata for GDCpp 2019-12-17 09:04:00 +00:00
Florian Rival
1d3f516e6f Update extension README to mention effects 2019-12-17 09:04:00 +00:00
Florian Rival
81aa68e69d Add back existing effects as an "Effect" extension
* Add tests for EnumerateEffects.
* Ensure fake effects are present for Storybook stories.
2019-12-17 09:04:00 +00:00
Florian Rival
8891c91d61 Add EffectMetadata and AddEffect to extensions
* Allow extensions to store EffectMetadata.
* Rework gdjs.PixiFiltersTools to allow new effects to be dynamically registered.
* Update EffectsList to read effects from extensions.
* Update GDJS ExporterHelper to export include files for effects
* Rename effectName to effectType to avoid confusions in gd::Effect
2019-12-17 09:04:00 +00:00
Florian Rival
702c8a459d Fix installation of Emscripten SDK (#1322) 2019-12-16 09:01:00 +00:00
Florian Rival
6164c6fe02 Remove improper removal of files before running GDevelop.js build 2019-12-15 20:53:12 +00:00
Florian Rival
a45c34f11d Try to fix emsdk sdk-1.37.37-64bit install 2019-12-15 20:11:12 +00:00
Bouh
38dd36cd44 Rename objects to instances for For Each and Pick All (#1320) 2019-12-15 14:48:24 +00:00
Florian Rival
1b3f7309b0 Make clearer that npm start must be kept running even for Electron in development 2019-12-15 14:37:03 +00:00
Florian Rival
c9ed8f25ce Make extra explicit that npm start is required by launching electron in development 2019-12-15 14:29:09 +00:00
Bouh
a6dab9a29f Fix pixel perfect rendering of Panel Sprite (9-patch) objects (#1314) 2019-12-12 21:01:42 +00:00
Bouh
ea7b07d2e4 Fix parameter description for Shape Painter Star action (#1315) 2019-12-10 21:56:16 +00:00
Florian Rival
f917db411f Quick refactoring in EventTextDialog 2019-12-10 20:53:08 +00:00
Bouh
7e6cb643b8 Update comment edition styling to be seamless (#1309)
* Also update event handles hover styling.
2019-12-10 20:50:24 +00:00
Bouh
11e18d7bc4 Add EventTextDialog to edit text and colors of comments/groups (#1310) 2019-12-10 20:21:52 +00:00
Florian Rival
56158e18f3 Add BBText object demo to the web-app 2019-12-09 23:29:52 +00:00
Florian Rival
1651893a61 Fix BBText object demo 2019-12-09 23:08:04 +00:00
Todor Imreorov
2809f53af8 Add BBText object (#1247)
* Uses `pixi-multistyle-text`
* Add standalone example
* Update the Yarn Dialogue example
2019-12-09 22:54:56 +00:00
Florian Rival
0c045d6e79 Update translations 2019-12-09 09:02:27 +00:00
Florian Rival
721b021b0a Fix memory leak and warning about texture not found.
Replaced the invalid texture for the PIXI image manager by a valid, hardcoded image. This:
* avoids a warning about not being able to load bunny.png (finally!)
* when creating a new Sprite, the invalid texture being used while the sprite is being set up, this was making PIXI adding a new event listener on the bunny.png texture. As the texture was not loaded, the event listener would never be removed, creating a leak.

Verified that the memory consumption is now stable on "Collisions with creation and deletion benchmark.gdg.json" benchmark game.
2019-12-08 23:16:16 +00:00
Todor Imreorov
9763d6523c Add React Developer Tools to the development Electron app (#1305) 2019-12-08 14:06:36 +00:00
Florian Rival
bfd6b1c542 Fix links not opening in a new tab in Changelog 2019-12-07 18:03:27 +00:00
Florian Rival
1d860ba852 Merge pull request #1293 from 4ian/feature/rework-textfield-and-layout
Rework text field styling, some buttons and some layouts.
2019-12-07 17:51:24 +00:00
Florian Rival
20ad0d5e75 Impove layout in Physics2Editor
* Add standardized spacing.
* Don't display the image preview if no resource is selected.
* Fix iResponsiveLineStackLayout with null children.

Also add various missing Trans markers.
2019-12-07 16:57:44 +00:00
Florian Rival
4c02468aa4 Improve ShapePainterEditor layout 2019-12-07 13:24:12 +00:00
Florian Rival
00f5cc2e54 Fix SelectField/SemiControlledAutoComplete styling when no label 2019-12-07 13:11:52 +00:00
Florian Rival
44be843ea6 Use "dense" text field without labels as possible 2019-12-07 01:15:46 +00:00
Florian Rival
24cbe2caca Fix "Add an animation" button height 2019-12-07 00:26:13 +00:00
Florian Rival
367e25707b Add title to ScenePropertiesDialog 2019-12-07 00:14:52 +00:00
Florian Rival
852620fcfd Fix extra space in color field of TextEditor toolbar 2019-12-07 00:10:39 +00:00
Florian Rival
4d229f32fa Use fields without margins when displayed in InlinePopover 2019-12-06 23:54:53 +00:00
Florian Rival
3af0cd862f Use ResponsiveLineStackLayout in EventsFunctionPropertiesEditor 2019-12-06 23:54:53 +00:00
Florian Rival
0cf7c6748e Add missing translation marker 2019-12-06 23:54:53 +00:00
Florian Rival
411f05720f Add missing margin between fields in PreferencesDialog 2019-12-06 23:54:52 +00:00
Florian Rival
722278fe21 Use ResponsiveLineStackLayout for fields that are shown in 2 columns 2019-12-06 23:54:52 +00:00
Florian Rival
bd8db5f573 Rework text field styling and some layouts
* Change the default text fields to use filled version
* Text fields embedded in InlinePopover or MiniToolbar keep their previous appearance with a single line.
* Add Layout.js with some predefined layouts
* Improved EventsFunctionPropertiesEditor fields
2019-12-06 23:54:52 +00:00
Florian Rival
d0247c7df3 Remove skeletal-animation-demo 2019-12-06 23:00:49 +00:00
Florian Rival
dcd1cfc789 Add Geometry Monster starter 2019-12-06 22:59:55 +00:00
Bouh
bf4c2a426e Add button to remove the font in the text editor (#1308) 2019-12-05 19:13:39 +00:00
Bouh
e10f3ca277 Fix outline color when drawing line with Shape Painter (#1313) 2019-12-05 08:23:01 +00:00
Florian Rival
16e9e8fa64 Fix formatting 2019-12-04 08:54:38 +00:00
Florian Rival
ef1ead6c90 Add link to Geometry Monster tutorial 2019-12-03 23:38:12 +00:00
Florian Rival
5449bc1ba4 Add watcher to automatically import changes to GDJS/extensions in newIDE (#1307)
* Add watcher to automatically import changes to GDJS/extensions in newIDE

  This watches for changes in GDJS or extensions and automatically run the "import-GDJS-Runtime.js" script.

  This allows for faster iterations when working on GDJS or JS extensions: do some changes, verify that the console says that changes were imported, launch the preview

* Adds an option in preferences to deactivate this in case of issues.

* Also move BrowserS3GDJSFinder and LocalGDJSFinder in newIDE/app/src/GameEngineFinder.

* Update READMEs to mention automatic import of GDJS and extensions runtime sources
2019-12-02 22:18:36 +00:00
Florian Rival
656f05e81e Fix error in SelectField when there is no value 2019-12-02 20:01:33 +00:00
Florian Rival
c4ba5f43fb Add test game for audio being played then immediately checked if stopped 2019-12-01 21:43:35 +00:00
Florian Rival
41a275ca8e Run Prettier on howler-sound-manager and update copyright 2019-12-01 21:28:33 +00:00
Florian Rival
2c9ec0d866 Fix sound/music restarted when play called with the sound/music not paused 2019-12-01 21:27:53 +00:00
Florian Rival
cd32e51134 Fix status of sound/music considered as stopped after play() was just called 2019-12-01 21:18:05 +00:00
Florian Rival
b1d81d4836 Fix project property "Folder Project"/"Single file" that could not be changed 2019-12-01 14:49:22 +00:00
Florian Rival
3fa3e57b24 Fix Sprite getWidth/getHeight returning old values just after changing animation or frame.
Fix verified in the new test game: the width/height reported is always synced with the animation.
2019-11-29 21:00:23 +00:00
Florian Rival
a4a436b935 Add test games checking for proper working of points position after animation change 2019-11-29 21:00:23 +00:00
Florian Rival
f4224c4322 Fix points position not valid immediately after changing animation/direction/frame of a Sprite.
This was because animation/direction/frame changes are not immediately applied. Instead, the current frame is marked as dirty. Verify if the frame is dirty and properly apply the changes when getting points position.

Example games in next commit.
2019-11-29 21:00:23 +00:00
Florian Rival
af21bc5aea Add command in GDJS to run TypeScript to type check the game engine 2019-11-28 23:01:43 +00:00
Bouh
af64a7d1ca Fix menu still showing as loading after startup on some computers (#1301)
Fix #1294
2019-11-28 18:24:09 +00:00
Todor Imreorov
62ed3d56c4 Update Piskel version (#1289)
* Color index shift brush (useful for cell shading sprites)
* Ability to source all layers when using the bucket tool
* Palette transfer tool (apply the currently selected palette colors to the frame you are on)
2019-11-25 23:49:48 +01:00
Florian Rival
0cfe95ab3e Display actual hash of a third party folder if not the same as the one expected 2019-11-25 23:48:29 +01:00
Florian Rival
e6dba8332e Merge branch 'master' of github.com:4ian/GDevelop 2019-11-25 23:41:01 +01:00
Todor Imreorov
00cd8df672 Remember DevTools open state in previews (#1296) 2019-11-25 11:57:20 +01:00
Florian Rival
940fe63e16 Fix autosave being loaded but not saved as the original file 2019-11-24 19:29:38 +01:00
Florian Rival
59b6694316 Fix display of unicode operators in RelationalOperatorField 2019-11-24 17:26:17 +01:00
Florian Rival
0883c86d0e Fix text diplay in AboutDialog 2019-11-21 20:05:09 +00:00
Florian Rival
3c6f490344 Bump newIDE version 2019-11-21 19:59:32 +00:00
Florian Rival
5e3e0db9f3 Re-validate expression for errors after inserting a function from expression selector 2019-11-21 12:22:28 +00:00
Florian Rival
bfee61e9e8 Remove forgotten console.logs 2019-11-20 23:00:41 +00:00
Florian Rival
097dd779f6 Fix scrollbar position in InlinePopover 2019-11-20 22:57:19 +00:00
Florian Rival
52453b7c58 Allow scroll in InlinePopover if content is too large 2019-11-20 22:30:28 +00:00
Florian Rival
dd2ac68dab Fix color of ForEach/While/Repeat events header texts for the dark theme 2019-11-20 22:18:20 +00:00
Florian Rival
6ea50f8114 Fix InlinePopover hidden behind Mosaic windows 2019-11-20 22:17:32 +00:00
Florian Rival
ec05ec6ea1 Fix duplicate React keys when searching for expressions 2019-11-20 21:45:43 +00:00
Florian Rival
3e7c5394ce Fix search bar not working in ExpressionSelector
This is because the Modal was stealing the focus.
"Fix" the issue by disabling the portal, so that the expression selector is inside the modal. The selector can not go outside of the modal anymore though, but it's less of a problem.
2019-11-20 21:07:59 +00:00
Florian Rival
55aae059ff Fix shortcuts issues with KeyboardShortcuts
* Ensure keyboard events are restricted to the EventsSheet by listening only for key presses and other events from the EventsSheet container. No listener on the document with a "focused" boolean that can be not synchronized with the real element having the focus (happened in the previous implementation if you scroll too much in EventsSheet or delete the selection: the blur() method of KeyboardShortcuts would never be called).
  * Additional care must be done when an element is removed (which can happen after deletion of the selection or scroll) to put back the focus to the container div (see _ensureFocus). Otherwise we lose the focus so keyboard shortcuts don't work anymore until you click again on some elemnt (which would not be a huge bug by itself).
* Improve shortcuts for drag'n'drop by properly listening to modifier keys when something is dragged.
2019-11-20 19:52:16 +00:00
Florian Rival
61a20f1af8 Add version number with git hash in Sentry error reporting 2019-11-17 14:58:20 +00:00
Florian Rival
60170609e9 Fix internal example loading from url on web-app 2019-11-17 13:17:27 +00:00
Florian Rival
0c55b3b8d4 Fix VariableField button position, warning and BackgroundColorRow size 2019-11-17 13:01:31 +00:00
Florian Rival
6b16934184 Fix drag'n'drop of points/polygons with touch 2019-11-16 17:42:28 +00:00
Florian Rival
c15acdde83 Hide external editor button in DirectionTools on small screens 2019-11-16 16:40:01 +00:00
Florian Rival
209dafc269 Disable overscroll in browsers 2019-11-16 00:05:01 +00:00
Florian Rival
757c9502ef Mark scripts in exported game to be loaded with cross-origin enabled 2019-11-15 23:48:56 +00:00
Florian Rival
be8ab96c4c Rework tabs and mosaic window titlebars to use same font, not uppercased and same button size 2019-11-15 22:47:54 +00:00
Florian Rival
1600091249 Fix inconsistent font for "Instance Variables" in InstancePropertiesEditor 2019-11-15 22:47:04 +00:00
Florian Rival
45b6caa06e Fix optimal system font not always being used on Windows/Linux 2019-11-15 21:56:17 +00:00
Florian Rival
59754d1c27 Call make-version-metadata after install 2019-11-14 23:51:26 +00:00
Florian Rival
656881a11a Display alert if trying to export from web-app while offline 2019-11-14 23:51:26 +00:00
Florian Rival
5e91bdf811 Update BrowserResourceSources library 2019-11-14 23:51:26 +00:00
Florian Rival
e6d0f6ed4c Update title bar color according to the theme when installed as PWA 2019-11-14 23:51:26 +00:00
Florian Rival
864acd3988 Add error explanation dialog for BrowserS3PreviewLauncher 2019-11-14 23:51:26 +00:00
Florian Rival
630ece0f7e Add service-worker for base app and assets 2019-11-14 23:51:26 +00:00
Florian Rival
3b102a74b7 Update subscription details and add missing translation markers 2019-11-14 23:51:26 +00:00
Florian Rival
5096ecee24 Expose version metadata to web-app 2019-11-14 23:51:26 +00:00
Florian Rival
29f1d873a6 Always show export button in Toolbar 2019-11-14 23:51:26 +00:00
Florian Rival
088e3acae2 Add missing browser exporters 2019-11-14 23:51:26 +00:00
Florian Rival
0fbe7a62e8 Refactor common parts of exports into GenericExports 2019-11-14 23:51:26 +00:00
Florian Rival
9e4849f218 Add BrowserOnlineWebExport 2019-11-14 23:51:26 +00:00
Florian Rival
db536894a4 Add profile button in web-app ProjectManager and remove unecessary indent 2019-11-14 23:51:26 +00:00
Florian Rival
a63c44b5ca Refactor LocalOnlineCordovaExport and LocalOnlineElectronExport to use ExporterPipeline 2019-11-14 23:51:26 +00:00
Florian Rival
8756276793 Fix include files for events based behavior methods 2019-11-14 23:51:26 +00:00
Florian Rival
878f64d024 Refactor LocalFacebookInstantGamesExport to use ExporterPipeline 2019-11-14 23:51:26 +00:00
Florian Rival
bd09c58439 Refactor some exporters to use ExportPipeline 2019-11-14 23:51:26 +00:00
Florian Rival
c645d4a2d6 Add BrowserOnlineElectronExport 2019-11-14 23:51:26 +00:00
Florian Rival
45e03d61a0 Make ExportPipeline more flexible 2019-11-14 23:51:26 +00:00
Florian Rival
d924cd90bd Add BrowserOnlineCordovaExport
* Add zip.js as an external library + typings + BrowserArchiver.
* Add BrowserFileSystem and unit tests.
* Make a first step toward having a generic ExportPipeline.
* Add BrowserOnlineCordovaExport.
* Various refactorings
2019-11-14 23:51:26 +00:00
Florian Rival
455fe8193a Remove useless function in BrowserS3FileSystem 2019-11-14 23:51:26 +00:00
Florian Rival
1eb5113283 Remove useless CopyDir from AbstractFileSystem 2019-11-14 23:51:26 +00:00
Florian Rival
3687c24e71 Enable Google Drive opening/saving in production build 2019-11-12 20:10:37 +00:00
Bouh
8d75a8373d Add "clamp" function (#1283) 2019-11-10 13:08:10 +00:00
Florian Rival
3fed377c6c Fix Travis build (update libstdc++6 version) 2019-11-10 13:03:14 +00:00
Florian Rival
957ab3f53b Fix apple-touch-icon.png 2019-11-07 23:20:56 +00:00
Florian Rival
e7e338805b Update favion/home screen icons 2019-11-07 22:57:06 +00:00
Florian Rival
83d0c4cc65 Fix ResourceSelector not showing the proper file when choosing Cancel when asked if the resource must be copied in project directory 2019-11-06 21:46:57 +00:00
Florian Rival
13461429f2 Separate login and account creation in two different dialogs 2019-11-05 23:44:38 +00:00
Florian Rival
b0fd36daff Add registerModule and requireModule to ObjectsRenderingService, to bundle third party libraries 2019-11-03 22:40:31 +00:00
Florian Rival
2e84b2e648 Fix typo 2019-10-29 19:30:29 +01:00
Bouh
2b4d263410 Fix selection dismissed when panning + some icons and descriptions (#1269) 2019-10-29 18:28:22 +00:00
dependabot[bot]
64343d7043 Bump axios from 0.16.2 to 0.18.1 in /newIDE/app (#1272)
Bumps [axios](https://github.com/axios/axios) from 0.16.2 to 0.18.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.18.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.16.2...v0.18.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-28 23:00:22 +00:00
Florian Rival
3e9c4d594c Improve typing of PreviewLauncher 2019-10-28 22:43:31 +00:00
Florian Rival
657ca85324 Refactor findGDJS and add flow typings 2019-10-27 21:32:42 +00:00
Florian Rival
6ac9c0a0ee Avoid watching old builds that are still somehow in pending state 2019-10-27 16:49:52 +00:00
Florian Rival
009a05e021 Bump newIDE version 2019-10-26 18:09:56 +01:00
Florian Rival
6c2ea56da6 Fix example opening 2019-10-26 18:09:27 +01:00
Florian Rival
a8ae298194 Bump newIDE version 2019-10-26 17:22:23 +01:00
Florian Rival
dda2effad0 Update translations 2019-10-26 17:19:58 +01:00
Florian Rival
4da0149b6b Merge pull request #1249 from 4ian/fix/use-center-point-as-flip-center
Use center point as flip center
2019-10-26 16:19:13 +01:00
Florian Rival
9602caaccc Add checks for origin and center point position after flipping/rotating/scaling sprite 2019-10-26 15:10:38 +01:00
Florian Rival
be2892b759 Fix hitboxes after flipX/flipY is called and add tests for sprite hitboxes/center/points 2019-10-26 14:15:14 +01:00
Florian Rival
e1552f649c Check that custom hitboxes are properly working with flipped, non default center, sprites 2019-10-26 11:11:43 +01:00
Florian Rival
74bbd45265 Add test game to check the proper rotation/flip of a sprite with a non default center 2019-10-26 11:04:23 +01:00
Florian Rival
5aa32f0eca Fix movesTowardTest and turnedTowardTest to properly use object center (in case origin is not 0;0) 2019-10-26 11:04:23 +01:00
Florian Rival
23987f63c7 Change sprites flipping to use the center point as center for flipping
As flipping can be considered as a way to "rotate" things, it makes sense to have it used as a center for flipping too.i
This is useful for objects that are moving and can be flipped according to if they are going left or right: they can now
be rotated and flipped "properly" (properly means that the center will stay at the same position when flipped/rotated)

This means that the center point won't move when the sprite is rotated or flipped.
The origin is still used as the point not moving in case of scaling. (this make sense because scaling is about the size, and origin about positioning)
This remove some calculations in the renderers, but add others in getDrawableX/Y and getCenterX/Y in case of flipping.
2019-10-26 11:04:23 +01:00
Bouh
d415d3cbee Add explicit mention to "scene" variable in various descriptions (#1268) 2019-10-26 10:43:48 +01:00
Bouh
f7f32d6be5 Add support for advanced shapes in Shape Painter (#1258) 2019-10-25 11:50:00 +01:00
Florian Rival
b223175bf9 Fix typo 2019-10-24 21:24:17 +01:00
Florian Rival
47463e6ca2 Rename Archiver to LocalArchiver 2019-10-24 19:02:35 +01:00
Florian Rival
e289059d1f Add LoadScript to fetch additional scripts in the IDE 2019-10-24 17:53:14 +01:00
Florian Rival
6320d19043 Ensure DirectionTools show button for an external image editor 2019-10-23 08:21:44 +01:00
Bouh
833ae75632 Add error messages when opening a non GD5 file (#1260) 2019-10-23 09:14:44 +02:00
Bouh
6058a4b1be Position comments and groups on top of selection when added (#1259) 2019-10-22 21:08:15 +02:00
Wend1go
52173ac07d Add "Scene just resumed" condition (#1262) 2019-10-21 17:47:36 +01:00
Florian Rival
a55ad60038 Update GDevelop screenshot in newIDE readme with a newer one 2019-10-19 16:49:54 +01:00
Florian Rival
0ef235261f Update newIDE README to explain how to test cloud storage providers in development 2019-10-19 16:48:21 +01:00
Florian Rival
3bd9432b5e Merge pull request #1257 from 4ian/feature/cloud-storage
Google Drive storage for loading/saving projects on web-app (only in development for now)
2019-10-19 16:24:28 +01:00
Florian Rival
6fd82f4f30 Disable Google Drive in non dev builds while waiting for validation 2019-10-19 16:19:56 +01:00
Florian Rival
5572536bff Update message for opening failures of a local file 2019-10-19 15:58:22 +01:00
Florian Rival
ab197464cc Update message in case of opening failure 2019-10-19 15:47:40 +01:00
Bouh
00f2873f48 Add ellipse, rounded rectangle and star to Shape Painter (#1256) 2019-10-19 14:38:37 +01:00
Florian Rival
c4329cac4f Use production Google Drive API keys when not in development 2019-10-19 14:35:44 +01:00
Florian Rival
4917c7ca2a Add translation for storage provider names 2019-10-19 13:19:31 +01:00
Florian Rival
13c0c8992b Handle links to open a game directly from Google Drive (from 'Open With' context menu) 2019-10-19 00:56:39 +01:00
Florian Rival
06dc0c5323 Fix 'Download Game File' removing all spaces from downloaded game file 2019-10-17 20:04:56 +01:00
Florian Rival
6039342fcf Add error message when project opening fails 2019-10-16 23:20:29 +01:00
Florian Rival
32c98d4dd4 Add disabled icons for Dropbox and OneDrive storages 2019-10-16 22:11:16 +01:00
Florian Rival
3a05067ea2 Fix typo in Facebook Instant Games actions 2019-10-16 21:22:43 +01:00
Florian Rival
d9bcf3daca Fix GDevelop.js build on Windows when Visual Studio is installed and update README 2019-10-16 21:11:48 +01:00
Florian Rival
29577af049 Merge branch 'master' of https://github.com/4ian/GDevelop 2019-10-16 08:55:55 +01:00
Florian Rival
5b5f213bd5 Fix GDevelop.js compilation on Windows
Need MinGW to be installed (to have mingw32-make)
and CMake in Program Files (or Program Files (x86))
2019-10-16 08:51:09 +01:00
Florian Rival
19a00ce2dc Add GoogleDriveStorageProvider and project storage refactorings
* Add GoogleDriveStorageProvider
* Add DownloadFileStorageProvider
* Add dialogs to choose between providers when opening/saving as
2019-10-15 21:33:33 +01:00
Florian Rival
c243200370 Fix documentation links and .gitignore 2019-10-13 23:45:55 +01:00
Florian Rival
09edeaa96b Add documentation about GDevelop architecture and remove outdated Core documentation 2019-10-13 23:19:08 +01:00
Florian Rival
c94964b2db Fix typo 2019-10-13 19:43:21 +01:00
Bouh
b40f51e03c Add menu item to extract events into a new group (#1254) 2019-10-13 13:26:28 +01:00
Florian Rival
4319ddcd0f Refactor project opening/saving into ProjectsStorage with StorageProviders. 2019-10-09 23:25:19 +01:00
Florian Rival
f0163fc1d1 Disable SkeletonObject extension
See https://github.com/4ian/GDevelop/issues/1242 for issues
2019-10-08 22:45:25 +01:00
Florian Rival
f5be2c73ce Add story for PropertiesEditor 2019-10-07 23:02:00 +01:00
Florian Rival
a4b0f316f1 Display all PropertiesEditor fields in column layout when on a small screen 2019-10-07 23:02:00 +01:00
Florian Rival
efa9ea4ea2 Only display a single secondary editor in SceneEditor when screen is small 2019-10-07 23:02:00 +01:00
Florian Rival
5170ff509c Add EditorNavigator to navigate between editors on small screens 2019-10-07 23:02:00 +01:00
Florian Rival
2471021114 Improve EditorMosaic to allow proper display on smaller screens 2019-10-07 23:02:00 +01:00
Florian Rival
3f956289dc Fix crash in ObjectGroupsList (still using react-sortable-hoc)
This should be converted to use SortableVirtualizedItemList in the future.
2019-10-06 22:51:01 +01:00
Florian Rival
f731ae8b21 Make pinch to move/zoom accessible even when touching instances on InstancesEditor 2019-10-06 19:39:52 +01:00
Florian Rival
76dfe55e25 Autogenerate the file to import examples for web-app 2019-10-06 18:09:04 +01:00
Florian Rival
22d46711df Add button in intro dialog and menu item to activate Fullscreen 2019-10-05 18:01:27 +01:00
Florian Rival
ca5198a08d Use Material icons for Project Manager item icons 2019-10-05 17:23:07 +01:00
Florian Rival
037551ec77 Fix some icon colors and text rendering 2019-10-05 17:22:26 +01:00
Florian Rival
3c309c200f Re-open closed mosaic window editors at the proper default position 2019-10-05 17:22:06 +01:00
Florian Rival
6231cbc5f1 Fix ItemRow adding extra space for non existing icon 2019-10-05 16:07:50 +01:00
Florian Rival
9e98605030 Fix formatting 2019-10-05 15:53:27 +01:00
Florian Rival
6834efbb9a Add export icon from MainFrame Toolbar for small screens 2019-10-05 15:08:17 +01:00
Florian Rival
c74fbc6c43 Force using the new instruction editor on small devices 2019-10-05 15:01:01 +01:00
Florian Rival
949f370118 Hide scrollbars on touchscreens 2019-10-03 23:10:13 +01:00
Florian Rival
91359e4674 Fix formatting 2019-10-03 23:10:13 +01:00
Florian Rival
f790fff439 Set touch-action to none to avoid zoom on double tap 2019-10-03 23:10:13 +01:00
Florian Rival
e13384dbb7 Add PinchHandler to navigate in the scene with two touches 2019-10-03 23:10:13 +01:00
Florian Rival
0c0539bb2a Round new instance positions 2019-10-03 23:10:13 +01:00
Florian Rival
caf089489b Drag objects from their icons only when on a touch screen 2019-10-03 23:10:13 +01:00
Florian Rival
18c0f05bb3 Update package-lock.json 2019-10-03 23:10:13 +01:00
Florian Rival
a9a0faaaaa Detect touch screens by using touch events 2019-10-03 23:10:13 +01:00
Florian Rival
df166e4d5d On touch screens, allow instruction edition by touching them again when selected 2019-10-03 23:10:13 +01:00
Florian Rival
f661b41d74 Render actions below conditions for small screens 2019-10-03 23:10:13 +01:00
Florian Rival
00e887e5f9 Add InfoBar for editing instruction and add alternate messages for touch screens 2019-10-03 23:10:13 +01:00
Florian Rival
e6fc7aac9d Fix formatting 2019-10-03 23:10:13 +01:00
Florian Rival
65346edc4a Fix undo not possible after dragging an instance of an object 2019-10-03 23:10:13 +01:00
Florian Rival
3620e0aef6 Make InstancesEditor buttons bigger on touch screens 2019-10-03 23:10:13 +01:00
Florian Rival
0211ab40e4 Hide context menu icons in SortableVirtualizedItemList for small screens 2019-10-03 23:10:13 +01:00
Florian Rival
ae945fc0bc Use react-dnd-multi-backend to automatically support touch screens for drag'n'drop 2019-10-03 23:10:13 +01:00
Florian Rival
4537dbf723 Add logging for startup times 2019-10-03 23:10:13 +01:00
Florian Rival
94358cd265 Rename TemporaryInstances to InstancesAdder and use it in SceneEditor
There is a unique place where the logic to create instances is now.
2019-10-03 23:10:13 +01:00
Florian Rival
337a36ee90 Refactor drag'n'drop in SortableVirtualizedItemListi and objects drag to scene
* Use a drop indicator and remove react-sortable-hoc
* Drag to scene can now follow the cursor with a temporary instance, to get an immediate feedback/preview of the object

TODO: Refactor the instance creation to use TemporaryInstances
2019-10-03 23:10:13 +01:00
Florian Rival
07fd7f77bd Refactor InstructionsList to use DropTarget 2019-10-03 23:10:13 +01:00
Florian Rival
69a774bec1 Move Utils/DragDropHelpers into UI/DragAndDrop 2019-10-03 23:10:13 +01:00
Florian Rival
dc9200feb5 Refactor Instruction to use DragSourceAndDropTarget
* Fix error "Expected to find a valid target" when dragging instructions on top of a subinstructions list (react-dnd does not support well having a drop target/source inside another in these conditions it seems)
2019-10-03 23:10:13 +01:00
Florian Rival
e7d1f99470 Use SortableVirtualizedItemList for ObjectsList implementation 2019-10-03 23:10:13 +01:00
Bouh
76165908fc Add help button for Video object (#1244) 2019-10-02 00:20:21 +02:00
Florian Rival
b19a27e6cf Add info message about the difference between physics shape and collision masks 2019-09-29 14:33:28 +01:00
1112 changed files with 105289 additions and 37511 deletions

View File

@@ -19,7 +19,7 @@ jobs:
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install sdk-1.37.37-64bit && ./emsdk activate sdk-1.37.37-64bit && cd ..
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
- run:
name: Install Wine for Electron builder
@@ -60,7 +60,7 @@ jobs:
# Build GDevelop IDE
- run:
name: Build GDevelop IDE
command: cd newIDE/electron-app && npm run build -- --mac --win --linux tar.gz --publish=never
command: cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
- run:
name: Clean dist folder to keep only installers/binaries.

8
.gitignore vendored
View File

@@ -1,8 +1,5 @@
/Core/GDCore/Tools/VersionPriv.h
/docs/GDJS Runtime Documentation
/docs/GDJS Documentation
/docs/GDCpp Documentation
/docs/GDCore Documentation
/docs
/ExtLibs/SFML
/ExtLibs/*.7z
/scripts/logs/*.txt
@@ -47,9 +44,6 @@
/Binaries/**/JsPlatform/*.dll.a
/Binaries/Output/Release_Windows/newIDE
*.autosave
/Binaries/Output/libGD.js/Release
/Binaries/Output/libGD.js/Debug
/Binaries/Output/libGD.js/libGD.raw.js
!/GDCpp/scripts/bcp.exe
!/scripts/libgettextlib-0-17.dll
!/scripts/libgettextsrc-0-17.dll

View File

@@ -24,7 +24,7 @@ addons:
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/commit/$(git rev-parse HEAD)
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/latest
paths:
- Binaries/Output/libGD.js/Release
- Binaries/embuild/GDevelop.js
apt:
sources:
- ubuntu-toolchain-r-test
@@ -48,7 +48,7 @@ before_install:
#use SFML.
- "export DISPLAY=:99.0"
# This workaround is required to avoid libstdc++ errors (Emscripten requires a recent version of libstdc++)
- wget -q -O libstdc++6 http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libstdc++6_5.4.0-6ubuntu1~16.04.10_amd64.deb
- wget -q -O libstdc++6 http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libstdc++6_5.4.0-6ubuntu1~16.04.12_amd64.deb
- sudo dpkg --force-all -i libstdc++6
install:
@@ -63,8 +63,8 @@ install:
# Install Emscripten (for GDevelop.js)
- git clone https://github.com/juj/emsdk.git
- cd emsdk
- ./emsdk install sdk-1.37.37-64bit
- ./emsdk activate sdk-1.37.37-64bit
- ./emsdk install 1.39.6
- ./emsdk activate 1.39.6
- source ./emsdk_env.sh
- cd ..
# Install GDevelop.js dependencies and compile it

View File

@@ -2,6 +2,7 @@
{
"files.associations": {
"*.idl": "java",
"Fastfile": "ruby",
"iosfwd": "cpp",
"functional": "cpp",
"type_traits": "cpp",
@@ -80,7 +81,10 @@
"__hash": "cpp",
"__debug": "cpp",
"__threading_support": "cpp",
"any": "cpp"
"any": "cpp",
"array": "cpp",
"cinttypes": "cpp",
"numeric": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,

View File

@@ -51,7 +51,12 @@ file(GLOB_RECURSE formatted_source_files tests/* GDCore/Events/* GDCore/Extensio
list(REMOVE_ITEM formatted_source_files "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.h" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs_dialogs_bitmaps.cpp")
gd_add_clang_utils(GDCore "${formatted_source_files}")
add_library(GDCore SHARED ${source_files})
IF(EMSCRIPTEN)
# Emscripten treats all libraries as static libraries
add_library(GDCore STATIC ${source_files})
ELSE()
add_library(GDCore SHARED ${source_files})
ENDIF()
add_dependencies(GDCore GDVersion)
IF(EMSCRIPTEN)
set_target_properties(GDCore PROPERTIES SUFFIX ".bc")

View File

@@ -1,592 +1,25 @@
/**
* \mainpage GDevelop Core
* \image html gdlogo.png
* \section welcome Welcome
* \section welcome GDevelop Core documentation
*
* The **GDevelop Core** library contains the main concepts, classes and tools that are used by the *platforms* and the *GDevelop IDE*.<br>
* This ensures that the IDE, or any tool based on GDevelop Core, is able to work with projects based on any arbitrary platform.
*
* Two official platforms are available for GDevelop:
* - The *C++ Platform* (GDCpp) to create native games.
* - The *JS Platform* (GDJS) to create HTML5 games.
* The **GDevelop Core** library contains the structure of a GDevelop game, classes and tools that are used by the *platforms* and the *GDevelop IDE*.
*
* \section gettingstarted Getting started
* First, please refer to these pages to install the required tools and to get help about setting up a basic extension:<br>
*
* -# \subpage setupDevEnv
* -# \ref overview
* -# \ref writeANewExtension
* In most cases, you should start by <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README.md">installing and launching the development version of GDevelop</a>.
*
* You can also read \subpage recommendedToolsAndConventions.
* - If you're interested in writing extensions for GDevelop, read <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md">the documentation about extensions</a>.
* - If you want to dig more into how GDevelop is architectured and work on the core, read <a href="https://github.com/4ian/GDevelop/blob/master/Core/GDevelop-Architecture-Overview.md">GDevelop Architecture Overview</a>. Then, you can browse this reference to get more information about a class or function.
*
* \section aboutdoc About this documentation
* \section other Other documentations
*
* If you never used GDevelop Core before, take a look at \ref overview.
*
* As everything that is developed around GDevelop is based on this library, you should take a look at it quite often: platforms, extensions
* and the IDE are intensively using the classes and tools offered by GDCore.
* When developing an extension for the C++ or JS platform, read these documentations:
* GDevelop is architectured around a `Core` (this library), platforms (`GDJS`, `GDCpp`) and extensions (`Extensions` folder). The editor (`newIDE` folder) is using all of these libraries.
*
* - [Open GDevelop C++ Platform documentation](../GDCpp Documentation/index.html)
* - [Open GDevelop JS Platform documentation](../GDJS Documentation/index.html)
*
*/
/**
* \page setupDevEnv Setting up the development environment
*
* If you didn't already downloaded GDevelop, get and extract the source from [GitHub](https://github.com/4ian/GD).
*
* Follow these steps to be able to compile GDevelop, it's super easy:
*
* <b>Windows</b>
*
* -# \subpage installWinCompiler
* -# \ref installAndUseCMake
* <br>
*
* <b>GNU/Linux</b>
* -# \subpage installLinuxLib
* -# \subpage installAndUseCMake
*
* <b>OS X</b>
* -# \subpage installMacOSTools
* -# \ref installAndUseCMake
*
* See the recommended tools and conventions for working on GDevelop on this page:
* \subpage recommendedToolsAndConventions
*/
/**
* \page installWinCompiler (Windows) Install TDM-GCC compiler
*
* GDevelop is compiled with TDM-GCC under Windows.<br>
* So as to prevent incompatibilities between the compiler (and the standard C++ library provided with) used by GDevelop and
* the compiler used by the extensions, GDevelop require the extensions and the platforms to use the same version of TDM-GCC.
*
* While a recent GCC version should work, if you compile GDevelop for an "official" distribution it's better
* to use the specific version provided here.
*
* \section installWinCompiler_download Download
*
* Download the current version of the compiler used by GDevelop on Windows here:
*
* https://sourceforge.net/projects/tdm-gcc/files/TDM-GCC%20Installer/Previous/1.1309.0/tdm-gcc-4.9.2.exe/download
*
* \section installWinCompiler_install Installation
*
* The installation is fairly simple :<br>
* <br>
* - Launch the installer.<br>
* - Choose Create.<br>
\image html compilerInstall1.png
* - Choose an installation directory.<br>
\image html compilerInstall2.png
* - Choose the components to be installed. You don't have to change anything, the default options are good enough.<br>
\image html compilerInstall3.png
* - Click on install so as to launch the installation process.<br>
*/
/**
* \page installAndUseCMake (All) Install CMake & launch the build
*
* Building is done using CMake: it is an open-source build system that can generate build files for lots of IDE and build tools (Makefiles...).
*
* \section installAndUseCMake_download Download and install CMake
*
* First, install CMake:
* Download it [here](http://www.cmake.org/cmake/resources/software.html) for Windows, get it using your package manager if you're
* using a Linux distribution or using Homebrew for Mac OS X.
*
* \section installAndUseCMake_use Using CMake to generate the build files
* Using CMake is not difficult and require only a few clicks/commands to enter. Windows users may use
* the GUI as shown in the next section. Linux and Mac OS X users may prefer to use the command line as shown
* as the end of this page.
*
* \subsection installAndUseCMake_use_gui Using the GUI
*
* - Start the CMake user interface (_cmake-gui_). Choose the GD root directory as the source directory, and Binaries/build as the directory where to build the binaries:
\image html usecmake1.png
* - Click on *Configure*. If asked to create the build directory, answer yes. Choose then your favorite generator: *MinGW Makefiles* (on Windows) or *Unix Makefiles* (on Linux/OS X) generate a traditional Makefile that can be built using the
* *mingw32-make* (on Windows) or *make* (on Linux/OS X) command. You can also choose the *Ninja* generator to use the [Ninja build system](http://martine.github.io/ninja/).
\image html usecmake2.png
* - When you click on Finish, CMake do a first configuration. If **errors occurred*, make sure that you have download all required development libraries.
* - Adjust any variable if necessary (no changes is needed by default), then click on Generate.
\image html usecmake3.png
* - You can then launch a terminal/command prompt, go to the build folder (`cd path/to/GD/Binaries/build`) and launch the build
* using the generator you've choosen: `mingw32-make`, or `make` on Linux/OS X.
*
* \subsection installAndUseCMake_use_cmd Using the command line
*
* Using the commandline with CMake is also easy:
*
* ~~~~~~~~~~~~~~~~~~~~~
* cd /path/to/GD/Binaries
* mkdir build
* cd build
* cmake ../..
* make
* ~~~~~~~~~~~~~~~~~~~~~
*
* For Windows, replace `cmake ../..` by `cmake ../.. -G "MinGW Makefiles"` and `make` by `mingw32-make`.
*
* or using the fast [Ninja build system](http://martine.github.io/ninja/) :
* ~~~~~~~~~~~~~~~~~~~~~
* cd /path/to/GD/Binaries
* mkdir build
* cd build
* cmake ../.. -G "Ninja"
* ninja
* ~~~~~~~~~~~~~~~~~~~~~
*
* \section installAndUseCMake_launch Launch GDevelop
*
* Binaries are created into *Binaries/Output/Release_{OS}* folder.
*
* To launch GDevelop in Windows, double click on **GDIDE**. For Linux, launch **StartGDevelop.sh**.
*/
/**
* \page installLinuxLib (Linux) Install development files
*
* \section installLibs Install development libraries
*
* GDevelop is compiled with gcc under Linux.<br>
* You need to have some packages to be installed before starting to build GD. These packages can vary according to the distribution you use.
* On Ubuntu, you may want to install these packages:
\code
sudo apt-get install libopenal-dev
sudo apt-get install libjpeg-dev
sudo apt-get install libglew-dev
sudo apt-get install libudev-dev
sudo apt-get install libxrandr-dev
sudo apt-get install libsndfile1-dev
sudo apt-get install libglu1-mesa-dev
sudo apt-get install libfreetype6-dev
\endcode
* Make sure you also have some basic tools installed:
\code
sudo apt-get install build-essential
sudo apt-get install p7zip-full
sudo apt-get install curl
\endcode
*
* If you want to package the app, you can also install:
\code
sudo apt-get install devscripts
\endcode
*
*
* \subsection installcmake Install CMake
* You'll need CMake to build GDevelop: See more on \subpage installAndUseCMake.
*/
/**
* \page installMacOSTools (Mac OS X) Install development tools
*
* Make sure that you have Apple Developer Tools installed (if you have Xcode and git, that should be the case).
*
* \section installTools Install development tools
*
* The simplest way of installing dependencies required by GDevelop is to use [Homebrew](http://brew.sh/). Install it
* and install these packages, using the terminal:
\code
brew install cmake
brew install p7zip
brew install pkgconfig
brew install freetype
\endcode
* If you want to generate the documentation and translations, install Doxygen and Gettext:
\code
brew install doxygen
brew install gettext
\endcode
*
* \section launchCompilation Launch compilation
*
* You should be able to compile GD using CMake. Go with a terminal to the GD source folder:
\code
cd /path/to/GD
mkdir build && cd build
cmake ../..
make -j4
\endcode
*
* More information about compilation here: \ref installAndUseCMake
*/
/**
* \page recommendedToolsAndConventions Recommended tools and advices to work with GD
*
* \section git Git and GitHub
*
* Git is an amazing *version control system*. If you never used it before, take a look at some tutorials, there
* are plenty of them on the internet.<br>
* Windows users could be interested in using [TortoiseGit](code.google.com/p/tortoisegit) or the official
* [GitHub client](https://windows.github.com/).
*
* \subsection pullrequest Submitting code thanks to Pull Request.
*
* Using *Pull request*, you can easily submit your changes so that they are integrated into the official
* GDevelop repository (http://github.com/4ian/gd).
*
* See this article on *GitHub help* about pull requests: https://help.github.com/articles/using-pull-requests.<br>
* Pull requests are extremely easy to use and the best way to contribute to GD!
*
* ------
*
* \section codingstyle Coding style
*
* As a rule of thumb, try to retain the original coding style used in a file when editing it, or look at other
* files when creating a new extension/dialog/feature/class.
*
* For both C++ and Javascript, *code indentation* should be 4 spaces (or tab set to a width of 4 spaces).<br>
* Lines should be cutted when reaching column 110 so that two files can be displayed side-by-side on a same screen.
* When cutting a line, indent the new lines with an additional 4 spaces.
*
* \subsection cpp C++
*
* *Naming* conventions:
* - Classes should be *CamelCase* (starting with a capital).
* - All variables (including member variables) should be *camelCase* (no capital for the first letter).
* - All functions (including class methods) should be *CamelCase* (starting with a capital).
*
* *Comments*:
* - Comment your classes and functions using *Doxygen* comments.
*
* \subsection js Javascript
*
* *Naming* conventions:
* - "Classes" should be *CamelCase* (starting with a capital).
* - All variables (including member variables) should be *camelCase* (no capital for the first letter).
* - All functions (including class methods) should be *camelCase* (no capital for the first letter).
*
* *Comments*:
* - Comment your classes and functions using *yuidoc* comments.
*/
/**
* \page overview Overview of GDCore
*
* \section platformstructure Structure of a platform
*
* A platform for GDevelop Core is a class inheriting from gd::Platform.<br>
* They contains the extensions of the platform (see below) and offer various methods, like gd::Platform::GetProjectExporters which
* is called by the IDE to export a gd::Project to a stand-alone game.
*
* \subsection platformloading Platforms loading
* A platform is stored in memory and managed by gd::PlatformManager. It loaded from a dynamic library file (.dll on windows, .so on Linux)
* thanks to gd::PlatformLoader.<br>
* It is responsibility of the IDE, or any other application using GDCore,
* to call the appropriate method of gd::PlatformLoader to trigger the loading of the platforms when needed.
*
* gd::PlatformLoader search for two symbols in the dynamic library file: *CreateGDPlatform* and
* *DestroyGDPlatform*. These symbols must exists and must create (or destroy) the platform class. For example:
*
* ~~~~~~~~~~~~~~~~~~~~~
* extern "C" gd::Platform * GD_API CreateGDPlatform() {
* return &JsPlatform::Get(); //Return the singleton object representing the JS Platform
* }
*
* extern "C" void GD_API DestroyGDPlatform() {
* JsPlatform::DestroySingleton(); //Destroy the singleton.
* }
* ~~~~~~~~~~~~~~~~~~~~~
*
* The platform dynamic library file is often located inside <i>GDevelop directory</i>/xxxPlatform (*xxx* being replaced by the platform acronym).
*
* In this folder, the platform can store basically anything it needs. For example, both GDJS and GDCpp are storing a folder called *Runtime* containing
* the game engine.
* If there is a sub directory called *Extensions*, the gd::PlatformLoader tries to load the extensions contained inside (see below).
*
* \section extensionsstructure Structure of an extension
*
* **Extensions** are seen by GDevelop Core as classes inheriting from gd::PlatformExtension.<br>
* They are stored inside the platform they belong to, and they are also loaded from a dynamic library file thanks to gd::ExtensionsLoader. The main
* job of an extension is to <b>declare</b> everything it provides: objects, actions and conditions, behaviors, expressions.<br>
* This is done directly using the standard method provided by gd::PlatformExtension, notably:
* - gd::PlatformExtension::AddCondition and gd::PlatformExtension::AddAction,
* - gd::PlatformExtension::AddExpression (and gd::PlatformExtension::AddStrExpression),
* - gd::PlatformExtension::AddObject and gd::PlatformExtension::AddBehavior
*
*
* Some platforms (like the C++ Platform) offer another base class which must be used instead of gd::PlatformExtension when declaring a platorm: as this base class
* inherits from gd::PlatformExtension, standard methods still work, but you may be able to declare some others features (the C++ Platform offers
* the possibility of declaring debugger related functions).
*
* \subsection extensionloading Extensions loading
*
* A single dynamic library file can contains an extension for more than one platform:<br>
* You just have to declare a class deriving from gd::PlatformExtension for each platform supported, and a creation function for each platform
* (the C++ platform expects a function called *CreateGDExtension* while JS Platform search for a function called *CreateGDJSExtension*).
*
* \subsection extensionexample Edit or write a new extension
*
* Refer to these pages for more information about extensions:
* - \subpage AboutExtensionCpp
* - \subpage writeANewExtension
*
* \section utf8section UTF8 strings
*
* Most parts of the codebase support UTF8 strings thanks to gd::String class. gd::String is a wrapper around std::string, exposing a similar
* interface as well as a few tool member functions and operators that are all UTF8 aware.
*
* Its usage is easy, especially if you're familiar with std::string. Some extra functions can be really useful, in particular
* the ones to convert the string from/to a number.
*
\code
gd::String str = "Hello";
str += " world";
str += " " + gd::String::From(2);
//str now contains "Hello world 2";
gd::String twopointfiveStr = "2.5";
double twopointfive = twopointfive.To<double>();
//twopointfive == 2.5
\endcode
*
*
* For more information, see the complete reference of the class. Tests cases have also been made for most functions.
*/
/**
* \page writeANewExtension Write a new extension
*
* \section writeANewExtension_createNewExtension Create a new extension
*
* Creation of a new extension can be made by following these steps:<br>
*
* - Copy the directory of an extension and rename it:
* \image html createnew1.png
* - Rename then the sources files :
* \image html createnew2.png
* - Open the *CMakeLists.txt* file and replace every occurrence of the extension old name with the new name.<br>
* - Open all the source files and again, replace every occurrence of the extension old name with the new name.<br>
* - In the *Extensions* directory, open the *CMakeLists.txt* file and add a line such as <code>ADD_SUBDIRECTORY(MyExtension)</code>.
* You can then start to modify the extension.<br>
* If your extension is fairly simple, you can create it from the AES Extension. <br>
* If your extension need an object, you can use for instance the TextObject Extension as a starting point.<br>
* <br>
* - You can compile your extension by relaunching CMake like described [here](\ref installAndUseCMake). After doing that, just compile as usual.
*
*/
/**
* \page AboutExtensionCpp About Extension.cpp
*
* An extension has to define (usually in a file called *Extension.cpp* for the C++ Platform or *JsExtension.cpp* for the JS Platform)
* a class that is derived from the gd::PlatformExtension class. This class contains, in its constructor, the declarations
* of everything that is provided by the extension.
* \section extensionDeclaration Declare the extension information
* The declarations are made using the methods provided by gd::PlatformExtension.
*
* The first declaration if often the information about the extension:
* \code
Extension()
{
SetExtensionInformation("TextObject",
_("Text object"),
_("Extension allowing to use an object displaying a text."),
"Florian Rival",
"Open Source (MIT License)");
* \endcode
The first parameter is the name of the extension. Choose carefully the name of the extension, as projects are directly referring to it.
* \section instructionsDeclaration Declare actions, conditions and expressions
Actions are declared like this :
* \code
AddAction("ActionName",
_("Name displayed to users"),
_("Description"),
_("Sentence displayed in event editor"),
_("Category"),
"path-to-an-24-by-24-icon-file.png",
"path-to-an-16-by-16-icon-file.png")
.AddParameter("theTypeOfTheParameter", _("Parameter1"))
.AddParameter("theTypeOfTheParameter", _("Parameter2"))
.SetFunctionName("MyFunctionName").SetIncludeFile("MyExtension/MyIncludeFile.h");
* \endcode
* Declare conditions and expressions in a similar way.<br>
* Parameters are added using gd::InstructionMetadata::AddParameter.
*
* The last line set the function name that will be called when generating the code of an event using the action:<br>
* You can either do it after declaring the function, or later using this syntax:
*
* \code
GetAllActions()["ExtensionName::ActionName"].SetFunctionName("MyFunctionName");
* \endcode
*
* Both methods are ok, but the latest allows to use the same code to declare an extension for the C++ and JS platform,
* then customize the names of the functions to call.
*
* \section objectsDeclaration Declare objects
*
* Adding an object is made using gd::PlatformExtension::AddObject method.
*
* \code
gd::ObjectMetadata & obj = AddObject<MyObject>(
"Name",
_("Name displayed to users"),
_("Description"),
"path-to-a-32-by-32-icon.png");
* \endcode
*
* The *C++ platform* also requires that you call *AddRuntimeObject* to declare the RuntimeObject class associated to the object being declared:<br>
* It has two template parameters: the first one is the corresponding object class declared with *AddObject* (class inheriting from *gd::Object*) and the
* second one is the *RuntimeObject* class.
* You must pass as parameter the metadata from the object previously declared and the name of the class inheriting from RuntimeObject.
*
* You will also want to specify the .h file associated to the object using gd::ObjectMetadata::SetIncludeFile. For example:
* \code
//obj is the gd::ObjectMetadata returned when you called AddObject.
AddRuntimeObject<TextObject, RuntimeTextObject>(obj, "RuntimeTextObject");
obj.SetIncludeFile("TextObject/TextObject.h");
* \endcode
*
* You can then declare the actions, conditions, and expressions related to the objects, using the AddAction/AddCondition/AddExpression methods provided
* by <i>obj</i>.
* \section eventsDeclaration Declaring events
*
* Events are declared like this :
* \code
AddEvent("Name",
_("Name displayed to users"),
"Description",
"Group",
"path-to-a-16-by-16-icon.png",
std::make_shared<EventClassName>())
* \endcode
*
* The event must be able to generate its code when events are being translated to C++ or Javascript:<br>
* This is done by calling SetCodeGenerator. For example:
*
* \code
AddEvent("Standard",
_("Standard event"),
_("Standard event: Actions are run if conditions are fulfilled."),
"",
"res/eventaddicon.png",
std::make_shared<gd::StandardEvent>())
.SetCodeGenerator(std::shared_ptr<gd::EventMetadata::CodeGenerator>(codeGen));
* \endcode
* \section behaviorsDeclaration Declaring the behaviors
Behaviors are declared like objects:
* \code
gd::BehaviorMetadata & aut = AddBehavior("Name",
_("Name displayed to users"),
_("DefaultNameUsedInEditor"),
_("Description."),
"Group",
"path-to-a-32-by-32-icon.png",
"BehaviorClassName",
std::make_shared<BehaviorClassName>(),
std::make_shared<BehaviorSharedDataClassName>());
* \endcode
* The last line can be replaced by <code>std::shared_ptr<gd::BehaviorsSharedData>()</code> if no shared data are being used.
*
* You can then declare the actions, conditions, and expressions related to the behavior like objects:<br>
* Call AddAction/AddCondition/AddExpression on the <i>aut</i> object.
* \section excludingNonRuntimeDeclaration (C++ platform) Excluding elements declaration from runtime
* When your extension is compiled for the C++ platform Runtime, GDevelop does not known anything about action/condition or even events classes.<br>
* You have then to exclude all actions/conditions/expressions/events declaration from extension at runtime (only Extension/Object/Behaviors declarations have to be kept).
* Use the *<code>GD_IDE_ONLY</code> define* to achieve this goal, as demonstrated in this skeleton of a complete extension declaration:
* \code
class Extension : public ExtensionBase //For C++ platform, extensions must derive from ExtensionBase
{
public:
Extension()
{
SetExtensionInformation("MyExtension",
_("Extension name"),
_("Extension declaration"),
"Author",
"license");
#if defined(GD_IDE_ONLY)
AddAction(...);
AddCondition(...);
AddExpression(...);
#endif
{
gd::ObjectMetadata & obj = AddObject("ObjectName",
_("Object name"),
_("Description"),
"CppPlatform/Extensions/myicon.png",
&CreateMyObject,
&DestroyMyObject);
AddRuntimeObject(obj, "RuntimeObjectName", CreateRuntimeObjectName);
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("MyExtension/MyIncludeFile.h");
obj.AddAction(...);
obj.AddCondition(...);
obj.AddExpression(...);
#endif
}
{
gd::BehaviorMetadata & aut = AddBehavior("BehaviorName",
_("Behavior name"),
"defaultGDname",
_("Description"),
"",
"CppPlatform/Extensions/myicon.png",
"PhysicsBehavior",
std::make_shared<BehaviorClassName>(),
std::make_shared<BehaviorSharedDataClassName>());
#if defined(GD_IDE_ONLY)
behaviorInfo.SetIncludeFile("MyExtension/MyIncludeFile.h");
aut.AddAction(...);
aut.AddCondition(...);
aut.AddExpression(...);
#endif
}
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
};
virtual ~Extension() {};
};
// Used by GDevelop to create the extension class
// -- Do not need to be modified. --
extern "C" ExtensionBase * GD_EXTENSION_API CreateGDExtension() {
return new Extension;
}
* \endcode
* - <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README.md">Getting started with the editor</a>
* - <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md">Getting started with the extensions</a>
*/
/**

View File

@@ -12,6 +12,15 @@ using namespace std;
namespace gd {
vector<gd::String> CommentEvent::GetAllSearchableStrings() const {
vector<gd::String> allSearchableStrings;
allSearchableStrings.push_back(com1);
allSearchableStrings.push_back(com2); ///< Com2 is deprecated
return allSearchableStrings;
}
void CommentEvent::SerializeTo(SerializerElement &element) const {
element.AddChild("color")
.SetAttribute("r", r)

View File

@@ -7,10 +7,11 @@
#define COMMENTEVENT_H
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
namespace gd {
class Layout;
class Project;
}
} // namespace gd
namespace gd {
@@ -45,6 +46,8 @@ class GD_CORE_API CommentEvent : public gd::BaseEvent {
const gd::String& GetComment() const { return com1; }
void SetComment(const gd::String& comment) { com1 = comment; }
virtual std::vector<gd::String> GetAllSearchableStrings() const;
virtual void SerializeTo(SerializerElement& element) const;
virtual void UnserializeFrom(gd::Project& project,
const SerializerElement& element);

View File

@@ -5,10 +5,6 @@
*/
#include "ForEachEvent.h"
#include <iostream>
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Serialization/SerializerElement.h"

View File

@@ -19,6 +19,14 @@ namespace gd {
GroupEvent::GroupEvent()
: BaseEvent(), creationTime(0), colorR(74), colorG(176), colorB(228) {}
vector<gd::String> GroupEvent::GetAllSearchableStrings() const {
vector<gd::String> allSearchableStrings;
allSearchableStrings.push_back(name);
return allSearchableStrings;
}
void GroupEvent::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
element.SetAttribute("source", source);

View File

@@ -106,6 +106,8 @@ class GD_CORE_API GroupEvent : public gd::BaseEvent {
virtual const gd::EventsList& GetSubEvents() const { return events; };
virtual gd::EventsList& GetSubEvents() { return events; };
virtual std::vector<gd::String> GetAllSearchableStrings() const;
virtual void SerializeTo(SerializerElement& element) const;
virtual void UnserializeFrom(gd::Project& project,
const SerializerElement& element);

View File

@@ -5,9 +5,6 @@
*/
#include "RepeatEvent.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Serialization/SerializerElement.h"

View File

@@ -6,9 +6,6 @@
#if defined(GD_IDE_ONLY)
#include "WhileEvent.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Serialization/SerializerElement.h"

View File

@@ -0,0 +1,58 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include <iostream>
#include "EffectsCodeGenerator.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Project/Layer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
namespace gd {
void ExposeProjectEffects(
const gd::Project& project,
const std::function<void(const gd::Effect& effect)>& worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and
// WholeProjectRefactorer::ExposeProjectEvents.
// Add layouts effects
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
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);
worker(effect);
}
}
}
}
void EffectsCodeGenerator::GenerateEffectsIncludeFiles(
const gd::Platform& platform,
const gd::Project& project,
std::set<gd::String>& includeFiles) {
ExposeProjectEffects(
project, [&platform, &includeFiles](const gd::Effect& effect) {
// TODO: this browse all the extensions every time we're trying to find
// a new effect. Might be a good idea to rework MetadataProvider to be
// faster (not sure if it is a bottleneck at all though - but could be
// for events code generation).
const gd::EffectMetadata& effectMetadata =
MetadataProvider::GetEffectMetadata(platform,
effect.GetEffectType());
for (auto& includeFile : effectMetadata.GetIncludeFiles())
includeFiles.insert(includeFile);
});
}
} // namespace gd

View File

@@ -0,0 +1,35 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EffectsCodeGenerator_H
#define GDCORE_EffectsCodeGenerator_H
#include <set>
#include <utility>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Project;
class Platform;
} // namespace gd
namespace gd {
/**
* \brief Internal class used to generate code from events
*/
class GD_CORE_API EffectsCodeGenerator {
public:
/**
* \brief Add all the include files required by the project effects.
*/
static void GenerateEffectsIncludeFiles(const gd::Platform& platform,
const gd::Project& project,
std::set<gd::String>& includeFiles);
};
} // namespace gd
#endif // GDCORE_EffectsCodeGenerator_H

View File

@@ -4,8 +4,6 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/ExpressionCodeGenerator.h"
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
@@ -662,7 +660,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (argOutput.empty()) {
if (!metadata.type.empty())
cout << "Warning: Unknown type of parameter \"" << metadata.type
<< "\".";
<< "\"." << std::endl;
argOutput += "\"" + ConvertToString(parameter) + "\"";
}
}

View File

@@ -35,10 +35,6 @@ namespace gd {
* \brief Internal class used to generate code from events
*/
class GD_CORE_API EventsCodeGenerator {
// Compatiblity with old ExpressionParser
friend class CallbacksForGeneratingExpressionCode;
friend class VariableCodeGenerationCallbacks;
// end of compatibility code
friend class ExpressionCodeGenerator;
public:

View File

@@ -24,117 +24,14 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
// Compatibility with old ExpressionParser
#include "GDCore/Events/CodeGeneration/ExpressionsCodeGeneration.h"
#include "GDCore/Events/CodeGeneration/VariableParserCallbacks.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Events/Parsers/VariableParser.h"
// end of compatibility code
namespace gd {
bool ExpressionCodeGenerator::useOldExpressionParser = false;
gd::String ExpressionCodeGenerator::GenerateExpressionCode(
EventsCodeGenerator& codeGenerator,
EventsCodeGenerationContext& context,
const gd::String& type,
const gd::String& expression,
const gd::String& objectName) {
// Compatibility with old ExpressionParser
if (useOldExpressionParser) {
if (type == "number") {
gd::String code = "";
gd::CallbacksForGeneratingExpressionCode callbacks(
code, codeGenerator, context);
gd::ExpressionParser parser(expression);
if (!parser.ParseMathExpression(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
callbacks) ||
code.empty()) {
std::cout << "Error (old ExpressionParser): \""
<< parser.GetFirstError() << "\" in: \"" << expression
<< "\" (number)" << std::endl;
code = "0";
}
return code;
} else if (type == "string") {
gd::String code = "";
gd::CallbacksForGeneratingExpressionCode callbacks(
code, codeGenerator, context);
gd::ExpressionParser parser(expression);
if (!parser.ParseStringExpression(
codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups(),
callbacks) ||
code.empty()) {
std::cout << "Error (old ExpressionParser): \""
<< parser.GetFirstError() << "\" in: \"" << expression
<< "\" (string)" << std::endl;
code = "\"\"";
}
return code;
} else if (type == "scenevar") {
gd::String code = "";
gd::VariableCodeGenerationCallbacks callbacks(
code,
codeGenerator,
context,
gd::EventsCodeGenerator::LAYOUT_VARIABLE);
gd::VariableParser parser(expression);
if (!parser.Parse(callbacks)) {
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
<< " in: " << expression << std::endl;
code = codeGenerator.GenerateBadVariable();
}
return code;
} else if (type == "globalvar") {
gd::String code = "";
gd::VariableCodeGenerationCallbacks callbacks(
code,
codeGenerator,
context,
gd::EventsCodeGenerator::PROJECT_VARIABLE);
gd::VariableParser parser(expression);
if (!parser.Parse(callbacks)) {
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
<< " in: " << expression << std::endl;
code = codeGenerator.GenerateBadVariable();
}
return code;
} else if (type == "objectvar") {
gd::String code = "";
// Object is either the object of the previous parameter or, if it is
// empty, the object being picked by the instruction.
gd::String object =
objectName.empty() ? context.GetCurrentObject() : objectName;
gd::VariableCodeGenerationCallbacks callbacks(
code, codeGenerator, context, object);
gd::VariableParser parser(expression);
if (!parser.Parse(callbacks)) {
std::cout << "Error (old VariableParser) :" << parser.GetFirstError()
<< " in: " << expression << std::endl;
code = codeGenerator.GenerateBadVariable();
}
return code;
}
std::cout << "Type error (old ExpressionParser): type \"" << type
<< "\" is not supported" << std::endl;
return "/* Error during code generation: type " + type +
" is not supported for old ExpressionParser. */ 0";
}
// end of compatibility code
gd::ExpressionParser2 parser(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups());
@@ -230,7 +127,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
}
}
void ExpressionCodeGenerator::OnVisitFunctionNode(FunctionNode& node) {
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
output += "/* Error during generation, function not found: " +
codeGenerator.ConvertToString(node.functionName) + " for type " +
@@ -462,4 +359,8 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
output += GenerateDefaultValue(node.type);
}
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) {
output += GenerateDefaultValue(node.type);
}
} // namespace gd

View File

@@ -60,11 +60,6 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
const gd::String& expression,
const gd::String& objectName = "");
static void UseOldExpressionParser(bool enable) {
useOldExpressionParser = enable;
};
static bool IsUsingOldExpressionParser() { return useOldExpressionParser; };
const gd::String& GetOutput() { return output; };
protected:
@@ -78,7 +73,8 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override;
void OnVisitIdentifierNode(IdentifierNode& node) override;
void OnVisitFunctionNode(FunctionNode& node) override;
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override;
void OnVisitFunctionCallNode(FunctionCallNode& node) override;
void OnVisitEmptyNode(EmptyNode& node) override;
private:
@@ -107,11 +103,9 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
gd::String output;
EventsCodeGenerator& codeGenerator;
EventsCodeGenerationContext& context;
static bool useOldExpressionParser;
};
} // namespace gd
#endif // GDCORE_ExpressionCodeGenerator_H
#endif
#endif

View File

@@ -1,229 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionsCodeGeneration.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
using namespace std;
namespace gd {
CallbacksForGeneratingExpressionCode::CallbacksForGeneratingExpressionCode(
gd::String& plainExpression_,
EventsCodeGenerator& codeGenerator_,
EventsCodeGenerationContext& context_)
: plainExpression(plainExpression_),
codeGenerator(codeGenerator_),
context(context_) {}
void CallbacksForGeneratingExpressionCode::OnConstantToken(gd::String text) {
plainExpression += text;
};
void CallbacksForGeneratingExpressionCode::OnStaticFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
codeGenerator.AddIncludeFiles(
expressionInfo.codeExtraInformation.GetIncludeFiles());
// Launch custom code generator if needed
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
parameters, codeGenerator, context);
return;
}
// Special case: For strings expressions, function without name is a string.
if (GetReturnType() == "string" && functionName.empty()) {
if (parameters.empty()) return;
plainExpression +=
codeGenerator.ConvertToStringExplicit(parameters[0].GetPlainString());
return;
}
// Prepare parameters
std::vector<gd::String> parametersCode =
codeGenerator.GenerateParametersCodes(
parameters, expressionInfo.parameters, context);
gd::String parametersStr;
for (std::size_t i = 0; i < parametersCode.size(); ++i) {
if (i != 0) parametersStr += ", ";
parametersStr += parametersCode[i];
}
plainExpression += expressionInfo.codeExtraInformation.functionCallName +
"(" + parametersStr + ")";
};
void CallbacksForGeneratingExpressionCode::OnObjectFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
const gd::ObjectsContainer& globalObjectsAndGroups = codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups = codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionInfo.codeExtraInformation.GetIncludeFiles());
if (parameters.empty()) return;
// Launch custom code generator if needed
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
parameters, codeGenerator, context);
return;
}
// Prepare parameters
std::vector<gd::String> parametersCode =
codeGenerator.GenerateParametersCodes(
parameters, expressionInfo.parameters, context);
gd::String parametersStr;
for (std::size_t i = 1; i < parametersCode.size(); ++i) {
if (i != 1) parametersStr += ", ";
parametersStr += parametersCode[i];
}
gd::String output = GetReturnType() == "string" ? "\"\"" : "0";
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(parameters[0].GetPlainString(), context);
for (std::size_t i = 0; i < realObjects.size(); ++i) {
context.ObjectsListNeeded(realObjects[i]);
gd::String objectType = gd::GetTypeOfObject(globalObjectsAndGroups, objectsAndGroups, realObjects[i]);
const ObjectMetadata& objInfo = MetadataProvider::GetObjectMetadata(
codeGenerator.GetPlatform(), objectType);
// Build gd::String to access the object
codeGenerator.AddIncludeFiles(objInfo.includeFiles);
output = codeGenerator.GenerateObjectFunctionCall(
realObjects[i],
objInfo,
expressionInfo.codeExtraInformation,
parametersStr,
output,
context);
}
plainExpression += output;
};
void CallbacksForGeneratingExpressionCode::OnObjectBehaviorFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
const gd::ObjectsContainer& globalObjectsAndGroups = codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups = codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionInfo.codeExtraInformation.GetIncludeFiles());
if (parameters.size() < 2) return;
// Launch custom code generator if needed
if (expressionInfo.codeExtraInformation.HasCustomCodeGenerator()) {
plainExpression += expressionInfo.codeExtraInformation.customCodeGenerator(
parameters, codeGenerator, context);
return;
}
// Prepare parameters
std::vector<gd::String> parametersCode =
codeGenerator.GenerateParametersCodes(
parameters, expressionInfo.parameters, context);
gd::String parametersStr;
for (std::size_t i = 2; i < parametersCode.size(); ++i) {
if (i != 2) parametersStr += ", ";
parametersStr += parametersCode[i];
}
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(parameters[0].GetPlainString(), context);
gd::String output = GetReturnType() == "string" ? "\"\"" : "0";
for (std::size_t i = 0; i < realObjects.size(); ++i) {
context.ObjectsListNeeded(realObjects[i]);
// Cast the object if needed
gd::String behaviorType =
gd::GetTypeOfBehavior(globalObjectsAndGroups, objectsAndGroups, parameters[1].GetPlainString());
const BehaviorMetadata& autoInfo = MetadataProvider::GetBehaviorMetadata(
codeGenerator.GetPlatform(), behaviorType);
// Build gd::String to access the behavior
codeGenerator.AddIncludeFiles(autoInfo.includeFiles);
output = codeGenerator.GenerateObjectBehaviorFunctionCall(
realObjects[i],
parameters[1].GetPlainString(),
autoInfo,
expressionInfo.codeExtraInformation,
parametersStr,
output,
context);
}
plainExpression += output;
};
bool CallbacksForGeneratingExpressionCode::OnSubMathExpression(
const gd::Platform& platform,
const gd::ObjectsContainer& globalObjectsAndGroups,
const gd::ObjectsContainer& objectsAndGroups,
gd::Expression& expression) {
gd::String newExpression;
CallbacksForGeneratingExpressionCode callbacks(
newExpression, codeGenerator, context);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseMathExpression(platform, globalObjectsAndGroups, objectsAndGroups, callbacks)) {
#if defined(GD_IDE_ONLY)
firstErrorStr = callbacks.GetFirstError();
firstErrorPos = callbacks.GetFirstErrorPosition();
#endif
return false;
}
return true;
}
bool CallbacksForGeneratingExpressionCode::OnSubTextExpression(
const gd::Platform& platform,
const gd::ObjectsContainer& globalObjectsAndGroups,
const gd::ObjectsContainer& objectsAndGroups,
gd::Expression& expression) {
gd::String newExpression;
CallbacksForGeneratingExpressionCode callbacks(
newExpression, codeGenerator, context);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseStringExpression(platform, globalObjectsAndGroups, objectsAndGroups, callbacks)) {
#if defined(GD_IDE_ONLY)
firstErrorStr = callbacks.GetFirstError();
firstErrorPos = callbacks.GetFirstErrorPosition();
#endif
return false;
}
return true;
}
} // namespace gd

View File

@@ -1,77 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EXPRESSIONSCODEGENERATION_H
#define EXPRESSIONSCODEGENERATION_H
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/String.h"
namespace gd {
class ExpressionMetadata;
class Expression;
class Project;
class Layout;
class Layout;
class EventsCodeGenerationContext;
class EventsCodeGenerator;
}
namespace gd {
// TODO: Replace and remove (ExpressionCodeGenerator)
/**
* \brief Used to generate code from expressions.
*
* Usage example :
* \code
* gd::String expressionOutputCppCode;
*
* CallbacksForGeneratingExpressionCode callbacks(expressionOutputCppCode,
* codeGenerator, context); gd::ExpressionParser
* parser(theOriginalGameDevelopExpression);
* parser.ParseStringExpression(platform, project, scene, callbacks);
*
* if (expressionOutputCppCode.empty()) expressionOutputCppCode = "\"\""; //If
* generation failed, we make sure output code is not empty. \endcode \see
* EventsCodeGenerator
*/
class GD_CORE_API CallbacksForGeneratingExpressionCode
: public gd::ParserCallbacks {
public:
CallbacksForGeneratingExpressionCode(gd::String& output,
EventsCodeGenerator& codeGenerator_,
EventsCodeGenerationContext& context_);
virtual ~CallbacksForGeneratingExpressionCode(){};
void OnConstantToken(gd::String text);
void OnStaticFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo);
void OnObjectFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo);
void OnObjectBehaviorFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo);
bool OnSubMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression);
bool OnSubTextExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression);
private:
gd::String& plainExpression;
EventsCodeGenerator& codeGenerator;
EventsCodeGenerationContext& context;
};
} // namespace gd
#endif // EXPRESSIONSCODEGENERATION_H

View File

@@ -1,68 +0,0 @@
/*
* GDevelop C++ Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "VariableParserCallbacks.h"
#include <string>
#include <vector>
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/CodeGeneration/ExpressionCodeGenerator.h"
#include "GDCore/Events/Parsers/VariableParser.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
using namespace std;
namespace gd {
VariableCodeGenerationCallbacks::VariableCodeGenerationCallbacks(
gd::String& output_,
gd::EventsCodeGenerator& codeGenerator_,
gd::EventsCodeGenerationContext& context_,
const gd::EventsCodeGenerator::VariableScope& scope_)
: output(output_),
codeGenerator(codeGenerator_),
context(context_),
scope(scope_) {
if (scope == gd::EventsCodeGenerator::OBJECT_VARIABLE) {
std::cout << "ERROR: Initializing VariableCodeGenerationCallbacks with "
"OBJECT_VARIABLE without object.";
}
}
VariableCodeGenerationCallbacks::VariableCodeGenerationCallbacks(
gd::String& output_,
gd::EventsCodeGenerator& codeGenerator_,
gd::EventsCodeGenerationContext& context_,
const gd::String& object_)
: output(output_),
codeGenerator(codeGenerator_),
context(context_),
scope(gd::EventsCodeGenerator::OBJECT_VARIABLE),
object(object_) {}
void VariableCodeGenerationCallbacks::OnRootVariable(gd::String variableName) {
output += codeGenerator.GenerateGetVariable(variableName, scope, context, object);
}
void VariableCodeGenerationCallbacks::OnChildVariable(gd::String variableName) {
output += codeGenerator.GenerateVariableAccessor(variableName);
}
void VariableCodeGenerationCallbacks::OnChildSubscript(
gd::String stringExpression) {
gd::String argumentCode = gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "string", stringExpression);
output += codeGenerator.GenerateVariableBracketAccessor(argumentCode);
}
}
#endif

View File

@@ -1,97 +0,0 @@
/*
* GDevelop C++ Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef VARIABLEPARSERCALLBACKS_H
#define VARIABLEPARSERCALLBACKS_H
#include <string>
#include <vector>
#include "GDCore/Events/Parsers/VariableParser.h"
#include "GDCore/String.h"
#include "EventsCodeGenerator.h"
namespace gd {
class EventsCodeGenerationContext;
} // namespace gd
// TODO: Replace and remove (ExpressionCodeGenerator)
namespace gd {
/**
* \brief Callbacks called to generate the code for getting a variable.
*
* Usage example:
\code
VariableCodeGenerationCallbacks callbacks(output, eventsCodeGenerator,
context, VariableCodeGenerationCallbacks::LAYOUT_VARIABLE);
gd::VariableParser parser(parameter);
if ( !parser.Parse(callbacks) )
{
//Error during parsing the variable name:
output = "runtimeContext->GetSceneVariables().GetBadVariable()";
}
//"output" now contains the C++ code to return the variable.
\endcode
*/
class VariableCodeGenerationCallbacks : public gd::VariableParserCallbacks {
public:
/**
* \brief Default constructor for generating code for a layout/global
* variable. \param output The string in which the code will be generated.
* \param codeGenerator The code generator being used.
* \param context The current code generation context.
* \param scope The scope of the variable being accessed: LAYOUT_VARIABLE,
* PROJECT_VARIABLE.
*/
VariableCodeGenerationCallbacks(gd::String& output,
gd::EventsCodeGenerator& codeGenerator_,
gd::EventsCodeGenerationContext& context_,
const gd::EventsCodeGenerator::VariableScope& scope_);
/**
* \brief Default constructor for generating code for an object variable.
* \param output The string in which the code will be generated.
* \param codeGenerator The code generator being used.
* \param context The current code generation context.
* \param object The name of the object
*/
VariableCodeGenerationCallbacks(gd::String& output,
gd::EventsCodeGenerator& codeGenerator_,
gd::EventsCodeGenerationContext& context_,
const gd::String& object);
/**
* \brief Called when the first variable has been parsed.
* \param variableName The variable name.
*/
virtual void OnRootVariable(gd::String variableName);
/**
* \brief Called when accessing the child of a structure variable.
* \param variableName The child variable name.
*/
virtual void OnChildVariable(gd::String variableName);
/**
* \brief Called when accessing the child of a structure variable using a
* string expression in square brackets. \param variableName The expression
* used to access the child variable.
*/
virtual void OnChildSubscript(gd::String stringExpression);
private:
gd::String& output;
gd::EventsCodeGenerator& codeGenerator;
gd::EventsCodeGenerationContext& context;
gd::EventsCodeGenerator::VariableScope scope;
const gd::String object; ///< The object name, when scope == OBJECT_VARIABLE.
};
}
#endif // VARIABLEPARSERCALLBACKS_H
#endif

View File

@@ -90,8 +90,8 @@ class GD_CORE_API BaseEvent {
bool HasSubEvents() const;
/**
* Event must be able to return all conditions std::vector they have.
* Used to preprocess the conditions.
* \brief Return a list of all conditions of the event.
* \note Used to preprocess or search in the conditions.
*/
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors() {
std::vector<gd::InstructionsList*> noConditions;
@@ -104,8 +104,8 @@ class GD_CORE_API BaseEvent {
};
/**
* Event must be able to return all actions std::vector they have.
* Used to preprocess the actions.
* \brief Return a list of all actions of the event.
* \note Used to preprocess or search in the actions.
*/
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors() {
std::vector<gd::InstructionsList*> noActions;
@@ -118,8 +118,17 @@ class GD_CORE_API BaseEvent {
};
/**
* Event must be able to return all expressions they have.
* Used to preprocess the expressions.
* \brief Return a list of all strings of the event.
* \note Used to preprocess or search in the event strings.
*/
virtual std::vector<gd::String> GetAllSearchableStrings() const {
std::vector<gd::String> noSearchableStrings;
return noSearchableStrings;
};
/**
* \brief Return a list of all expressions of the event.
* \note Used to preprocess or search in the expressions.
*/
virtual std::vector<gd::Expression*> GetAllExpressions() {
std::vector<gd::Expression*> noExpr;

View File

@@ -99,6 +99,23 @@ bool EventsList::Contains(const gd::BaseEvent& eventToSearch,
return false;
}
bool EventsList::MoveEventToAnotherEventsList(const gd::BaseEvent& eventToMove,
gd::EventsList& newEventsList,
std::size_t newPosition) {
for (std::size_t i = 0; i < GetEventsCount(); ++i) {
if (events[i].get() == &eventToMove) {
std::shared_ptr<BaseEvent> event = events[i];
events.erase(events.begin() + i);
newEventsList.InsertEvent(event, newPosition);
return true;
}
}
return false;
}
EventsList::EventsList(const EventsList& other) { Init(other); }
EventsList& EventsList::operator=(const EventsList& other) {

View File

@@ -52,7 +52,8 @@ class GD_CORE_API EventsList {
* \brief Insert the specified event to the list.
* \note The event passed by parameter is not copied.
* \param event The smart pointer to the event that must be inserted into the
* list \param position Insertion position. If the position is invalid, the
* list
* \param position Insertion position. If the position is invalid, the
* object is inserted at the end of the objects list.
*/
void InsertEvent(std::shared_ptr<gd::BaseEvent> event,
@@ -142,6 +143,25 @@ class GD_CORE_API EventsList {
*/
bool Contains(const gd::BaseEvent& eventToSearch,
bool recursive = true) const;
/**
* Move the specified event, that must be in the events list, to another
* events list *without* invalidating the event (i.e: without
* destroying/cloning it) in memory.
*
* \warning newEventsList is supposed not to be contained inside the event
* (you should not try
* to move an event inside one of its children/grand children events).
*
* \param eventToMove The event to be moved
* \param newEventsList The new events list
* \param newPosition The position in the new events list
* \return true if the move was made, false otherwise (for example, if
* eventToMove is not found in the list)
*/
bool MoveEventToAnotherEventsList(const gd::BaseEvent& eventToMove,
gd::EventsList& newEventsList,
std::size_t newPosition);
///@}
/** \name std::vector API compatibility

View File

@@ -1,897 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include <algorithm>
#include <iostream>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
using namespace std;
namespace gd {
gd::String ExpressionParser::parserSeparators = " ,+-*/%.<>=&|;()#^![]{}";
size_t ExpressionParser::GetMinimalParametersNumber(
const std::vector<gd::ParameterMetadata>& parametersInfos) {
size_t nb = 0;
for (std::size_t i = 0; i < parametersInfos.size(); ++i) {
if (!parametersInfos[i].optional && !parametersInfos[i].codeOnly) nb++;
}
return nb;
}
size_t ExpressionParser::GetMaximalParametersNumber(
const std::vector<gd::ParameterMetadata>& parametersInfos) {
size_t nb = 0;
for (std::size_t i = 0; i < parametersInfos.size(); ++i) {
if (!parametersInfos[i].codeOnly) nb++;
}
return nb;
}
/**
* Add blank parameters when code-only parameters are expected.
* \param Parameters information
* \param vector of parameters without code only parameters.
*/
std::vector<gd::Expression> CompleteParameters(
const std::vector<gd::ParameterMetadata>& parametersInfo,
const std::vector<gd::Expression>& parameters) {
std::vector<gd::Expression> completeParameters = parameters;
for (std::size_t i = 0; i < parametersInfo.size();
++i) // Code only parameters are not included in expressions parameters.
{
if (parametersInfo[i].codeOnly) {
if (i > completeParameters.size()) {
cout << "Bad parameter count in expression.";
}
if (i >= completeParameters.size())
completeParameters.push_back(gd::Expression(""));
else
completeParameters.insert(completeParameters.begin() + i,
gd::Expression(""));
} else {
if (i >= completeParameters.size()) {
completeParameters.push_back(gd::Expression(""));
}
}
}
return completeParameters;
}
bool ExpressionParser::ValidSyntax(const gd::String& str) {
static const gd::String numerics = "0123456789.e";
static const gd::String operators = "+/*-%";
size_t parenthesisLevel = 0;
gd::String lastOperator;
bool parsingNumber = false;
bool parsingScientificNotationNumber = false;
bool parsingDecimalNumber = false;
bool requestNumber = false;
gd::String lastNumber;
bool numberWasParsedLast = false;
for (auto it = str.begin(); it != str.end(); ++it) {
char32_t currentChar = *it;
if (currentChar == U' ' || currentChar == U'\n') {
if (requestNumber) {
firstErrorStr = _("Number expected");
return false;
}
if (parsingNumber) {
parsingNumber = false;
parsingScientificNotationNumber = false;
parsingDecimalNumber = false;
requestNumber = false;
lastNumber.clear();
numberWasParsedLast = true;
}
} else if (numerics.find(currentChar) != gd::String::npos) {
requestNumber = false;
if (currentChar == U'.') {
if (!parsingNumber) {
firstErrorStr = _("Syntax error");
return false;
}
if (parsingDecimalNumber) {
firstErrorStr = _("Syntax error in a number.");
return false;
}
parsingDecimalNumber = true;
}
if (currentChar == U'e') {
if (parsingScientificNotationNumber) {
firstErrorStr = _("Syntax error in a number.");
return false;
}
parsingScientificNotationNumber = true;
requestNumber = true;
}
if (numberWasParsedLast) {
firstErrorStr = _("Operator missing before a number");
return false;
}
parsingNumber = true;
lastNumber += currentChar;
} else if (currentChar == U')') {
if (requestNumber) {
firstErrorStr = _("Number expected");
return false;
}
if (parsingNumber) {
parsingNumber = false;
parsingScientificNotationNumber = false;
parsingDecimalNumber = false;
lastNumber.clear();
numberWasParsedLast = true;
}
if (!numberWasParsedLast) {
firstErrorStr = _("Superfluous operator before a paranthesis");
return false;
}
if (parenthesisLevel > 0)
parenthesisLevel--;
else {
firstErrorStr = _("Bad closing paranthesis");
return false;
}
auto previousIt = it;
--previousIt;
if (*previousIt == U'(') {
firstErrorStr = _("Empty paranthesis");
return false;
}
} else if (currentChar == U'(') {
if (requestNumber) {
firstErrorStr = _("Number expected");
return false;
}
if (parsingNumber) {
parsingNumber = false;
parsingScientificNotationNumber = false;
parsingDecimalNumber = false;
lastNumber.clear();
numberWasParsedLast = true;
}
if (numberWasParsedLast) {
firstErrorStr = _("Operator missing before a paranthesis");
return false;
}
parenthesisLevel++;
numberWasParsedLast = false;
} else if (operators.find(currentChar) != gd::String::npos) {
if (currentChar == U'-' && parsingNumber &&
parsingScientificNotationNumber) {
lastNumber += currentChar;
requestNumber = true;
} else {
if (requestNumber) {
firstErrorStr = _("Number expected");
return false;
}
if (parsingNumber) {
parsingNumber = false;
parsingScientificNotationNumber = false;
parsingDecimalNumber = false;
lastNumber.clear();
numberWasParsedLast = true;
}
if (currentChar != U'-' && currentChar != U'+' &&
!numberWasParsedLast) {
firstErrorStr = _("Operators without any number between them");
return false;
}
numberWasParsedLast = false;
}
} else {
firstErrorStr = _("Syntax error");
return false;
}
}
if (parsingNumber) {
parsingNumber = false;
parsingScientificNotationNumber = false;
parsingDecimalNumber = false;
lastNumber.clear();
numberWasParsedLast = true;
} else if (requestNumber) {
firstErrorStr = _("Number expected");
return false;
}
if (parenthesisLevel != 0) {
firstErrorStr = _("Paranthesis mismatch");
return false;
}
if (!numberWasParsedLast) {
firstErrorStr = _("Alone operator at the end of the expression");
return false;
}
return true;
}
bool ExpressionParser::ParseMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::ParserCallbacks& callbacks) {
callbacks.SetReturnType("expression");
gd::String expression = expressionPlainString;
size_t parsePosition = 0;
size_t firstPointPos = expression.find(".");
size_t firstParPos = expression.find("(");
gd::String expressionWithoutFunctions;
gd::String nonFunctionToken;
size_t nonFunctionTokenStartPos = gd::String::npos;
while (firstPointPos != string::npos || firstParPos != string::npos) {
// Identify name
size_t nameEnd = firstPointPos < firstParPos ? firstPointPos : firstParPos;
size_t nameStart = expression.find_last_of(parserSeparators, nameEnd - 1);
nameStart++;
gd::String nameBefore = expression.substr(nameStart, nameEnd - nameStart);
gd::String objectName = nameBefore.FindAndReplace("~", " ");
// Identify function name
gd::String functionName = nameBefore;
size_t functionNameEnd = nameEnd;
vector<gd::Expression> parameters;
bool nameIsFunction = firstPointPos > firstParPos;
if (!nameIsFunction) {
parameters.push_back(gd::Expression(objectName));
functionNameEnd = expression.find_first_of(" (", nameEnd);
if (nameEnd + 1 < expression.length())
functionName =
expression.substr(nameEnd + 1, functionNameEnd - (nameEnd + 1));
if (functionNameEnd == string::npos) {
functionName = "";
functionNameEnd = expression.length() - 1;
}
}
// Now we're going to identify the expression
gd::ExpressionMetadata instructionInfos;
if (functionName.substr(0, functionName.length() - 1)
.find_first_of(parserSeparators) == string::npos) {
bool functionFound = false;
bool staticFunctionFound = false;
bool objectFunctionFound = false;
bool behaviorFunctionFound = false;
// First try to bind to a static expression
if (nameIsFunction &&
MetadataProvider::HasExpression(platform, functionName)) {
functionFound = true;
staticFunctionFound = true;
instructionInfos =
MetadataProvider::GetExpressionMetadata(platform, functionName);
}
// Then search in object expression
else if (!nameIsFunction &&
MetadataProvider::HasObjectExpression(
platform,
gd::GetTypeOfObject(project, layout, objectName),
functionName)) {
functionFound = true;
objectFunctionFound = true;
instructionInfos = MetadataProvider::GetObjectExpressionMetadata(
platform,
gd::GetTypeOfObject(project, layout, objectName),
functionName);
}
// And in behaviors expressions
else if (!nameIsFunction) {
size_t firstDoublePoints = functionName.find("::");
if (firstDoublePoints != string::npos) {
gd::String autoName = functionName.substr(0, firstDoublePoints);
if (firstDoublePoints + 2 < functionName.length())
functionName = functionName.substr(firstDoublePoints + 2,
functionName.length());
else
functionName = "";
if (MetadataProvider::HasBehaviorExpression(
platform,
gd::GetTypeOfBehavior(project, layout, autoName),
functionName)) {
parameters.push_back(gd::Expression(autoName));
functionFound = true;
behaviorFunctionFound = true;
instructionInfos = MetadataProvider::GetBehaviorExpressionMetadata(
platform,
gd::GetTypeOfBehavior(project, layout, autoName),
functionName);
// Verify that object has behavior.
vector<gd::String> behaviors =
gd::GetBehaviorsOfObject(project, layout, objectName);
if (find(behaviors.begin(), behaviors.end(), autoName) ==
behaviors.end()) {
cout << "Bad behavior requested" << endl;
functionFound = false;
}
}
}
}
if (functionFound) // Add the function
{
// Identify parameters
size_t parametersEnd = expression.find_first_of("(", functionNameEnd);
gd::String currentParameterStr;
char32_t previousChar = '(';
bool takeSymbolsInAccount = true;
if (parametersEnd != string::npos) {
size_t level = 0;
parametersEnd++;
while (parametersEnd < expression.length() &&
!(expression[parametersEnd] == ')' && level == 0)) {
// Be sure we are not in quotes
if (expression[parametersEnd] == U'\"' && previousChar != U'\\')
takeSymbolsInAccount = !takeSymbolsInAccount;
// So as to be sure paranthesis don't belong to a parameter
if (expression[parametersEnd] == U'(' && takeSymbolsInAccount)
level++;
if (expression[parametersEnd] == U')' && takeSymbolsInAccount)
level--;
// Add the character to the current parameter or terminate the
// latter
if ((expression[parametersEnd] == U',' && level == 0) &&
takeSymbolsInAccount) {
parameters.push_back(currentParameterStr);
currentParameterStr.clear();
} else
currentParameterStr += expression[parametersEnd];
previousChar = expression[parametersEnd];
parametersEnd++;
}
if (currentParameterStr.find_first_not_of(" ") !=
string::npos) // Add last parameter if needed
{
parameters.push_back(currentParameterStr);
}
// Testing function call is properly closed
if (parametersEnd == expression.length() ||
expression[parametersEnd] != U')') {
firstErrorStr = _("Paranthesis not closed");
firstErrorPos = parametersEnd - 1;
return false;
}
// Testing the number of parameters
if (parameters.size() >
GetMaximalParametersNumber(instructionInfos.parameters) ||
parameters.size() <
GetMinimalParametersNumber(instructionInfos.parameters)) {
firstErrorPos = functionNameEnd;
firstErrorStr = _("Incorrect number of parameters");
firstErrorStr += " ";
firstErrorStr += _("Expected (maximum) :");
firstErrorStr += gd::String::From(
GetMaximalParametersNumber(instructionInfos.parameters));
return false;
}
// Preparing parameters
parameters =
CompleteParameters(instructionInfos.parameters, parameters);
for (std::size_t i = 0; i < instructionInfos.parameters.size(); ++i) {
if (!PrepareParameter(platform,
project,
layout,
callbacks,
parameters[i],
instructionInfos.parameters[i],
functionNameEnd))
return false;
}
} else {
firstErrorPos = functionNameEnd;
firstErrorStr = _("Parameters' parenthesis missing");
return false;
}
callbacks.OnConstantToken(
nonFunctionToken +
expression.substr(parsePosition, nameStart - parsePosition));
expressionWithoutFunctions +=
expression.substr(parsePosition, nameStart - parsePosition);
nonFunctionToken.clear();
nonFunctionTokenStartPos = gd::String::npos;
if (objectFunctionFound)
callbacks.OnObjectFunction(
functionName, parameters, instructionInfos);
else if (behaviorFunctionFound)
callbacks.OnObjectBehaviorFunction(
functionName, parameters, instructionInfos);
else if (staticFunctionFound)
callbacks.OnStaticFunction(
functionName, parameters, instructionInfos);
if (objectFunctionFound || behaviorFunctionFound || staticFunctionFound)
expressionWithoutFunctions += "0";
parsePosition = parametersEnd + 1;
firstPointPos = expression.find(".", parametersEnd + 1);
firstParPos = expression.find("(", parametersEnd + 1);
} else // Math function or math constant : Pass it.
{
nonFunctionToken += expression.substr(
parsePosition, functionNameEnd + 1 - parsePosition);
expressionWithoutFunctions += expression.substr(
parsePosition, functionNameEnd + 1 - parsePosition);
nonFunctionTokenStartPos = (nonFunctionTokenStartPos != gd::String::npos
? nonFunctionTokenStartPos
: parsePosition);
parsePosition = functionNameEnd + 1;
firstPointPos = expression.find(".", functionNameEnd + 1);
firstParPos = expression.find("(", functionNameEnd + 1);
}
} else // Not a function call : Pass it
{
nonFunctionToken +=
expression.substr(parsePosition, nameEnd + 1 - parsePosition);
expressionWithoutFunctions +=
expression.substr(parsePosition, nameEnd + 1 - parsePosition);
nonFunctionTokenStartPos = (nonFunctionTokenStartPos != gd::String::npos
? nonFunctionTokenStartPos
: parsePosition);
parsePosition = nameEnd + 1;
firstPointPos = expression.find(".", nameEnd + 1);
firstParPos = expression.find("(", nameEnd + 1);
}
}
if (parsePosition < expression.length() || !nonFunctionToken.empty())
callbacks.OnConstantToken(
nonFunctionToken +
expression.substr(parsePosition, expression.length()));
expressionWithoutFunctions +=
expression.substr(parsePosition, expression.length());
return ValidSyntax(expressionWithoutFunctions);
}
bool ExpressionParser::ParseStringExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::ParserCallbacks& callbacks) {
callbacks.SetReturnType("string");
gd::String expression = expressionPlainString;
size_t parsePosition = 0;
// Searching for first token.
size_t firstPointPos = expression.find(".");
size_t firstParPos = expression.find("(");
size_t firstQuotePos = expression.find("\"");
if (firstPointPos == string::npos && firstParPos == string::npos &&
firstQuotePos == string::npos) {
firstErrorPos = 0;
firstErrorStr =
_("The expression is invalid or empty. Enter a text ( surrounded by "
"quotes ) or a function.");
return false;
}
while (firstPointPos != string::npos || firstParPos != string::npos ||
firstQuotePos != string::npos) {
if (firstQuotePos < firstPointPos &&
firstQuotePos < firstParPos) // Adding a constant text
{
callbacks.OnConstantToken(
expression.substr(parsePosition, firstQuotePos - parsePosition));
// Finding start and end of quotes
size_t finalQuotePosition = expression.find("\"", firstQuotePos + 1);
while (finalQuotePosition ==
expression.find("\\\"", finalQuotePosition - 1) + 1)
finalQuotePosition = expression.find("\"", finalQuotePosition + 1);
if (finalQuotePosition == string::npos) {
firstErrorPos = firstQuotePos;
firstErrorStr = _("Quotes not closed.");
return false;
}
// Generating final text, by replacing \" by quotes
gd::String finalText = expression.substr(
firstQuotePos + 1, finalQuotePosition - (firstQuotePos + 1));
size_t foundPos = finalText.find("\\\"");
while (foundPos != string::npos) {
if (foundPos != string::npos) finalText.replace(foundPos, 2, "\"");
foundPos = finalText.find("\\\"", foundPos);
}
// Adding constant text instruction
//(Function without name is considered as a constant text)
vector<gd::Expression> parameters;
parameters.push_back(finalText);
gd::ExpressionMetadata noParametersInfo;
callbacks.OnStaticFunction("", parameters, noParametersInfo);
parsePosition = finalQuotePosition + 1;
} else // Adding a function
{
// Identify name
size_t nameEnd =
firstPointPos < firstParPos ? firstPointPos : firstParPos;
size_t nameStart = expression.find_last_of(parserSeparators, nameEnd - 1);
nameStart++;
callbacks.OnConstantToken(
expression.substr(parsePosition, nameStart - parsePosition));
gd::String nameBefore = expression.substr(nameStart, nameEnd - nameStart);
gd::String objectName = nameBefore.FindAndReplace("~", " ");
// Identify function name
gd::String functionName = nameBefore;
size_t functionNameEnd = nameEnd;
vector<gd::Expression> parameters;
bool nameIsFunction = firstPointPos > firstParPos;
if (!nameIsFunction) {
parameters.push_back(gd::Expression(objectName));
functionNameEnd = expression.find_first_of("( ", nameEnd);
if (nameEnd + 1 < expression.length())
functionName =
expression.substr(nameEnd + 1, functionNameEnd - (nameEnd + 1));
}
// Identify parameters
size_t parametersEnd = expression.find_first_of("(", functionNameEnd) + 1;
char32_t previousChar = U'(';
bool takeSymbolsInAccount = true;
size_t level = 0;
gd::String currentParameterStr;
while (parametersEnd < expression.length() &&
!(expression[parametersEnd] == U')' && level == 0)) {
// Be sure we are not in quotes
if (expression[parametersEnd] == U'\"' && previousChar != U'\\')
takeSymbolsInAccount = !takeSymbolsInAccount;
// So as to be sure paranthesis don't belong to a parameter
if (expression[parametersEnd] == U'(' && takeSymbolsInAccount) level++;
if (expression[parametersEnd] == U')' && takeSymbolsInAccount) level--;
// Add the character to the current parameter or terminate the latter
if ((expression[parametersEnd] == ',' && level == 0) &&
takeSymbolsInAccount) {
gd::Expression currentParameter(currentParameterStr);
parameters.push_back(currentParameter);
currentParameterStr.clear();
} else
currentParameterStr += expression[parametersEnd];
previousChar = expression[parametersEnd];
parametersEnd++;
}
if (parametersEnd == expression.length() ||
expression[parametersEnd] != U')') {
firstErrorPos = parametersEnd - 1;
firstErrorStr = _("Paranthesis not closed");
return false;
}
if (currentParameterStr.find_first_not_of(" ") !=
string::npos) // Add last parameter if needed
{
gd::Expression lastParameter(currentParameterStr);
parameters.push_back(lastParameter);
}
bool functionFound = false;
// First try to bind to a static str expression
if (nameIsFunction &&
MetadataProvider::HasStrExpression(platform, functionName)) {
functionFound = true;
const gd::ExpressionMetadata& expressionInfo =
MetadataProvider::GetStrExpressionMetadata(platform, functionName);
// Testing the number of parameters
if (parameters.size() >
GetMaximalParametersNumber(expressionInfo.parameters) ||
parameters.size() <
GetMinimalParametersNumber(expressionInfo.parameters)) {
firstErrorPos = functionNameEnd;
firstErrorStr = _("Incorrect number of parameters");
return false;
}
// Preparing parameters
parameters = CompleteParameters(expressionInfo.parameters, parameters);
for (std::size_t i = 0;
i < parameters.size() && i < expressionInfo.parameters.size();
++i) {
if (!PrepareParameter(platform,
project,
layout,
callbacks,
parameters[i],
expressionInfo.parameters[i],
functionNameEnd))
return false;
}
callbacks.OnStaticFunction(functionName, parameters, expressionInfo);
}
// Then an object member expression
else if (!nameIsFunction &&
MetadataProvider::HasObjectStrExpression(
platform,
gd::GetTypeOfObject(project, layout, objectName),
functionName)) {
functionFound = true;
const gd::ExpressionMetadata& expressionInfo =
MetadataProvider::GetObjectStrExpressionMetadata(
platform,
gd::GetTypeOfObject(project, layout, nameBefore),
functionName);
// Testing the number of parameters
if (parameters.size() >
GetMaximalParametersNumber(expressionInfo.parameters) ||
parameters.size() <
GetMinimalParametersNumber(expressionInfo.parameters)) {
firstErrorPos = functionNameEnd;
firstErrorStr = _("Incorrect number of parameters");
return false;
}
// Preparing parameters
parameters = CompleteParameters(expressionInfo.parameters, parameters);
for (std::size_t i = 0;
i < parameters.size() && i < expressionInfo.parameters.size();
++i) {
if (!PrepareParameter(platform,
project,
layout,
callbacks,
parameters[i],
expressionInfo.parameters[i],
functionNameEnd))
return false;
}
callbacks.OnObjectFunction(functionName, parameters, expressionInfo);
}
// And search behaviors expressions
else {
size_t firstDoublePoints = functionName.find("::");
if (firstDoublePoints != string::npos) {
gd::String autoName = functionName.substr(0, firstDoublePoints);
if (firstDoublePoints + 2 < functionName.length())
functionName = functionName.substr(firstDoublePoints + 2,
functionName.length());
else
functionName = "";
if (MetadataProvider::HasBehaviorStrExpression(
platform,
gd::GetTypeOfBehavior(project, layout, autoName),
functionName)) {
parameters.push_back(gd::Expression(autoName));
functionFound = true;
const gd::ExpressionMetadata& expressionInfo =
MetadataProvider::GetBehaviorStrExpressionMetadata(
platform,
gd::GetTypeOfBehavior(project, layout, autoName),
functionName);
// Verify that object has behavior.
vector<gd::String> behaviors =
gd::GetBehaviorsOfObject(project, layout, objectName);
if (find(behaviors.begin(), behaviors.end(), autoName) ==
behaviors.end()) {
cout << "Bad behavior requested" << endl;
functionFound = false;
} else {
// Testing the number of parameters
if (parameters.size() >
GetMaximalParametersNumber(expressionInfo.parameters) ||
parameters.size() <
GetMinimalParametersNumber(expressionInfo.parameters)) {
firstErrorPos = functionNameEnd;
firstErrorStr = _("Incorrect number of parameters");
return false;
}
// Preparing parameters
parameters =
CompleteParameters(expressionInfo.parameters, parameters);
for (std::size_t i = 0; i < parameters.size() &&
i < expressionInfo.parameters.size();
++i) {
if (!PrepareParameter(platform,
project,
layout,
callbacks,
parameters[i],
expressionInfo.parameters[i],
functionNameEnd))
return false;
}
callbacks.OnObjectBehaviorFunction(
functionName, parameters, expressionInfo);
}
}
}
}
// Note : _No_ support for implicit conversion from math result to string
if (!functionFound) // Function was not found
{
firstErrorPos = nameStart;
firstErrorStr = _("Function not recognized.");
return false;
}
parsePosition = parametersEnd + 1;
}
// Searching for next token
size_t firstPlusPos = expression.find("+", parsePosition);
firstPointPos = expression.find(".", parsePosition);
firstParPos = expression.find("(", parsePosition);
firstQuotePos = expression.find("\"", parsePosition);
// Checking for a + between token
if ((firstPointPos != string::npos || firstParPos != string::npos ||
firstQuotePos != string::npos)) {
size_t nextTokenPos = firstPointPos;
if (firstParPos < nextTokenPos) nextTokenPos = firstParPos;
if (firstQuotePos < nextTokenPos) nextTokenPos = firstQuotePos;
if (nextTokenPos < firstPlusPos) {
firstErrorPos = nextTokenPos;
firstErrorStr = _("Symbol missing between two +.");
return false;
} else if (expression.find("+", firstPlusPos + 1) < nextTokenPos) {
firstErrorPos = firstPlusPos;
firstErrorStr = _("Symbol missing between two +.");
return false;
}
}
}
if (expression.substr(parsePosition, expression.length())
.find_first_not_of(" \n") != gd::String::npos) {
firstErrorPos = parsePosition;
firstErrorStr = _("Bad symbol at the end of the expression.");
return false;
}
return true;
}
bool ExpressionParser::PrepareParameter(
const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
ParserCallbacks& callbacks,
gd::Expression& parameter,
const gd::ParameterMetadata& parametersInfo,
const size_t positionInExpression) {
if (ParameterMetadata::IsExpression("number", parametersInfo.type)) {
if (parametersInfo.optional && parameter.GetPlainString().empty())
parameter = parametersInfo.defaultValue.empty()
? gd::Expression("0")
: gd::Expression(parametersInfo.defaultValue);
if (!callbacks.OnSubMathExpression(platform, project, layout, parameter)) {
firstErrorStr = callbacks.firstErrorStr;
firstErrorPos = callbacks.firstErrorPos + positionInExpression;
return false;
}
} else if (ParameterMetadata::IsExpression("string", parametersInfo.type)) {
if (parametersInfo.optional && parameter.GetPlainString().empty())
parameter = parametersInfo.defaultValue.empty()
? gd::Expression("\"\"")
: gd::Expression(parametersInfo.defaultValue);
if (!callbacks.OnSubTextExpression(platform, project, layout, parameter)) {
firstErrorStr = callbacks.firstErrorStr;
firstErrorPos = callbacks.firstErrorPos + positionInExpression;
return false;
}
}
return true;
}
ExpressionParser::ExpressionParser(const gd::String& expressionPlainString_)
: expressionPlainString(expressionPlainString_) {}
} // namespace gd

View File

@@ -1,179 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONPARSER_H
#define GDCORE_EXPRESSIONPARSER_H
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Expression;
class ParserCallbacks;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
}
namespace gd {
/** \brief Parse an expression
*
* Parse an expression, calling callbacks when a token is reached
* \see gd::ParserCallbacks
*/
class GD_CORE_API ExpressionParser {
public:
ExpressionParser(const gd::String &expressionPlainString_);
virtual ~ExpressionParser(){};
/**
* \brief Parse the expression, calling each functor when necessary
* \return True if expression was correctly parsed.
*/
bool ParseMathExpression(const gd::Platform &platform,
const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
gd::ParserCallbacks &callbacks);
/**
* \brief Parse the expression, calling each functor when necessary
* \return True if expression was correctly parsed.
*/
bool ParseStringExpression(const gd::Platform &platform,
const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
gd::ParserCallbacks &callbacks);
/**
* \brief Return the description of the error that was found
*/
const gd::String &GetFirstError() { return firstErrorStr; }
/**
* \brief Return the position of the error that was found
* \return The position, or gd::String::npos if no error is found
*/
size_t GetFirstErrorPosition() { return firstErrorPos; }
private:
gd::String firstErrorStr;
size_t firstErrorPos;
/**
* Tool function to add a parameter
*/
bool AddParameterToList(const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
ParserCallbacks &,
std::vector<gd::Expression> &parameters,
gd::String parameterStr,
std::vector<gd::ParameterMetadata> parametersInfos,
const size_t positionInExpression);
/**
* Tool function to prepare a parameter
*/
bool PrepareParameter(const gd::Platform &platform,
const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
ParserCallbacks &,
gd::Expression &parameter,
const gd::ParameterMetadata &parametersInfo,
const size_t positionInExpression);
/**
* Return the minimal number of parameters which can be used when calling an
* expression ( i.e. ParametersCount-OptionalParameters-CodeOnlyParameters )
*/
size_t GetMinimalParametersNumber(
const std::vector<gd::ParameterMetadata> &parametersInfos);
/**
* Return the maximal number of parameters which can be used when calling an
* expression ( i.e. ParametersCount-CodeOnlyParameters )
*/
size_t GetMaximalParametersNumber(
const std::vector<gd::ParameterMetadata> &parametersInfos);
bool ValidSyntax(const gd::String &str);
gd::String expressionPlainString;
static gd::String parserSeparators;
};
/** \brief Callbacks called by parser during parsing
*
* Parser will call the appropriate functions during parsing, allowing to do
* special works. \see gd::ExpressionParser
*/
class GD_CORE_API ParserCallbacks {
friend class ExpressionParser;
public:
ParserCallbacks() : returnType("expression"){};
virtual ~ParserCallbacks(){};
/**
* \brief Get the type of the expression for which callbacks are used:
* "expression" or "string".
*/
const gd::String &GetReturnType() { return returnType; }
virtual void OnConstantToken(gd::String text) = 0;
virtual void OnStaticFunction(
gd::String functionName,
const std::vector<gd::Expression> &parameters,
const gd::ExpressionMetadata &expressionInfo) = 0;
virtual void OnObjectFunction(
gd::String functionName,
const std::vector<gd::Expression> &parameters,
const gd::ExpressionMetadata &expressionInfo) = 0;
virtual void OnObjectBehaviorFunction(
gd::String functionName,
const std::vector<gd::Expression> &parameters,
const gd::ExpressionMetadata &expressionInfo) = 0;
virtual bool OnSubMathExpression(const gd::Platform &platform,
const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
gd::Expression &expression) = 0;
virtual bool OnSubTextExpression(const gd::Platform &platform,
const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
gd::Expression &expression) = 0;
/**
* \brief Return the description of the error that was found
*/
const gd::String &GetFirstError() { return firstErrorStr; }
/**
* \brief Return the position of the error that was found
* \return The position, or gd::String::npos if no error is found
*/
size_t GetFirstErrorPosition() { return firstErrorPos; }
protected:
gd::String firstErrorStr;
size_t firstErrorPos;
private:
/**
* \brief Set the return type of the expression: Done by ExpressionParser
* according to which Parse* method is called. \see gd::ExpressionParser
*/
void SetReturnType(gd::String type) { returnType = type; }
gd::String returnType; // The type of the expression ("expression" (default),
// "string"...)
};
} // namespace gd
#endif // GDEXPRESSIONPARSER_H

View File

@@ -22,15 +22,6 @@ using namespace std;
namespace gd {
gd::String ExpressionParser2::NUMBER_FIRST_CHAR = ".0123456789";
gd::String ExpressionParser2::DOT = ".";
gd::String ExpressionParser2::PARAMETERS_SEPARATOR = ",";
gd::String ExpressionParser2::QUOTE = "\"";
gd::String ExpressionParser2::BRACKETS = "()[]{}";
gd::String ExpressionParser2::EXPRESSION_OPERATORS = "+-<>?^=\\:!";
gd::String ExpressionParser2::TERM_OPERATORS = "/*";
gd::String ExpressionParser2::UNARY_OPERATORS = "+-";
gd::String ExpressionParser2::WHITESPACES = " \n\r";
gd::String ExpressionParser2::NAMESPACE_SEPARATOR = "::";
ExpressionParser2::ExpressionParser2(
@@ -76,7 +67,7 @@ size_t GetMaximumParametersNumber(
} // namespace
std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
const gd::FunctionNode& function, size_t functionStartPosition) {
const gd::FunctionCallNode& function, size_t functionStartPosition) {
if (gd::MetadataProvider::IsBadExpressionMetadata(
function.expressionMetadata)) {
return gd::make_unique<ExpressionParserError>(
@@ -118,11 +109,13 @@ std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
}
std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
SkipWhitespace();
if (!IsAnyChar("\"")) {
size_t textStartPosition = GetCurrentPosition();
SkipAllWhitespaces();
if (!CheckIfChar(IsQuote)) {
auto text = gd::make_unique<TextNode>("");
text->diagnostic =
RaiseSyntaxError(_("A text must start with a double quote (\")."));
text->location = ExpressionParserLocation(textStartPosition, GetCurrentPosition());
return text;
}
SkipChar();
@@ -157,6 +150,7 @@ std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
}
auto text = gd::make_unique<TextNode>(parsedText);
text->location = ExpressionParserLocation(textStartPosition, GetCurrentPosition());
if (!textParsingHasEnded) {
text->diagnostic =
RaiseSyntaxError(_("A text must end with a double quote (\"). Add a "
@@ -167,24 +161,25 @@ std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
}
std::unique_ptr<NumberNode> ExpressionParser2::ReadNumber() {
SkipWhitespace();
size_t numberStartPosition = GetCurrentPosition();
SkipAllWhitespaces();
gd::String parsedNumber;
bool numberHasStarted = false;
bool digitFound = false;
bool dotFound = false;
while (!IsEndReached()) {
if (IsAnyChar("0")) {
if (CheckIfChar(IsZeroDigit)) {
numberHasStarted = true;
digitFound = true;
if (!parsedNumber.empty()) { // Ignore leading 0s.
parsedNumber += GetCurrentChar();
}
} else if (IsAnyChar("123456789")) {
} else if (CheckIfChar(IsNonZeroDigit)) {
numberHasStarted = true;
digitFound = true;
parsedNumber += GetCurrentChar();
} else if (IsAnyChar(".") && !dotFound) {
} else if (CheckIfChar(IsDot) && !dotFound) {
numberHasStarted = true;
dotFound = true;
if (parsedNumber == "") {
@@ -209,6 +204,7 @@ std::unique_ptr<NumberNode> ExpressionParser2::ReadNumber() {
// valid in most languages so we allow this.
auto number = gd::make_unique<NumberNode>(parsedNumber);
number->location = ExpressionParserLocation(numberStartPosition, GetCurrentPosition());
if (!numberHasStarted || !digitFound) {
number->diagnostic = RaiseSyntaxError(
_("A number was expected. You must enter a number here."));

View File

@@ -48,8 +48,9 @@ class GD_CORE_API ExpressionParser2 {
*
* \param type Type of the expression: "string", "number",
* type supported by gd::ParameterMetadata::IsObject, types supported by
* gd::ParameterMetadata::IsExpression or "unknown". \param expression The
* expression to parse \param objectName Specify the object name, only for the
* gd::ParameterMetadata::IsExpression or "unknown".
* \param expression The expression to parse
* \param objectName Specify the object name, only for the
* case of "objectvar" type.
*
* \return The node representing the expression as a parsed tree.
@@ -71,18 +72,21 @@ class GD_CORE_API ExpressionParser2 {
///@{
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
const gd::String &objectName = "") {
size_t expressionStartPosition = GetCurrentPosition();
auto expression = Expression(type, objectName);
// Check for extra characters at the end of the expression
if (!IsEndReached()) {
auto op = gd::make_unique<OperatorNode>();
op->op = ' ';
auto op = gd::make_unique<OperatorNode>(type, ' ');
op->leftHandSide = std::move(expression);
op->rightHandSide = ReadUntilEnd("unknown");
op->rightHandSide->diagnostic = RaiseSyntaxError(
_("The expression has extra character at the end that should be "
"removed (or completed if your expression is not finished)."));
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
return std::move(op);
}
@@ -91,22 +95,24 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<ExpressionNode> Expression(
const gd::String &type, const gd::String &objectName = "") {
SkipWhitespace();
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
SkipWhitespace();
SkipAllWhitespaces();
if (IsEndReached()) return leftHandSide;
if (IsAnyChar(",)]")) return leftHandSide;
if (IsAnyChar(EXPRESSION_OPERATORS)) {
auto op = gd::make_unique<OperatorNode>();
op->op = GetCurrentChar();
if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
if (CheckIfChar(IsExpressionOperator)) {
auto op = gd::make_unique<OperatorNode>(type, GetCurrentChar());
op->leftHandSide = std::move(leftHandSide);
op->diagnostic = ValidateOperator(type, GetCurrentChar());
SkipChar();
op->rightHandSide = Expression(type, objectName);
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
return std::move(op);
}
@@ -124,31 +130,35 @@ class GD_CORE_API ExpressionParser2 {
"properly written.");
}
auto op = gd::make_unique<OperatorNode>();
op->op = ' ';
auto op = gd::make_unique<OperatorNode>(type, ' ');
op->leftHandSide = std::move(leftHandSide);
op->rightHandSide = Expression(type, objectName);
op->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(op);
}
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
const gd::String &objectName) {
SkipWhitespace();
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
SkipWhitespace();
SkipAllWhitespaces();
// This while loop is used instead of a recursion (like in Expression)
// to guarantee the proper operator precedence. (Expression could also
// be reworked to use a while loop).
while (IsAnyChar(TERM_OPERATORS)) {
auto op = gd::make_unique<OperatorNode>();
op->op = GetCurrentChar();
while (CheckIfChar(IsTermOperator)) {
auto op = gd::make_unique<OperatorNode>(type, GetCurrentChar());
op->leftHandSide = std::move(factor);
op->diagnostic = ValidateOperator(type, GetCurrentChar());
SkipChar();
op->rightHandSide = Factor(type, objectName);
SkipWhitespace();
op->location = ExpressionParserLocation(expressionStartPosition,
GetCurrentPosition());
SkipAllWhitespaces();
factor = std::move(op);
}
@@ -158,12 +168,12 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
const gd::String &objectName) {
SkipWhitespace();
SkipAllWhitespaces();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor;
if (IsAnyChar(QUOTE)) {
if (CheckIfChar(IsQuote)) {
factor = ReadText();
if (type == "number")
factor->diagnostic =
@@ -173,14 +183,17 @@ class GD_CORE_API ExpressionParser2 {
factor->diagnostic = RaiseTypeError(
_("You entered a text, but this type was expected:") + type,
expressionStartPosition);
} else if (IsAnyChar(UNARY_OPERATORS)) {
auto unaryOperator = gd::make_unique<UnaryOperatorNode>(GetCurrentChar());
} else if (CheckIfChar(IsUnaryOperator)) {
auto unaryOperator =
gd::make_unique<UnaryOperatorNode>(type, GetCurrentChar());
unaryOperator->diagnostic = ValidateUnaryOperator(type, GetCurrentChar());
SkipChar();
unaryOperator->factor = Factor(type, objectName);
unaryOperator->location = ExpressionParserLocation(
expressionStartPosition, GetCurrentPosition());
factor = std::move(unaryOperator);
} else if (IsAnyChar(NUMBER_FIRST_CHAR)) {
} else if (CheckIfChar(IsNumberFirstChar)) {
factor = ReadNumber();
if (type == "string")
factor->diagnostic = RaiseTypeError(
@@ -190,16 +203,16 @@ class GD_CORE_API ExpressionParser2 {
factor->diagnostic = RaiseTypeError(
_("You entered a number, but this type was expected:") + type,
expressionStartPosition);
} else if (IsAnyChar("(")) {
} else if (CheckIfChar(IsOpeningParenthesis)) {
SkipChar();
factor = SubExpression(type, objectName);
if (!IsAnyChar(")")) {
if (!CheckIfChar(IsClosingParenthesis)) {
factor->diagnostic =
RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
"parenthesis for each opening parenthesis."));
}
SkipIfIsAnyChar(")");
SkipIfChar(IsClosingParenthesis);
} else if (IsIdentifierAllowedChar()) {
// This is a place where the grammar differs according to the
// type being expected.
@@ -218,92 +231,131 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<SubExpressionNode> SubExpression(
const gd::String &type, const gd::String &objectName) {
return std::move(
gd::make_unique<SubExpressionNode>(Expression(type, objectName)));
size_t expressionStartPosition = GetCurrentPosition();
auto subExpression =
gd::make_unique<SubExpressionNode>(type, Expression(type, objectName));
subExpression->location =
ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
return std::move(subExpression);
};
std::unique_ptr<IdentifierOrFunctionOrEmptyNode> Identifier(
const gd::String &type) {
size_t identifierStartPosition = GetCurrentPosition();
gd::String name = ReadIdentifierName();
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
Identifier(const gd::String &type) {
auto identifierAndLocation = ReadIdentifierName();
gd::String name = identifierAndLocation.name;
auto nameLocation = identifierAndLocation.location;
SkipWhitespace();
SkipAllWhitespaces();
// We consider a namespace separator to be allowed here and be part of the
// function name (or object name, but object names are not allowed to
// contain a ":"). This is because functions from extensions have their
// extension name prefix, and separated by the namespace separator. This
// could maybe be refactored to create different nodes in the future.
if (IsNamespaceSeparator()) {
SkipNamespaceSeparator();
SkipAllWhitespaces();
auto postNamespaceIdentifierAndLocation = ReadIdentifierName();
name += NAMESPACE_SEPARATOR;
name += ReadIdentifierName();
name += postNamespaceIdentifierAndLocation.name;
ExpressionParserLocation completeNameLocation(
nameLocation.GetStartPosition(),
postNamespaceIdentifierAndLocation.location.GetEndPosition());
nameLocation = completeNameLocation;
}
if (IsAnyChar("(")) {
SkipChar();
return FreeFunction(type, name, identifierStartPosition);
} else if (IsAnyChar(DOT)) {
SkipChar();
if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
return FreeFunction(type, name, nameLocation, openingParenthesisLocation);
} else if (CheckIfChar(IsDot)) {
ExpressionParserLocation dotLocation = SkipChar();
SkipAllWhitespaces();
return ObjectFunctionOrBehaviorFunction(
type, name, identifierStartPosition);
type, name, nameLocation, dotLocation);
} else {
auto identifier = gd::make_unique<IdentifierNode>(name, type);
if (type == "string") {
identifier->diagnostic =
RaiseTypeError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."),
identifierStartPosition);
nameLocation.GetStartPosition());
} else if (type == "number") {
identifier->diagnostic = RaiseTypeError(_("You must enter a number."),
identifierStartPosition);
identifier->diagnostic = RaiseTypeError(
_("You must enter a number."), nameLocation.GetStartPosition());
} else if (!gd::ParameterMetadata::IsObject(type)) {
identifier->diagnostic = RaiseTypeError(
_("You've entered a name, but this type was expected:") + type,
identifierStartPosition);
nameLocation.GetStartPosition());
}
identifier->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
return std::move(identifier);
}
}
std::unique_ptr<VariableNode> Variable(const gd::String &type,
const gd::String &objectName) {
size_t identifierStartPosition = GetCurrentPosition();
auto identifierAndLocation = ReadIdentifierName();
const gd::String &name = identifierAndLocation.name;
const auto &nameLocation = identifierAndLocation.location;
gd::String name = ReadIdentifierName();
auto variable = gd::make_unique<VariableNode>(type, name, objectName);
variable->child = VariableAccessorOrVariableBracketAccessor();
variable->location = ExpressionParserLocation(
nameLocation.GetStartPosition(), GetCurrentPosition());
variable->nameLocation = nameLocation;
return std::move(variable);
}
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
VariableAccessorOrVariableBracketAccessor() {
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
SkipWhitespace();
if (IsAnyChar("[")) {
size_t childStartPosition = GetCurrentPosition();
SkipAllWhitespaces();
if (CheckIfChar(IsOpeningSquareBracket)) {
SkipChar();
child =
auto child =
gd::make_unique<VariableBracketAccessorNode>(Expression("string"));
if (!IsAnyChar("]")) {
if (!CheckIfChar(IsClosingSquareBracket)) {
child->diagnostic =
RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
"bracket for each opening bracket."));
}
SkipIfIsAnyChar("]");
SkipIfChar(IsClosingSquareBracket);
child->child = VariableAccessorOrVariableBracketAccessor();
} else if (IsAnyChar(DOT)) {
SkipChar();
SkipWhitespace();
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
child = gd::make_unique<VariableAccessorNode>(ReadIdentifierName());
return std::move(child);
} else if (CheckIfChar(IsDot)) {
auto dotLocation = SkipChar();
SkipAllWhitespaces();
auto identifierAndLocation = ReadIdentifierName();
auto child =
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
child->child = VariableAccessorOrVariableBracketAccessor();
child->nameLocation = identifierAndLocation.location;
child->dotLocation = dotLocation;
child->location =
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
return std::move(child);
}
return child;
return std::move(std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>());
}
std::unique_ptr<FunctionNode> FreeFunction(const gd::String &type,
const gd::String &functionFullName,
size_t functionStartPosition) {
std::unique_ptr<FunctionCallNode> FreeFunction(
const gd::String &type,
const gd::String &functionFullName,
const ExpressionParserLocation &identifierLocation,
const ExpressionParserLocation &openingParenthesisLocation) {
// TODO: error if trying to use function for type != "number" && != "string"
// + Test for it
@@ -316,31 +368,46 @@ class GD_CORE_API ExpressionParser2 {
platform, functionFullName);
auto parametersAndError = Parameters(metadata.parameters);
auto function = gd::make_unique<FunctionNode>(
auto function = gd::make_unique<FunctionCallNode>(
type, std::move(parametersAndError.first), metadata, functionFullName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic = ValidateFunction(*function, functionStartPosition);
function->diagnostic =
ValidateFunction(*function, identifierLocation.GetStartPosition());
function->location = ExpressionParserLocation(
identifierLocation.GetStartPosition(), GetCurrentPosition());
function->functionNameLocation = identifierLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
return std::move(function);
}
std::unique_ptr<FunctionOrEmptyNode> ObjectFunctionOrBehaviorFunction(
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode>
ObjectFunctionOrBehaviorFunction(
const gd::String &type,
const gd::String &objectName,
size_t functionStartPosition) {
gd::String objectFunctionOrBehaviorName = ReadIdentifierName();
const ExpressionParserLocation &objectNameLocation,
const ExpressionParserLocation &objectNameDotLocation) {
auto identifierAndLocation = ReadIdentifierName();
const gd::String &objectFunctionOrBehaviorName = identifierAndLocation.name;
const auto &objectFunctionOrBehaviorNameLocation =
identifierAndLocation.location;
SkipWhitespace();
SkipAllWhitespaces();
if (IsNamespaceSeparator()) {
SkipNamespaceSeparator();
ExpressionParserLocation namespaceSeparatorLocation =
SkipNamespaceSeparator();
SkipAllWhitespaces();
return BehaviorFunction(type,
objectName,
objectFunctionOrBehaviorName,
functionStartPosition);
} else if (IsAnyChar("(")) {
SkipChar();
objectNameLocation,
objectNameDotLocation,
objectFunctionOrBehaviorNameLocation,
namespaceSeparatorLocation);
} else if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
gd::String objectType =
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);
@@ -356,38 +423,56 @@ class GD_CORE_API ExpressionParser2 {
auto parametersAndError = Parameters(metadata.parameters, objectName);
auto function =
gd::make_unique<FunctionNode>(type,
objectName,
std::move(parametersAndError.first),
metadata,
objectFunctionOrBehaviorName);
gd::make_unique<FunctionCallNode>(type,
objectName,
std::move(parametersAndError.first),
metadata,
objectFunctionOrBehaviorName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic =
ValidateFunction(*function, functionStartPosition);
ValidateFunction(*function, objectNameLocation.GetStartPosition());
function->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = objectNameLocation;
function->objectNameDotLocation = objectNameDotLocation;
function->functionNameLocation = objectFunctionOrBehaviorNameLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
return std::move(function);
}
auto node = gd::make_unique<EmptyNode>(type);
auto node = gd::make_unique<ObjectFunctionNameNode>(
type, objectName, objectFunctionOrBehaviorName);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis (for an object expression), or double colon "
"(::) was expected (for a behavior expression)."));
node->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
node->objectNameLocation = objectNameLocation;
node->objectNameDotLocation = objectNameDotLocation;
node->objectFunctionOrBehaviorNameLocation =
objectFunctionOrBehaviorNameLocation;
return std::move(node);
}
std::unique_ptr<FunctionOrEmptyNode> BehaviorFunction(
std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
const gd::String &type,
const gd::String &objectName,
const gd::String &behaviorName,
size_t functionStartPosition) {
gd::String functionName = ReadIdentifierName();
const ExpressionParserLocation &objectNameLocation,
const ExpressionParserLocation &objectNameDotLocation,
const ExpressionParserLocation &behaviorNameLocation,
const ExpressionParserLocation &behaviorNameNamespaceSeparatorLocation) {
auto identifierAndLocation = ReadIdentifierName();
const gd::String &functionName = identifierAndLocation.name;
const auto &functionNameLocation = identifierAndLocation.location;
SkipWhitespace();
SkipAllWhitespaces();
if (IsAnyChar("(")) {
SkipChar();
if (CheckIfChar(IsOpeningParenthesis)) {
ExpressionParserLocation openingParenthesisLocation = SkipChar();
gd::String behaviorType = GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, behaviorName);
@@ -403,23 +488,41 @@ class GD_CORE_API ExpressionParser2 {
auto parametersAndError =
Parameters(metadata.parameters, objectName, behaviorName);
auto function =
gd::make_unique<FunctionNode>(type,
objectName,
behaviorName,
std::move(parametersAndError.first),
metadata,
functionName);
gd::make_unique<FunctionCallNode>(type,
objectName,
behaviorName,
std::move(parametersAndError.first),
metadata,
functionName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic =
ValidateFunction(*function, functionStartPosition);
ValidateFunction(*function, objectNameLocation.GetStartPosition());
function->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
function->objectNameLocation = objectNameLocation;
function->objectNameDotLocation = objectNameDotLocation;
function->behaviorNameLocation = behaviorNameLocation;
function->behaviorNameNamespaceSeparatorLocation =
behaviorNameNamespaceSeparatorLocation;
function->openingParenthesisLocation = openingParenthesisLocation;
function->functionNameLocation = functionNameLocation;
return std::move(function);
} else {
auto node = gd::make_unique<EmptyNode>(type);
auto node = gd::make_unique<ObjectFunctionNameNode>(
type, objectName, behaviorName, functionName);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis was expected here to call a function."));
node->location = ExpressionParserLocation(
objectNameLocation.GetStartPosition(), GetCurrentPosition());
node->objectNameLocation = objectNameLocation;
node->objectNameDotLocation = objectNameDotLocation;
node->objectFunctionOrBehaviorNameLocation = behaviorNameLocation;
node->behaviorNameNamespaceSeparatorLocation =
behaviorNameNamespaceSeparatorLocation;
node->behaviorFunctionNameLocation = functionNameLocation;
return std::move(node);
}
}
@@ -437,9 +540,9 @@ class GD_CORE_API ExpressionParser2 {
WrittenParametersFirstIndex(objectName, behaviorName);
while (!IsEndReached()) {
SkipWhitespace();
SkipAllWhitespaces();
if (IsAnyChar(")")) {
if (CheckIfChar(IsClosingParenthesis)) {
SkipChar();
return std::make_pair(std::move(parameters), nullptr);
} else {
@@ -479,8 +582,8 @@ class GD_CORE_API ExpressionParser2 {
GetCurrentPosition());
}
SkipWhitespace();
SkipIfIsAnyChar(PARAMETERS_SEPARATOR);
SkipAllWhitespaces();
SkipIfChar(IsParameterSeparator);
parameterIndex++;
}
}
@@ -497,7 +600,7 @@ class GD_CORE_API ExpressionParser2 {
*/
///@{
std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
const gd::FunctionNode &function, size_t functionStartPosition);
const gd::FunctionCallNode &function, size_t functionStartPosition);
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
const gd::String &type, gd::String::value_type operatorChar) {
@@ -525,7 +628,8 @@ class GD_CORE_API ExpressionParser2 {
} else if (gd::ParameterMetadata::IsObject(type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -, /, *) can't be used with an object name. Remove the operator."),
_("Operators (+, -, /, *) can't be used with an object name. Remove "
"the operator."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
@@ -561,7 +665,8 @@ class GD_CORE_API ExpressionParser2 {
} else if (gd::ParameterMetadata::IsObject(type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -) can't be used with an object name. Remove the operator."),
_("Operators (+, -) can't be used with an object name. Remove the "
"operator."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
@@ -579,55 +684,133 @@ class GD_CORE_API ExpressionParser2 {
* Read tokens or characters
*/
///@{
void SkipChar() { currentPosition++; }
ExpressionParserLocation SkipChar() {
size_t startPosition = currentPosition;
return ExpressionParserLocation(startPosition, ++currentPosition);
}
void SkipWhitespace() {
void SkipAllWhitespaces() {
while (currentPosition < expression.size() &&
WHITESPACES.find(expression[currentPosition]) != gd::String::npos) {
IsWhitespace(expression[currentPosition])) {
currentPosition++;
}
}
void SkipIfIsAnyChar(const gd::String &allowedCharacters) {
if (IsAnyChar(allowedCharacters)) {
void SkipIfChar(
const std::function<bool(gd::String::value_type)> &predicate) {
if (CheckIfChar(predicate)) {
currentPosition++;
}
}
void SkipNamespaceSeparator() {
ExpressionParserLocation SkipNamespaceSeparator() {
size_t startPosition = currentPosition;
// Namespace separator is a special kind of delimiter as it is 2 characters
// long
if (IsNamespaceSeparator()) {
currentPosition += NAMESPACE_SEPARATOR.size();
}
return ExpressionParserLocation(startPosition, currentPosition);
}
bool IsAnyChar(const gd::String &allowedCharacters) {
if (currentPosition < expression.size() &&
allowedCharacters.find(expression[currentPosition]) !=
gd::String::npos) {
return true;
}
bool CheckIfChar(
const std::function<bool(gd::String::value_type)> &predicate) {
if (currentPosition >= expression.size()) return false;
gd::String::value_type character = expression[currentPosition];
return false;
return predicate(character);
}
bool IsIdentifierAllowedChar() {
if (currentPosition < expression.size() &&
PARAMETERS_SEPARATOR.find(expression[currentPosition]) ==
gd::String::npos &&
DOT.find(expression[currentPosition]) == gd::String::npos &&
QUOTE.find(expression[currentPosition]) == gd::String::npos &&
BRACKETS.find(expression[currentPosition]) == gd::String::npos &&
EXPRESSION_OPERATORS.find(expression[currentPosition]) ==
gd::String::npos &&
TERM_OPERATORS.find(expression[currentPosition]) == gd::String::npos) {
if (currentPosition >= expression.size()) return false;
gd::String::value_type character = expression[currentPosition];
// Quickly compare if the character is a number or ASCII character.
if ((character >= '0' && character <= '9') ||
(character >= 'A' && character <= 'Z') ||
(character >= 'a' && character <= 'z'))
return true;
// Otherwise do the full check against separators forbidden in identifiers.
if (!IsParameterSeparator(character) && !IsDot(character) &&
!IsQuote(character) && !IsBracket(character) &&
!IsExpressionOperator(character) && !IsTermOperator(character)) {
return true;
}
return false;
}
static bool IsWhitespace(gd::String::value_type character) {
return character == ' ' || character == '\n' || character == '\r';
}
static bool IsParameterSeparator(gd::String::value_type character) {
return character == ',';
}
static bool IsDot(gd::String::value_type character) {
return character == '.';
}
static bool IsQuote(gd::String::value_type character) {
return character == '"';
}
static bool IsBracket(gd::String::value_type character) {
return character == '(' || character == ')' || character == '[' ||
character == ']' || character == '{' || character == '}';
}
static bool IsOpeningParenthesis(gd::String::value_type character) {
return character == '(';
}
static bool IsClosingParenthesis(gd::String::value_type character) {
return character == ')';
}
static bool IsOpeningSquareBracket(gd::String::value_type character) {
return character == '[';
}
static bool IsClosingSquareBracket(gd::String::value_type character) {
return character == ']';
}
static bool IsExpressionEndingChar(gd::String::value_type character) {
return character == ',' || IsClosingParenthesis(character) ||
IsClosingSquareBracket(character);
}
static bool IsExpressionOperator(gd::String::value_type character) {
return character == '+' || character == '-' || character == '<' ||
character == '>' || character == '?' || character == '^' ||
character == '=' || character == '\\' || character == ':' ||
character == '!';
}
static bool IsUnaryOperator(gd::String::value_type character) {
return character == '+' || character == '-';
}
static bool IsTermOperator(gd::String::value_type character) {
return character == '/' || character == '*';
}
static bool IsNumberFirstChar(gd::String::value_type character) {
return character == '.' || (character >= '0' && character <= '9');
}
static bool IsNonZeroDigit(gd::String::value_type character) {
return (character >= '1' && character <= '9');
}
static bool IsZeroDigit(gd::String::value_type character) {
return character == '0';
}
bool IsNamespaceSeparator() {
// Namespace separator is a special kind of delimiter as it is 2 characters
// long
@@ -638,12 +821,19 @@ class GD_CORE_API ExpressionParser2 {
bool IsEndReached() { return currentPosition >= expression.size(); }
gd::String ReadIdentifierName() {
struct IdentifierAndLocation {
gd::String name;
ExpressionParserLocation location;
};
IdentifierAndLocation ReadIdentifierName() {
gd::String name;
size_t startPosition = currentPosition;
while (currentPosition < expression.size() &&
(IsIdentifierAllowedChar()
// Allow whitespace in identifier name for compatibility
|| expression[currentPosition] == ' ')) {
||
expression[currentPosition] == ' ')) {
name += expression[currentPosition];
currentPosition++;
}
@@ -651,12 +841,23 @@ class GD_CORE_API ExpressionParser2 {
// Trim whitespace at the end (we allow them for compatibility inside
// the name, but after the last character that is not whitespace, they
// should be ignore again).
size_t lastCharacterPos = name.find_last_not_of(WHITESPACES);
if (!name.empty() && (lastCharacterPos + 1) < name.size()) {
name.erase(lastCharacterPos + 1);
if (!name.empty() && IsWhitespace(name[name.size() - 1])) {
size_t lastCharacterPos = name.size() - 1;
while (lastCharacterPos < name.size() &&
IsWhitespace(name[lastCharacterPos])) {
lastCharacterPos--;
}
if ((lastCharacterPos + 1) < name.size()) {
name.erase(lastCharacterPos + 1);
}
}
return name;
IdentifierAndLocation identifierAndLocation{
name,
// The location is ignoring the trailing whitespace (only whitespace
// inside the identifier are allowed for compatibility).
ExpressionParserLocation(startPosition, startPosition + name.size())};
return identifierAndLocation;
}
std::unique_ptr<TextNode> ReadText();
@@ -664,24 +865,32 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<NumberNode> ReadNumber();
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size() &&
WHITESPACES.find(expression[currentPosition]) == gd::String::npos) {
!IsWhitespace(expression[currentPosition])) {
text += expression[currentPosition];
currentPosition++;
}
return gd::make_unique<EmptyNode>(type, text);
auto node = gd::make_unique<EmptyNode>(type, text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
}
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
size_t startPosition = GetCurrentPosition();
gd::String text;
while (currentPosition < expression.size()) {
text += expression[currentPosition];
currentPosition++;
}
return gd::make_unique<EmptyNode>(type, text);
auto node = gd::make_unique<EmptyNode>(type, text);
node->location =
ExpressionParserLocation(startPosition, GetCurrentPosition());
return node;
}
size_t GetCurrentPosition() { return currentPosition; }
@@ -746,15 +955,6 @@ class GD_CORE_API ExpressionParser2 {
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
static gd::String NUMBER_FIRST_CHAR;
static gd::String DOT;
static gd::String PARAMETERS_SEPARATOR;
static gd::String QUOTE;
static gd::String BRACKETS;
static gd::String EXPRESSION_OPERATORS;
static gd::String TERM_OPERATORS;
static gd::String UNARY_OPERATORS;
static gd::String WHITESPACES;
static gd::String NAMESPACE_SEPARATOR;
};

View File

@@ -20,6 +20,24 @@ class ExpressionMetadata;
namespace gd {
struct ExpressionParserLocation {
ExpressionParserLocation() : isValid(false){};
ExpressionParserLocation(size_t position)
: isValid(true), startPosition(position), endPosition(position){};
ExpressionParserLocation(size_t startPosition_, size_t endPosition_)
: isValid(true),
startPosition(startPosition_),
endPosition(endPosition_){};
size_t GetStartPosition() const { return startPosition; }
size_t GetEndPosition() const { return endPosition; }
bool IsValid() const { return isValid; }
private:
bool isValid;
size_t startPosition;
size_t endPosition;
};
/**
* \brief A diagnostic that can be attached to a gd::ExpressionNode.
*/
@@ -40,30 +58,25 @@ struct ExpressionParserError : public ExpressionParserDiagnostic {
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
size_t position_)
: type(type_),
message(message_),
startPosition(position_),
endPosition(position_){};
: type(type_), message(message_), location(position_){};
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
size_t startPosition_,
size_t endPosition_)
: type(type_),
message(message_),
startPosition(startPosition_),
endPosition(endPosition_){};
location(startPosition_, endPosition_){};
virtual ~ExpressionParserError(){};
bool IsError() override { return true; }
const gd::String &GetMessage() override { return message; }
size_t GetStartPosition() override { return startPosition; }
size_t GetEndPosition() override { return endPosition; }
size_t GetStartPosition() override { return location.GetStartPosition(); }
size_t GetEndPosition() override { return location.GetEndPosition(); }
private:
gd::String type;
gd::String message;
size_t startPosition;
size_t endPosition;
ExpressionParserLocation location;
};
/**
@@ -75,16 +88,26 @@ struct ExpressionNode {
virtual void Visit(ExpressionParser2NodeWorker &worker){};
std::unique_ptr<ExpressionParserDiagnostic> diagnostic;
ExpressionParserLocation location; ///< The location of the entire node. Some
///nodes might have other locations stored
///inside them. For example, a function
///can store the position of the object
///name, the dot, the function name,
///etc...
};
struct SubExpressionNode : public ExpressionNode {
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
: expression(std::move(expression_)){};
SubExpressionNode(const gd::String &type_,
std::unique_ptr<ExpressionNode> expression_)
: type(type_), expression(std::move(expression_)){};
virtual ~SubExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitSubExpressionNode(*this);
};
gd::String type; // "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
std::unique_ptr<ExpressionNode> expression;
};
@@ -92,6 +115,8 @@ struct SubExpressionNode : public ExpressionNode {
* \brief An operator node. For example: "lhs + rhs".
*/
struct OperatorNode : public ExpressionNode {
OperatorNode(const gd::String &type_, gd::String::value_type op_)
: type(type_), op(op_){};
virtual ~OperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitOperatorNode(*this);
@@ -99,6 +124,9 @@ struct OperatorNode : public ExpressionNode {
std::unique_ptr<ExpressionNode> leftHandSide;
std::unique_ptr<ExpressionNode> rightHandSide;
gd::String type; // "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
gd::String::value_type op;
};
@@ -106,13 +134,17 @@ struct OperatorNode : public ExpressionNode {
* \brief A unary operator node. For example: "-2".
*/
struct UnaryOperatorNode : public ExpressionNode {
UnaryOperatorNode(gd::String::value_type op_) : op(op_){};
UnaryOperatorNode(const gd::String &type_, gd::String::value_type op_)
: type(type_), op(op_){};
virtual ~UnaryOperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitUnaryOperatorNode(*this);
};
std::unique_ptr<ExpressionNode> factor;
gd::String type; // "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
gd::String::value_type op;
};
@@ -170,6 +202,8 @@ struct VariableNode : public ExpressionNode {
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
child; // Can be nullptr if no accessor
ExpressionParserLocation nameLocation;
};
/**
@@ -185,6 +219,8 @@ struct VariableAccessorNode
};
gd::String name;
ExpressionParserLocation nameLocation;
ExpressionParserLocation dotLocation;
};
/**
@@ -203,12 +239,14 @@ struct VariableBracketAccessorNode
std::unique_ptr<ExpressionNode> expression;
};
struct IdentifierOrFunctionOrEmptyNode : public ExpressionNode {};
struct IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
: public ExpressionNode {};
/**
* \brief An identifier node, usually representing an object.
* \brief An identifier node, usually representing an object or a function name.
*/
struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode {
struct IdentifierNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
IdentifierNode(const gd::String &identifierName_, const gd::String &type_)
: identifierName(identifierName_), type(type_){};
virtual ~IdentifierNode(){};
@@ -220,64 +258,140 @@ struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode {
gd::String type;
};
struct FunctionOrEmptyNode : public IdentifierOrFunctionOrEmptyNode {
virtual ~FunctionOrEmptyNode(){};
struct FunctionCallOrObjectFunctionNameOrEmptyNode
: public IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode {
virtual ~FunctionCallOrObjectFunctionNameOrEmptyNode(){};
void Visit(ExpressionParser2NodeWorker &worker) override{};
};
/**
* \brief A function node. For example: "MyExtension::MyFunction(1, 2)".
* \brief The name of a function to call on an object or the behavior
* For example: "MyObject.Function" or "MyObject.Physics" or
* "MyObject.Physics::LinearVelocity".
*/
struct FunctionNode : public FunctionOrEmptyNode {
FunctionNode(const gd::String &type_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
struct ObjectFunctionNameNode
: public FunctionCallOrObjectFunctionNameOrEmptyNode {
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &objectFunctionOrBehaviorName_)
: type(type_),
objectName(objectName_),
objectFunctionOrBehaviorName(objectFunctionOrBehaviorName_) {}
ObjectFunctionNameNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &behaviorName_,
const gd::String &behaviorFunctionName_)
: type(type_),
objectName(objectName_),
objectFunctionOrBehaviorName(behaviorName_),
behaviorFunctionName(behaviorFunctionName_) {}
virtual ~ObjectFunctionNameNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitObjectFunctionNameNode(*this);
};
gd::String type; // This could be removed if the type ("string", "number",
// type supported by gd::ParameterMetadata::IsObject, types
// supported by gd::ParameterMetadata::IsExpression or
// "unknown") was stored in ExpressionMetadata.
gd::String objectName;
gd::String objectFunctionOrBehaviorName; ///< Behavior name if
///`behaviorFunctionName` is not
///empty.
gd::String behaviorFunctionName; ///< If empty, then
///objectFunctionOrBehaviorName is filled
///with the behavior name.
ExpressionParserLocation
objectNameLocation; ///< Location of the object name.
ExpressionParserLocation
objectNameDotLocation; ///< Location of the "." after the object name.
ExpressionParserLocation objectFunctionOrBehaviorNameLocation; ///< Location
///of object
///function
///name or
///behavior
///name.
ExpressionParserLocation
behaviorNameNamespaceSeparatorLocation; ///< Location of the "::"
///separator, if any.
ExpressionParserLocation behaviorFunctionNameLocation; ///< Location of the
///behavior function
///name, if any.
};
/**
* \brief A function call node (either free function, object function or object
* behavior function).
* For example: "MyExtension::MyFunction(1, 2)", "MyObject.Function()" or
* "MyObject.Physics::LinearVelocity()".
*/
struct FunctionCallNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
FunctionCallNode(const gd::String &type_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: type(type_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
FunctionNode(const gd::String &type_,
const gd::String &objectName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: type(type_),
objectName(objectName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
FunctionNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &behaviorName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
FunctionCallNode(const gd::String &type_,
const gd::String &objectName_,
const gd::String &behaviorName_,
std::vector<std::unique_ptr<ExpressionNode>> parameters_,
const ExpressionMetadata &expressionMetadata_,
const gd::String &functionName_)
: type(type_),
objectName(objectName_),
behaviorName(behaviorName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
virtual ~FunctionNode(){};
virtual ~FunctionCallNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitFunctionNode(*this);
worker.OnVisitFunctionCallNode(*this);
};
gd::String type; // This could be removed if the type ("string" or "number")
// was stored in ExpressionMetadata.
gd::String type; // This could be removed if the type ("string", "number",
// type supported by gd::ParameterMetadata::IsObject, types
// supported by gd::ParameterMetadata::IsExpression or
// "unknown") was stored in ExpressionMetadata.
gd::String objectName;
gd::String behaviorName;
std::vector<std::unique_ptr<ExpressionNode>> parameters;
const ExpressionMetadata &expressionMetadata;
gd::String functionName;
ExpressionParserLocation
functionNameLocation; ///< Location of the function name.
ExpressionParserLocation
objectNameLocation; ///< Location of the object name, if any.
ExpressionParserLocation
objectNameDotLocation; ///< Location of the "." after the object name.
ExpressionParserLocation
behaviorNameLocation; ///< Location of the behavior name, if any.
ExpressionParserLocation
behaviorNameNamespaceSeparatorLocation; ///< Location of the "::"
///separator, if any.
ExpressionParserLocation
openingParenthesisLocation; ///< Location of the "(".
};
/**
* \brief An empty node, used when parsing failed/a syntax error was
* encountered and any other node could not make sense.
*/
struct EmptyNode : public FunctionOrEmptyNode {
struct EmptyNode : public FunctionCallOrObjectFunctionNameOrEmptyNode {
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
: type(type_), text(text_){};
virtual ~EmptyNode(){};
@@ -285,10 +399,12 @@ struct EmptyNode : public FunctionOrEmptyNode {
worker.OnVisitEmptyNode(*this);
};
gd::String type;
gd::String type; // "string", "number", type supported by
// gd::ParameterMetadata::IsObject, types supported by
// gd::ParameterMetadata::IsExpression or "unknown".
gd::String text;
};
} // namespace gd
#endif
#endif

View File

@@ -92,7 +92,15 @@ class GD_CORE_API ExpressionParser2NodePrinter
void OnVisitIdentifierNode(IdentifierNode& node) override {
output += node.identifierName;
}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
output +=
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
} else {
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
}
};
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (!node.behaviorName.empty()) {
output +=
node.objectName + "." + node.behaviorName + "::" + node.functionName;

View File

@@ -16,10 +16,11 @@ class TextNode;
class VariableNode;
class VariableAccessorNode;
class VariableBracketAccessorNode;
class IdentifierOrFunctionOrEmptyNode;
class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
class IdentifierNode;
class FunctionOrEmptyNode;
class FunctionNode;
class FunctionCallOrObjectFunctionNameOrEmptyNode;
class ObjectFunctionNameNode;
class FunctionCallNode;
class EmptyNode;
} // namespace gd
@@ -42,10 +43,11 @@ class GD_CORE_API ExpressionParser2NodeWorker {
friend class VariableNode;
friend class VariableAccessorNode;
friend class VariableBracketAccessorNode;
friend class IdentifierOrFunctionOrEmptyNode;
friend class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
friend class IdentifierNode;
friend class FunctionOrEmptyNode;
friend class FunctionNode;
friend class FunctionCallOrObjectFunctionNameOrEmptyNode;
friend class ObjectFunctionNameNode;
friend class FunctionCallNode;
friend class EmptyNode;
public:
@@ -62,10 +64,11 @@ class GD_CORE_API ExpressionParser2NodeWorker {
virtual void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) = 0;
virtual void OnVisitIdentifierNode(IdentifierNode& node) = 0;
virtual void OnVisitFunctionNode(FunctionNode& node) = 0;
virtual void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) = 0;
virtual void OnVisitFunctionCallNode(FunctionCallNode& node) = 0;
virtual void OnVisitEmptyNode(EmptyNode& node) = 0;
};
} // namespace gd
#endif
#endif

View File

@@ -1,141 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Parsers/VariableParser.h"
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Layout;
}
namespace gd {
class Project;
}
namespace gd {
class Platform;
}
#include "GDCore/Tools/Localization.h"
namespace gd {
VariableParser::~VariableParser() {}
bool VariableParser::Parse(VariableParserCallbacks& callbacks_) {
callbacks = &callbacks_;
rootVariableParsed = false;
firstErrorStr.clear();
firstErrorPos = 0;
currentPositionIt = expression.begin();
currentTokenType = TS_INVALID;
currentToken.clear();
S();
return firstErrorStr == "";
}
void VariableParser::ReadToken() {
currentTokenType = TS_INVALID;
currentToken.clear();
while (currentPositionIt != expression.end()) {
char32_t currentChar = *currentPositionIt;
if (currentChar == U'[' || currentChar == U']' || currentChar == U'.') {
if (currentTokenType == TS_VARNAME)
return; // We've parsed a variable name.
}
if (currentChar == U'[') {
currentTokenType = TS_OPENING_BRACKET;
currentToken.clear();
++currentPositionIt;
return;
} else if (currentChar == U']') {
currentTokenType = TS_CLOSING_BRACKET;
currentToken.clear();
++currentPositionIt;
return;
} else if (currentChar == U'.') {
currentTokenType = TS_PERIOD;
currentToken.clear();
++currentPositionIt;
return;
}
currentTokenType = TS_VARNAME; // We're parsing a variable name.
currentToken.push_back(currentChar);
++currentPositionIt;
}
// Can be reached if we are at the end of the expression. In this case,
// currentTokenType will be either TS_VARNAME or TS_INVALID.
}
void VariableParser::S() {
ReadToken();
if (currentTokenType != TS_VARNAME) {
firstErrorStr = _("Expecting a variable name.");
firstErrorPos = std::distance<gd::String::const_iterator>(
expression.begin(), currentPositionIt);
return;
}
if (!rootVariableParsed) {
rootVariableParsed = true;
if (callbacks) callbacks->OnRootVariable(currentToken);
} else if (callbacks)
callbacks->OnChildVariable(currentToken);
X();
}
void VariableParser::X() {
ReadToken();
if (currentTokenType == TS_INVALID)
return; // Ended parsing.
else if (currentTokenType == TS_PERIOD)
S();
else if (currentTokenType == TS_OPENING_BRACKET) {
gd::String strExpr = SkipStringExpression();
ReadToken();
if (currentTokenType != TS_CLOSING_BRACKET) {
firstErrorStr = _("Expecting ]");
firstErrorPos = std::distance<gd::String::const_iterator>(
expression.begin(), currentPositionIt);
return;
}
if (callbacks) callbacks->OnChildSubscript(strExpr);
X();
}
}
gd::String VariableParser::SkipStringExpression() {
gd::String stringExpression;
bool insideStringLiteral = false;
bool lastCharacterWasBackslash = false;
unsigned int nestedBracket = 0;
while (currentPositionIt != expression.end()) {
char32_t currentChar = *currentPositionIt;
if (currentChar == U'\"') {
if (!insideStringLiteral)
insideStringLiteral = true;
else if (!lastCharacterWasBackslash)
insideStringLiteral = false;
} else if (currentChar == U'[' && !insideStringLiteral) {
nestedBracket++;
} else if (currentChar == U']' && !insideStringLiteral) {
if (nestedBracket == 0)
return stringExpression; // Found the end of the string litteral.
nestedBracket--;
}
lastCharacterWasBackslash = currentChar == U'\\';
stringExpression.push_back(currentChar);
++currentPositionIt;
}
// End of the expression reached (so expression is invalid by the way)
return stringExpression;
}
} // namespace gd

View File

@@ -1,148 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_VARIABLEPARSER_H
#define GDCORE_VARIABLEPARSER_H
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Layout;
}
namespace gd {
class Project;
}
namespace gd {
class Platform;
}
namespace gd {
class VariableParserCallbacks;
}
namespace gd {
/** \brief Parse a variable expression.
*
* Parse an variable expression ( like
myVariable.child["subchild"+ToString(i)].subsubchild ),
* calling callbacks when a token is reached.
*
* Usage example:
\code
//...
//VariableCodeGenerationCallbacks is a class inheriting from
gd::VariableParserCallbacks VariableCodeGenerationCallbacks callbacks(output,
*this, context, VariableCodeGenerationCallbacks::PROJECT_VARIABLE);
gd::VariableParser parser(parameter);
if ( !parser.Parse(callbacks) )
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter <<
endl;
\endcode
*
* Here is the parsed grammar: <br>
* S -> VarName X <br>
* X -> e | . S | [StringExpression] X <br>
*
* where e = nothing (end of expression), StringExpression = A valid string
expression and
* S is the start.
*
* \see gd::VariableParserCallbacks
*/
class GD_CORE_API VariableParser {
public:
/**
* \brief Default constructor
* \param expressionPlainString The string representing the expression to be
* parsed.
*/
VariableParser(const gd::String& expressionPlainString_)
: currentPositionIt(), expression(expressionPlainString_){};
virtual ~VariableParser();
/**
* Parse the expression, calling each callback when necessary.
* \param callbacks The callbacks to be called.
* \return true if expression was correctly parsed.
* \see gd::VariableParserCallbacks
*/
bool Parse(VariableParserCallbacks& callbacks);
/**
* \brief Return the description of the error that was found
*/
const gd::String& GetFirstError() { return firstErrorStr; }
/**
* \brief Return the position of the error that was found
* \return The position, or gd::String::npos if no error is found
*/
size_t GetFirstErrorPosition() { return firstErrorPos; }
gd::String firstErrorStr;
size_t firstErrorPos;
private:
void S();
void X();
/**
* \brief Skip the string expression, starting from the current position.
* \return The string expression skipped. currentPosition is now put on the
* closing bracket.
*/
gd::String SkipStringExpression();
void ReadToken();
enum TokenType {
TS_PERIOD,
TS_OPENING_BRACKET,
TS_CLOSING_BRACKET,
TS_VARNAME,
TS_INVALID
};
TokenType currentTokenType;
gd::String currentToken;
gd::String::const_iterator currentPositionIt;
gd::String expression;
VariableParserCallbacks* callbacks;
bool rootVariableParsed;
};
/**
* \brief Callbacks called by VariableParser when parsing a variable expression.
*/
class GD_CORE_API VariableParserCallbacks {
public:
/**
* \brief Called when the first variable has been parsed. ( varName1 in
* varName1.child for example. ) \param variableName The variable name.
*/
virtual void OnRootVariable(gd::String variableName) = 0;
/**
* \brief Called when accessing the child of a structure variable. ( child in
* varName1.child for example. ) \param variableName The child variable name.
*/
virtual void OnChildVariable(gd::String variableName) = 0;
/**
* \brief Called when accessing the child of a structure variable using a
* string expression in square brackets. ( "subscript" in
* varName1["subscript"] for example. )
*
* \param variableName The expression used to access the child variable.
*/
virtual void OnChildSubscript(gd::String stringExpression) = 0;
};
} // namespace gd
#endif // GDEXPRESSIONPARSER_H

View File

@@ -8,10 +8,15 @@
#include "GDCore/CommonTools.h"
#include "GDCore/String.h"
EventsCodeNameMangler *EventsCodeNameMangler::_singleton = NULL;
EventsCodeNameMangler *EventsCodeNameMangler::_singleton = nullptr;
gd::String EventsCodeNameMangler::GetMangledObjectsListName(
const gd::String& EventsCodeNameMangler::GetMangledObjectsListName(
const gd::String &originalObjectName) {
auto it = mangledObjectNames.find(originalObjectName);
if (it != mangledObjectNames.end()) {
return it->second;
}
gd::String partiallyMangledName = originalObjectName;
static const gd::String allowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -27,11 +32,17 @@ gd::String EventsCodeNameMangler::GetMangledObjectsListName(
}
}
return "GD" + partiallyMangledName + "Objects";
mangledObjectNames[originalObjectName] = "GD" + partiallyMangledName + "Objects";
return mangledObjectNames[originalObjectName];
}
gd::String EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
const gd::String& EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
const gd::String &externalEventsName) {
auto it = mangledExternalEventsNames.find(externalEventsName);
if (it != mangledExternalEventsNames.end()) {
return it->second;
}
gd::String partiallyMangledName = externalEventsName;
static const gd::String allowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -47,23 +58,24 @@ gd::String EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
}
}
return "GDExternalEvents" + partiallyMangledName;
mangledExternalEventsNames[externalEventsName] = "GDExternalEvents" + partiallyMangledName;
return mangledExternalEventsNames[externalEventsName];
}
gd::String ManObjListName(const gd::String &objectName) {
const gd::String& ManObjListName(const gd::String &objectName) {
return EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName);
}
EventsCodeNameMangler *EventsCodeNameMangler::Get() {
if (NULL == _singleton) _singleton = new EventsCodeNameMangler;
if (nullptr == _singleton) _singleton = new EventsCodeNameMangler;
return (static_cast<EventsCodeNameMangler *>(_singleton));
}
void EventsCodeNameMangler::DestroySingleton() {
if (NULL != _singleton) {
if (nullptr != _singleton) {
delete _singleton;
_singleton = NULL;
_singleton = nullptr;
}
}

View File

@@ -6,26 +6,34 @@
#if defined(GD_IDE_ONLY)
#ifndef EVENTSCODENAMEMANGLER_H
#define EVENTSCODENAMEMANGLER_H
#include <unordered_map>
#include "GDCore/String.h"
/**
* Manage name mangling, so as to ensure all names used in code are valid.
* \brief Mangle object names, so as to ensure all names used in code are valid.
*
* \see ManObjListName
*/
class GD_CORE_API EventsCodeNameMangler {
public:
/**
* Get the mangled name from a name : All characters that are not 0-9, a-z,
* Get the mangled name from a name: All characters that are not 0-9, a-z,
* A-Z or _ are replaced by "_"+AsciiCodeOfTheCharacter.
*
* The mangled name is memoized as this is intensively used during project
* export and events code generation.
*/
gd::String GetMangledObjectsListName(const gd::String &originalObjectName);
const gd::String &GetMangledObjectsListName(
const gd::String &originalObjectName);
/**
* Get the mangled function name to be used to call external events named \a
* externalEventsName.
*
* The mangled name is memoized as this is intensively used during project
* export and events code generation.
*/
gd::String GetExternalEventsFunctionMangledName(
const gd::String &GetExternalEventsFunctionMangledName(
const gd::String &externalEventsName);
static EventsCodeNameMangler *Get();
@@ -35,14 +43,22 @@ class GD_CORE_API EventsCodeNameMangler {
EventsCodeNameMangler(){};
virtual ~EventsCodeNameMangler(){};
static EventsCodeNameMangler *_singleton;
std::unordered_map<gd::String, gd::String>
mangledObjectNames; ///< Memoized results of mangling for objects
std::unordered_map<gd::String, gd::String>
mangledExternalEventsNames; ///< Memoized results of mangling for
/// external events
};
/**
* Shortcut to
* EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName). \see
* EventsCodeNameMangler \return Mangled object name
* Shortcut for
* `EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName)`.
*
* \see EventsCodeNameMangler
* \return Mangled object name
*/
gd::String GD_CORE_API ManObjListName(const gd::String &objectName);
const gd::String &GD_CORE_API ManObjListName(const gd::String &objectName);
#endif // EVENTSCODENAMEMANGLER_H
#endif

View File

@@ -35,10 +35,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
.AddParameter("expression", _("Channel identifier"))
.AddParameter("yesorno", _("Repeat the sound"), "", true)
.SetDefaultValue("no")
.AddParameter(
"expression", _("Volume (from 0 to 100, 100 by default)"), "", true)
.AddParameter("expression", _("Volume"), "", true)
.SetParameterLongDescription(_("From 0 to 100, 100 by default."))
.SetDefaultValue("100")
.AddParameter("expression", _("Pitch (speed) (1 by default)"), "", true)
.AddParameter("expression", _("Pitch (speed)"), "", true)
.SetParameterLongDescription(_("1 by default."))
.SetDefaultValue("1")
.MarkAsAdvanced();
@@ -93,10 +94,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
.AddParameter("expression", _("Channel identifier"))
.AddParameter("yesorno", _("Repeat the sound"), "", true)
.SetDefaultValue("no")
.AddParameter(
"expression", _("Volume (from 0 to 100, 100 by default)"), "", true)
.AddParameter("expression", _("Volume"), "", true)
.SetParameterLongDescription(_("From 0 to 100, 100 by default."))
.SetDefaultValue("100")
.AddParameter("expression", _("Pitch (speed) (1 by default)"), "", true)
.AddParameter("expression", _("Pitch (speed)"), "", true)
.SetParameterLongDescription(_("1 by default."))
.SetDefaultValue("1")
.MarkAsAdvanced();
@@ -141,117 +143,97 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
_("Volume of the sound on a channel"),
_("This action modifies the volume of the sound on the "
"specified channel. The volume is between 0 and 100."),
_("Do _PARAM2__PARAM3_ to the volume of the sound on channel "
"_PARAM1_"),
_("the volume of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/actions/sonVolume24.png",
"res/actions/sonVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("ModVolumeMusicCanal",
_("Volume of the music on a channel"),
_("This action modifies the volume of the music on the "
"specified channel. The volume is between 0 and 100."),
_("Do _PARAM2__PARAM3_ to the volume of the music on channel "
"_PARAM1_"),
_("the volume of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/actions/musicVolume24.png",
"res/actions/musicVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("ModGlobalVolume",
_("Game global volume"),
_("This action modifies the global volume of the game. The "
"volume is between 0 and 100."),
_("Do _PARAM1__PARAM2_ to global sound level"),
_("the global sound level"),
_("Audio"),
"res/actions/volume24.png",
"res/actions/volume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
extension
.AddAction("ModPitchSoundChannel",
_("Pitch of the sound of a channel"),
_("This action modifies the pitch (speed) of the sound on a "
"channel.\n1 is the default pitch."),
_("Do _PARAM2__PARAM3_ to the pitch of the sound on channel "
"_PARAM1_"),
_("the pitch of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/actions/son24.png",
"res/actions/son.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("ModPitchMusicChannel",
_("Pitch of the music on a channel"),
_("This action modifies the pitch of the music on the "
"specified channel. 1 is the default pitch"),
_("Do _PARAM2__PARAM3_ to the pitch of the music on channel "
"_PARAM1_"),
_("the pitch of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/actions/music24.png",
"res/actions/music.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("ModPlayingOffsetSoundChannel",
_("Playing offset of the sound on a channel"),
_("This action modifies the playing offset of the sound on a "
"channel"),
_("Do _PARAM2__PARAM3_ to the playing offset of the sound on "
"channel _PARAM1_"),
_("the playing offset of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/actions/son24.png",
"res/actions/son.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("ModPlayingOffsetMusicChannel",
_("Playing offset of the music on a channel"),
_("This action modifies the playing offset of the music on "
"the specified channel"),
_("Do _PARAM2__PARAM3_ to the playing offset of the music on "
"channel _PARAM1_"),
_("the playing offset of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/actions/music24.png",
"res/actions/music.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction("PlaySound",
@@ -265,10 +247,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
.AddParameter("soundfile", _("Audio file (or audio resource name)"))
.AddParameter("yesorno", _("Repeat the sound"), "", true)
.SetDefaultValue("no")
.AddParameter(
"expression", _("Volume (from 0 to 100, 100 by default)"), "", true)
.AddParameter("expression", _("Volume"), "", true)
.SetParameterLongDescription(_("From 0 to 100, 100 by default."))
.SetDefaultValue("100")
.AddParameter("expression", _("Pitch (speed) (1 by default)"), "", true)
.AddParameter("expression", _("Pitch (speed)"), "", true)
.SetParameterLongDescription(_("1 by default."))
.SetDefaultValue("1")
.MarkAsSimple();
@@ -284,10 +267,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
.AddParameter("musicfile", _("Audio file (or audio resource name)"))
.AddParameter("yesorno", _("Repeat the sound"), "", true)
.SetDefaultValue("no")
.AddParameter(
"expression", _("Volume (from 0 to 100, 100 by default)"), "", true)
.AddParameter("expression", _("Volume"), "", true)
.SetParameterLongDescription(_("From 0 to 100, 100 by default."))
.SetDefaultValue("100")
.AddParameter("expression", _("Pitch (speed) (1 by default)"), "", true)
.AddParameter("expression", _("Pitch (speed)"), "", true)
.SetParameterLongDescription(_("1 by default."))
.SetDefaultValue("1")
.MarkAsSimple();
@@ -369,16 +353,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
_("Volume of the sound on a channel"),
_("Test the volume of the sound on the specified channel. The volume "
"is between 0 and 100."),
_("The volume of the sound on channel _PARAM1_ is _PARAM2__PARAM3_"),
_("the volume of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/conditions/sonVolume24.png",
"res/conditions/sonVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Volume to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
@@ -386,30 +368,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
_("Volume of the music on a channel"),
_("Test the volume of the music on a specified channel. The volume "
"is between 0 and 100."),
_("The volume of the music on channel _PARAM1_ is _PARAM2__PARAM3_"),
_("the volume of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/conditions/musicVolume24.png",
"res/conditions/musicVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Volume to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
"GlobalVolume",
_("Global volume"),
_("Test the global sound level. The volume is between 0 and 100."),
_("The global game volume is _PARAM1__PARAM2_"),
_("the global game volume"),
_("Audio"),
"res/conditions/volume24.png",
"res/conditions/volume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Volume to test"))
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number");
extension
.AddCondition(
@@ -417,16 +395,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
_("Pitch of the sound of a channel"),
_("Test the pitch of the sound on the specified channel. 1 is the "
"default pitch."),
_("The pitch of the sound on channel _PARAM1_ is _PARAM2__PARAM3_"),
_("the pitch of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/conditions/sonVolume24.png",
"res/conditions/sonVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Pitch to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
@@ -434,50 +410,42 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
_("Pitch of the music on a channel"),
_("Test the pitch (speed) of the music on a specified channel. 1 is "
"the default pitch."),
_("The pitch of the music on channel _PARAM1_ is _PARAM2__PARAM3_"),
_("the pitch of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/conditions/musicVolume24.png",
"res/conditions/musicVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Pitch to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
"SoundChannelPlayingOffset",
_("Playing offset of the sound on a channel"),
_("Test the playing offset of the sound on the specified channel."),
_("The playing offset of the sound on channel _PARAM1_ is "
"_PARAM2__PARAM3_"),
_("the playing offset of the sound on channel _PARAM1_"),
_("Audio/Sounds on channels"),
"res/conditions/sonVolume24.png",
"res/conditions/sonVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Playing position (in seconds)"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
"MusicChannelPlayingOffset",
_("Playing offset of the music on a channel"),
_("Test the playing offset of the music on the specified channel."),
_("The playing offset of the music on channel _PARAM1_ is "
"_PARAM2__PARAM3_"),
_("the playing offset of the music on channel _PARAM1_"),
_("Audio/Music on channels"),
"res/conditions/musicVolume24.png",
"res/conditions/musicVolume.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Playing position (in seconds)"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddExpression("SoundChannelPlayingOffset",

View File

@@ -14,8 +14,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("BuiltinObject",
_("Base object"),
_("Base object"),
_("Features for all objects"),
_("Common features that can be used for all objects in GDevelop."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/base_object/events");
@@ -27,64 +27,56 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("PosX",
_("Compare X position of an object"),
_("Compare the X position of the object."),
_("The X position of _PARAM0_ is _PARAM1__PARAM2_"),
_("the X position"),
_("Position"),
"res/conditions/position24.png",
"res/conditions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("X position"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsSimple();
obj.AddAction("MettreX",
_("X position of an object"),
_("Change the X position of an object."),
_("Do _PARAM1__PARAM2_ to the X position of _PARAM0_"),
_("the X position"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
obj.AddCondition("PosY",
_("Compare Y position of an object"),
_("Compare the Y position of an object."),
_("The Y position of _PARAM0_ is _PARAM1__PARAM2_"),
_("the Y position"),
_("Position"),
"res/conditions/position24.png",
"res/conditions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Y position"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsSimple();
obj.AddAction("MettreY",
_("Y position of an object"),
_("Change the Y position of an object."),
_("Do _PARAM1__PARAM2_ to the Y position of _PARAM0_"),
_("the Y position"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
obj.AddAction(
"MettreXY",
_("Position of an object"),
_("Change the position of an object."),
_("Do _PARAM1__PARAM2_;_PARAM3__PARAM4_ to the position of _PARAM0_"),
_("Change the position of _PARAM0_: _PARAM1_ _PARAM2_ (x axis), _PARAM3_ _PARAM4_ (y axis)"),
_("Position"),
"res/actions/position24.png",
"res/actions/position.png")
@@ -117,15 +109,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("SetAngle",
_("Angle"),
_("Change the angle of rotation of an object."),
_("Do _PARAM1__PARAM2_ to angle of _PARAM0_"),
_("the angle"),
_("Angle"),
"res/actions/direction24.png",
"res/actions/direction.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.SetManipulatedType("number");
.UseStandardOperatorParameters("number");
obj.AddAction("Rotate",
_("Rotate"),
@@ -152,9 +142,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle to rotate towards (in degrees)"))
.AddParameter(
"expression",
_("Angular speed (in degrees per second) (0 for immediate rotation)"))
.AddParameter("expression", _("Angular speed (in degrees per second)"))
.SetParameterLongDescription(_("Enter 0 for an immediate rotation."))
.AddCodeOnlyParameter("currentScene", "");
obj.AddAction(
@@ -170,9 +159,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter(
"expression",
_("Angular speed (in degrees per second) (0 for immediate rotation)"))
.AddParameter("expression", _("Angular speed (in degrees per second)"))
.SetParameterLongDescription(_("Enter 0 for an immediate rotation."))
.AddCodeOnlyParameter("currentScene", "")
.MarkAsAdvanced();
@@ -250,7 +238,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Arreter",
_("Stop the object"),
_("Stop the object by deleting all of its forces."),
_("Stop object _PARAM0_"),
_("Stop _PARAM0_ (remove all forces)"),
_("Movement"),
"res/actions/arreter24.png",
"res/actions/arreter.png")
@@ -261,7 +249,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Delete",
_("Delete an object"),
_("Delete the specified object."),
_("Delete object _PARAM0_"),
_("Delete _PARAM0_"),
_("Objects"),
"res/actions/delete24.png",
"res/actions/delete.png")
@@ -273,15 +261,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("ChangePlan",
_("Z order"),
_("Modify the Z-order of an object"),
_("Do _PARAM1__PARAM2_ to Z-order of _PARAM0_"),
_("the z-order"),
_("Z order"),
"res/actions/planicon24.png",
"res/actions/planicon.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.SetManipulatedType("number");
.UseStandardOperatorParameters("number");
obj.AddAction("ChangeLayer",
_("Layer"),
@@ -299,31 +285,27 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("ModVarObjet",
_("Modify a variable of an object"),
_("Modify the value of a variable of an object"),
_("Do _PARAM2__PARAM3_ to variable _PARAM1_ of _PARAM0_"),
_("the variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.SetManipulatedType("number");
.UseStandardOperatorParameters("number");
obj.AddAction(
"ModVarObjetTxt",
_("Modify the text of a variable of an object"),
_("Modify the text of a variable of an object"),
_("Do _PARAM2__PARAM3_ to the text of variable _PARAM1_ of _PARAM0_"),
_("the text of variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("string", _("Text"))
.SetManipulatedType("string");
.UseStandardOperatorParameters("string");
obj.AddCondition(
"ObjectVariableChildExists",
@@ -364,7 +346,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Cache",
_("Hide"),
_("Hide the specified object."),
_("Hide the object _PARAM0_"),
_("Hide _PARAM0_"),
_("Visibility"),
"res/actions/visibilite24.png",
"res/actions/visibilite.png")
@@ -375,7 +357,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Montre",
_("Show"),
_("Show the specified object"),
_("Show object _PARAM0_"),
_("Show _PARAM0_"),
_("Visibility"),
"res/actions/visibilite24.png",
"res/actions/visibilite.png")
@@ -387,30 +369,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("Angle",
_("Angle"),
_("Compare the angle of the specified object."),
_("Angle of _PARAM0_ is _PARAM1__PARAM2_ deg."),
_("the angle (in degrees)"),
_("Angle"),
"res/conditions/direction24.png",
"res/conditions/direction.png")
.AddParameter("object", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare (in degrees)"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("Plan",
_("Compare Z order"),
_("Compare the Z-order of the specified object."),
_("Z Order of _PARAM0_ is _PARAM1__PARAM2_"),
_("the z Order"),
_("Z order"),
"res/conditions/planicon24.png",
"res/conditions/planicon.png")
.AddParameter("object", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Z order"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("Layer",
_("Compare layer"),
@@ -427,7 +405,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("Visible",
_("Visibility of an object"),
_("Check if an object is visible."),
_("The object _PARAM0_ is visible"),
_("_PARAM0_ is visible (not marked as hidden)"),
_("Visibility"),
"res/conditions/visibilite24.png",
"res/conditions/visibilite.png")
@@ -460,16 +438,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("Vitesse",
_("Speed"),
_("Compare the overall speed of an object"),
_("Overall speed of _PARAM0_ is _PARAM1__PARAM2_"),
_("the overall speed"),
_("Movement"),
"res/conditions/vitesse24.png",
"res/conditions/vitesse.png")
.AddParameter("object", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Speed"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("AngleOfDisplacement",
_("Angle of movement"),
@@ -488,31 +464,27 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("VarObjet",
_("Value of an object's variable"),
_("Compare the value of a variable of an object."),
_("Variable _PARAM1_ of _PARAM0_ is _PARAM2__PARAM3_"),
_("the variable _PARAM1_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number");
obj.AddCondition(
"VarObjetTxt",
_("Text of an object's variable"),
_("Compare the text of a variable of an object."),
_("The text of variable _PARAM1_ of _PARAM0_ is _PARAM2__PARAM3_"),
_("the text of variable _PARAM1_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("string", _("Text to test"))
.SetManipulatedType("string");
.UseStandardRelationalOperatorParameters("string");
obj.AddCondition("VarObjetDef",
_("Variable defined"),
@@ -906,11 +878,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter(
"objectListWithoutPicking",
_("Groups containing objects that can be created by the action"))
.AddParameter("string",
_("Text representing the name of the object to create"))
.AddParameter("objectListWithoutPicking", _("Group of potential objects"))
.SetParameterLongDescription(
_("Group containing objects that can be created by the action."))
.AddParameter("string", _("Name of the object to create"))
.SetParameterLongDescription(_(
"Text representing the name of the object to create. If no objects "
"with this name are found in the group, no object will be created."))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
@@ -919,11 +893,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension
.AddAction("AjoutObjConcern",
_("Pick all objects"),
_("Pick all the specified objects. When you pick all objects, "
_("Pick all instances"),
_("Pick all instances of the specified object(s). When you "
"pick all instances, "
"the next conditions and actions of this event work on all "
"of them."),
_("Pick all _PARAM1_ objects"),
_("Pick all instances of _PARAM1_"),
_("Objects"),
"res/actions/add24.png",
"res/actions/add.png")
@@ -1045,15 +1020,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"compare that number to a value. If previous conditions on the "
"objects have not been used, this condition counts how many of "
"these objects exist in the current scene."),
_("The number of _PARAM0_ objects is _PARAM1__PARAM2_"),
_("the number of _PARAM0_ objects"),
_("Objects"),
"res/conditions/nbObjet24.png",
"res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsSimple();
extension
.AddCondition(
@@ -1113,12 +1086,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Ray source Y position"))
.AddParameter("expression", _("Ray angle (in degrees)"))
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
.AddParameter(
"scenevar",
_("Variable where to store the X position of the intersection"))
.AddParameter(
"scenevar",
_("Variable where to store the Y position of the intersection"))
.AddParameter("scenevar", _("Result X position scene variable"))
.SetParameterLongDescription(
_("Scene variable where to store the X position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddParameter("scenevar", _("Result Y position scene variable"))
.SetParameterLongDescription(
_("Scene variable where to store the Y position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
@@ -1141,12 +1116,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Ray source Y position"))
.AddParameter("expression", _("Ray target X position"))
.AddParameter("expression", _("Ray target Y position"))
.AddParameter(
"scenevar",
_("Variable where to store the X position of the intersection"))
.AddParameter(
"scenevar",
_("Variable where to store the Y position of the intersection"))
.AddParameter("scenevar", _("Result X position scene variable"))
.SetParameterLongDescription(
_("Scene variable where to store the X position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddParameter("scenevar", _("Result Y position scene variable"))
.SetParameterLongDescription(
_("Scene variable where to store the Y position of the intersection. "
"If no intersection is found, the variable won't be changed."))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();

View File

@@ -24,86 +24,73 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCondition("CameraX",
_("Camera center X position"),
_("Compare the X position of the center of a camera."),
_("X position of camera _PARAM4_ is _PARAM1__PARAM2_ "
"(layer: _PARAM3_)"),
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced()
.SetManipulatedType("number");
.MarkAsAdvanced();
extension
.AddCondition("CameraY",
_("Camera center Y position"),
_("Compare the Y position of the center of a camera."),
_("The Y position of camera _PARAM4_ is _PARAM1__PARAM2_ "
"(layer: _PARAM3_)"),
_("the Y position of camera _PARAM4_ (layer: _PARAM3_)"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"))
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"))
.SetDefaultValue("0")
.MarkAsAdvanced()
.SetManipulatedType("number");
.MarkAsAdvanced();
extension
.AddAction(
"CameraX",
_("Camera center X position"),
_("Change the X position of the center of the specified camera."),
_("Do _PARAM1__PARAM2_ to X position of camera _PARAM4_ (layer: "
"_PARAM3_)"),
_("the X position of camera _PARAM4_ (layer: _PARAM3_)"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.UseStandardOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced()
.SetManipulatedType("number");
.MarkAsAdvanced();
extension
.AddAction(
"CameraY",
_("Camera center Y position"),
_("Change the Y position of the center of the specified camera."),
_("Do _PARAM1__PARAM2_ to Y position of camera _PARAM4_ (layer: "
"_PARAM3_)"),
_("the Y position of camera _PARAM4_ (layer: _PARAM3_)"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.UseStandardOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced()
.SetManipulatedType("number");
.MarkAsAdvanced();
extension
.AddCondition("CameraWidth",
_("Width of a camera"),
_("Test the width of a camera of a layer"),
_("The width of camera _PARAM2_ of layer _PARAM1_ is "
"_PARAM3__PARAM4_"),
_("the width of camera _PARAM2_ of layer _PARAM1_"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
@@ -111,17 +98,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("layer", _("Layer"))
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition("CameraHeight",
_("Height of a camera"),
_("Test the height of a camera of a layer"),
_("The height of camera _PARAM2_ of layer _PARAM1_ is "
"_PARAM3__PARAM4_"),
_("the height of camera _PARAM2_ of layer _PARAM1_"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
@@ -129,48 +113,40 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("layer", _("Layer (base layer if empty)"))
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition("CameraAngle",
_("Angle of a camera of a layer"),
_("Test a camera angle."),
_("Angle of camera is _PARAM1__PARAM2_ (layer: _PARAM3_, "
"camera: _PARAM4_)"),
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
_("Layers and cameras"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced()
.SetManipulatedType("number");
.MarkAsAdvanced();
extension
.AddAction("RotateCamera",
_("Change camera angle"),
_("This action modifies the angle of a camera in the "
"specified layer."),
_("Do _PARAM1__PARAM2_ to angle of camera (layer: _PARAM3_, "
"camera: _PARAM4_)"),
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
_("Layers and cameras"),
"res/actions/camera24.png",
"res/actions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.UseStandardOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.SetManipulatedType("number");
.SetDefaultValue("0");
extension
.AddAction("AddCamera",
@@ -380,8 +356,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
extension
.AddAction(
"SetLayerEffectParameter",
_("Effect parameter"),
_("Change the parameter of an effect"),
_("Effect parameter (number)"),
_("Change the value of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
@@ -395,11 +373,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.MarkAsAdvanced();
extension
.AddCondition(
"LayerEffectEnabled",
_("Layer effect is enabled"),
_("The effect on a layer is enabled"),
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
.AddAction(
"SetLayerEffectStringParameter",
_("Effect parameter (string)"),
_("Change the value (string) of a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
"names) in the effects window."),
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
@@ -407,14 +387,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("string", _("Parameter name"))
.AddParameter("string", _("New value"))
.MarkAsAdvanced();
extension
.AddAction(
"EnableLayerEffect",
_("Enable layer effect"),
_("Enable an effect on a layer"),
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
"SetLayerEffectBooleanParameter",
_("Effect parameter (enable or disable)"),
_("Enable or disable a parameter of an effect.") + "\n" +
_("You can find the parameter names (and change the effect "
"names) in the effects window."),
_("Enable _PARAM3_ for effect _PARAM2_ of layer _PARAM1_: _PARAM4_"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
@@ -422,6 +406,36 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("string", _("Parameter name"))
.AddParameter("yesorno", _("Enable this parameter"))
.MarkAsAdvanced();
extension
.AddCondition("LayerEffectEnabled",
_("Layer effect is enabled"),
_("The effect on a layer is enabled"),
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.MarkAsAdvanced();
extension
.AddAction("EnableLayerEffect",
_("Enable layer effect"),
_("Enable an effect on a layer"),
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("yesorno", _("Enable"), "", true)
.MarkAsAdvanced();
@@ -430,17 +444,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"LayerTimeScale",
_("Layer time scale"),
_("Compare the time scale applied to the objects of the layer."),
_("The time scale of layer _PARAM1_ is _PARAM2__PARAM3_"),
_("the time scale of layer _PARAM1_"),
_("Layers and cameras/Time"),
"res/conditions/time24.png",
"res/conditions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction(

View File

@@ -98,7 +98,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddAction(
"LireFichierExp",
_("Read a value"),
_("Read the value saved in the specified element and store it in a "
_("Read the value saved in the specified element and store it in a scene"
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
@@ -115,7 +115,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddAction(
"LireFichierTxt",
_("Read a text"),
_("Read the text saved in the specified element and store it in a "
_("Read the text saved in the specified element and store it in a scene "
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),

View File

@@ -37,17 +37,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsJoystickExtension(
.AddCondition("JoystickAxis",
_("Value of an axis of a joystick"),
_("Test the value of an axis of a joystick."),
_("The value of the axis _PARAM2_ of joystick _PARAM1_ is "
"_PARAM3__PARAM4_"),
_("the value of the axis _PARAM2_ of joystick _PARAM1_"),
_("Joystick"),
"res/conditions/joystick24.png",
"res/conditions/joystick.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Joystick number (first joystick: 0)"))
.AddParameter("joyaxis", _("Axis"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number");
extension
.AddAction(

View File

@@ -21,6 +21,16 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
#if defined(GD_IDE_ONLY)
extension
.AddExpression("clamp",
_("Clamp (restrict a value to a given range)"),
_("Restrict a value to a given range"),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("Value"))
.AddParameter("expression", _("Min"))
.AddParameter("expression", _("Max"));
extension
.AddExpression("AngleDifference",
_("Difference between two angles"),
@@ -322,7 +332,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("trunc",
_("Truncation"),
_("Troncate a number"),
_("Truncate a number"),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));

View File

@@ -164,35 +164,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition("SourisX",
_("Cursor X position"),
_("Compare the X position of the cursor or of a touch."),
_("Cursor X position is _PARAM1__PARAM2_"),
_("the cursor X position"),
_("Mouse and touch"),
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("X position"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.SetManipulatedType("number");
.SetDefaultValue("0");
extension
.AddCondition("SourisY",
_("Cursor Y position"),
_("Compare the Y position of the cursor or of a touch."),
_("Cursor Y position is _PARAM1__PARAM2_"),
_("the cursor Y position"),
_("Mouse and touch"),
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Y position"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.SetManipulatedType("number");
.SetDefaultValue("0");
extension
.AddCondition("SourisBouton",
@@ -224,37 +220,33 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition("TouchX",
_("Touch X position"),
_("Compare the X position of a specific touch."),
_("Touch #_PARAM1_ X position is _PARAM2__PARAM3_"),
_("the touch #_PARAM1_ X position"),
_("Mouse and touch/Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("X position"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.SetManipulatedType("number");
.SetDefaultValue("0");
extension
.AddCondition("TouchY",
_("Touch Y position"),
_("Compare the Y position of a specific touch."),
_("Touch #_PARAM1_ Y position is _PARAM2__PARAM3_"),
_("the touch #_PARAM1_ Y position"),
_("Mouse and touch/Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Y position"))
.UseStandardRelationalOperatorParameters("number")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0")
.SetManipulatedType("number");
.SetDefaultValue("0");
extension
.AddCondition(

View File

@@ -26,29 +26,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"SendRequest",
_("Send a request to a web page"),
_("Send a request to the specified web page.\n\nPlease note that for "
"the web platform games, the game must be hosted on the same host "
"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 (example: http://www.some-server.org/)"))
.AddParameter("string", _("Path to page (Example: /page.php)"))
.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\" (if empty, GET will be used)"),
"",
true)
.AddParameter("string", _("Method: \"POST\" or \"GET\""), "", true)
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
.SetDefaultValue("\"GET\"")
.AddParameter(
"string",
_("Content type (application/x-www-form-urlencoded by default)"),
"",
true)
.AddParameter(
"scenevar", _("Store the response in this variable"), "", true)
.AddParameter("string", _("Content type"), "", true)
.SetParameterLongDescription(
_("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*."))
.MarkAsComplex();
extension
@@ -68,8 +70,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
extension
.AddAction(
"JSONToVariableStructure",
_("Convert JSON to a variable"),
_("Parse a JSON object and store it into a variable"),
_("Convert JSON to a scene variable"),
_("Parse a JSON object and store it into a scene variable"),
_("Parse JSON string _PARAM0_ and store it into variable _PARAM1_"),
_("Network"),
"res/actions/net24.png",
@@ -109,11 +111,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
extension
.AddStrExpression("ToJSON",
_("Convert variable to JSON"),
_("Convert a variable to JSON"),
_("Convert scene variable to JSON"),
_("Convert a scene variable to JSON"),
_("Conversion"),
"res/conditions/toujours24.png")
.AddParameter("scenevar", _("The variable to be stringified"));
.AddParameter("scenevar", _("Scene variable to be stringified"));
extension
.AddStrExpression("GlobalVarToJSON",

View File

@@ -91,6 +91,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
.AddCodeOnlyParameter("currentScene", "")
.MarkAsSimple();
extension
.AddCondition("SceneJustResumed",
_("Scene just resumed"),
_("The scene has just resumed after being paused."),
_("Scene just resumed"),
_("Scene"),
"res/conditions/depart24.png",
"res/conditions/depart.png")
.SetHelpPath("/interface/scene-editor/events")
.AddCodeOnlyParameter("currentScene", "")
.MarkAsSimple();
extension
.AddAction("Scene",
_("Change the scene"),

View File

@@ -31,35 +31,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
#if defined(GD_IDE_ONLY)
obj.AddAction("Opacity",
_("Change Sprite opacity"),
_("Change sprite opacity"),
_("Change the opacity of a Sprite. 0 is fully transparent, 255 "
"is opaque (default)."),
_("Do _PARAM1__PARAM2_ to the opacity of _PARAM0_"),
_("the opacity"),
_("Visibility"),
"res/actions/opacity24.png",
"res/actions/opacity.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value (between 0 and 255)"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
obj.AddAction("ChangeAnimation",
_("Change the animation"),
_("Change the animation of the object, using the animation "
"number in the animations list."),
_("Do _PARAM1__PARAM2_ to the number of current animation of "
"_PARAM0_"),
_("the number of the animation"),
_("Animations and images"),
"res/actions/animation24.png",
"res/actions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
obj.AddAction("SetAnimationName",
_("Change the animation (by name)"),
@@ -80,35 +75,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Change the direction of the object.\nIf the object is set to "
"automatically rotate, the direction is its angle.\nIf the object "
"is in 8 directions mode, the valid directions are 0..7"),
_("Do _PARAM1__PARAM2_ to the direction of _PARAM0_"),
_("the direction"),
_("Direction"),
"res/actions/direction24.png",
"res/actions/direction.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("ChangeSprite",
_("Current frame"),
_("Modify the current frame of the object"),
_("Do _PARAM1__PARAM2_ to animation frame of _PARAM0_"),
_("the animation frame"),
_("Animations and images"),
"res/actions/sprite24.png",
"res/actions/sprite.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("PauseAnimation",
_("Pause the animation"),
_("Pause the current animation of the object"),
_("Pause the current animation of _PARAM0_"),
_("Pause the animation of the object"),
_("Pause the animation of _PARAM0_"),
_("Animations and images"),
"res/actions/animation24.png",
"res/actions/animation.png")
@@ -118,8 +109,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddAction("PlayAnimation",
_("Play the animation"),
_("Play the current animation of the object"),
_("Play the current animation of _PARAM0_"),
_("Play the animation of the object"),
_("Play the animation of _PARAM0_"),
_("Animations and images"),
"res/actions/animation24.png",
"res/actions/animation.png")
@@ -132,16 +123,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Animation speed scale"),
_("Modify the animation speed scale (1 = the default speed, >1 = "
"faster and <1 = slower)."),
_("Do _PARAM1__PARAM2_ to the animation speed scale of _PARAM0_"),
_("the animation speed scale"),
_("Animations and images"),
"res/actions/animation24.png",
"res/actions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsSimple();
obj.AddAction("TourneVersPos",
_("Rotate an object toward a position"),
@@ -162,91 +151,79 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddAction("ChangeScale",
_("Scale"),
_("Modify the scale of the specified object."),
_("Do _PARAM1__PARAM2_ to the scale of _PARAM0_"),
_("the scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("ChangeScaleWidth",
_("Scale on X axis"),
_("Modify the scale of the width of an object."),
_("Do _PARAM1__PARAM2_ to the width's scale of _PARAM0_"),
_("the width's scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("ChangeScaleHeight",
_("Scale on Y axis"),
_("Modify the scale of the height of an object."),
_("Do _PARAM1__PARAM2_ to the height's scale of _PARAM0_"),
_("the height's scale"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("ChangeWidth",
_("Width"),
_("Change the width of a Sprite object."),
_("Do _PARAM1__PARAM2_ to the width of _PARAM0_"),
_("the width"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("ChangeHeight",
_("Height"),
_("Change the height of a Sprite object."),
_("Do _PARAM1__PARAM2_ to the height of _PARAM0_"),
_("the height"),
_("Size"),
"res/actions/scale24.png",
"res/actions/scale.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition(
"Animation",
_("Current animation"),
_("Compare the number of the current animation of the object."),
_("The number of the current animation of _PARAM0_ is "
"_PARAM1__PARAM2_"),
_("Compare the number of the animation played by the object."),
_("the number of the animation"),
_("Animations and images"),
"res/conditions/animation24.png",
"res/conditions/animation.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Number to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("AnimationName",
_("Current animation name"),
_("Check the current animation of the object."),
_("Check the animation by played by the object."),
_("The animation of _PARAM0_ is _PARAM1_"),
_("Animations and images"),
"res/conditions/animation24.png",
@@ -262,33 +239,29 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Compare the direction of the object. If 8 direction mode is "
"activated for the sprite, the value taken for direction will be "
"from 0 to 7. Otherwise, the direction is in degrees."),
_("Direction of _PARAM0_ is _PARAM1__PARAM2_"),
_("the direction"),
_("Direction"),
"res/conditions/direction24.png",
"res/conditions/direction.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Direction to test"))
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number");
obj.AddCondition("Sprite",
_("Current frame"),
_("Test the number of the current animation frame."),
_("The animation frame of _PARAM0_ is _PARAM1__PARAM2_"),
_("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",
"res/conditions/sprite.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Animation frame to test"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("AnimStopped",
_("Animation paused"),
_("Test if the animation of an object is paused"),
_("Check if the animation of an object is paused."),
_("The animation of _PARAM0_ is paused"),
_("Animations and images"),
"res/conditions/animation24.png",
@@ -312,62 +285,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddCondition("ScaleWidth",
_("Scale on X axis"),
_("Compare the scale of the width of an object."),
_("The width's scale of _PARAM0_ is _PARAM1__PARAM2_"),
_("the width's scale"),
_("Size"),
"res/conditions/scaleWidth24.png",
"res/conditions/scaleWidth.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("ScaleHeight",
_("Scale on Y axis"),
_("Compare the scale of the height of an object."),
_("The height's scale of _PARAM0_ is _PARAM1__PARAM2_"),
_("the height's scale"),
_("Size"),
"res/conditions/scaleHeight24.png",
"res/conditions/scaleHeight.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddCondition("Opacity",
_("Opacity"),
_("Compare the opacity of a Sprite, between 0 (fully "
"transparent) to 255 (opaque)."),
_("The opacity of _PARAM0_ is _PARAM1__PARAM2_"),
_("the opacity"),
_("Visibility"),
"res/conditions/opacity24.png",
"res/conditions/opacity.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsSimple()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsSimple();
obj.AddCondition(
"BlendMode",
_("Blend mode"),
_("Compare the number of the blend mode currently used by an object"),
_("The number of the current blend mode of _PARAM0_ is "
"_PARAM1__PARAM2_"),
_("the number of the current blend mode"),
_("Effects"),
"res/conditions/opacity24.png",
"res/conditions/opacity.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression",
_("Value to compare (0: Alpha, 1: Add, 2: Multiply, 3: None)"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
obj.AddAction("CopyImageOnImageOfSprite",
_("Copy an image on the current one of an object"),

View File

@@ -38,15 +38,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
.AddCondition("TimeScale",
_("Time scale"),
_("Test the time scale."),
_("The time scale is _PARAM1__PARAM2_"),
_("the time scale"),
_("Timers and time"),
"res/conditions/time24.png",
"res/conditions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition("TimerPaused",
@@ -123,16 +121,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddExpression("TimeDelta",
_("Time elapsed since the last image"),
_("Time elapsed since the last image"),
_("Time elapsed since the last frame"),
_("Time elapsed since the last frame rendered on screen"),
_("Time"),
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddExpression("TempsFrame",
_("Time elapsed since the last image"),
_("Time elapsed since the last image"),
_("Time elapsed since the last frame"),
_("Time elapsed since the last frame rendered on screen"),
_("Time"),
"res/actions/time.png")
.SetHidden()
@@ -140,8 +138,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddExpression("ElapsedTime",
_("Time elapsed since the last image"),
_("Time elapsed since the last image"),
_("Time elapsed since the last frame"),
_("Time elapsed since the last frame rendered on screen"),
_("Time"),
"res/actions/time.png")
.SetHidden()

View File

@@ -25,27 +25,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddCondition("VarScene",
_("Value of a scene variable"),
_("Compare the value of a scene variable."),
_("Scene variable _PARAM0_ is _PARAM1__PARAM2_"),
_("the scene variable _PARAM0_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number");
extension
.AddCondition("VarSceneTxt",
_("Text of a scene variable"),
_("Compare the text of a scene variable."),
_("The text of scene variable _PARAM0_ is _PARAM1__PARAM2_"),
_("the text of scene variable _PARAM0_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("string", _("Text to compare"))
.SetManipulatedType("string");
.UseStandardRelationalOperatorParameters("string");
extension
.AddCondition(
@@ -89,30 +85,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddCondition("VarGlobal",
_("Value of a global variable"),
_("Compare the value of a global variable."),
_("Global variable _PARAM0_ is _PARAM1__PARAM2_"),
_("the global variable _PARAM0_"),
_("Variables/Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to compare"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddCondition(
"VarGlobalTxt",
_("Text of a global variable"),
_("Compare the text of a global variable."),
_("The text of the global variable _PARAM0_ is _PARAM1__PARAM2_"),
_("the text of the global variable _PARAM0_"),
_("Variables/Global variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("string", _("Text to compare"))
.MarkAsAdvanced()
.SetManipulatedType("string");
.UseStandardRelationalOperatorParameters("string")
.MarkAsAdvanced();
extension
.AddCondition("VarGlobalDef",
@@ -131,56 +123,48 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddAction("ModVarScene",
_("Value of a scene variable"),
_("Modify the value of a scene variable."),
_("Do _PARAM1__PARAM2_ to scene variable _PARAM0_"),
_("the scene variable _PARAM0_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.SetManipulatedType("number");
.UseStandardOperatorParameters("number");
extension
.AddAction("ModVarSceneTxt",
_("String of a scene variable"),
_("Modify the text of a scene variable."),
_("Do _PARAM1__PARAM2_ to the text of scene variable _PARAM0_"),
_("the text of scene variable _PARAM0_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("scenevar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("string", _("Text"))
.SetManipulatedType("string");
.UseStandardOperatorParameters("string");
extension
.AddAction("ModVarGlobal",
_("Value of a global variable"),
_("Modify the value of a global variable"),
_("Do _PARAM1__PARAM2_ to global variable _PARAM0_"),
_("the global variable _PARAM0_"),
_("Variables/Global variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("expression", _("Value"))
.MarkAsAdvanced()
.SetManipulatedType("number");
.UseStandardOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction(
"ModVarGlobalTxt",
_("String of a global variable"),
_("Modify the text of a global variable."),
_("Do _PARAM1__PARAM2_ to the text of global variable _PARAM0_"),
_("the text of global variable _PARAM0_"),
_("Variables/Global variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("string", _("Text"))
.MarkAsAdvanced()
.SetManipulatedType("string");
.UseStandardOperatorParameters("string")
.MarkAsAdvanced();
extension
.AddAction("VariableRemoveChild",

View File

@@ -27,7 +27,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
_("De/activate fullscreen"),
_("This action activates or deactivates fullscreen."),
_("Activate fullscreen: _PARAM1_ (keep aspect ratio: _PARAM2_)"),
_("Game's window"),
_("Game's window and resolution"),
"res/actions/fullscreen24.png",
"res/actions/fullscreen.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -41,11 +41,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
extension
.AddAction("SetWindowMargins",
_("Change the window's margins"),
_("This action changes the margins, in pixels, of the game's "
"window."),
_("This action changes the margins, in pixels, between the "
"game frame and the window borders."),
_("Set margins of game window to "
"_PARAM1_;_PARAM2_;_PARAM3_;_PARAM4_"),
_("Game's window"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -55,28 +55,93 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
.AddParameter("expression", _("Left"));
extension
.AddAction("SetWindowSize",
_("Change the size of the window"),
_("This action changes the size of the game's window."),
_("Change window size: _PARAM1_x_PARAM2_"),
_("Game's window"),
.AddAction("SetGameResolutionSize",
_("Change the resolution of the game"),
_("Changes the resolution of the game, effectively changing "
"the game area size. This won't change the size of the "
"window in which the game is running."),
_("Set game resolution to _PARAM1_x_PARAM2_"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Width"))
.AddParameter("expression", _("Height"));
extension
.AddAction(
"SetWindowSize",
_("Change the size of the game window"),
_("This action changes the size of the game window. Note that this "
"will only work on platform supporting this operation: games "
"running in browsers or on mobile phones can not update their "
"window size. Game resolution can still be updated."),
_("Set game window size to _PARAM1_x_PARAM2_ (also update game "
"resolution: _PARAM3_)"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Width"))
.AddParameter("expression", _("Height"))
.AddParameter(
"yesorno",
_("Do you want to use this size as the default size for new scene "
"cameras\?\n(Yes to change the size of the game's viewable "
"area,\nNo to stretch the game to the window's size)."));
.AddParameter("yesorno",
_("Also update the game resolution? If not, the game will "
"be stretched or reduced to fit in the window."));
extension
.AddAction("CenterWindow",
_("Center the game window on the screen"),
_("This action centers the game window on the screen. This "
"only works on Windows, macOS and Linux (not when the game "
"is executed in a web-browser or on iOS/Android)."),
_("Center the game window"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddAction("SetGameResolutionResizeMode",
_("Change the game resolution resize mode"),
_("Set if the width or the height of the game resolution "
"should be changed to fit the game window - or if the game "
"resolution should not be updated automatically."),
_("Set game resolution resize mode to _PARAM1_"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("stringWithSelector",
_("Resize mode"),
"[\"adaptWidth\", \"adaptHeight\", \"\"]",
false)
.SetParameterLongDescription(
_("Empty to disable resizing. \"adaptWidth\" will update the game "
"width to fit in the window or screen. \"adaptHeight\" will do the "
"same but with the game height."));
extension
.AddAction("SetAdaptGameResolutionAtRuntime",
_("Automatically adapt the game resolution"),
_("Set if the game resolution should be automatically adapted "
"when the game window or screen size change. This will only "
"be the case if the game resolution resize mode is "
"configured to adapt the width or the height of the game."),
_("Automatically adapt the game resolution: _PARAM1_"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("yesorno",
_("Update resolution during the game to fit the screen or "
"window size?"));
extension
.AddAction("SetWindowIcon",
_("Change the window's icon"),
_("This action changes the icon of the game's window."),
_("Use _PARAM1_ as the icon for the game's window."),
_("Game's window"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")
@@ -87,7 +152,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
_("Change the window's title"),
_("This action changes the title of the game's window."),
_("Change window title to _PARAM1_"),
_("Game's window"),
_("Game's window and resolution"),
"res/actions/window24.png",
"res/actions/window.png")
.AddCodeOnlyParameter("currentScene", "")

View File

@@ -59,7 +59,8 @@ gd::InstructionMetadata& BehaviorMetadata::AddCondition(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsBehaviorInstruction();
return conditionsInfos[nameWithNamespace];
#endif
}
@@ -83,7 +84,8 @@ gd::InstructionMetadata& BehaviorMetadata::AddAction(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsBehaviorInstruction();
return actionsInfos[nameWithNamespace];
#endif
}
@@ -107,7 +109,8 @@ gd::InstructionMetadata& BehaviorMetadata::AddScopedCondition(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsBehaviorInstruction();
return conditionsInfos[nameWithNamespace];
#endif
}
@@ -131,7 +134,8 @@ gd::InstructionMetadata& BehaviorMetadata::AddScopedAction(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsBehaviorInstruction();
return actionsInfos[nameWithNamespace];
#endif
}

View File

@@ -0,0 +1,25 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EffectMetadata.h"
namespace gd {
EffectMetadata::EffectMetadata(const gd::String& type_) : type(type_) {}
EffectMetadata& EffectMetadata::SetIncludeFile(const gd::String& includeFile) {
includeFiles.clear();
includeFiles.push_back(includeFile);
return *this;
}
EffectMetadata& EffectMetadata::AddIncludeFile(const gd::String& includeFile) {
if (std::find(includeFiles.begin(), includeFiles.end(), includeFile) ==
includeFiles.end())
includeFiles.push_back(includeFile);
return *this;
}
} // namespace gd

View File

@@ -0,0 +1,121 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EFFECTMETADATA_H
#define EFFECTMETADATA_H
#include <functional>
#include <map>
#include <memory>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
namespace gd {
/**
* \brief Contains user-friendly information about an effect.
*
* \ingroup Events
*/
class GD_CORE_API EffectMetadata {
public:
/**
* \brief Construct an effect metadata, with the given type
*/
EffectMetadata(const gd::String& type_);
/**
* \brief Default constructor, only used for initializing `badEffectMetadata`.
*/
EffectMetadata() {}
virtual ~EffectMetadata(){};
/**
* \brief Set the name shown to the user.
*/
EffectMetadata& SetFullName(const gd::String& fullname_) {
fullname = fullname_;
return *this;
};
/**
* \brief Set the description shown to the user.
*/
EffectMetadata& SetDescription(const gd::String& description_) {
description = description_;
return *this;
};
/**
* Set the help path of the effect, relative to the documentation root.
*/
EffectMetadata& SetHelpPath(const gd::String& path) {
helpPath = path;
return *this;
}
/**
* \brief Clear any existing include file and add the specified include file.
*/
EffectMetadata& SetIncludeFile(const gd::String& includeFile);
/**
* \brief Add a file to the already existing include files.
*/
EffectMetadata& AddIncludeFile(const gd::String& includeFile);
/**
* \brief Return a reference to the properties of this effect.
*/
std::map<gd::String, gd::PropertyDescriptor>& GetProperties() {
return properties;
}
/**
* \brief Return a (const) reference to the properties of this effect.
*/
const std::map<gd::String, gd::PropertyDescriptor>& GetProperties() const {
return properties;
}
/**
* \brief Get the help path of the effect, relative to the documentation root.
*/
const gd::String& GetHelpPath() const { return helpPath; }
/**
* \brief Get the type of the effect (its internal name, like "BlackAndWhite").
*/
const gd::String& GetType() const { return type; }
/**
* \brief Get the user facing name of the effect (like "Black and White").
*/
const gd::String& GetFullName() const { return fullname; }
/**
* \brief Get the user friendly description of the effect.
*/
const gd::String& GetDescription() const { return description; }
/**
* \brief Get the required include files for this effect.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
return includeFiles;
}
private:
gd::String extensionNamespace;
gd::String type;
gd::String helpPath;
gd::String fullname;
gd::String description;
std::vector<gd::String> includeFiles;
std::map<gd::String, gd::PropertyDescriptor> properties;
};
} // namespace gd
#endif // EFFECTMETADATA_H

View File

@@ -200,7 +200,17 @@ class GD_CORE_API ExpressionMetadata {
* \see AddParameter
*/
ExpressionMetadata& SetDefaultValue(gd::String defaultValue_) {
if (!parameters.empty()) parameters.back().defaultValue = defaultValue_;
if (!parameters.empty()) parameters.back().SetDefaultValue(defaultValue_);
return *this;
};
/**
* \brief Set the long description shown in the editor for the last added parameter.
*
* \see AddParameter
*/
ExpressionMetadata &SetParameterLongDescription(gd::String longDescription) {
if (!parameters.empty()) parameters.back().SetLongDescription(longDescription);
return *this;
};

View File

@@ -3,23 +3,24 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "InstructionMetadata.h"
#include <algorithm>
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "InstructionMetadata.h"
namespace gd {
InstructionMetadata::InstructionMetadata()
: sentence(
"Unknown or unsupported instruction"), // Avoid translating this
// string, so that it's safe
// and *fast* to use a
// InstructionMetadata.
: sentence("Unknown or unsupported instruction"), // Avoid translating this
// string, so that it's
// safe and *fast* to use
// a InstructionMetadata.
canHaveSubInstructions(false),
hidden(true),
usageComplexity(5),
isPrivate(false) {}
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}
InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -40,8 +41,9 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
extensionNamespace(extensionNamespace_),
hidden(false),
usageComplexity(5),
isPrivate(false) {
}
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}
ParameterMetadata::ParameterMetadata() : optional(false), codeOnly(false) {}
@@ -86,11 +88,82 @@ InstructionMetadata& InstructionMetadata::AddCodeOnlyParameter(
return *this;
}
InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
AddParameter("operator", _("Modification's sign"));
AddParameter(type == "number" ? "expression" : type, _("Value"));
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;
if (isObjectInstruction || isBehaviorInstruction) {
gd::String templateSentence =
_("Change <subject> of _PARAM0_: <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
.FindAndReplace("<value>",
"_PARAM" + gd::String::From(valueParamIndex) + "_");
} else {
gd::String templateSentence = _("Change <subject>: <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
.FindAndReplace("<value>",
"_PARAM" + gd::String::From(valueParamIndex) + "_");
}
return *this;
}
InstructionMetadata&
InstructionMetadata::UseStandardRelationalOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
AddParameter("relationalOperator", _("Sign of the test"));
AddParameter(type == "number" ? "expression" : type, _("Value to compare"));
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;
if (isObjectInstruction || isBehaviorInstruction) {
gd::String templateSentence = _("<subject> of _PARAM0_ <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
.FindAndReplace("<value>",
"_PARAM" + gd::String::From(valueParamIndex) + "_");
} else {
gd::String templateSentence = _("<subject> <operator> <value>");
sentence =
templateSentence.FindAndReplace("<subject>", sentence)
.FindAndReplace(
"<operator>",
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
.FindAndReplace("<value>",
"_PARAM" + gd::String::From(valueParamIndex) + "_");
}
return *this;
}
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", type);
element.SetAttribute("supplementaryInformation", supplementaryInformation);
element.SetAttribute("optional", optional);
element.SetAttribute("description", description);
element.SetAttribute("longDescription", longDescription);
element.SetAttribute("codeOnly", codeOnly);
element.SetAttribute("defaultValue", defaultValue);
element.SetAttribute("name", name);
@@ -102,6 +175,7 @@ void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
description = element.GetStringAttribute("description");
longDescription = element.GetStringAttribute("longDescription");
codeOnly = element.GetBoolAttribute("codeOnly");
defaultValue = element.GetStringAttribute("defaultValue");
name = element.GetStringAttribute("name");

View File

@@ -139,6 +139,19 @@ class GD_CORE_API ParameterMetadata {
return *this;
}
/**
* \brief Get the user friendly, long description for the parameter.
*/
const gd::String &GetLongDescription() const { return longDescription; }
/**
* \brief Set the user friendly, long description for the parameter.
*/
ParameterMetadata &SetLongDescription(const gd::String &longDescription_) {
longDescription = longDescription_;
return *this;
}
/**
* \brief Return true if the type of the parameter is "object", "objectPtr" or
* "objectList".
@@ -207,11 +220,12 @@ class GD_CORE_API ParameterMetadata {
gd::String description; ///< Description shown in editor
bool codeOnly; ///< True if parameter is relative to code generation only,
///< i.e. must not be shown in editor
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
private:
gd::String name; ///< The name of the parameter to be used in code
///< generation. Optional.
gd::String longDescription; ///< Long description shown in the editor.
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
gd::String name; ///< The name of the parameter to be used in code
///< generation. Optional.
};
/**
@@ -319,44 +333,93 @@ class GD_CORE_API InstructionMetadata {
bool IsHidden() const { return hidden; }
/**
* \brief Add a parameter to the instruction ( condition or action )
* information class. \param type One of the type handled by GDevelop. This
* \brief Add a parameter to the instruction metadata.
*
* \param type One of the type handled by GDevelop. This
* will also determine the type of the argument used when calling the function
* in the generated code. \see EventsCodeGenerator::GenerateParametersCodes
* in the generated code.
* \param description Description for parameter
* \param optionalObjectType If type is "object", this parameter will describe
* which objects are allowed. If it is empty, all objects are allowed. \param
* parameterIsOptional true if the parameter must be optional, false
* which objects are allowed. If it is empty, all objects are allowed.
* \param parameterIsOptional true if the parameter must be optional, false
* otherwise.
*
* \see EventsCodeGenerator::GenerateParametersCodes
*/
InstructionMetadata &AddParameter(const gd::String &type,
const gd::String &description,
const gd::String &label,
const gd::String &optionalObjectType = "",
bool parameterIsOptional = false);
/**
* \brief Add a parameter not displayed in editor.
*
* \param type One of the type handled by GDevelop. This will also determine
* the type of the argument used when calling the function in C++ code. \see
* EventsCodeGenerator::GenerateParametersCodes \param
* supplementaryInformation Can be used if needed. For example, when type ==
* "inlineCode", the content of supplementaryInformation is inserted in the
* generated C++ code.
* the type of the argument used when calling the function in the generated
* code. \param supplementaryInformation Depends on `type`. For example, when
* `type == "inlineCode"`, the content of supplementaryInformation is inserted
* in the generated code.
*
* \see EventsCodeGenerator::GenerateParametersCodes
*/
InstructionMetadata &AddCodeOnlyParameter(
const gd::String &type, const gd::String &supplementaryInformation);
/**
* \brief Set the default value used in editor (or if an optional parameter is
* empty during code generation) for the latest added parameter.
* empty during code generation) for the last added parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetDefaultValue(gd::String defaultValue_) {
if (!parameters.empty()) parameters.back().defaultValue = defaultValue_;
InstructionMetadata &SetDefaultValue(const gd::String &defaultValue_) {
if (!parameters.empty()) parameters.back().SetDefaultValue(defaultValue_);
return *this;
};
/**
* \brief Set the long description shown in the editor for the last added
* parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetParameterLongDescription(
const gd::String &longDescription) {
if (!parameters.empty())
parameters.back().SetLongDescription(longDescription);
return *this;
};
/**
* \brief Add the default parameters for an instruction manipulating the
* specified type ("string", "number") with the default operators.
*/
InstructionMetadata &UseStandardOperatorParameters(const gd::String &type);
/**
* \brief Add the default parameters for an instruction comparing the
* specified type ("string", "number") with the default relational operators.
*/
InstructionMetadata &UseStandardRelationalOperatorParameters(
const gd::String &type);
/**
* \brief Mark the instruction as an object instruction. Automatically called
* when using `AddAction`/`AddCondition` on an `ObjectMetadata`.
*/
InstructionMetadata &SetIsObjectInstruction() {
isObjectInstruction = true;
return *this;
}
/**
* \brief Mark the instruction as a behavior instruction. Automatically called
* when using `AddAction`/`AddCondition` on a `BehaviorMetadata`.
*/
InstructionMetadata &SetIsBehaviorInstruction() {
isBehaviorInstruction = true;
return *this;
}
/**
* \brief Consider that the instruction is easy for an user to understand.
*/
@@ -425,7 +488,7 @@ class GD_CORE_API InstructionMetadata {
* obj.AddAction("String",
* _("Change the string"),
* _("Change the string of a text"),
* _("Do _PARAM1__PARAM2_ to the string of _PARAM0_"),
* _("the string"),
* _("Text"),
* "CppPlatform/Extensions/text24.png",
* "CppPlatform/Extensions/text.png");
@@ -558,6 +621,8 @@ class GD_CORE_API InstructionMetadata {
int usageComplexity; ///< Evaluate the instruction from 0 (simple&easy to
///< use) to 10 (complex to understand)
bool isPrivate;
bool isObjectInstruction;
bool isBehaviorInstruction;
};
} // namespace gd

View File

@@ -8,6 +8,7 @@
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/String.h"
@@ -18,6 +19,7 @@ namespace gd {
gd::BehaviorMetadata MetadataProvider::badBehaviorInfo;
gd::ObjectMetadata MetadataProvider::badObjectInfo;
gd::EffectMetadata MetadataProvider::badEffectMetadata;
gd::InstructionMetadata MetadataProvider::badInstructionMetadata;
gd::ExpressionMetadata MetadataProvider::badExpressionMetadata;
gd::ExpressionMetadata MetadataProvider::badStrExpressionMetadata;
@@ -63,6 +65,26 @@ const ObjectMetadata& MetadataProvider::GetObjectMetadata(
return GetExtensionAndObjectMetadata(platform, objectType).GetMetadata();
}
ExtensionAndMetadata<EffectMetadata>
MetadataProvider::GetExtensionAndEffectMetadata(const gd::Platform& platform,
gd::String type) {
for (auto& extension : platform.GetAllPlatformExtensions()) {
auto objectsTypes = extension->GetExtensionEffectTypes();
for (std::size_t j = 0; j < objectsTypes.size(); ++j) {
if (objectsTypes[j] == type)
return ExtensionAndMetadata<EffectMetadata>(
*extension, extension->GetEffectMetadata(type));
}
}
return ExtensionAndMetadata<EffectMetadata>(badExtension, badEffectMetadata);
}
const EffectMetadata& MetadataProvider::GetEffectMetadata(
const gd::Platform& platform, gd::String objectType) {
return GetExtensionAndEffectMetadata(platform, objectType).GetMetadata();
}
ExtensionAndMetadata<InstructionMetadata>
MetadataProvider::GetExtensionAndActionMetadata(const gd::Platform& platform,
gd::String actionType) {

View File

@@ -10,6 +10,7 @@
namespace gd {
class BehaviorMetadata;
class ObjectMetadata;
class EffectMetadata;
class ExpressionMetadata;
class ExpressionMetadata;
class Platform;
@@ -71,6 +72,12 @@ class GD_CORE_API MetadataProvider {
static ExtensionAndMetadata<ObjectMetadata> GetExtensionAndObjectMetadata(
const gd::Platform& platform, gd::String type);
/**
* Get the metadata about an effect, and its associated extension.
*/
static ExtensionAndMetadata<EffectMetadata> GetExtensionAndEffectMetadata(
const gd::Platform& platform, gd::String type);
/**
* Get the metadata of an action, and its associated extension.
* Works for object, behaviors and static actions.
@@ -151,6 +158,12 @@ class GD_CORE_API MetadataProvider {
static const ObjectMetadata& GetObjectMetadata(const gd::Platform& platform,
gd::String type);
/**
* Get the metadata about an effect.
*/
static const EffectMetadata& GetEffectMetadata(const gd::Platform& platform,
gd::String type);
/**
* Get the metadata of an action.
* Works for object, behaviors and static actions.
@@ -308,6 +321,7 @@ class GD_CORE_API MetadataProvider {
static PlatformExtension badExtension;
static BehaviorMetadata badBehaviorInfo;
static ObjectMetadata badObjectInfo;
static EffectMetadata badEffectMetadata;
static gd::InstructionMetadata badInstructionMetadata;
static gd::ExpressionMetadata badExpressionMetadata;
static gd::ExpressionMetadata badStrExpressionMetadata;

View File

@@ -77,7 +77,8 @@ gd::InstructionMetadata& ObjectMetadata::AddCondition(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsObjectInstruction();
return conditionsInfos[nameWithNamespace];
#endif
}
@@ -101,7 +102,8 @@ gd::InstructionMetadata& ObjectMetadata::AddAction(
group,
icon,
smallicon)
.SetHelpPath(GetHelpPath());
.SetHelpPath(GetHelpPath())
.SetIsObjectInstruction();
return actionsInfos[nameWithNamespace];
#endif
}

View File

@@ -67,7 +67,7 @@ void ParameterMetadataTools::IterateOverParameters(
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
const gd::String& parameterValueOrDefault =
parameterValue.empty() && parameterMetadata.optional
? parameterMetadata.defaultValue
? parameterMetadata.GetDefaultValue()
: parameterValue;
fn(parameterMetadata, parameterValueOrDefault, lastObjectName);
@@ -100,4 +100,4 @@ size_t ParameterMetadataTools::GetObjectParameterIndexFor(
return gd::String::npos;
}
} // namespace gd
} // namespace gd

View File

@@ -14,19 +14,19 @@ using namespace std;
namespace gd {
Platform::Platform() {}
Platform::Platform(): enableExtensionLoadingLogs(true) {}
Platform::~Platform() {}
bool Platform::AddExtension(std::shared_ptr<gd::PlatformExtension> extension) {
if (!extension) return false;
std::cout << "Loading " << extension->GetName() << "...";
if (enableExtensionLoadingLogs) std::cout << "Loading " << extension->GetName() << "...";
if (IsExtensionLoaded(extension->GetName())) {
std::cout << " (replacing existing extension)";
if (enableExtensionLoadingLogs) std::cout << " (replacing existing extension)";
RemoveExtension(extension->GetName());
}
std::cout << std::endl;
if (enableExtensionLoadingLogs) std::cout << std::endl;
extensionsLoaded.push_back(extension);

View File

@@ -156,6 +156,12 @@ class GD_CORE_API Platform {
///@}
/**
* \brief Activate or disable the logs on the standard output when
* loading an extension.
*/
void EnableExtensionLoadingLogs(bool enable) { enableExtensionLoadingLogs = enable; };
/**
* \brief Called when the IDE is about to shut down: Take this opportunity for
* erasing for example any temporary file.
@@ -174,6 +180,7 @@ class GD_CORE_API Platform {
extensionsLoaded; ///< Extensions of the platform
std::map<gd::String, CreateFunPtr>
creationFunctionTable; ///< Creation functions for objects
bool enableExtensionLoadingLogs;
};
} // namespace gd

View File

@@ -162,6 +162,13 @@ gd::BehaviorMetadata& PlatformExtension::AddBehavior(
return behaviorsInfo[nameWithNamespace];
}
gd::EffectMetadata& PlatformExtension::AddEffect(const gd::String& name) {
gd::String nameWithNamespace =
GetNameSpace().empty() ? name : GetNameSpace() + name;
effectsMetadata[nameWithNamespace] = EffectMetadata(nameWithNamespace);
return effectsMetadata[nameWithNamespace];
}
gd::EventMetadata& PlatformExtension::AddEvent(
const gd::String& name_,
const gd::String& fullname_,
@@ -207,6 +214,14 @@ std::vector<gd::String> PlatformExtension::GetExtensionObjectsTypes() const {
return objects;
}
std::vector<gd::String> PlatformExtension::GetExtensionEffectTypes() const {
std::vector<gd::String> effectNames;
for (auto& it : effectsMetadata)
effectNames.push_back(it.first);
return effectNames;
}
gd::ObjectMetadata& PlatformExtension::GetObjectMetadata(
const gd::String& objectType) {
if (objectsInfos.find(objectType) != objectsInfos.end())
@@ -227,6 +242,16 @@ gd::BehaviorMetadata& PlatformExtension::GetBehaviorMetadata(
return badBehaviorMetadata;
}
gd::EffectMetadata& PlatformExtension::GetEffectMetadata(
const gd::String& effectName) {
if (effectsMetadata.find(effectName) != effectsMetadata.end())
return effectsMetadata.find(effectName)->second;
std::cout << "Warning: Effect with name \"" << effectName
<< "\" not found in an extension!" << std::endl;
return badEffectMetadata;
}
std::vector<gd::String> PlatformExtension::GetBehaviorsTypes() const {
std::vector<gd::String> behaviors;
@@ -379,6 +404,7 @@ void PlatformExtension::SetNameSpace(gd::String nameSpace_) {
name == "BuiltinCommonConversions" ||
name == "BuiltinStringInstructions" ||
name == "BuiltinMathematicalTools" ||
name == "Effects" || // Well-known effects are not namespaced.
name == "CommonDialogs") // New name for BuiltinInterface
{
nameSpace = "";

View File

@@ -14,6 +14,7 @@
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/String.h"
#include "GDCore/Tools/VersionPriv.h"
@@ -23,6 +24,7 @@ class InstructionMetadata;
class ExpressionMetadata;
class ObjectMetadata;
class BehaviorMetadata;
class EffectMetadata;
class BaseEvent;
class EventMetadata;
class EventCodeGenerator;
@@ -206,6 +208,12 @@ class GD_CORE_API PlatformExtension {
std::shared_ptr<gd::Behavior> instance,
std::shared_ptr<gd::BehaviorsSharedData> sharedDatasInstance);
/**
* \brief Declare a new effect as being part of the extension.
* \param name The internal name of the effect (also called effect type).
*/
gd::EffectMetadata& AddEffect(const gd::String& name_);
/**
* \brief Declare a new event as being part of the extension.
* \note This method does nothing when used for GD C++ runtime.
@@ -284,6 +292,12 @@ class GD_CORE_API PlatformExtension {
*/
CreateFunPtr GetObjectCreationFunctionPtr(gd::String objectType) const;
/**
* \brief Return a vector containing all the effect types provided by the
* extension.
*/
std::vector<gd::String> GetExtensionEffectTypes() const;
/**
* \brief Create a custom event.
*
@@ -318,6 +332,11 @@ class GD_CORE_API PlatformExtension {
*/
BehaviorMetadata& GetBehaviorMetadata(const gd::String& behaviorType);
/**
* \brief Return the metadata for the effect with the given name.
*/
EffectMetadata& GetEffectMetadata(const gd::String& effectName);
/**
* \brief Return a map containing all the events provided by the extension
*/
@@ -455,6 +474,7 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::ObjectMetadata> objectsInfos;
std::map<gd::String, gd::BehaviorMetadata> behaviorsInfo;
std::map<gd::String, gd::EffectMetadata> effectsMetadata;
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
@@ -465,6 +485,7 @@ class GD_CORE_API PlatformExtension {
ObjectMetadata badObjectMetadata;
BehaviorMetadata badBehaviorMetadata;
EffectMetadata badEffectMetadata;
#if defined(GD_IDE_ONLY)
static std::map<gd::String, gd::InstructionMetadata>
badConditionsMetadata; ///< Used when a condition is not found in the

View File

@@ -100,13 +100,6 @@ class GD_CORE_API AbstractFileSystem {
virtual bool CopyFile(const gd::String& file,
const gd::String& destination) = 0;
/**
* \brief Copy a whole directory
* \return true if the operation succeeded.
*/
virtual bool CopyDir(const gd::String& source,
const gd::String& destination) = 0;
/**
* \brief Write the content of a string to a file.
* \return true if the operation succeeded.

View File

@@ -63,7 +63,16 @@ class GD_CORE_API ExpressionObjectsAnalyzer
context.AddObjectName(node.identifierName);
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.objectName.empty()) {
context.AddObjectName(node.objectName);
if (!node.behaviorFunctionName.empty()) {
context.AddBehaviorName(node.objectName, node.objectFunctionOrBehaviorName);
}
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (!node.objectName.empty()) {
context.AddObjectName(node.objectName);

View File

@@ -35,7 +35,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
virtual ~ExpressionObjectRenamer(){};
static bool Rename(gd::ExpressionNode & node, const gd::String& objectName, const gd::String& objectNewName) {
if (ExpressionValidator::HasNoErrors(node)) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectRenamer renamer(objectName, objectNewName);
node.Visit(renamer);
@@ -77,7 +77,13 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
node.identifierName = objectNewName;
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (node.objectName == objectName) {
hasDoneRenaming = true;
node.objectName = objectNewName;
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (node.objectName == objectName) {
hasDoneRenaming = true;
node.objectName = objectNewName;
@@ -107,7 +113,7 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
virtual ~ExpressionObjectFinder(){};
static bool CheckIfHasObject(gd::ExpressionNode & node, const gd::String & objectName) {
if (ExpressionValidator::HasNoErrors(node)) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectFinder finder(objectName);
node.Visit(finder);
@@ -148,7 +154,12 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
hasObject = true;
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (node.objectName == objectName) {
hasObject = true;
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (node.objectName == objectName) {
hasObject = true;
}
@@ -184,7 +195,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
actions[aId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -194,7 +205,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
actions[aId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -237,7 +248,7 @@ bool EventsRefactorer::RenameObjectInConditions(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
conditions[cId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -247,7 +258,7 @@ bool EventsRefactorer::RenameObjectInConditions(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectRenamer::Rename(*node, oldName, newName)) {
conditions[cId].SetParameter(pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
@@ -323,7 +334,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
@@ -334,7 +345,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", actions[aId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
@@ -384,7 +395,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
"number", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
@@ -395,7 +406,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
"string", instrInfos.parameters[pNb].type)) {
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", conditions[cId].GetParameter(pNb).GetPlainString());
if (ExpressionObjectFinder::CheckIfHasObject(*node, name)) {
deleteMe = true;
break;
@@ -595,7 +606,8 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
gd::String search,
bool matchCase,
bool inConditions,
bool inActions) {
bool inActions,
bool inEventStrings) {
vector<EventsSearchResult> results;
for (std::size_t i = 0; i < events.size(); ++i) {
@@ -631,6 +643,16 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
}
}
if (inEventStrings) {
if (!eventAddedInResults &&
SearchStringInEvent(project, layout, events[i], search, matchCase)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
i));
}
}
if (events[i].CanHaveSubEvents()) {
vector<EventsSearchResult> subResults =
SearchInEvents(project,
@@ -639,7 +661,8 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
search,
matchCase,
inConditions,
inActions);
inActions,
inEventStrings);
std::copy(
subResults.begin(), subResults.end(), std::back_inserter(results));
}
@@ -711,6 +734,22 @@ bool EventsRefactorer::SearchStringInConditions(
return false;
}
bool EventsRefactorer::SearchStringInEvent(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::BaseEvent& event,
gd::String search,
bool matchCase) {
for (gd::String str : event.GetAllSearchableStrings()) {
if (matchCase) {
if (str.find(search) != gd::String::npos) return true;
} else {
if (str.FindCaseInsensitive(search) != gd::String::npos) return true;
}
}
return false;
}
EventsSearchResult::EventsSearchResult(std::weak_ptr<gd::BaseEvent> event_,
gd::EventsList* eventsList_,
std::size_t positionInList_)

View File

@@ -103,7 +103,8 @@ class GD_CORE_API EventsRefactorer {
gd::String search,
bool matchCase,
bool inConditions,
bool inAction);
bool inActions,
bool inEventStrings);
/**
* Replace all occurrences of a gd::String in events
@@ -202,6 +203,11 @@ class GD_CORE_API EventsRefactorer {
gd::InstructionsList& conditions,
gd::String search,
bool matchCase);
static bool SearchStringInEvent(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::BaseEvent& events,
gd::String search,
bool matchCase);
EventsRefactorer(){};
};

View File

@@ -63,7 +63,8 @@ class GD_CORE_API ExpressionParameterSearcher
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
for (size_t i = 0; i < node.parameters.size() &&
i < node.expressionMetadata.parameters.size();

View File

@@ -0,0 +1,361 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
#define GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Describe completions to be shown to the user.
*
* The IDE is responsible for actually *searching* and showing the completions -
* this is only describing what must be listed.
*/
struct ExpressionCompletionDescription {
public:
/**
* The different kind of completions that can be described.
*/
enum CompletionKind {
Object,
Behavior,
Expression,
Variable,
};
/**
* \brief Create a completion for an object with the given prefix
*/
static ExpressionCompletionDescription ForObject(const gd::String& prefix_) {
return ExpressionCompletionDescription(Object, "", prefix_);
}
/**
* \brief Create a completion for a behavior with the given prefix of
* the specified object
*/
static ExpressionCompletionDescription ForBehavior(
const gd::String& prefix_, const gd::String& objectName_) {
return ExpressionCompletionDescription(Behavior, "", prefix_, 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_);
}
/**
* \brief Create a completion for an expression (free, object or behavior
* expression) with the given prefix
*/
static ExpressionCompletionDescription ForExpression(
const gd::String& type_,
const gd::String& prefix_,
const gd::String& objectName_ = "",
const gd::String& behaviorName_ = "") {
return ExpressionCompletionDescription(
Expression, type_, prefix_, objectName_, behaviorName_);
}
/** Check if two description of completions are equal */
bool operator==(const ExpressionCompletionDescription& other) const {
return completionKind == other.completionKind && type == other.type &&
prefix == other.prefix && objectName == other.objectName &&
behaviorName == other.behaviorName;
};
/** \brief Return the kind of the completion */
CompletionKind GetCompletionKind() const { return completionKind; }
/**
* \brief Return the type of the completion (same type as types supported in
* expressions)
* (in other words, for expression this is the type of what must be returned).
*/
const gd::String& GetType() const { return type; }
/**
* \brief Return the prefix currently entered and that must be completed.
*/
const gd::String& GetPrefix() const { return prefix; }
/**
* \brief Return the object name, if completing an object expression or a
* behavior.
*/
const gd::String& GetObjectName() const { return objectName; }
/**
* \brief Return the behavior name, if completing an object behavior
* expression.
*
* \warning If completing a behavior, the behavior (partial) name is returned
* by `GetPrefix`.
*/
const gd::String& GetBehaviorName() const { return behaviorName; }
/**
* \brief Set if the completion description is informative, i.e: it's not used
* to
* complete anything. Rather, it should display information about what is
* described by the completion.
*/
ExpressionCompletionDescription& SetIsInformative(bool isInformative_) {
isInformative = isInformative_;
return *this;
}
/**
* \brief Check if the completion description is informative, i.e: it's not
* used to
* complete anything. Rather, it should display information about what is
* described by the completion.
*/
bool IsInformative() const { return isInformative; }
/** Default constructor, only to be used by Emscripten bindings. */
ExpressionCompletionDescription() : completionKind(Object){};
private:
ExpressionCompletionDescription(CompletionKind completionKind_,
const gd::String& type_,
const gd::String& prefix_,
const gd::String& objectName_ = "",
const gd::String& behaviorName_ = "")
: completionKind(completionKind_),
type(type_),
prefix(prefix_),
objectName(objectName_),
behaviorName(behaviorName_),
isInformative(false) {}
CompletionKind completionKind;
gd::String type;
gd::String prefix;
gd::String objectName;
gd::String behaviorName;
bool isInformative;
};
/**
* \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.IsInformative() ? "informative" : "non-informative") << " }";
return os;
}
/**
* \brief Returns the list of completion descriptions for an expression node.
*
* \see gd::ExpressionCompletionDescription
*/
class GD_CORE_API ExpressionCompletionFinder
: public ExpressionParser2NodeWorker {
public:
/**
* \brief Given the expression, find the node at the specified location
* and returns completions for it.
*/
static std::vector<ExpressionCompletionDescription>
GetCompletionDescriptionsFor(gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNode* nodeAtLocation =
gd::ExpressionNodeLocationFinder::GetNodeAtPosition(node,
searchedPosition);
if (nodeAtLocation == nullptr) {
std::vector<ExpressionCompletionDescription> emptyCompletions;
return emptyCompletions;
}
gd::ExpressionCompletionFinder autocompletionProvider(searchedPosition);
nodeAtLocation->Visit(autocompletionProvider);
return autocompletionProvider.GetCompletionDescriptions();
}
/**
* \brief Return the completions found for the visited node.
*/
const std::vector<ExpressionCompletionDescription>&
GetCompletionDescriptions() {
return completions;
};
virtual ~ExpressionCompletionFinder(){};
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
completions.push_back(ExpressionCompletionDescription::ForObject(""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
}
void OnVisitOperatorNode(OperatorNode& node) override {
completions.push_back(ExpressionCompletionDescription::ForObject(""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
completions.push_back(ExpressionCompletionDescription::ForObject(""));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, ""));
}
void OnVisitNumberNode(NumberNode& node) override {
// No completions
}
void OnVisitTextNode(TextNode& node) override {
// No completions
}
void OnVisitVariableNode(VariableNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForVariable(node.type, node.name));
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
// No completions
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
// No completions
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (gd::ParameterMetadata::IsObject(node.type)) {
// Only show completions of objects if an object is required
completions.push_back(
ExpressionCompletionDescription::ForObject(node.identifierName));
} else {
// Show completions for expressions and objects otherwise.
completions.push_back(
ExpressionCompletionDescription::ForObject(node.identifierName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.identifierName));
}
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty() ||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
// Behavior function (or behavior function being written, with the
// function name missing)
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.objectName));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.objectFunctionOrBehaviorName, node.objectName));
} else if (IsCaretOn(node.behaviorNameNamespaceSeparatorLocation) ||
IsCaretOn(node.behaviorFunctionNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type,
node.behaviorFunctionName,
node.objectName,
node.objectFunctionOrBehaviorName));
}
} else {
// Object function or behavior name
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.objectName));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.objectFunctionOrBehaviorName, node.objectName));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, node.objectFunctionOrBehaviorName, node.objectName));
}
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (!node.behaviorName.empty()) {
// Behavior function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.objectName));
} else if (IsCaretOn(node.objectNameDotLocation) ||
IsCaretOn(node.behaviorNameLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.behaviorName, node.objectName));
} else {
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type,
node.functionName,
node.objectName,
node.behaviorName)
.SetIsInformative(IsCaretOn(node.openingParenthesisLocation)));
}
} else if (!node.objectName.empty()) {
// Object function
if (IsCaretOn(node.objectNameLocation)) {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.objectName));
} 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
// need behavior completions. Do this unless we're on the parenthesis
// (at which point we're only showing informative message about the
// function).
if (!IsCaretOn(node.openingParenthesisLocation)) {
completions.push_back(ExpressionCompletionDescription::ForBehavior(
node.functionName, node.objectName));
}
completions.push_back(
ExpressionCompletionDescription::ForExpression(
node.type, node.functionName, node.objectName)
.SetIsInformative(IsCaretOn(node.openingParenthesisLocation)));
}
} else {
// Free function
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type,
node.functionName)
.SetIsInformative(IsCaretOn(node.openingParenthesisLocation)));
}
}
void OnVisitEmptyNode(EmptyNode& node) override {
completions.push_back(
ExpressionCompletionDescription::ForObject(node.text));
completions.push_back(
ExpressionCompletionDescription::ForExpression(node.type, node.text));
}
private:
bool IsCaretOn(const ExpressionParserLocation& location,
bool inclusive = false) {
return (location.GetStartPosition() <= searchedPosition &&
((!inclusive && searchedPosition < location.GetEndPosition()) ||
(inclusive && searchedPosition <= location.GetEndPosition())));
}
ExpressionCompletionFinder(size_t searchedPosition_)
: searchedPosition(searchedPosition_){};
std::vector<ExpressionCompletionDescription> completions;
size_t searchedPosition;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H

View File

@@ -0,0 +1,126 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONNODELOCATIONFINDER_H
#define GDCORE_EXPRESSIONNODELOCATIONFINDER_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Find the deepest node at the specified location in an expression.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionNodeLocationFinder
: public ExpressionParser2NodeWorker {
public:
/**
* \brief Initialize the finder to search at the specified position.
*/
ExpressionNodeLocationFinder(size_t searchedPosition_)
: searchedPosition(searchedPosition_), foundNode(nullptr){};
virtual ~ExpressionNodeLocationFinder(){};
/**
* \brief Helper function to find the deepest node at the search position, if
* any.
*/
static ExpressionNode* GetNodeAtPosition(gd::ExpressionNode& node,
size_t searchedPosition) {
gd::ExpressionNodeLocationFinder finder(searchedPosition);
node.Visit(finder);
return finder.GetNode();
}
/**
* \brief Return the deepest node found at the search position, if any.
*/
ExpressionNode* GetNode() { return foundNode; };
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
CheckSearchPositionInNode(node);
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
if (CheckSearchPositionInNode(node)) {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
CheckSearchPositionInNode(node);
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {
CheckSearchPositionInNode(node);
}
void OnVisitTextNode(TextNode& node) override {
CheckSearchPositionInNode(node);
}
void OnVisitVariableNode(VariableNode& node) override {
if (CheckSearchPositionInNode(node)) {
if (node.child) node.child->Visit(*this);
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (CheckSearchPositionInNode(node)) {
if (node.child) node.child->Visit(*this);
}
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
if (CheckSearchPositionInNode(node)) {
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
CheckSearchPositionInNode(node);
}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
CheckSearchPositionInNode(node);
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
CheckSearchPositionInNode(node);
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {
CheckSearchPositionInNode(node, /*inclusive=*/true);
}
private:
bool CheckSearchPositionInNode(ExpressionNode& node, bool inclusive = false) {
if (node.location.GetStartPosition() <= searchedPosition &&
((!inclusive && searchedPosition < node.location.GetEndPosition()) ||
(inclusive && searchedPosition <= node.location.GetEndPosition()))) {
foundNode = &node;
return true;
}
return false;
}
size_t searchedPosition;
ExpressionNode* foundNode;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONNODELOCATIONFINDER_H

View File

@@ -83,7 +83,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitIdentifierNode(IdentifierNode& node) override {
ReportAnyError(node);
}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
ReportAnyError(node);
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
ReportAnyError(node);
for (auto& parameter : node.parameters) {
parameter->Visit(*this);

View File

@@ -1,59 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/ExpressionsCorrectnessTesting.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
namespace gd {
CallbacksForExpressionCorrectnessTesting::
CallbacksForExpressionCorrectnessTesting(const gd::ObjectsContainer& project_,
const gd::ObjectsContainer& layout_)
: project(project_), layout(layout_) {}
bool CallbacksForExpressionCorrectnessTesting::OnSubMathExpression(
const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForExpressionCorrectnessTesting callbacks(project, layout);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseMathExpression(platform, project, layout, callbacks)) {
#if defined(GD_IDE_ONLY)
firstErrorStr = callbacks.GetFirstError();
firstErrorPos = callbacks.GetFirstErrorPosition();
#endif
return false;
}
return true;
}
bool CallbacksForExpressionCorrectnessTesting::OnSubTextExpression(
const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForExpressionCorrectnessTesting callbacks(project, layout);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseStringExpression(platform, project, layout, callbacks)) {
#if defined(GD_IDE_ONLY)
firstErrorStr = callbacks.GetFirstError();
firstErrorPos = callbacks.GetFirstErrorPosition();
#endif
return false;
}
return true;
}
} // namespace gd

View File

@@ -1,75 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONSCORRECTNESSTESTING_H
#define GDCORE_EXPRESSIONSCORRECTNESSTESTING_H
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/String.h"
namespace gd {
class ExpressionMetadata;
class Expression;
class Project;
class Layout;
}
namespace gd {
// TODO: Replace and remove (ExpressionValidator)
/**
* \brief Parser callbacks used to check expressions correctness
*
* Usage example:
* \code
* gd::CallbacksForExpressionCorrectnessTesting callbacks(game, scene);
* gd::ExpressionParser expressionParser(expression);
* if ( !expressionParser.ParseMathExpression(game, scene, callbacks) )
* //Expression is not valid
* else
* //Expression is correct
* \endcode
*
* \see gd::ExpressionParser
* \see gd::ParserCallbacks
*
* \ingroup IDE
*/
class GD_CORE_API CallbacksForExpressionCorrectnessTesting
: public gd::ParserCallbacks {
public:
CallbacksForExpressionCorrectnessTesting(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout);
virtual ~CallbacksForExpressionCorrectnessTesting(){};
void OnConstantToken(gd::String text){};
void OnStaticFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo){};
void OnObjectFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo){};
void OnObjectBehaviorFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo){};
bool OnSubMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression);
bool OnSubTextExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression);
private:
const gd::ObjectsContainer& project;
const gd::ObjectsContainer& layout;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONSCORRECTNESSTESTING_H

View File

@@ -0,0 +1,179 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Go through the nodes and change the position of a parameter of the
* given function.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParameterMover
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterMover(const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_,
const gd::String& behaviorType_,
const gd::String& objectType_,
const gd::String& functionName_,
std::size_t oldIndex_,
std::size_t newIndex_)
: hasDoneMoving(false),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
behaviorType(behaviorType_),
objectType(objectType_),
functionName(functionName_),
oldIndex(oldIndex_),
newIndex(newIndex_){};
virtual ~ExpressionParameterMover(){};
bool HasDoneMoving() const { return hasDoneMoving; }
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
auto moveParameter =
[this](std::vector<std::unique_ptr<gd::ExpressionNode>>& parameters) {
if (oldIndex >= parameters.size() || newIndex >= parameters.size())
return;
auto movedParameterNode = std::move(parameters[oldIndex]);
parameters.erase(parameters.begin() + oldIndex);
parameters.insert(parameters.begin() + newIndex,
std::move(movedParameterNode));
};
if (node.functionName == functionName) {
if (behaviorType.empty() && !objectType.empty() &&
!node.objectName.empty()) {
// Move parameter of an object function
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
if (thisObjectType == behaviorType) {
moveParameter(node.parameters);
hasDoneMoving = true;
}
} else if (!behaviorType.empty() && !node.behaviorName.empty()) {
// Move parameter of a behavior function
const gd::String& thisBehaviorType = gd::GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, node.behaviorName);
if (thisBehaviorType == behaviorType) {
moveParameter(node.parameters);
hasDoneMoving = true;
}
} else if (behaviorType.empty() && objectType.empty()) {
// Move parameter of a free function
moveParameter(node.parameters);
hasDoneMoving = true;
}
}
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
bool hasDoneMoving;
const gd::ObjectsContainer& globalObjectsContainer;
const gd::ObjectsContainer& objectsContainer;
const gd::String& behaviorType; // The behavior type of the function which
// must have a parameter moved (optional).
const gd::String& objectType; // The object type of the function which
// must have a parameter moved (optional). If
// `behaviorType` is not empty, it takes
// precedence over `objectType`.
const gd::String& functionName;
std::size_t oldIndex;
std::size_t newIndex;
};
bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
auto& metadata = isCondition ? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& expression =
instruction.GetParameter(pNb).GetPlainString();
gd::ExpressionParser2 parser(
platform, GetGlobalObjectsContainer(), GetObjectsContainer());
auto node = gd::ParameterMetadata::IsExpression("number", type)
? parser.ParseExpression("number", expression)
: (gd::ParameterMetadata::IsExpression("string", type)
? parser.ParseExpression("string", expression)
: std::unique_ptr<gd::ExpressionNode>());
if (node) {
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
GetObjectsContainer(),
behaviorType,
objectType,
functionName,
oldIndex,
newIndex);
node->Visit(mover);
if (mover.HasDoneMoving()) {
instruction.SetParameter(
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
}
}
}
return false;
}
ExpressionsParameterMover::~ExpressionsParameterMover() {}
} // namespace gd

View File

@@ -0,0 +1,85 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef ExpressionsParameterMover_H
#define ExpressionsParameterMover_H
#include <map>
#include <memory>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Platform;
class EventsList;
} // namespace gd
namespace gd {
/**
* \brief Move in expressions, in parameters of actions or conditions, a
* parameter from one position to another.
*
* \see InstructionsParameterMover
* \ingroup IDE
*/
class GD_CORE_API ExpressionsParameterMover
: public ArbitraryEventsWorkerWithContext {
public:
ExpressionsParameterMover(const gd::Platform &platform_)
: platform(platform_){};
virtual ~ExpressionsParameterMover();
ExpressionsParameterMover &SetFreeExpressionMovedParameter(
const gd::String &functionName_,
std::size_t oldIndex_,
std::size_t newIndex_) {
objectType = "";
behaviorType = "";
functionName = functionName_;
oldIndex = oldIndex_;
newIndex = newIndex_;
return *this;
}
ExpressionsParameterMover &SetObjectExpressionMovedParameter(
const gd::String &objectType_,
const gd::String &functionName_,
std::size_t oldIndex_,
std::size_t newIndex_) {
objectType = objectType_;
behaviorType = "";
functionName = functionName_;
oldIndex = oldIndex_;
newIndex = newIndex_;
return *this;
};
ExpressionsParameterMover &SetBehaviorExpressionMovedParameter(
const gd::String &behaviorType_,
const gd::String &functionName_,
std::size_t oldIndex_,
std::size_t newIndex_) {
objectType = "";
behaviorType = behaviorType_;
functionName = functionName_;
oldIndex = oldIndex_;
newIndex = newIndex_;
return *this;
};
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
const gd::Platform &platform;
gd::String functionName;
std::size_t oldIndex;
std::size_t newIndex;
gd::String behaviorType;
gd::String objectType;
};
} // namespace gd
#endif // ExpressionsParameterMover_H

View File

@@ -71,13 +71,41 @@ class GD_CORE_API ExpressionFunctionRenamer
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitFunctionNode(FunctionNode& node) override {
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
if (!node.behaviorFunctionName.empty()) {
// Behavior function name
if (!behaviorType.empty() &&
node.behaviorFunctionName == oldFunctionName) {
const gd::String& thisBehaviorType =
gd::GetTypeOfBehavior(globalObjectsContainer,
objectsContainer,
node.objectFunctionOrBehaviorName);
if (thisBehaviorType == behaviorType) {
node.behaviorFunctionName = newFunctionName;
hasDoneRenaming = true;
}
}
} else {
// Object function name
if (behaviorType.empty() && !objectType.empty() &&
node.objectFunctionOrBehaviorName == oldFunctionName) {
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
if (thisObjectType == objectType) {
node.objectFunctionOrBehaviorName = newFunctionName;
hasDoneRenaming = true;
}
}
}
}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
if (node.functionName == oldFunctionName) {
if (!objectType.empty() && !node.objectName.empty()) {
if (behaviorType.empty() && !objectType.empty() &&
!node.objectName.empty()) {
// Replace an object function
const gd::String& thisObjectType = gd::GetTypeOfObject(
globalObjectsContainer, objectsContainer, node.objectName);
if (thisObjectType == behaviorType) {
if (thisObjectType == objectType) {
node.functionName = newFunctionName;
hasDoneRenaming = true;
}
@@ -89,7 +117,7 @@ class GD_CORE_API ExpressionFunctionRenamer
node.functionName = newFunctionName;
hasDoneRenaming = true;
}
} else {
} else if (behaviorType.empty() && objectType.empty()) {
// Replace a free function
node.functionName = newFunctionName;
hasDoneRenaming = true;
@@ -106,9 +134,11 @@ class GD_CORE_API ExpressionFunctionRenamer
const gd::ObjectsContainer& globalObjectsContainer;
const gd::ObjectsContainer& objectsContainer;
const gd::String& behaviorType; // The behavior type for which the expression
// must be replaced (optional)
// must be replaced (optional).
const gd::String& objectType; // The object type for which the expression
// must be replaced (optional)
// must be replaced (optional). If
// `behaviorType` is not empty, it takes
// precedence over `objectType`.
const gd::String& oldFunctionName;
const gd::String& newFunctionName;
};

View File

@@ -0,0 +1,37 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool InstructionsParameterMover::DoVisitInstruction(
gd::Instruction& instruction, bool isCondition) {
if (instruction.GetType() == instructionType) {
std::vector<gd::Expression> updatedParameters = instruction.GetParameters();
if (oldIndex < updatedParameters.size() ||
newIndex < updatedParameters.size()) {
gd::Expression movedParameter = updatedParameters[oldIndex];
updatedParameters.erase(updatedParameters.begin() + oldIndex);
updatedParameters.insert(updatedParameters.begin() + newIndex,
movedParameter);
instruction.SetParameters(updatedParameters);
}
}
return false;
}
InstructionsParameterMover::~InstructionsParameterMover() {}
} // namespace gd

View File

@@ -0,0 +1,53 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef InstructionsParameterMover_H
#define InstructionsParameterMover_H
#include <map>
#include <memory>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
} // namespace gd
namespace gd {
/**
* \brief Reorder the parameters of the instruction with the specified
* type in events, moving the parameter at the specified \a oldIndex to
* \a newIndex.
*
* \see ExpressionsParameterMover
* \ingroup IDE
*/
class GD_CORE_API InstructionsParameterMover : public ArbitraryEventsWorker {
public:
InstructionsParameterMover(const gd::Project& project_,
const gd::String& instructionType_,
std::size_t oldIndex_,
std::size_t newIndex_)
: project(project_),
instructionType(instructionType_),
oldIndex(oldIndex_),
newIndex(newIndex_){};
virtual ~InstructionsParameterMover();
private:
bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) override;
const gd::Project& project;
gd::String instructionType;
std::size_t oldIndex;
std::size_t newIndex;
};
} // namespace gd
#endif // InstructionsParameterMover_H

View File

@@ -10,34 +10,49 @@
namespace gd {
gd::String SceneNameMangler::GetMangledSceneName(gd::String sceneName) {
static const gd::String allowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const gd::String allowedExceptFirst = "0123456789";
SceneNameMangler *SceneNameMangler::_singleton = nullptr;
std::size_t i = 0;
for (auto it = sceneName.begin(); it != sceneName.end(); ++it) {
char32_t character = *it;
if (allowedCharacters.find(character) == gd::String::npos &&
(allowedExceptFirst.find(character) == gd::String::npos ||
i == 0)) // Also disallow some characters to be in first position
{
// Replace the character by an underscore and its unicode codepoint (in
// base 10)
auto it2 = it;
++it2;
sceneName.replace(it, it2, "_" + gd::String::From(character));
// The iterator it may have been invalidated:
// re-assign it with a new iterator pointing to the same position.
it = sceneName.begin();
std::advance(it, i);
}
++i;
const gd::String &SceneNameMangler::GetMangledSceneName(
const gd::String &sceneName) {
auto it = mangledSceneNames.find(sceneName);
if (it != mangledSceneNames.end()) {
return it->second;
}
return sceneName;
gd::String partiallyMangledName = sceneName;
static const gd::String alwaysAllowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const gd::String allowedExceptFirstCharacters = "0123456789";
for (size_t i = 0; i < partiallyMangledName.size();
++i) // Replace all unallowed letter by an underscore and the unicode
// code point of the letter
{
if (alwaysAllowedCharacters.find_first_of(
std::u32string(1, partiallyMangledName[i])) == gd::String::npos &&
(i == 0 ||
allowedExceptFirstCharacters.find(
std::u32string(1, partiallyMangledName[i])) == gd::String::npos)) {
char32_t unallowedChar = partiallyMangledName[i];
partiallyMangledName.replace(i, 1, "_" + gd::String::From(unallowedChar));
}
}
mangledSceneNames[sceneName] = partiallyMangledName;
return mangledSceneNames[sceneName];
}
SceneNameMangler *SceneNameMangler::Get() {
if (nullptr == _singleton) _singleton = new SceneNameMangler;
return (static_cast<SceneNameMangler *>(_singleton));
}
void SceneNameMangler::DestroySingleton() {
if (nullptr != _singleton) {
delete _singleton;
_singleton = nullptr;
}
}
} // namespace gd

View File

@@ -6,12 +6,14 @@
#ifndef SCENENAMEMANGLER_H
#define SCENENAMEMANGLER_H
#include <unordered_map>
#include "GDCore/String.h"
namespace gd {
/**
* \brief Used to mangle the name of a scene
* \brief Mangle the name of a scene, so that it can be used in code or file
* names.
*
* \ingroup IDE
*/
@@ -21,12 +23,22 @@ class GD_CORE_API SceneNameMangler {
* \brief Mangle the name of a scene, replacing all characters that are not
* 0-9, a-z or A-Z by "_"+UnicodeCodePointOfTheCharacter. The first character
* must be a letter, otherwise it is also replaced in the same manner.
*
* The mangled name is memoized as this is intensively used during project
* export and events code generation.
*/
static gd::String GetMangledSceneName(gd::String sceneName);
const gd::String& GetMangledSceneName(const gd::String& sceneName);
static SceneNameMangler* Get();
static void DestroySingleton();
private:
SceneNameMangler(){};
virtual ~SceneNameMangler(){};
static SceneNameMangler* _singleton;
std::unordered_map<gd::String, gd::String>
mangledSceneNames; ///< Memoized results of mangling
};
} // namespace gd

View File

@@ -9,7 +9,9 @@
#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/InstructionsParameterMover.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
@@ -85,7 +87,8 @@ void WholeProjectRefactorer::ExposeProjectEvents(
void WholeProjectRefactorer::ExposeProjectEvents(
gd::Project& project, gd::ArbitraryEventsWorkerWithContext& worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources).
// project (this time for resources) and ExposeProjectEffects (this time for
// effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
@@ -331,9 +334,6 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
}
}
/**
* \brief Refactor the project after an events function is renamed
*/
void WholeProjectRefactorer::RenameEventsFunction(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
@@ -391,6 +391,76 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
}
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(functionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
const gd::String& eventsFunctionType = GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetFreeExpressionMovedParameter(
eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex) {
auto& eventsFunctions = eventsBasedBehavior.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(functionName)) return;
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(functionName);
const gd::String& eventsFunctionType =
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetBehaviorExpressionMovedParameter(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
functionName,
oldIndex,
newIndex);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::RenameBehaviorProperty(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
@@ -400,7 +470,17 @@ void WholeProjectRefactorer::RenameBehaviorProperty(
auto& properties = eventsBasedBehavior.GetPropertyDescriptors();
if (!properties.Has(oldPropertyName)) return;
const auto& property = properties.Get(oldPropertyName);
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
ExposeProjectEvents(project, expressionRenamer);
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -425,15 +505,6 @@ void WholeProjectRefactorer::RenameBehaviorProperty(
eventsBasedBehavior.GetName(),
EventsBasedBehavior::GetPropertyConditionName(newPropertyName)));
ExposeProjectEvents(project, conditionRenamer);
gd::ExpressionsRenamer expressionRenamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
expressionRenamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
EventsBasedBehavior::GetPropertyExpressionName(oldPropertyName),
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
ExposeProjectEvents(project, expressionRenamer);
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(

View File

@@ -50,7 +50,10 @@ class GD_CORE_API WholeProjectRefactorer {
gd::ArbitraryEventsWorkerWithContext& worker);
/**
* \brief Refactor the project after an events function extension is renamed
* \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.
*/
static void RenameEventsFunctionsExtension(
gd::Project& project,
@@ -59,7 +62,10 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newName);
/**
* \brief Refactor the project after an events function is renamed
* \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.
*/
static void RenameEventsFunction(
gd::Project& project,
@@ -68,8 +74,11 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newFunctionName);
/**
* \brief Refactor the project after an events function of a behavior is
* \brief Refactor the project **before** an events function of a behavior 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.
*/
static void RenameBehaviorEventsFunction(
gd::Project& project,
@@ -79,8 +88,40 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newFunctionName);
/**
* \brief Refactor the project after a property of a behavior is
* \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.
*/
static void MoveEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex);
/**
* \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.
*/
static void MoveBehaviorEventsFunctionParameter(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& functionName,
std::size_t oldIndex,
std::size_t newIndex);
/**
* \brief Refactor the project **before** a property of a behavior is
* 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.
*/
static void RenameBehaviorProperty(
gd::Project& project,
@@ -90,7 +131,10 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newPropertyName);
/**
* \brief Refactor the project after a behavior is renamed.
* \brief Refactor the project **before** a behavior is renamed.
*
* \warning Do the renaming of the specified behavior after calling this.
* This is because the behavior is expected to have its old name for the refactoring.
*/
static void RenameEventsBasedBehavior(
gd::Project& project,

View File

@@ -12,21 +12,60 @@ namespace gd {
#if defined(GD_IDE_ONLY)
void Effect::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("effectName", GetEffectName());
SerializerElement& parametersElement = element.AddChild("parameters");
for (auto& parameter : parameters)
parametersElement.AddChild(parameter.first).SetValue(parameter.second);
element.SetAttribute("effectType", GetEffectType());
SerializerElement& doubleParametersElement =
element.AddChild("doubleParameters");
for (auto& parameter : doubleParameters)
doubleParametersElement.AddChild(parameter.first)
.SetValue(parameter.second);
SerializerElement& stringParametersElement =
element.AddChild("stringParameters");
for (auto& parameter : stringParameters)
stringParametersElement.AddChild(parameter.first)
.SetValue(parameter.second);
SerializerElement& booleanParametersElement =
element.AddChild("booleanParameters");
for (auto& parameter : booleanParameters)
booleanParametersElement.AddChild(parameter.first)
.SetValue(parameter.second);
}
#endif
void Effect::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name"));
SetEffectName(element.GetStringAttribute("effectName"));
SetEffectType(element.GetStringAttribute(
"effectType",
"",
// Compatibility with GD <= 5.0.0-beta83
"effectName"
// end of compatibility code
));
parameters.clear();
const SerializerElement& parametersElement = element.GetChild("parameters");
for (auto& child : parametersElement.GetAllChildren())
SetParameter(child.first, child.second->GetValue().GetDouble());
doubleParameters.clear();
const SerializerElement& doubleParametersElement =
element.GetChild("doubleParameters",
0,
// Compatibility with GD <= 5.0.0-beta83
"parameters"
// end of compatibility code
);
for (auto& child : doubleParametersElement.GetAllChildren())
SetDoubleParameter(child.first, child.second->GetValue().GetDouble());
stringParameters.clear();
const SerializerElement& stringParametersElement =
element.GetChild("stringParameters");
for (auto& child : stringParametersElement.GetAllChildren())
SetStringParameter(child.first, child.second->GetValue().GetString());
booleanParameters.clear();
const SerializerElement& booleanParametersElement =
element.GetChild("booleanParameters");
for (auto& child : booleanParametersElement.GetAllChildren())
SetBooleanParameter(child.first, child.second->GetValue().GetBool());
}
} // namespace gd

View File

@@ -27,17 +27,51 @@ class GD_CORE_API Effect {
void SetName(const gd::String& name_) { name = name_; }
const gd::String& GetName() const { return name; }
void SetEffectName(const gd::String& effectName_) {
effectName = effectName_;
void SetEffectType(const gd::String& effectType_) {
effectType = effectType_;
}
const gd::String& GetEffectName() const { return effectName; }
const gd::String& GetEffectType() const { return effectType; }
void SetParameter(const gd::String& name, double value) {
parameters[name] = value;
void SetDoubleParameter(const gd::String& name, double value) {
doubleParameters[name] = value;
}
double GetParameter(const gd::String& name) { return parameters[name]; }
const std::map<gd::String, double>& GetAllParameters() const {
return parameters;
double GetDoubleParameter(const gd::String& name) {
return doubleParameters[name];
}
void SetStringParameter(const gd::String& name, const gd::String& value) {
stringParameters[name] = value;
}
const gd::String& GetStringParameter(const gd::String& name) {
return stringParameters[name];
}
void SetBooleanParameter(const gd::String& name, bool value) {
booleanParameters[name] = value;
}
bool GetBooleanParameter(const gd::String& name) {
return booleanParameters[name];
}
const std::map<gd::String, double>& GetAllDoubleParameters() const {
return doubleParameters;
}
const std::map<gd::String, gd::String>& GetAllStringParameters() const {
return stringParameters;
}
const std::map<gd::String, bool>& GetAllBooleanParameters() const {
return booleanParameters;
}
void ClearParameters() {
doubleParameters.clear();
stringParameters.clear();
booleanParameters.clear();
}
#if defined(GD_IDE_ONLY)
@@ -53,9 +87,11 @@ class GD_CORE_API Effect {
void UnserializeFrom(const SerializerElement& element);
private:
gd::String name; ///< The name of the layer
gd::String effectName; ///< The name of the effect to apply
std::map<gd::String, double> parameters;
gd::String name; ///< The name of the layer.
gd::String effectType; ///< The name of the effect to apply.
std::map<gd::String, double> doubleParameters; ///< Values of parameters being doubles, keyed by names.
std::map<gd::String, gd::String> stringParameters; ///< Values of parameters being strings, keyed by names.
std::map<gd::String, bool> booleanParameters; ///< Values of parameters being booleans, keyed by names.
};
} // namespace gd

View File

@@ -93,6 +93,9 @@ class GD_CORE_API EventsFunctionsContainer
void MoveEventsFunction(std::size_t oldIndex, std::size_t newIndex) {
return Move(oldIndex, newIndex);
};
std::size_t GetEventsFunctionPosition(const gd::EventsFunction& eventsFunction) {
return GetPosition(eventsFunction);
};
/**
* \brief Provide a raw access to the vector containing the functions.
@@ -141,4 +144,4 @@ class GD_CORE_API EventsFunctionsContainer
} // namespace gd
#endif // GDCORE_EVENTSFUNCTIONSCONTAINER_H
#endif
#endif

View File

@@ -70,6 +70,19 @@ void EventsFunctionsExtension::UnserializeFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
}
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(
const gd::String& eventsFunctionName) {
// The list of all supported lifecycle events function names.
// If adding a new one, code generator(s) must be updated.
return eventsFunctionName == "onFirstSceneLoaded" ||
eventsFunctionName == "onSceneLoaded" ||
eventsFunctionName == "onScenePreEvents" ||
eventsFunctionName == "onScenePostEvents" ||
eventsFunctionName == "onScenePaused" ||
eventsFunctionName == "onSceneResumed" ||
eventsFunctionName == "onSceneUnloading";
}
} // namespace gd
#endif

View File

@@ -8,8 +8,8 @@
#define GDCORE_EVENTSFUNCTIONEXTENSION_H
#include <vector>
#include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/SerializableWithNameList.h"
namespace gd {
@@ -59,7 +59,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
}
const gd::String& GetShortDescription() const { return shortDescription; };
EventsFunctionsExtension& SetShortDescription(const gd::String& shortDescription_) {
EventsFunctionsExtension& SetShortDescription(
const gd::String& shortDescription_) {
shortDescription = shortDescription_;
return *this;
}
@@ -97,15 +98,15 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
/**
* \brief Return a reference to the list of the events based behaviors.
*/
SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors() {
gd::SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors() {
return eventsBasedBehaviors;
}
/**
* \brief Return a const reference to the list of the events based behaviors.
*/
const SerializableWithNameList<EventsBasedBehavior>& GetEventsBasedBehaviors()
const {
const gd::SerializableWithNameList<EventsBasedBehavior>&
GetEventsBasedBehaviors() const {
return eventsBasedBehaviors;
}
@@ -124,6 +125,12 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
const gd::SerializerElement& element);
///@}
/** \name Lifecycle event functions
*/
///@{
static bool IsExtensionLifecycleEventsFunction(const gd::String& eventsFunctionName);
///@}
private:
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
@@ -139,7 +146,7 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::String fullName;
gd::String tags;
gd::String author;
SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
};
} // namespace gd

View File

@@ -63,7 +63,7 @@ Layout::Layout()
void Layout::SetName(const gd::String& name_) {
name = name_;
mangledName = gd::SceneNameMangler::GetMangledSceneName(name);
mangledName = gd::SceneNameMangler::Get()->GetMangledSceneName(name);
};
bool Layout::HasBehaviorSharedData(const gd::String& behaviorName) {

View File

@@ -59,6 +59,7 @@ Project::Project()
minFPS(20),
verticalSync(false),
scaleMode("linear"),
adaptGameResolutionAtRuntime(true),
sizeOnStartupMode("adaptWidth"),
imageManager(std::make_shared<ImageManager>())
#if defined(GD_IDE_ONLY)
@@ -574,9 +575,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
element.GetChild("properties", 0, "Info");
SetName(propElement.GetChild("name", 0, "Nom").GetValue().GetString());
SetVersion(propElement.GetStringAttribute("version", "1.0.0"));
SetDefaultWidth(
propElement.GetChild("windowWidth", 0, "WindowW").GetValue().GetInt());
SetDefaultHeight(
SetGameResolutionSize(
propElement.GetChild("windowWidth", 0, "WindowW").GetValue().GetInt(),
propElement.GetChild("windowHeight", 0, "WindowH").GetValue().GetInt());
SetMaximumFPS(
propElement.GetChild("maxFPS", 0, "FPSmax").GetValue().GetInt());
@@ -585,6 +585,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
SetVerticalSyncActivatedByDefault(
propElement.GetChild("verticalSync").GetValue().GetBool());
SetScaleMode(propElement.GetStringAttribute("scaleMode", "linear"));
SetAdaptGameResolutionAtRuntime(
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
#if defined(GD_IDE_ONLY)
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
@@ -851,8 +853,8 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("name").SetValue(GetName());
propElement.SetAttribute("version", GetVersion());
propElement.AddChild("author").SetValue(GetAuthor());
propElement.AddChild("windowWidth").SetValue(GetMainWindowDefaultWidth());
propElement.AddChild("windowHeight").SetValue(GetMainWindowDefaultHeight());
propElement.AddChild("windowWidth").SetValue(GetGameResolutionWidth());
propElement.AddChild("windowHeight").SetValue(GetGameResolutionHeight());
propElement.AddChild("latestCompilationDirectory")
.SetValue(GetLastCompilationDirectory());
propElement.AddChild("maxFPS").SetValue(GetMaximumFPS());
@@ -860,6 +862,7 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("verticalSync")
.SetValue(IsVerticalSynchronizationEnabledByDefault());
propElement.SetAttribute("scaleMode", scaleMode);
propElement.SetAttribute("adaptGameResolutionAtRuntime", adaptGameResolutionAtRuntime);
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
propElement.SetAttribute("projectFile", gameFile);
propElement.SetAttribute("folderProject", folderProject);
@@ -956,8 +959,9 @@ gd::String Project::GetBadObjectNameWarning() {
void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// See also gd::WholeProjectRefactorer::ExposeProjectEvents for a method that
// traverse the whole project (this time for events). Ideally, this method
// could be moved outside of gd::Project.
// traverse the whole project (this time for events) and ExposeProjectEffects
// (this time for effects). Ideally, this method could be moved outside of
// gd::Project.
// Add project resources
worker.ExposeResources(&GetResourcesManager());
@@ -1058,6 +1062,7 @@ void Project::Init(const gd::Project& game) {
minFPS = game.minFPS;
verticalSync = game.verticalSync;
scaleMode = game.scaleMode;
adaptGameResolutionAtRuntime = game.adaptGameResolutionAtRuntime;
sizeOnStartupMode = game.sizeOnStartupMode;
#if defined(GD_IDE_ONLY)

View File

@@ -200,22 +200,36 @@ class GD_CORE_API Project : public ObjectsContainer {
* the first time. To change the width at runtime, use the functions related
* to RuntimeScene.renderWindow
*/
void SetDefaultWidth(unsigned int width) { windowWidth = width; }
void SetGameResolutionSize(unsigned int width, unsigned int height) {
windowWidth = width;
windowHeight = height;
}
/**
* Get the default width of the project main window
* \brief Returns the default game resolution width.
*/
unsigned int GetMainWindowDefaultWidth() const { return windowWidth; }
unsigned int GetGameResolutionWidth() const { return windowWidth; }
/**
* Change the default height of the project main window
* \brief Returns the default game resolution height.
*/
void SetDefaultHeight(unsigned int height) { windowHeight = height; }
unsigned int GetGameResolutionHeight() const { return windowHeight; }
/**
* Return the default height of the project main window
* \brief Returns true if the game resolution should be adapted to the window
* size at runtime.
*/
unsigned int GetMainWindowDefaultHeight() const { return windowHeight; }
bool GetAdaptGameResolutionAtRuntime() const {
return adaptGameResolutionAtRuntime;
}
/**
* \brief Set if the game resolution should be adapted to the window size at
* runtime. \see SetSizeOnStartupMode
*/
void SetAdaptGameResolutionAtRuntime(bool adaptGameResolutionAtRuntime_) {
adaptGameResolutionAtRuntime = adaptGameResolutionAtRuntime_;
}
/**
* \brief Get how the game size should be adapted to the screen.
@@ -924,6 +938,8 @@ class GD_CORE_API Project : public ObjectsContainer {
///< are below this number )
bool verticalSync; ///< If true, must activate vertical synchronization.
gd::String scaleMode;
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty

View File

@@ -16,6 +16,7 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
element.AddChild("value").SetStringValue(currentValue);
element.AddChild("type").SetStringValue(type);
element.AddChild("label").SetStringValue(label);
element.AddChild("description").SetStringValue(description);
SerializerElement& extraInformationElement =
element.AddChild("extraInformation");
extraInformationElement.ConsiderAsArray();
@@ -29,6 +30,7 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
currentValue = element.GetChild("value").GetStringValue();
type = element.GetChild("type").GetStringValue();
label = element.GetChild("label").GetStringValue();
description = element.GetChild("description").GetStringValue();
extraInformation.clear();
const SerializerElement& extraInformationElement =

View File

@@ -16,12 +16,15 @@ namespace gd {
/**
* \brief Used to describe a property shown in a property grid.
* \see gd::Object
* \see gd::EffectMetadata
*/
class GD_CORE_API PropertyDescriptor {
public:
/**
* \brief Create a property being a simple gd::String with the specified
* value. \param propertyValue The value of the property.
* value.
*
* \param propertyValue The value of the property.
*/
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string"), label(""), hidden(false) {}
@@ -64,6 +67,14 @@ class GD_CORE_API PropertyDescriptor {
return *this;
}
/**
* \brief Change the description displayed to the user, if any.
*/
PropertyDescriptor& SetDescription(gd::String description_) {
description = description_;
return *this;
}
/**
* \brief Add an information about the property.
* \note The information are arbitrary and are interpreted by the class
@@ -79,6 +90,7 @@ class GD_CORE_API PropertyDescriptor {
const gd::String& GetValue() const { return currentValue; }
const gd::String& GetType() const { return type; }
const gd::String& GetLabel() const { return label; }
const gd::String& GetDescription() const { return description; }
const std::vector<gd::String>& GetExtraInfo() const {
return extraInformation;
}
@@ -116,6 +128,7 @@ class GD_CORE_API PropertyDescriptor {
type; ///< The type of the property. This is arbitrary and interpreted by
///< the class responsible for updating the property grid.
gd::String label; //< The user-friendly property name
gd::String description; //< The user-friendly property description
std::vector<gd::String>
extraInformation; ///< Can be used to store for example the available
///< choices, if a property is a displayed as a combo

View File

@@ -425,7 +425,7 @@ class GD_CORE_API ResourcesManager {
bool MoveResourceDownInList(const gd::String& name);
/**
* Change the position of the specified resource.
* \brief Change the position of the specified resource.
*/
void MoveResource(std::size_t oldIndex, std::size_t newIndex);

View File

@@ -25,11 +25,11 @@ gd::String GetTranslation(const char* str) { // TODO: Inline?
// }
ensureCache.prepare();
var translatedStr = getTranslation(Pointer_stringify($0));
var translatedStr = getTranslation(UTF8ToString($0));
return ensureString(translatedStr);
},
str);
return gd::String(translatedStr); // TODO: Is copying necessary?
}
} // namespace gd
#endif
#endif

View File

@@ -129,6 +129,11 @@ class SerializableWithNameList {
*/
bool Has(const gd::String& name) const;
/**
* \brief Get the position of an element in the list
*/
std::size_t GetPosition(const T& element) const;
/** \name std::vector-like API
* These functions ensure that the class can be used just like a std::vector
* for iterations.

View File

@@ -109,6 +109,15 @@ void SerializableWithNameList<T>::Move(std::size_t oldIndex,
elements.insert(elements.begin() + newIndex, std::move(object));
}
template <typename T>
std::size_t SerializableWithNameList<T>::GetPosition(const T& element) const {
for(std::size_t index = 0;index<elements.size();++index) {
if (&element == elements[index].get()) return index;
}
return (size_t)-1;
}
template <typename T>
void SerializableWithNameList<T>::SerializeElementsTo(
const gd::String& elementName, SerializerElement& serializerElement) const {

View File

@@ -0,0 +1,122 @@
# GDevelop Architecture Overview
GDevelop is architectured around a `Core` library, platforms (`GDJS`, `GDCpp`) and extensions (`Extensions` folder). The editor (`newIDE` folder) is using all of these libraries. This is a recap table of the main folders:
| Directory | Description |
| --- | --- |
| `Core` | GDevelop core library, containing common tools to implement the IDE and work with GDevelop games. |
| `GDCpp` | GDevelop C++ game engine, used to **build native games**. |
| `GDJS` | GDevelop JS game engine, used to build **HTML5 games**. |
| `GDevelop.js` | Bindings of Core/GDCpp/GDJS and Extensions to JavaScript (used by the IDE). |
| `newIDE` | The game editor, written in JavaScript with React, Electron and Pixi.js. |
| `Extensions` | Extensions for C++ or JS game engines, providing objects, events and new features. |
The rest of this page is an introduction to the main concepts of GDevelop architecture.
## Some vocabulary: "Runtime" and "IDE"
**IDE** stands for "Integrated Development Environment". A synonym for it is also simply "editor". In GDevelop, the software itself, that is used to create games and running as an app or a web-app, is called the "GDevelop Editor" or the "GDevelop IDE"
> This term "IDE" is also used in some folders. When you browse `Core`, `GDCpp` or `GDJS` subfolders, some folders are called `IDE`. They contain classes and tools that are **only useful for the editor** (they are not per se mandatory to describe the structure of a game).
**Runtime** is a word used to describe classes, tools and source files being used during a game. This could also be called "in game" or a "game engine".
> When you browse `GDCpp` or `GDJS` subfolders, you can find folders called `Runtime`. They contain the **game engine** of GDevelop.
Extensions do have the same distinction between the "**IDE**" part and the "**Runtime**" part. For example, most extensions have:
* A file called [`JsExtension.js`(https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js)], which contains the *declaration* of the extension for the **IDE**
* One or more files implementing the feature for the game, in other words for **Runtime**. This can be a [Runtime Object](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimeobject.js) or a [Runtime Behavior](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimebehavior.js), [functions called by actions or conditions](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/examplejsextensiontools.js) or by the game engine.
### "Runtime" and "IDE" difference using an example: the `gd::Variable` class
In GDevelop, developers can associate and manipulate variables in their games. To represent them, we have two things:
* The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
* The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop-app.com/GDJS%20Runtime%20Documentation/gdjs.Variable.html). This JavaScript class is what is used during a game.
The editor `gd::Variable` **know nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine know almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
> Note that the name `gdjs.Variable` is maybe a *bad decision*: it should have been called a `gdjs.RuntimeVariable`, like `gdjs.RuntimeObject` and like most other classes of the game engine.
## What's inside "Core" (`GDCore` folder)?
GDevelop "Core" is basically containing everything that is used to **describe and manipulate the structure of a game** (called a `Project` internally). This includes events, scenes, objects, behaviors, events etc... All of these are implemented using C++ classes, in [this folder named `Project`](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Project).
GDevelop "Core" also contains **tools** to manipulate these `Project`. In particular, `Core/GDCore/IDE` folder is containing C++ classes allowing to do operations on the structure of a game. For example, [WholeProjectRefactorer](https://github.com/4ian/GDevelop/blob/master/Core/GDCore/IDE/WholeProjectRefactorer.cpp) is a very powerful tool, used to rename all objects in a game, update events after an object is deleted and more generally do Project wide refactoring. The directory contains other "tool" functions to manipulate [resources of projects](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Project) or do [search in events](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Events).
## What's inside GDJS? Why do I see some C++ file there?
While `GDJS/Runtime` folder is the game engine that is executed inside a game, `GDJS/GDJS` is the "IDE" part, which are the C++ classes describing to the editor various things like:
* How [do you do an export](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/IDE),
* What are [the default extensions](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Extensions/Builtin),
* How do you [generate JS code from events](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration),
The game engine is in GDJS/Runtime and is all JavaScript.
## What about events?
An "**event**" is by default something that [is mostly empty](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
[Default events are defined](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Events/Builtin) in GDevelop Core.
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
A `gd::Instruction` is "just" a type (the name of the action or condition), and some parameters. You can think of it as a function in a programming language (`add(2, 3)` is calling a function called "add" with parameter "2" and "3"). Conditions have the special thing that they are functions returning true or false (and potentially being used to do filter on the objects being picked for next conditions and actions).
### Why can't I see any "RuntimeEvent" or events during the game?
They do not exist anymore! ✨
Events are translated (we also say "transpiled" or "generated") into "real" code in a programming language. This process is called "Code Generation", and is [done here for the JavaScript game engine](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration).
### Can I extract Events classes and code generator to make a development environment based on GDevelop events?
You're welcome to do so! Please get in touch :)
## I can see more than one Extensions folder, why?
The idea of GDevelop editor and game engine is to have a lean game engine, supporting almost nothing. Then, one can add "mods", "plugins", "modules" for GDevelop. We chose to call them "**Extensions**" in GDevelop.
* `GDevelop/Core/GDCore/Extensions` is the **declaration** of default (we say "builtin") extensions, that are available for any game and are "mandatory". They are called Extensions but they could be named "Extensions that will always be in your game". In programming languages, this is called a "[Standard Library](https://en.wikipedia.org/wiki/Standard_library)" (but don't get too distracted by this naming).
* `GDevelop/GDJS/GDJS/Extensions/` and `GDevelop/GDCpp/GDCpp/Extensions/` are reusing these **declarations** and **adding** their own declarations. Mainly, they are setting the name of the functions to be called (either in JS or in C++) for each action, condition or expression.
* `GDevelop/Extensions/` is the folder for the "mods"/"plugins" for GDevelop - the one that are not mandatory. They are not part of GDCore - they work on their own.
> In theory, all extensions could be moved to `GDevelop/Extensions/`. In practice, it's more pragmatic to have a set of "builtin" extensions with basic features.
## What's GDevelop.js? Do we care about this?
Everything in GDevelop.js is meant to create a "bridge" allowing to run and use C++ from JavaScript for the **IDE**, so that [we can write an editor entirely in JavaScript](https://github.com/4ian/GDevelop/tree/master/newIDE) and have it working in a web browser.
* We're using Emscripten which is compiling C++, but instead of writing a "native binary", it's writing a file that works in a browser (basically, JavaScript!).
* The most useful file is [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl) that is describing everything in C++ that must be exposed to JavaScript (in the editor, not in game. Remember that during the game, we're at **Runtime**, so all of this does not exist anymore)
* Rest of the files are mostly bridges doing "weird stuff" to translate from JS to C++ or vice versa. It requires a bit of knowledge about how the ["bridge", made by Emscripten, called WebIDL](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html) is working.
You don't need to work with these unless you want to expose something that is written in C++ to the editor, and writing the interface in Bindings.idl is not sufficient.
90% of the time, you can just read or write about a class in [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl). If you work in C++ classes, you may have sometime to add a header file in Wrapper.cpp and your C++ class is "automagically" compiled and available in JavaScript after writing the corresponding interface in Bindings.idl.
### I want all the gory details about GDevelop.js 🧐
All the required C++ files are imported into this huge list: https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Wrapper.cpp#L1-L79. C++ compilation is done with a "build system" called CMake, which is using [this file](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/CMakeLists.txt#L82-L101) to see what to compile.
> In an ideal world, there would be something to do this automatically, so GDevelop.js folder would not even exist 😉
> If you're interested and want to know more about GDevelop.js bridging between C++ and JavaScript, look at [this talk from GDevelop original author](https://www.youtube.com/watch?v=6La7jSCnYyk).
## Misc questions
### What's the deal with C++? Why so much of it?
GDevelop was originally written in C++. It's a scary language at first but is portable across almost any existing machine in this universe, can be pretty good, safe and readable with the latest C++ features.
### What's the deal with JavaScript? Why so much of it?
JavaScript, with the latest language proposals, is actually a very capable language, fast to write and safe with typing:
* Performance is getting pretty good with recent browsers JIT features.
* Frontend frameworks like React, used for GDevelop IDE, allow for very fast and modular interface development.
* The web is an incredible and unmatched target when it comes to cross platform (and cross form factor) apps.
More generally, the web is a great target for games with the rise of WebGL and WebAssembly - though GDevelop should stay modular to adapt to newer platforms for the exported games in the future.

View File

@@ -6,7 +6,7 @@ GDevelop Core is a portable C++ library, compiled to be used in JavaScript in th
## 1) Getting started 🤓
First, take a look at the *Readme.md* at the root of the repository and the [developer documentation](http://4ian.github.io/GD-Documentation/).
First, take a look at the *Readme.md* at the root of the repository and the [developer documentation](https://docs.gdevelop-app.com/).
## 2) How to contribute 😎
@@ -14,7 +14,7 @@ Any contribution is welcome! Whether you want to submit a bug report, a feature
or any pull request so as to add a nice feature, do not hesitate to get in touch.
* Check the [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
* Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
* To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).

View File

@@ -964,7 +964,7 @@ HTML_COLORSTYLE_GAMMA = 80
# page will contain the date and time when the page was generated. Setting
# this to NO can help when comparing the output of multiple runs.
HTML_TIMESTAMP = YES
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the

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