Compare commits

...

232 Commits

Author SHA1 Message Date
Florian Rival
8811ed7046 [Experimental][WIP] Batch actions related to the same objects 2019-03-13 00:12:24 +00:00
Florian Rival
137a23b86a Bump newIDE version 2019-03-11 22:17:21 +00:00
Florian Rival
3dae2c50e6 Add geodash example and betabox-basics-learning-experience 2019-03-11 22:13:08 +00:00
Florian Rival
e288414684 Update translations 2019-03-11 21:53:00 +00:00
Florian Rival
0d965fc23c Merge branch 'master' of github.com:4ian/GDevelop 2019-03-11 21:01:36 +00:00
Florian Rival
728188114e Update forum links 2019-03-11 21:01:23 +00:00
Florian Rival
72a61be8ec Add button to report wrong translation 2019-03-11 20:34:19 +00:00
Florian Rival
3e822e4ef0 Remove useless minus sign 2019-03-11 20:34:08 +00:00
Bouh
29f7501dcf Add button to "Open Project Folder" in Resources Editor (#968) 2019-03-10 21:21:57 +00:00
Florian Rival
2544c71f30 Fix Flow 2019-03-10 19:09:20 +00:00
Florian Rival
6c629f6d68 Add performance warning for text object character size action 2019-03-09 19:54:32 +00:00
Florian Rival
fa022747e2 Make warnings about instruction/behaviors translatable 2019-03-09 19:51:44 +00:00
Florian Rival
d0a2491acd Fix wrong forbidden characters in translations 2019-03-07 23:48:56 +00:00
Florian Rival
816513ad85 Fix wrong merging of some duplicated translations and update translations 2019-03-07 23:42:54 +00:00
Florian Rival
b36cb17e4c Fix Prettier 2019-03-07 23:25:01 +00:00
Florian Rival
4fb27b6135 Don't mark Algolia as translatable 2019-03-07 23:18:31 +00:00
Florian Rival
96674f36b4 Fix click while renaming project item and warning when name is not changed 2019-03-07 23:13:37 +00:00
Florian Rival
c31de12048 Add max displayed rows number in TextEditor to avoid dialog mis positioning 2019-03-07 23:05:28 +00:00
Florian Rival
aaeaa8338b Fix Prettier 2019-03-07 22:03:50 +00:00
Florian Rival
f5827d4a48 Add missing translation in InstructionParametersEditor 2019-03-07 21:54:48 +00:00
Florian Rival
afe9d18b03 Button to open the events function editor from instruction editor 2019-03-07 21:53:51 +00:00
Zat
175e12b25b Allow both "Center" and "Centre" for Center point in expressions (#955) 2019-03-05 18:13:05 +00:00
Florian Rival
c4bb03d360 Revert to Pixi.js 3.0.11 for the IDE - will upgrade properly later 2019-03-03 16:27:03 +00:00
Florian Rival
acc34e8bda Add hint and differentiate buttons between string/number expression fields 2019-03-02 20:35:19 +00:00
Florian Rival
edecc01e14 Clean folder when importing GDJS Runtime files in newIDE 2019-03-02 20:34:13 +00:00
Florian Rival
808721a29f Update newIDE Pixi.js to version 4.8.6 2019-03-02 18:42:32 +00:00
Florian Rival
b57316b4f4 Fix Storybook build (babel stuck by locales large file) and add language button in StartPage 2019-03-02 16:04:40 +00:00
Florian Rival
8d57aa5353 Update translations and add translation support to LinkEvent 2019-03-02 15:18:49 +00:00
Florian Rival
2ce1c56005 Improve translation coverage and fix BrowserPreviewLinkDialog 2019-03-02 13:00:14 +00:00
Florian Rival
fc7c092cfb Exclude some files from the i18n coverage script 2019-03-02 12:19:35 +00:00
Florian Rival
e100b6f2c3 Add support for translations in Main menu 2019-03-02 01:41:09 +00:00
Florian Rival
ac2867f1a7 Fix Prettier 2019-02-28 23:08:18 +00:00
Florian Rival
3166daf041 Fix a bunch of mistakes/typos 2019-02-28 23:07:34 +00:00
Florian Rival
0c2d58e70b Make "Downhill Bike Racing" a starter 2019-02-28 22:57:27 +00:00
Florian Rival
af9ab09b3b Make some tooltips translatable 2019-02-28 22:45:43 +00:00
Florian Rival
0c8560e60d Separate languages in LanguageDialog according to progress 2019-02-28 22:18:35 +00:00
Florian Rival
5a44fd8b9f Fix wording in Particle actions/conditions 2019-02-24 17:11:57 +00:00
Florian Rival
20ce83ccfe Add image resource to the dummy example object 2019-02-23 14:58:05 +00:00
Florian Rival
0cc4543948 Add support for "resource" property and field in PropertiesEditor 2019-02-23 14:58:05 +00:00
Florian Rival
5ff045d74a Fix Prettier 2019-02-23 12:34:47 +00:00
Florian Rival
0fdf123f8d Improve Flow typing and fix tests 2019-02-23 12:17:10 +00:00
Florian Rival
ec0b934557 Add VideoResource 2019-02-22 20:20:13 +00:00
Florian Rival
7fa1e50c17 Fix typos 2019-02-22 20:00:46 +00:00
Florian Rival
9a7c5ead3a Fix edition button for children variables 2019-02-19 22:52:36 +01:00
Todor Imreorov
72282d4e6d Show instance variables in panel + inherited object variables + fix crash
* Show instance variables in InstancePropertiesEditor
* Display parent variables (not editable) and overriden variables in italic.
* Also Fix (existing) crashes when deleting variables.
  This was wrongly written:
    * no recursive search (`contains` 2nd argument not passed)
    * comparing a VariableAndName with a gdVariable made no sense.
2019-02-19 22:19:27 +01:00
Florian Rival
be950a7779 Add pseudolocalization (fake language) to detect hardcoded strings 2019-02-19 00:19:45 +01:00
Zat
d37ef30041 Update Howler to 2.1.1 (#939) 2019-02-17 12:43:36 +00:00
Florian Rival
53360bc189 Reload all extensions (including builtin) after a language change 2019-02-17 00:55:58 +00:00
Florian Rival
bfd6712a93 Ensure changes to language are immediately applied to interface.
Still need to reload GDevelop built-in extensions.
2019-02-17 00:25:29 +00:00
Florian Rival
6902f1495d Update translations 2019-02-16 19:40:26 +00:00
Florian Rival
156fd3bc19 Make Debugger Toolbar translatable 2019-02-16 19:33:35 +00:00
Florian Rival
b1303b4ad6 Update package-lock.json 2019-02-16 19:33:05 +00:00
Florian Rival
2fa3bb7b1b Merge pull request #919 from 4ian/refactor/cra2-and-jslingui
Add support for translations in GDevelop 5 🌎

For contributors, please merge master in your branches for your Pull Requests. 
See this comment: https://github.com/4ian/GDevelop/pull/919#issuecomment-464371931
Don't forget to remove `node_modules` folder in `newIDE/app`, and relaunch `npm install` or `yarn`, then `npm start` or `yarn start`.
2019-02-16 18:56:24 +00:00
Florian Rival
fca6f7627a Add unsafe-eval to fix issue with webpack for now 2019-02-16 18:22:43 +00:00
Florian Rival
e7bca87063 Update react-scripts and fix npm install 2019-02-12 23:50:28 +00:00
Florian Rival
a7afaa1241 Update translations 2019-02-10 20:34:27 +00:00
Florian Rival
e98f25255c Exclude deprecated extensions from translations 2019-02-10 18:35:52 +00:00
Florian Rival
b4bd225f78 Exclude GDCpp messages from translations (as only used in old IDE) 2019-02-10 18:10:44 +00:00
Florian Rival
2c0253fafa Improve translation handling and update all locale messages 2019-02-10 18:03:28 +00:00
Florian Rival
c8201949df Add LanguageDialog to choose language 2019-02-09 17:47:11 +00:00
Florian Rival
0426bcb2a6 Run Prettier 2019-02-09 16:11:52 +00:00
Florian Rival
9025d055a0 Fix translated dialog titles rendering and strings not to be translated 2019-02-09 16:10:32 +00:00
Florian Rival
bef6f04f89 Run "codemod" to internationalize files 2019-02-09 16:10:32 +00:00
Florian Rival
800d4f7098 Fix Prettier and npm/yarn format command to exclude locales folder 2019-02-09 16:02:22 +00:00
Florian Rival
e8d9d4fa4b Use custom build of @lingui/react while waiting for new version for Flow fixes 2019-02-09 15:51:42 +00:00
Florian Rival
c9e2d3f4e0 Fix broken TimeExtension.cpp 2019-02-09 14:12:56 +00:00
Florian Rival
43230003b8 Fix ExampleJsExtension broken after internationalization 2019-02-09 14:08:22 +00:00
Florian Rival
725069026c Fix issue with incompatible babel-loader version 2019-02-09 14:08:22 +00:00
Florian Rival
7729137a72 Upgrade to Storybook v4 2019-02-09 14:08:22 +00:00
Florian Rival
3c81b3decb Internationalize a few files and add a script to codemod most newIDE files 2019-02-09 14:08:22 +00:00
Florian Rival
460946e8f4 Add analyze-translations-coverage script 2019-02-09 14:08:22 +00:00
Florian Rival
6908ab9b3f Fix ElectronEventsBridge regression (menus not working anymore) 2019-02-09 14:08:22 +00:00
Florian Rival
0286f97145 Add temporary translation files to .gitignore 2019-02-09 14:08:22 +00:00
Florian Rival
a40ba2551d Update README to add commands for updating translations 2019-02-09 14:08:22 +00:00
Florian Rival
02c65bcf66 [WIP] Add translations support, using js-lingui, to newIDE (including libGD.js)
* Upgrade to Flow 0.92 and make fixes for it
* Convert all extensions to use _("...") instead of t("...") for translations,
  to be able to be parsed by gettext
* Handle translation in JS extensions
* Move Providers out of MainFrame
* Remove i18next
* Adapt ExtractTranslations script to handle js files (for extensions)
2019-02-09 14:08:22 +00:00
Florian Rival
ecf5401626 Update to create-react-app v2 2019-02-09 14:08:22 +00:00
Florian Rival
8866719ef0 Fix Time expression and add "timestamp" support 2019-02-09 13:27:55 +00:00
Wend1go
6a02f1b3c5 Add handling of 'stringWithSelector' parameters (#911) 2019-02-08 19:37:38 +00:00
Florian Rival
02d7c84e17 Update description of some actions/conditions 2019-02-08 17:49:49 +00:00
Florian Rival
0a9fb45841 Update link to download libGD.js 2019-02-06 23:48:16 +00:00
Florian Rival
6ddace1ec4 Add an example of an object action in ExampleJsExtension 2019-02-06 23:34:43 +00:00
Florian Rival
fc4107107b Make parameters like objectvar use the last object (not necessarily the parameter just before) 2019-02-06 23:17:13 +00:00
Florian Rival
30da9e6556 Add support for stringWithSelector parameter type 2019-02-06 22:53:53 +00:00
Florian Rival
939de1e928 Fix potential error in Facebook Instant Games export 2019-02-06 22:45:23 +00:00
Florian Rival
c8b3572cd8 Fix parameter extra information being prefixed by extension namespace 2019-02-06 22:44:29 +00:00
Florian Rival
d350243b2e Fix spelling of some conditions and actions 2019-02-06 22:08:50 +00:00
Florian Rival
ea68c31c07 Fix typo in "Common actions" category title 2019-02-05 12:01:53 +00:00
Wend1go
0dd837d127 Fix typo/spelling in README (#926) 2019-02-05 10:01:30 +00:00
Florian Rival
b5c019ae6b Add mention to JavaScript flavor in the newIDE readme. 2019-02-05 00:10:34 +00:00
Florian Rival
b4337d3746 Mention JavaScript flavor in extension README. 2019-02-05 00:08:45 +00:00
Franco Maciel
afc0a86903 Ensure size of the game during preview is the same as in settings (#924) 2019-02-04 22:15:55 +00:00
Florian Rival
2e6821d231 Add documentation about JS features/code style/typing 2019-02-04 21:30:54 +00:00
Kink
ea8a827937 Remove simulated clicks in game when the cursor leaves the window (#923) 2019-02-04 17:57:09 +00:00
Florian Rival
bc352424e1 Move "About GDevelop" menu item to help menu on Windows/Linux 2019-02-04 13:47:37 +00:00
Florian Rival
8e83f1581e Remove menu item to close the window (Win/Linux)
Was confusing with "Quit GDevelop 5" menu item
and was adding a Ctrl+W as shortcut that would
close the window.
2019-02-04 13:43:59 +00:00
Kink
ba593b46dd Update ColorPicker placement to avoid it going out of screen (#917) 2019-02-03 23:46:38 +00:00
Florian Rival
d8cfead994 Fix broken alignment for text and icons in Link events 2019-02-03 23:32:32 +00:00
Kink
cf7db809ac Properly show disabled Link events and adapt the rendering to the theme (#916) 2019-02-03 23:23:18 +00:00
Florian Rival
26dd33854f Bump newIDE version 2019-01-30 22:17:33 +00:00
Florian Rival
1ed530d946 Add endless-up-runner and space-invaders examples 2019-01-30 21:55:11 +00:00
Florian Rival
b85008c269 Fix prettier 2019-01-30 21:52:49 +00:00
Florian Rival
08ddb6e519 Fix potential crash if new release without description 2019-01-30 21:35:12 +00:00
Florian Rival
d37c55e86f Update version of libGD.js 2019-01-28 23:43:12 +00:00
Florian Rival
e2c7e1a145 Merge pull request #780 from 4ian/feature/expression-parser-2
Use ExpressionParser2 as the default parser for expression in games
2019-01-28 23:36:07 +00:00
Florian Rival
6ebf40647e Bump newIDE version 2019-01-28 23:16:20 +00:00
Florian Rival
ed42f1c54a Fix wrong parameters in downhill-bike-physics-demo 2019-01-28 23:16:20 +00:00
Florian Rival
c94097acfb Fix deprecated usage of X and Y with point name 2019-01-28 23:16:20 +00:00
Florian Rival
9c6c8564fa Fix handling of object parameters in expressions 2019-01-28 23:16:20 +00:00
Florian Rival
4b33373296 Fix ConvertToString and code generation for strings with backlash/escaped characters 2019-01-28 23:16:20 +00:00
Florian Rival
7ce11c5252 Add expression parser test checking for big list of naughty strings 2019-01-28 23:16:20 +00:00
Florian Rival
0d864ce6a7 Update error message and add test case 2019-01-28 23:16:20 +00:00
Florian Rival
a6360152cd Fix typing 2019-01-28 23:16:20 +00:00
Florian Rival
827fdbf05a Bump GDevelop Core version 2019-01-28 23:16:20 +00:00
Florian Rival
a4c372d945 Convert PointX/PointY argument to a string 2019-01-28 23:16:20 +00:00
Florian Rival
93708cb2f3 Properly handle numbers with leading 0 (normalize them to remove the extra 0) 2019-01-28 23:16:19 +00:00
Florian Rival
3035bc9386 Improve error messages for empty expressions 2019-01-28 23:16:19 +00:00
Florian Rival
4c789967c3 Fix code generation of invalid variable expressions 2019-01-28 23:16:19 +00:00
Florian Rival
75797f2c41 Fix real-time error highlighting in case expression is being written 2019-01-28 23:16:19 +00:00
Florian Rival
571686f180 Add highlighting of errors 2019-01-28 23:16:19 +00:00
Florian Rival
df68978adc Add edge cases fixes and fuzzy tests for ExpressionParser2 2019-01-28 23:16:19 +00:00
Florian Rival
7a87fd9924 Add support for unary operator in ExpressionParser2 2019-01-28 23:16:19 +00:00
Florian Rival
8d1502cb20 Fix parsing of behaviors expressions 2019-01-28 23:16:19 +00:00
Florian Rival
34b5038bd5 Refactor to use the new ExpressionCodeGenerator (or old one with a flag) 2019-01-28 23:16:19 +00:00
Florian Rival
a9e1120f00 Add gd::ExpressionCodeGenerator::GenerateExpressionCode 2019-01-28 23:16:19 +00:00
Florian Rival
b14238c692 Use ExpressionParser2 for EventsVariablesFinder 2019-01-28 23:16:19 +00:00
Florian Rival
dfe4a75fbd Use ExpressionParser2 for EventsContextAnalyzer 2019-01-28 23:16:19 +00:00
Florian Rival
4fd88072e9 Improve ExpressionParser2 and use it for GenericExpressionField and EventsRefactorer 2019-01-28 23:16:19 +00:00
Florian Rival
f2e6f19c34 Add ExpressionParser2NodePrinter 2019-01-28 23:16:18 +00:00
Florian Rival
66029c62ed Add Variable code generation for ExpressionParser2 2019-01-28 23:16:18 +00:00
Florian Rival
43d78a74bd Enhance ExpressionParser2 and add ExpressionCodeGenerator 2019-01-28 23:16:18 +00:00
Florian Rival
0ec6ebad07 Add ExpressionParser2 and tests 2019-01-28 23:16:18 +00:00
Florian Rival
8743672038 Update links to social network pages/profiles 2019-01-28 23:02:31 +00:00
Florian Rival
3fd032d898 Fix potential crash when deleting events or instructions 2019-01-27 23:54:56 +00:00
Florian Rival
2b40f57189 Fix Duration parameter of Vibrate action 2019-01-24 23:12:37 +00:00
Wend1go
fc3f8a945b Add actions for loading/saving files (raw or JSON) to Filesystem (#884) 2019-01-24 22:04:43 +00:00
Florian Rival
ed24871130 Fix crash in profiler when using a lot of nested groups 2019-01-23 22:12:49 +00:00
Florian Rival
e416fb0586 Merge branch 'master' of github.com:4ian/GDevelop 2019-01-23 21:52:45 +00:00
Florian Rival
5778131deb Improve debugger performance 2019-01-23 21:52:31 +00:00
Todor Imreorov
8e81f78596 Remember zoom/grid/window mask state for scenes and external layouts (#892) 2019-01-23 21:23:09 +00:00
Todor Imreorov
1abdcaf0c0 Warn user when renaming resource that it might break objects (#899) 2019-01-23 19:14:24 +00:00
Florian Rival
4af867ba3a Fix expression transforming structure variable to JSON 2019-01-23 17:47:15 +00:00
Todor Imreorov
f75c2297d7 Fix "Locate File" and related menu options after a resource is renamed (#898) 2019-01-22 19:28:47 +01:00
Florian Rival
72c4f88936 Add screen-shake and update downhill-bike-physics-demo example 2019-01-21 16:48:10 +01:00
Florian Rival
34bcfdfee7 Customize the style of markdown rendered text 2019-01-20 20:54:47 +00:00
Florian Rival
7e78d4de5a Display changelog when launching a new version for first time
* Also display changelog in AboutDialog
2019-01-20 20:54:47 +00:00
Bouh
971b7a2322 Prevent renaming elements with an already existing name in Project Manager (#888) 2019-01-19 17:44:07 +00:00
Todor Imreorov
9a95aabc87 Add --disable-update-check option to disable check for updates (#887) 2019-01-19 16:28:38 +00:00
Florian Rival
53dcfa1cbf Fix enabling/disabling alert messages 2019-01-18 17:45:20 +00:00
Florian Rival
0ed7ccfb72 Set shortcut to close project to Ctrl/Cmd+Shift+W 2019-01-17 17:31:15 +00:00
Florian Rival
0fcd5cbbb9 Bump newIDE version 2019-01-17 17:30:48 +00:00
Florian Rival
fb6b959636 Add downhill-bike-physics-demo example 2019-01-14 22:42:26 +00:00
Florian Rival
1d7f0f9f94 Fix Prettier 2019-01-14 21:54:34 +00:00
Florian Rival
4be527c18a Try to prevent any crash by catching any exception when setting window title 2019-01-14 21:47:03 +00:00
Florian Rival
5e770d460d Merge branch 'master' of github.com:4ian/GDevelop 2019-01-14 21:25:31 +00:00
Florian Rival
73daf0e940 Fix Facebook instant games needing a bundle configuration file 2019-01-14 21:25:22 +00:00
Franco Maciel
58d88835c5 Fix initial motors speed in actions of Physics2 behavior (#872) 2019-01-14 21:09:09 +00:00
Florian Rival
0aa66837d4 Fix login dialog not showing on top of export dialog
Fix #868
2019-01-14 11:29:02 +00:00
Florian Rival
11349086c7 Update all remaining examples from old Physics engine to Physics Engine 2.0 2019-01-13 23:36:29 +00:00
Zat
d3d636744e Fix wheel joint argument definitions (#871) 2019-01-13 22:10:50 +00:00
Bouh
5baf2d9735 Fix sentence a FB Instant Game action (#869) 2019-01-13 19:13:04 +00:00
Florian Rival
d0daf0b5a8 Update bouncing-ball-and-rope example with new Physics engine 2019-01-12 17:35:39 +00:00
Florian Rival
21841e0bce Add Ragdoll example 2019-01-12 17:06:35 +00:00
Florian Rival
417ab2ff3d Gray out the deprecated Physics Engine category in InstructionSelector 2019-01-11 20:04:24 +00:00
Florian Rival
caee5fdcc7 Change default name of Physics2 behavior 2019-01-11 19:48:25 +00:00
Florian Rival
1fbd58fa75 Change wording of the old Physics extension 2019-01-11 19:48:25 +00:00
Florian Rival
389deadac5 Also improve flow typing of Debugger Toolbar
Note that the way translations are done might be changed later, but
this is for reference if needed.
2019-01-11 19:48:25 +00:00
Florian Rival
7d52165a82 Add deprecation alert for instructions of old Physics engine 2019-01-11 19:48:25 +00:00
Florian Rival
5e3e7e25fc Fix opening of ExpressionSelector not showing the top with the search field 2019-01-10 08:35:08 +00:00
Florian Rival
56afb11e2c Hide object drop-down list after an object is selected 2019-01-10 08:26:07 +00:00
Florian Rival
b6a0cfef32 Fix linter warning 2019-01-10 08:10:17 +00:00
Florian Rival
37c3a6d38a Add explicit "OK" button in message box to fix issue on Linux
See #856
2019-01-10 08:07:55 +00:00
Zat
30f830589c Add expressions relative to body to Physics2 behavior (#854) 2019-01-09 09:26:31 +00:00
Florian Rival
5f8546c3b9 Avoid crash of the debugger with Particle Emitters
This is done by using _renderer as the name for the renderer property,
with an underscore, which is filtered by the debugger when the game
is dumped.
2019-01-06 22:59:21 +00:00
Zat
4c2997b2d9 Add missing Physics2Behavior expressions (#848) 2019-01-06 20:43:12 +00:00
Franco Maciel
2338b5bfd6 Use AABB to check if cursor is on object (#852) 2019-01-06 20:23:19 +00:00
Franco Maciel
8a07e9b6c5 Prevent crash with repeated and aligned vertices in Physics2 behavior (#850) 2019-01-06 17:26:43 +00:00
Florian Rival
61303c7cf9 Fix Prettier 2019-01-06 00:59:07 +00:00
Florian Rival
43eaf30750 Fix error in Flow typing in recent Flow versions 2019-01-05 19:01:30 +00:00
Florian Rival
db278485fc Improve Flow typing 2019-01-05 18:42:19 +00:00
Florian Rival
3f5b42dc90 Fix tests of InstructionOrExpressionSelector and make them more robust against changes 2019-01-05 18:38:55 +00:00
Florian Rival
cd285b5676 Display icons in the list of instructions or expressions 2019-01-05 18:11:47 +00:00
Franco Maciel
509ac8966e Prevent undefined vertices in Physics2 behaviors (#845) 2019-01-05 17:03:42 +00:00
Florian Rival
bda2a10b5f Improve Flow typing of InstructionOrExpressionSelector 2019-01-05 17:03:11 +00:00
Florian Rival
841e1a66b6 Fix updating shared properties of Physics2 2019-01-05 13:52:18 +00:00
Franco Maciel
3fc588b6e3 Add a new editor (with Polygon support) for Physics2 engine (#825)
* Physics editor and custom polygon support
* Add shape preview
* Dynamic dimension labels in function of the shape
* Add polygon editor
* Preview box, circle and edge
* Prevent invalid properties values
* Add BehaviorsEditorService
2019-01-05 12:28:45 +00:00
Florian Rival
50ebf6e5a2 Add support for creating objects in JS extensions + example 2019-01-05 12:03:24 +00:00
Florian Rival
cbe33d2bd1 Improve/fix mistakes in gdjs.TextRuntimeObject documentation 2019-01-03 22:45:24 +00:00
Florian Rival
8c8c297adb Make mouse button labels more accessible 2019-01-03 21:07:18 +00:00
Florian Rival
a8d927994b Change the mouse button labels in newIDE 2019-01-03 21:07:18 +00:00
Florian Rival
1fb3231f98 Use constants to represents button in the game engine 2019-01-03 21:07:18 +00:00
Bouh
70a58f1bd4 Fix Middle mouse button handling (#841) 2019-01-03 18:34:03 +00:00
Franco Maciel
302bcb718f Fix initial opacity undefined 2019-01-03 13:59:08 +00:00
Florian Rival
857c706f48 Add help link for PanelSprite object editor 2018-12-30 22:55:11 +01:00
Wend1go
5b5f91c1f9 Add example for screenshot extension (#835) 2018-12-30 21:54:15 +01:00
Wend1go
0652e19190 Brighten up Screenshot icons to better fit the dark theme (#834) 2018-12-30 11:34:25 +01:00
Wend1go
5feb4caa31 Add FileSystem extension (special path, file existence and creating directory) (#828) 2018-12-29 20:11:53 +01:00
Florian Rival
1cd5519b93 Fix snapshot tests 2018-12-29 20:08:56 +01:00
Florian Rival
96761c461d Adapt descriptions of storage actions to make clear that no files are written 2018-12-29 18:27:21 +01:00
Florian Rival
1a82663968 Add dismissable alerts to warn/help the user 2018-12-27 18:50:15 +01:00
Florian Rival
9111b56eb3 Add GetAngle/Get(X|Y)Velocity to top-down behavior 2018-12-27 16:53:44 +01:00
Florian Rival
d7f22bcf26 Update isometric-game example 2018-12-27 14:02:05 +01:00
Florian Rival
b9b0ed812d Update Platformer example 2018-12-27 12:03:00 +01:00
Florian Rival
6734644df4 Fix non prettified files 2018-12-27 12:00:51 +01:00
Florian Rival
ddeff6bfae Add shortcut to open Project Manager and focus search field when opening 2018-12-27 11:56:01 +01:00
Florian Rival
8f10eac512 Fix snapshot tests 2018-12-27 11:37:31 +01:00
Florian Rival
162524f779 Automatically close project manager after opening functions 2018-12-27 11:19:32 +01:00
Wend1go
1da347b08b Add Screenshot extension (for games running on Windows/macOS/Linux) (#806)
* Add functionality to take screenshots, when running on Electron.
2018-12-27 11:07:42 +01:00
Florian Rival
01b7f81507 Fix regression on rounding of platformer object X position and skeleton collision 2018-12-26 23:22:10 +01:00
Florian Rival
5a3d2cc2b9 Reduce iterations of polygon benchmark 2018-12-26 23:22:10 +01:00
Florian Rival
cb659eee6d Add pixel-perfect-platform-engine and update other examples 2018-12-26 23:22:10 +01:00
Florian Rival
0f24410a2e Fix platform engine 1-pixel offset bug
* "Bug" is fixed by ignoring edges when doing collision tests for
the platform engine.
* Also add an option to round coordinates and ensure pixel-perfect
alignement of characters on platforms (on by default)
* Also add an optional parameter to ignore edges when
doing a collision test (or when separating objects)
2018-12-26 23:22:10 +01:00
Todor Imreorov
a31ea1475d Add button to choose a new file for a resource in ResourcePropertiesEditor (#822) 2018-12-26 17:55:59 +01:00
Todor Imreorov
0cecf685ca Autoclose project manager when opening an editor (#826) 2018-12-26 14:36:49 +01:00
Florian Rival
f39af51fda Add benchmark for polygon collision test and add test runner for Firefox 2018-12-26 13:45:45 +01:00
Florian Rival
704eaacc7e Enable back all GDJS tests 2018-12-26 12:52:51 +01:00
Florian Rival
1f7bec0ff1 Add option to set scale mode ("sampling") of the game to nearest ("pixel-perfect") 2018-12-25 23:27:09 +01:00
Florian Rival
69ffa45ea9 Fix touch/mouse handling regression 2018-12-25 21:37:11 +01:00
Florian Rival
2558052a61 Fix visual artifacts when rendering rescaled games.
Artifacts where visible on rescaled games, especially when canvas
was small and the game pixel perfect. This was due to changing
Pixi renderer size instead of scaling the canvas.

For a perfect "pixel perfect" rendering, "image-rendering: pixelated"
would be needed on the canvas (to be added as a game settings)
(see for instance: https://gablaxian.com/articles/creating-a-game-with-javascript/scaling-the-canvas)
2018-12-25 18:15:27 +01:00
Florian Rival
abf40200cb Fix prettier for ExamplesInformation.js (auto-generated file) 2018-12-24 19:36:27 +01:00
Florian Rival
2fd4459364 Add 2 example for the new Physics Engine 2018-12-24 18:34:10 +01:00
Florian Rival
2ed9088b58 Fix lint warnings 2018-12-24 18:02:39 +01:00
Florian Rival
f69b9132ad Add test case for JSON array with numbers 2018-12-24 14:02:58 +01:00
Franco Maciel
fb7d3d589d Fix LShift key code (#823) 2018-12-24 13:19:08 +01:00
Franco Maciel
87acba5105 Fix JSON array parser separators (#819) 2018-12-23 19:17:36 +01:00
Franco Maciel
2d314ff51b Add New physics behavior (Physics2) (#775) 2018-12-23 00:15:25 +01:00
Florian Rival
5054678193 Fix functions list rendering and always shown functions in project manager 2018-12-22 23:44:25 +01:00
Todor Imreorov
aaf9d318a9 Add error color to resources with missing files (#818) 2018-12-22 22:52:34 +01:00
Florian Rival
cc92445d4c Update READMEs with more links/information/commands 2018-12-22 16:39:19 +01:00
Florian Rival
1649591442 Add check-format to check non prettified files and add it to TravisCI 2018-12-22 12:09:04 +01:00
Florian Rival
77faff3919 Upgrade Prettier and run Prettier on newIDE/app/src to fix some files 2018-12-22 11:41:50 +01:00
Florian Rival
873280d818 Fix tests after libGD.js update 2018-12-22 00:05:21 +01:00
Florian Rival
238b6a2a09 Fix un/serialization of JSON arrays by gd::Serializer 2018-12-21 21:02:13 +01:00
1295 changed files with 210987 additions and 59433 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
/ExtLibs/*.7z
/scripts/Repository keys
/scripts/logs/*.txt
/scripts/gdcore-gdcpp-gdjs-extensions-messages.pot
/Binaries/.build*
/Binaries/.embuild*
/Binaries/build*

View File

@@ -69,6 +69,7 @@ script:
- cd newIDE/app
- npm test
- npm run flow
- npm run check-format
- cd ../..
#GDJS game engine tests, disabled on Travis CI because ChromeHeadless can't be started.
#See them running on Semaphore-CI instead: https://semaphoreci.com/4ian/gd

View File

@@ -77,7 +77,9 @@
"cstdint": "cpp",
"forward_list": "cpp",
"mutex": "cpp",
"__hash": "cpp"
"__hash": "cpp",
"__debug": "cpp",
"__threading_support": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,
@@ -97,6 +99,7 @@
// Support for Flowtype:
"javascript.validate.enable": false,
"flow.useNPMPackagedFlow": true,
"flow.useLSP": false, // As we are in a monorepo, see https://github.com/flowtype/flow-for-vscode/issues/301
// Clang format styling (duplicated in scripts/CMakeClangUtils.txt)
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}"

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

View File

@@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_create_folder.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/create_folder16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="-14.99721"
inkscape:cy="14.343353"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<path
style="opacity:1;fill:#80bbdb;fill-opacity:1;fill-rule:nonzero;stroke:#548dad;stroke-width:0.97142506;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10.44448,1025.9309 c 0.65513,0 1.18165,0.5595 1.18165,1.2556 l 0,1.2733 16.256617,0 c 1.376723,0 2.484776,1.1774 2.484776,2.6403 l 0,13.0531 c 0,1.4629 -1.108053,2.6403 -2.484776,2.6403 l -23.7654964,0 c -1.3767228,0 -2.4847757,-1.1774 -2.4847757,-2.6403 l 0,-10.7551 0,-2.298 0,-3.9136 c 0,-0.6961 0.5265186,-1.2556 1.1816486,-1.2556 z"
id="rect4901"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscssssssccsss" />
<path
style="opacity:1;fill:#5fa1c7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.1084458,1031.4784 0,0.7321 0,11.4039 c 0,1.5514 1.0713447,2.7998 2.4024592,2.7998 l 22.978188,0 c 1.331114,0 2.40246,-1.2484 2.40246,-2.7998 l 0,-12.136 -27.7831072,0 z"
id="path4919"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="opacity:1;fill:#d9db30;fill-opacity:1;fill-rule:nonzero;stroke:#bec021;stroke-width:1.38540256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4939"
sodipodi:sides="5"
sodipodi:cx="24.252151"
sodipodi:cy="1028.8562"
sodipodi:r1="7.8899426"
sodipodi:r2="3.9449718"
sodipodi:arg1="0.79249024"
sodipodi:arg2="1.4208088"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 29.791477,1034.4747 -4.949845,-1.7178 -4.221206,3.1037 0.104117,-5.2384 -4.256247,-3.0555 5.014193,-1.5197 1.590701,-4.9922 2.994825,4.2992 5.239353,-0.03 -3.163289,4.1767 z"
inkscape:transform-center-x="0.36431952"
inkscape:transform-center-y="-0.39846334" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 B

View File

@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_file_delete.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_file_delete32.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="56.026486"
inkscape:cy="-2.8092946"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px"
inkscape:snap-grids="false">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<g
id="g4225"
transform="matrix(1.1128183,0,0,1.1128183,55.344213,-128.05315)">
<path
id="rect4177"
transform="translate(-9.5367432e-7,1020.3622)"
d="m -43.681641,13.882812 c -0.563451,0 -1.017578,0.452174 -1.017578,1.015626 l 0,22.210937 c 0,0.563451 0.454127,1.015625 1.017578,1.015625 l 16.652344,0 c 0.563451,0 1.017578,-0.452174 1.017578,-1.015625 l 0,-15.246094 -6.05664,-7.980469 -11.613282,0 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4218"
d="m -31.837816,1034.2449 0,7.043 c 0,0.3498 0.283047,0.6328 0.632812,0.6328 l 5.19336,0 -5.826172,-7.6758 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<rect
style="opacity:1;fill:#a6a6a6;fill-opacity:1;fill-rule:nonzero;stroke:#5a5a5a;stroke-width:0.99999934;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4158"
width="8.9121208"
height="8.7162371"
x="10.293939"
y="1038.0229" />
<rect
style="opacity:1;fill:#a6a6a6;fill-opacity:1;fill-rule:nonzero;stroke:#5a5a5a;stroke-width:0.99999934;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4160"
width="8.9631233"
height="2.6948419"
x="-516.44586"
y="897.6803"
inkscape:transform-center-x="-3.1829089"
inkscape:transform-center-y="-3.4427076"
transform="matrix(0.86246101,-0.50612351,0.50612351,0.86246101,0,0)" />
<rect
y="1044.6921"
x="8.1238213"
height="1.985044"
width="2.0916421"
id="rect4162"
style="opacity:1;fill:#a6a6a6;fill-opacity:1;fill-rule:nonzero;stroke:#5a5a5a;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_file.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_file16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="-51.539982"
inkscape:cy="23.000437"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<g
id="g4225"
transform="matrix(1.1128183,0,0,1.1128183,55.344213,-128.05315)">
<path
id="rect4177"
transform="translate(-9.5367432e-7,1020.3622)"
d="m -43.681641,13.882812 c -0.563451,0 -1.017578,0.452174 -1.017578,1.015626 l 0,22.210937 c 0,0.563451 0.454127,1.015625 1.017578,1.015625 l 16.652344,0 c 0.563451,0 1.017578,-0.452174 1.017578,-1.015625 l 0,-15.246094 -6.05664,-7.980469 -11.613282,0 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4218"
d="m -31.837816,1034.2449 0,7.043 c 0,0.3498 0.283047,0.6328 0.632812,0.6328 l 5.19336,0 -5.826172,-7.6758 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

View File

@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_folder.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_folder16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="3.5385856"
inkscape:cy="13.013876"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<path
style="opacity:1;fill:#80bbdb;fill-opacity:1;fill-rule:nonzero;stroke:#548dad;stroke-width:0.97142506;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10.44448,1025.9309 c 0.65513,0 1.18165,0.5595 1.18165,1.2556 l 0,1.2733 16.256617,0 c 1.376723,0 2.484776,1.1774 2.484776,2.6403 l 0,13.0531 c 0,1.4629 -1.108053,2.6403 -2.484776,2.6403 l -23.7654964,0 c -1.3767228,0 -2.4847757,-1.1774 -2.4847757,-2.6403 l 0,-10.7551 0,-2.298 0,-3.9136 c 0,-0.6961 0.5265186,-1.2556 1.1816486,-1.2556 z"
id="rect4901"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscssssssccsss" />
<path
style="opacity:1;fill:#5fa1c7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.1084458,1031.4784 0,0.7321 0,11.4039 c 0,1.5514 1.0713447,2.7998 2.4024592,2.7998 l 22.978188,0 c 1.331114,0 2.40246,-1.2484 2.40246,-2.7998 l 0,-12.136 -27.7831072,0 z"
id="path4919"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_load_file.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_load_file16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="-15.256846"
inkscape:cy="-4.5327826"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<g
id="g4206">
<path
id="rect4177"
d="m 6.7344825,1022.8736 c -0.6270186,0 -1.1323794,0.5032 -1.1323794,1.1303 l 0,24.7167 c 0,0.627 0.5053608,1.1302 1.1323794,1.1302 l 18.5310335,0 c 0.627018,0 1.132379,-0.5032 1.132379,-1.1302 l 0,-16.9661 -6.73994,-8.8809 -12.9234725,0 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.66922748;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4218"
d="m 19.914509,1022.8735 0,7.8376 c 0,0.3892 0.31498,0.7042 0.704204,0.7042 l 5.779267,0 -6.483471,-8.5418 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.66922748;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4202"
transform="translate(-0.22767735,0)">
<ellipse
style="opacity:1;fill:#d4d68c;fill-opacity:1;fill-rule:nonzero;stroke:#c8cb6c;stroke-width:0.69685775;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="ellipse4194"
cx="16.227676"
cy="1044.2556"
rx="7.8167491"
ry="1.7860432" />
<ellipse
ry="0.47607893"
rx="3.9621422"
cy="1044.1664"
cx="16.227676"
id="ellipse4198"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.45964342;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<path
style="fill:#368fac;fill-opacity:1;fill-rule:evenodd;stroke:#2b7188;stroke-width:0.81720334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 15.999999,1032.9608 -4.451864,5.7416 2.225932,0 0,5.1577 4.451864,0 0,-5.1577 2.225932,0 z"
id="path4210"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_path_exists.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_path_exists16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="3.5385856"
inkscape:cy="13.013876"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<path
style="opacity:1;fill:#80bbdb;fill-opacity:1;fill-rule:nonzero;stroke:#548dad;stroke-width:0.97142506;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10.44448,1025.9309 c 0.65513,0 1.18165,0.5595 1.18165,1.2556 l 0,1.2733 16.256617,0 c 1.376723,0 2.484776,1.1774 2.484776,2.6403 l 0,13.0531 c 0,1.4629 -1.108053,2.6403 -2.484776,2.6403 l -23.7654964,0 c -1.3767228,0 -2.4847757,-1.1774 -2.4847757,-2.6403 l 0,-10.7551 0,-2.298 0,-3.9136 c 0,-0.6961 0.5265186,-1.2556 1.1816486,-1.2556 z"
id="rect4901"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscssssssccsss" />
<path
style="opacity:1;fill:#5fa1c7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.1084458,1031.4784 0,0.7321 0,11.4039 c 0,1.5514 1.0713447,2.7998 2.4024592,2.7998 l 22.978188,0 c 1.331114,0 2.40246,-1.2484 2.40246,-2.7998 l 0,-12.136 -27.7831072,0 z"
id="path4919"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:33.25431824px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#9b80db;fill-opacity:1;stroke:#6e6f5e;stroke-width:1.19503343;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="5.8434677"
y="1048.4673"
id="text4229"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4231"
x="5.8434677"
y="1048.4673"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Arial Black';-inkscape-font-specification:'Arial Black, ';fill:#9b80db;fill-opacity:1;stroke:#6e6f5e;stroke-width:1.19503343;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">?</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="filesystem_save_file.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/filesystem_save_file32.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="30.546725"
inkscape:cy="-3.8184969"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<g
id="g4206">
<path
id="rect4177"
d="m 6.7344825,1022.8736 c -0.6270186,0 -1.1323794,0.5032 -1.1323794,1.1303 l 0,24.7167 c 0,0.627 0.5053608,1.1302 1.1323794,1.1302 l 18.5310335,0 c 0.627018,0 1.132379,-0.5032 1.132379,-1.1302 l 0,-16.9661 -6.73994,-8.8809 -12.9234725,0 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.66922748;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4218"
d="m 19.914509,1022.8735 0,7.8376 c 0,0.3892 0.31498,0.7042 0.704204,0.7042 l 5.779267,0 -6.483471,-8.5418 z"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:#6e6f5e;stroke-width:1.66922748;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4202"
transform="translate(-0.22767735,0)">
<ellipse
style="opacity:1;fill:#d4d68c;fill-opacity:1;fill-rule:nonzero;stroke:#c8cb6c;stroke-width:0.69685775;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="ellipse4194"
cx="16.227676"
cy="1044.2556"
rx="7.8167491"
ry="1.7860432" />
<ellipse
ry="0.47607893"
rx="3.9621422"
cy="1044.1664"
cx="16.227676"
id="ellipse4198"
style="opacity:1;fill:#fcfdf1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.45964342;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<path
style="fill:#6aac36;fill-opacity:1;fill-rule:evenodd;stroke:#54882b;stroke-width:0.81720334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 15.999999,1043.6019 -4.451864,-5.7416 2.225932,0 0,-5.1577 4.451864,0 0,5.1577 2.225932,0 z"
id="path4210"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 31.999999 32.000001"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="take_screenshot.svg"
inkscape:export-filename="/home/matthias/Programme/GD_MyFork/GDevelop/Binaries/Output/Release_Windows/JsPlatform/Extensions/take_screenshot16.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs4">
<inkscape:path-effect
effect="skeletal"
id="path-effect6933"
is_visible="true"
pattern="m -9.5367432e-7,1025.3622 c 0,-2.76 2.23999995367432,-5 4.99999995367432,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 -2.76,0 -4.99999995367432,-2.24 -4.99999995367432,-5 z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6855"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect6842"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect4583"
is_visible="true"
pattern="M 0,5 C 0,2.24 2.24,0 5,0 7.76,0 10,2.24 10,5 10,7.76 7.76,10 5,10 2.24,10 0,7.76 0,5 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4234"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#617da1;fill-opacity:1;fill-rule:evenodd;stroke:#617da1;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4231"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="0.48062283"
inkscape:cy="15.066705"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="false"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-nodes="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid6838" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="bg"
inkscape:groupmode="layer"
id="layer1"
style="opacity:0"
sodipodi:insensitive="true"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.48900003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5d3b0b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4136"
width="537.14288"
height="537.14288"
x="108.57143"
y="232.3622"
ry="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="vg"
transform="translate(0,-1020.3622)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#535353;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4139"
width="31.889814"
height="20.333548"
x="0.055091862"
y="1026.1954"
ry="2.8092401" />
<circle
style="opacity:1;fill:#8c8c8c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4208"
cx="15.999999"
cy="1038.0809"
r="6.34375" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4210"
width="5.0598979"
height="5.2740483"
x="2.8781402"
y="1028.0603"
ry="2.6370242" />
<rect
style="opacity:1;fill:#535353;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4212"
width="11.124999"
height="4.3597975"
x="19.03125"
y="1023.6198"
ry="1.2027028" />
<rect
ry="0.59375"
y="1024.6122"
x="20.748579"
height="2.375"
width="7.6903405"
id="rect4214"
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="4.75"
cy="1038.0809"
cx="15.999999"
id="circle4216"
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -3,6 +3,7 @@
#include <utility>
#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"
@@ -13,8 +14,8 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
using namespace std;
@@ -295,11 +296,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(objectInParameter)) {
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
condition.SetParameter(pNb, gd::Expression(""));
condition.SetType("");
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectInParameter) !=
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
condition.SetParameter(pNb, gd::Expression(""));
condition.SetType("");
@@ -328,7 +332,8 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::String objectName = condition.GetParameters().empty()
? ""
: condition.GetParameter(0).GetPlainString();
gd::String objectType = gd::GetTypeOfObject(GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectName);
gd::String objectType = gd::GetTypeOfObject(
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectName);
if (!objectName.empty() &&
MetadataProvider::HasObjectCondition(
platform, objectType, condition.GetType()) &&
@@ -442,120 +447,41 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
return outputCode;
}
/**
* Generate code for an action.
*/
gd::String EventsCodeGenerator::GenerateActionCode(
gd::Instruction& action, EventsCodeGenerationContext& context) {
gd::String actionCode;
gd::InstructionMetadata instrInfos =
MetadataProvider::GetActionMetadata(platform, action.GetType());
AddIncludeFiles(instrInfos.codeExtraInformation.GetIncludeFiles());
if (instrInfos.codeExtraInformation.HasCustomCodeGenerator()) {
return instrInfos.codeExtraInformation.customCodeGenerator(
action, *this, context);
}
// TODO: Could be moved to a EventsValidator
void EventsCodeGenerator::ValidateAction(
gd::InstructionMetadata& instructionMetadata, gd::Instruction& action) {
// Be sure there is no lack of parameter.
while (action.GetParameters().size() < instrInfos.parameters.size()) {
while (action.GetParameters().size() <
instructionMetadata.parameters.size()) {
vector<gd::Expression> parameters = action.GetParameters();
parameters.push_back(gd::Expression(""));
action.SetParameters(parameters);
}
// Verify that there are not mismatch between object type in parameters
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].type)) {
for (std::size_t pNb = 0; pNb < instructionMetadata.parameters.size();
++pNb) {
if (ParameterMetadata::IsObject(instructionMetadata.parameters[pNb].type)) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(objectInParameter)) {
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
action.SetParameter(pNb, gd::Expression(""));
action.SetType("");
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
} else if (!instructionMetadata.parameters[pNb]
.supplementaryInformation.empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instructionMetadata.parameters[pNb]
.supplementaryInformation) {
action.SetParameter(pNb, gd::Expression(""));
action.SetType("");
}
}
}
// Call free function first if available
if (MetadataProvider::HasAction(platform, action.GetType())) {
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateFreeAction(arguments, instrInfos, context);
}
// Call object function if available
gd::String objectName = action.GetParameters().empty()
? ""
: action.GetParameter(0).GetPlainString();
gd::String objectType = gd::GetTypeOfObject(GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectName);
if (MetadataProvider::HasObjectAction(
platform, objectType, action.GetType()) &&
!instrInfos.parameters.empty()) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
const ObjectMetadata& objInfo =
MetadataProvider::GetObjectMetadata(platform, objectType);
AddIncludeFiles(objInfo.includeFiles);
context.SetCurrentObject(realObjects[i]);
context.ObjectsListNeeded(realObjects[i]);
// Prepare arguments and generate the whole action code
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateObjectAction(
realObjects[i], objInfo, arguments, instrInfos, context);
context.SetNoCurrentObject();
}
}
// Assign to a behavior member function if found
gd::String behaviorType =
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
action.GetParameters().size() < 2
? ""
: action.GetParameter(1).GetPlainString());
if (MetadataProvider::HasBehaviorAction(
platform, behaviorType, action.GetType()) &&
instrInfos.parameters.size() >= 2) {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
const BehaviorMetadata& autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
AddIncludeFiles(autoInfo.includeFiles);
context.SetCurrentObject(realObjects[i]);
context.ObjectsListNeeded(realObjects[i]);
// Prepare arguments and generate the whole action code
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode +=
GenerateBehaviorAction(realObjects[i],
action.GetParameter(1).GetPlainString(),
autoInfo,
arguments,
instrInfos,
context);
context.SetNoCurrentObject();
}
}
return actionCode;
}
/**
@@ -564,17 +490,169 @@ gd::String EventsCodeGenerator::GenerateActionCode(
gd::String EventsCodeGenerator::GenerateActionsListCode(
gd::InstructionsList& actions, EventsCodeGenerationContext& context) {
gd::String outputCode;
// Generation of actions related to a same object (or same group)
// is batched, so that we only iterate once on the objects, and not
// one time for every action.
gd::String batchedObjectName;
gd::String batchedObjectType;
std::vector<gd::Instruction*> batchedActions;
auto generateBatchedObjectActionsCode = [&]() {
if (batchedActions.empty()) {
return gd::String("");
}
gd::String batchedActionsCode;
std::vector<gd::String> realObjects =
ExpandObjectsName(batchedObjectName, context);
for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context
context.SetCurrentObject(realObjects[i]);
context.ObjectsListNeeded(realObjects[i]);
gd::String actionsCode =
batchedActions.size() > 1
? "/*Batching for " + batchedObjectName + "*/\n"
: "/*No batching for " + batchedObjectName + "*/\n";
for (auto& actionPtr : batchedActions) {
auto& action = *actionPtr;
gd::InstructionMetadata instrInfos =
MetadataProvider::GetActionMetadata(platform, action.GetType());
if (MetadataProvider::HasObjectAction(
platform, batchedObjectType, action.GetType()) &&
!instrInfos.parameters.empty()) {
const ObjectMetadata& objInfo =
MetadataProvider::GetObjectMetadata(platform, batchedObjectType);
AddIncludeFiles(objInfo.includeFiles);
// Prepare arguments and generate the whole action code
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(),
instrInfos.parameters,
context); // TODO: FACTOR or move into Generate function?
actionsCode += GenerateObjectAction(
realObjects[i], objInfo, arguments, instrInfos, context);
} else {
gd::String behaviorType = gd::GetTypeOfBehavior(
GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
action.GetParameters().size() < 2
? ""
: action.GetParameter(1).GetPlainString());
if (MetadataProvider::HasBehaviorAction(
platform, behaviorType, action.GetType()) &&
instrInfos.parameters.size() >= 2) {
// Setup context
const BehaviorMetadata& autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
AddIncludeFiles(autoInfo.includeFiles);
// Prepare arguments and generate the whole action code
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(),
instrInfos.parameters,
context); // TODO: FACTOR or move into Generate function?
actionsCode +=
GenerateBehaviorAction(realObjects[i],
action.GetParameter(1).GetPlainString(),
autoInfo,
arguments,
instrInfos,
context);
}
}
}
batchedActionsCode +=
GenerateObjectLoop(realObjects[i], context, actionsCode); //TODO: use for conditions too
context.SetNoCurrentObject();
}
batchedObjectName = "";
batchedObjectType = "";
batchedActions.clear();
return batchedActionsCode;
};
// Defer the generation of the object (or behavior) action.
auto batchObjectAction = [&](const gd::String& objectName,
const gd::String& objectType,
gd::Instruction& action) {
gd::String outputCode;
if (objectName != batchedObjectName || objectType != batchedObjectType) {
// Dump any batched action for a different object before.
outputCode += generateBatchedObjectActionsCode();
batchedObjectName = objectName;
batchedObjectType = objectType;
}
batchedActions.push_back(&action);
return outputCode;
};
for (std::size_t aId = 0; aId < actions.size(); ++aId) {
gd::Instruction& action = actions[aId];
gd::InstructionMetadata instrInfos =
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
gd::String actionCode = GenerateActionCode(actions[aId], context);
AddIncludeFiles(instrInfos.codeExtraInformation.GetIncludeFiles());
outputCode += "{";
if (!actions[aId].GetType().empty()) outputCode += actionCode;
outputCode += "}";
// Generate custom code if needed
if (instrInfos.codeExtraInformation.HasCustomCodeGenerator()) {
outputCode += generateBatchedObjectActionsCode(); // Dump any batched
// action before.
outputCode += instrInfos.codeExtraInformation.customCodeGenerator(
action, *this, context);
} else {
// No custom code
ValidateAction(instrInfos, action);
// Call free function first if available
if (MetadataProvider::HasAction(platform, action.GetType())) {
outputCode += generateBatchedObjectActionsCode(); // Dump any batched
// action before.
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(),
instrInfos.parameters,
context); // TODO: FACTOR or move into Generate function?
outputCode += GenerateFreeAction(arguments, instrInfos, context);
}
// Call object function if available
gd::String objectName = action.GetParameters().empty()
? ""
: action.GetParameter(0).GetPlainString();
gd::String objectType = gd::GetTypeOfObject(
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), objectName);
if (MetadataProvider::HasObjectAction(
platform, objectType, action.GetType()) &&
!instrInfos.parameters.empty()) {
outputCode += batchObjectAction(objectName, objectType, action);
}
// Assign to a behavior member function if found
gd::String behaviorType =
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
action.GetParameters().size() < 2
? ""
: action.GetParameter(1).GetPlainString());
if (MetadataProvider::HasBehaviorAction(
platform, behaviorType, action.GetType()) &&
instrInfos.parameters.size() >= 2) {
outputCode += batchObjectAction(objectName, objectType, action);
}
}
}
// Dump any remaining batched actions.
outputCode += generateBatchedObjectActionsCode();
return outputCode;
}
@@ -582,34 +660,24 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
const gd::String& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& previousParameter,
const gd::String& lastObjectName,
std::vector<std::pair<gd::String, gd::String> >*
supplementaryParametersTypes) {
gd::String argOutput;
if (ParameterMetadata::IsExpression("number", metadata.type)) {
CallbacksForGeneratingExpressionCode callbacks(argOutput, *this, context);
gd::ExpressionParser parser(parameter);
if (!parser.ParseMathExpression(platform, GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), callbacks)) {
cout << "Error :" << parser.GetFirstError() << " in: " << parameter
<< endl;
argOutput = "0";
}
if (argOutput.empty()) argOutput = "0";
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter);
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
CallbacksForGeneratingExpressionCode callbacks(argOutput, *this, context);
gd::ExpressionParser parser(parameter);
if (!parser.ParseStringExpression(platform, GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), callbacks)) {
cout << "Error in text expression" << parser.GetFirstError() << endl;
argOutput = "\"\"";
}
if (argOutput.empty()) argOutput = "\"\"";
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter);
} else if (ParameterMetadata::IsExpression("variable", metadata.type)) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, metadata.type, parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.type)) {
// It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects.
argOutput = GenerateObject(parameter, metadata.type, context);
} else if (metadata.type == "relationalOperator") {
argOutput += parameter == "=" ? "==" : parameter;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
@@ -628,14 +696,12 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
}
argOutput = "\"" + argOutput + "\"";
} else if (metadata.type == "object" || metadata.type == "behavior") {
} else if (metadata.type == "behavior") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "key") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "objectvar" || metadata.type == "scenevar" ||
metadata.type == "globalvar" || metadata.type == "password" ||
metadata.type == "musicfile" || metadata.type == "soundfile" ||
metadata.type == "police") {
} else if (metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "mouse") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
@@ -682,6 +748,7 @@ vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
while (parameters.size() < parametersInfo.size())
parameters.push_back(gd::Expression(""));
gd::String lastObjectName = "";
for (std::size_t pNb = 0;
pNb < parametersInfo.size() && pNb < parameters.size();
++pNb) {
@@ -689,12 +756,18 @@ vector<gd::String> EventsCodeGenerator::GenerateParametersCodes(
parametersInfo[pNb].optional)
parameters[pNb] = gd::Expression(parametersInfo[pNb].defaultValue);
gd::String argOutput = GenerateParameterCodes(
parameters[pNb].GetPlainString(),
parametersInfo[pNb],
context,
pNb == 0 ? "" : parameters[pNb - 1].GetPlainString(),
supplementaryParametersTypes);
gd::String argOutput =
GenerateParameterCodes(parameters[pNb].GetPlainString(),
parametersInfo[pNb],
context,
lastObjectName,
supplementaryParametersTypes);
// Memorize the last object name. By convention, parameters that require
// an object (mainly, "objectvar") should be placed after the object
// in the list of parameters (if possible, just after).
if (gd::ParameterMetadata::IsObject(parametersInfo[pNb].GetType()))
lastObjectName = parameters[pNb].GetPlainString();
arguments.push_back(argOutput);
}
@@ -799,23 +872,10 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
}
gd::String EventsCodeGenerator::ConvertToString(gd::String plainString) {
for (size_t i = 0; i < plainString.length(); ++i) {
if (plainString[i] == '\\') {
if (i + 1 >= plainString.length() || plainString[i + 1] != '\"') {
if (i + 1 < plainString.length())
plainString.insert(i + 1, "\\");
else
plainString += ("\\");
++i;
}
} else if (plainString[i] == '"') {
plainString.insert(i, "\\");
++i;
}
}
plainString = plainString.FindAndReplace("\n", "\\n");
plainString = plainString.FindAndReplace("\\", "\\\\")
.FindAndReplace("\r", "\\r")
.FindAndReplace("\n", "\\n")
.FindAndReplace("\"", "\\\"");
return plainString;
}
@@ -831,10 +891,12 @@ std::vector<gd::String> EventsCodeGenerator::ExpandObjectsName(
// Note: this logic is duplicated in EventsContextAnalyzer::ExpandObjectsName
std::vector<gd::String> realObjects;
if (globalObjectsAndGroups.GetObjectGroups().Has(objectName))
realObjects =
globalObjectsAndGroups.GetObjectGroups().Get(objectName).GetAllObjectsNames();
realObjects = globalObjectsAndGroups.GetObjectGroups()
.Get(objectName)
.GetAllObjectsNames();
else if (objectsAndGroups.GetObjectGroups().Has(objectName))
realObjects = objectsAndGroups.GetObjectGroups().Get(objectName).GetAllObjectsNames();
realObjects =
objectsAndGroups.GetObjectGroups().Get(objectName).GetAllObjectsNames();
else
realObjects.push_back(objectName);
@@ -893,7 +955,9 @@ gd::String EventsCodeGenerator::GenerateObjectFunctionCall(
gd::String parametersStr,
gd::String defaultOutput,
gd::EventsCodeGenerationContext& context) {
return "TODO (GenerateObjectFunctionCall)";
// To be used for testing only.
return objectListName + "." + codeInfo.functionCallName + "(" +
parametersStr + ") ?? " + defaultOutput;
}
gd::String EventsCodeGenerator::GenerateObjectBehaviorFunctionCall(
@@ -904,7 +968,10 @@ gd::String EventsCodeGenerator::GenerateObjectBehaviorFunctionCall(
gd::String parametersStr,
gd::String defaultOutput,
gd::EventsCodeGenerationContext& context) {
return "TODO (GenerateObjectBehaviorFunctionCall)";
// To be used for testing only.
return objectListName + "::" + behaviorName + "." +
codeInfo.functionCallName + "(" + parametersStr + ") ?? " +
defaultOutput;
}
gd::String EventsCodeGenerator::GenerateFreeCondition(
@@ -1031,6 +1098,18 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
return call + ";\n";
}
gd::String EventsCodeGenerator::GenerateObjectLoop(
const gd::String& objectName,
gd::EventsCodeGenerationContext& context,
const gd::String& innerLoopCode) {
gd::String loopCode; //TODO: For GDCpp
loopCode += "foreach("+objectName+") {\n";
loopCode += " " + innerLoopCode + ";\n";
loopCode += "}\n";
return loopCode;
}
gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
@@ -1056,14 +1135,13 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
instrInfos.codeExtraInformation.functionCallName,
2);
return "For each picked object \"" + objectName + "\", call " + call +
".\n";
return "For object \"" + objectName + "\", call " + call + ".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 1);
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
return "For object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ").\n";
}
}
@@ -1093,14 +1171,14 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
arguments,
instrInfos.codeExtraInformation.functionCallName,
2);
return "For each picked object \"" + objectName + "\", call " + call +
return "For object \"" + objectName + "\", call " + call +
" for behavior \"" + behaviorName + "\".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 2);
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
return "For object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" + " for behavior \"" + behaviorName + "\".\n";
}
}
@@ -1135,9 +1213,10 @@ EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
maxCustomConditionsDepth(0),
maxConditionsListsSize(0){};
EventsCodeGenerator::EventsCodeGenerator(const gd::Platform& platform_,
gd::ObjectsContainer & globalObjectsAndGroups_,
const gd::ObjectsContainer & objectsAndGroups_)
EventsCodeGenerator::EventsCodeGenerator(
const gd::Platform& platform_,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_)
: platform(platform_),
globalObjectsAndGroups(globalObjectsAndGroups_),
objectsAndGroups(objectsAndGroups_),

View File

@@ -1,3 +1,8 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EVENTSCODEGENERATOR_H
#define GDCORE_EVENTSCODEGENERATOR_H
@@ -22,18 +27,19 @@ class EventsCodeGenerationContext;
class ExpressionCodeGenerationInformation;
class InstructionMetadata;
class Platform;
}
} // namespace gd
namespace gd {
/**
* \brief Internal class used to generate code from events
* \todo For now, this class generates only C++ code for GD C++ Platform.
*
* \see CallbacksForGeneratingExpressionCode
*/
class GD_CORE_API EventsCodeGenerator {
// Compatiblity with old ExpressionParser
friend class CallbacksForGeneratingExpressionCode;
friend class VariableCodeGenerationCallbacks;
// end of compatibility code
friend class ExpressionCodeGenerator;
public:
/**
@@ -53,9 +59,9 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Construct a code generator for the specified
* objects/groups and platform
*/
EventsCodeGenerator(const gd::Platform& platform,
gd::ObjectsContainer & globalObjectsAndGroups_,
const gd::ObjectsContainer & objectsAndGroups_);
EventsCodeGenerator(const gd::Platform& platform,
gd::ObjectsContainer& globalObjectsAndGroups_,
const gd::ObjectsContainer& objectsAndGroups_);
virtual ~EventsCodeGenerator(){};
/**
@@ -141,19 +147,6 @@ class GD_CORE_API EventsCodeGenerator {
gd::String returnBoolean,
EventsCodeGenerationContext& context);
/**
* \brief Generate code for a single action
*
* The generation is really done in GenerateFreeAction/GenerateObjectAction or
* GenerateBehaviorAction.
*
* \param condition instruction to be done.
* \param context Context used for generation
* \return Code
*/
gd::String GenerateActionCode(gd::Instruction& action,
EventsCodeGenerationContext& context);
/**
* \brief Generate code for declaring objects lists.
*
@@ -182,9 +175,9 @@ class GD_CORE_API EventsCodeGenerator {
virtual gd::String ConvertToString(gd::String plainString);
/**
* \brief Convert a plain string ( with line feed, quotes ) to a string that
* \brief Convert a plain string (with line feed, quotes) to a string that
can be inserted into code.
* The string construction must be explicit : for example, quotes must be
* The string construction must be explicit: for example, quotes must be
added if the target language need quotes.
*
* Usage example :
@@ -278,23 +271,31 @@ class GD_CORE_API EventsCodeGenerator {
void ReportError();
/**
* \brief Return true if an error has occurred during code generation ( in
* this case, generated code is not usable )
* \brief Return true if an error has occurred during code generation (in
* this case, generated code is not usable).
*
* \todo TODO: This is actually not used and should be moved to a more
* complete error reporting.
*/
bool ErrorOccurred() const { return errorOccurred; };
/**
* \brief Get the global objects/groups used for code generation.
*/
gd::ObjectsContainer& GetGlobalObjectsAndGroups() const { return globalObjectsAndGroups; }
gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
return globalObjectsAndGroups;
}
/**
* \brief Get the objects/groups used for code generation.
*/
const gd::ObjectsContainer& GetObjectsAndGroups() const { return objectsAndGroups; }
const gd::ObjectsContainer& GetObjectsAndGroups() const {
return objectsAndGroups;
}
/**
* \brief Return true if the code generation is done for a given project and layout.
* \brief Return true if the code generation is done for a given project and
* layout.
*/
bool HasProjectAndLayout() const { return hasProjectAndLayout; }
@@ -380,27 +381,36 @@ class GD_CORE_API EventsCodeGenerator {
* \brief Generate the code to notify the profiler of the beginning of a
* section.
*/
virtual gd::String GenerateProfilerSectionBegin(const gd::String& section) { return ""; };
virtual gd::String GenerateProfilerSectionBegin(const gd::String& section) {
return "";
};
/**
* \brief Generate the code to notify the profiler of the end of a section.
*/
virtual gd::String GenerateProfilerSectionEnd(const gd::String& section) { return ""; };
virtual gd::String GenerateProfilerSectionEnd(const gd::String& section) {
return "";
};
/**
* \brief Get the namespace to be used to store code generated objects/values/functions,
* with the extra "dot" at the end to be used to access to a property/member.
* \brief Get the namespace to be used to store code generated
* objects/values/functions, with the extra "dot" at the end to be used to
* access to a property/member.
*
* Example: "gdjs.something."
*/
virtual gd::String GetCodeNamespaceAccessor() { return ""; };
/**
* \brief Get the namespace to be used to store code generated objects/values/functions.
* \brief Get the namespace to be used to store code generated
* objects/values/functions.
*
* Example: "gdjs.something"
*/
virtual gd::String GetCodeNamespace() { return ""; };
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE };
protected:
/**
* \brief Generate the code for a single parameter.
@@ -452,10 +462,69 @@ class GD_CORE_API EventsCodeGenerator {
const gd::String& parameter,
const gd::ParameterMetadata& metadata,
gd::EventsCodeGenerationContext& context,
const gd::String& previousParameter,
const gd::String& lastObjectName,
std::vector<std::pair<gd::String, gd::String> >*
supplementaryParametersTypes);
/**
* \brief Generate the code to get a variable.
*/
virtual gd::String GenerateGetVariable(
const gd::String& variableName,
const VariableScope& scope,
gd::EventsCodeGenerationContext& context,
const gd::String& objectName) {
if (scope == LAYOUT_VARIABLE) {
return "getLayoutVariable(" + variableName + ")";
} else if (scope == PROJECT_VARIABLE) {
return "getProjectVariable(" + variableName + ")";
}
return "getVariableForObject(" + objectName + ", " + variableName + ")";
}
/**
* \brief Generate the code to get the child of a variable.
*/
virtual gd::String GenerateVariableAccessor(gd::String childName) {
return ".getChild(" + ConvertToStringExplicit(childName) + ")";
};
/**
* \brief Generate the code to get the child of a variable,
* using generated the expression.
*/
virtual gd::String GenerateVariableBracketAccessor(
gd::String expressionCode) {
return ".getChild(" + expressionCode + ")";
};
/**
* \brief Generate the code to reference a variable which is
* in an empty/null state.
*/
virtual gd::String GenerateBadVariable() { return "fakeBadVariable"; }
/**
* \brief Generate the code to reference an object.
* \param objectName the name of the object.
* \param type what is the expected type (object, objectPtr...) in which the
* object must be generated.
* \param context The context for code generation
*/
virtual gd::String GenerateObject(const gd::String& objectName,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
return "fakeObjectListOf_" + objectName;
}
/**
* \brief Generate the code to reference an object which is
* in an empty/null state.
*/
virtual gd::String GenerateBadObject() { return "fakeNullObject"; }
/**
* \brief Call a function of the current object.
* \note The current object is the object being manipulated by a condition or
@@ -577,6 +646,11 @@ class GD_CORE_API EventsCodeGenerator {
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateObjectLoop(
const gd::String& objectName,
gd::EventsCodeGenerationContext& context,
const gd::String& innerLoopCode);
virtual gd::String GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
@@ -613,31 +687,36 @@ class GD_CORE_API EventsCodeGenerator {
std::size_t startFromArgument = 0);
/**
* \brief Must return an expression whose value is true.
* \brief Return the "true" keyword in the target language.
*/
gd::String GenerateTrue() const { return "true"; };
/**
* \brief Must return an expression whose value is false.
* \brief Return the "false" keyword in the target language.
*/
gd::String GenerateFalse() const { return "false"; };
/**
* \brief Generate the list of comma-separated arguments to be used to call a
* function. \param arguments The code already generated for the arguments
* function.
*
* \param arguments The code already generated for the arguments
* \param startFrom Index of the first argument, the previous will be ignored.
*/
virtual gd::String GenerateArgumentsList(
const std::vector<gd::String>& arguments, size_t startFrom = 0);
const gd::Platform& platform; ///< The platform being used.
gd::ObjectsContainer & globalObjectsAndGroups;
const gd::ObjectsContainer & objectsAndGroups;
void ValidateAction(gd::InstructionMetadata & instructionMetadata, gd::Instruction& action);
bool hasProjectAndLayout; ///< true only if project and layout are valid references. If false, they should not be used.
gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
const gd::Platform& platform; ///< The platform being used.
gd::ObjectsContainer& globalObjectsAndGroups;
const gd::ObjectsContainer& objectsAndGroups;
bool hasProjectAndLayout; ///< true only if project and layout are valid
///< references. If false, they should not be used.
gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
bool errorOccurred; ///< Must be set to true if an error occured.
bool compilationForRuntime; ///< Is set to true if the code generation is
@@ -652,7 +731,7 @@ class GD_CORE_API EventsCodeGenerator {
gd::String customCodeInMain; ///< Custom code inserted before events ( in
///< main function )
std::set<gd::String>
customGlobalDeclarations; ///< Custom global C++ declarations inserted
customGlobalDeclarations; ///< Custom global C++ declarations inserted
///< after includes
size_t maxCustomConditionsDepth; ///< The maximum depth value for all the
///< custom conditions created.

View File

@@ -0,0 +1,465 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionCodeGenerator.h"
#include <memory>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.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/IDE/Events/ExpressionValidator.h"
#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());
auto node = parser.ParseExpression(type, expression, objectName);
gd::ExpressionValidator validator;
node->Visit(validator);
ExpressionCodeGenerator generator(codeGenerator, context);
if (!validator.GetErrors().empty()) {
std::cout << "Error: \"" << validator.GetErrors()[0]->GetMessage()
<< "\" in: \"" << expression << "\" (" << type << ")"
<< std::endl;
return generator.GenerateDefaultValue(type);
}
node->Visit(generator);
return generator.GetOutput();
}
void ExpressionCodeGenerator::OnVisitOperatorNode(OperatorNode& node) {
node.leftHandSide->Visit(*this);
output += " ";
output.push_back(node.op);
output += " ";
node.rightHandSide->Visit(*this);
}
void ExpressionCodeGenerator::OnVisitUnaryOperatorNode(
UnaryOperatorNode& node) {
output.push_back(node.op);
output += "("; // Add extra parenthesis to ensure that things like --2 are
// properly outputted as -(-2) (GDevelop don't have -- or ++
// operators, but JavaScript and C++ have).
node.factor->Visit(*this);
output += ")";
}
void ExpressionCodeGenerator::OnVisitSubExpressionNode(
SubExpressionNode& node) {
output += "(";
node.expression->Visit(*this);
output += ")";
}
void ExpressionCodeGenerator::OnVisitNumberNode(NumberNode& node) {
output += node.number;
}
void ExpressionCodeGenerator::OnVisitTextNode(TextNode& node) {
output += codeGenerator.ConvertToStringExplicit(node.text);
}
void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
// This "translation" from the type to an enum could be avoided
// if all types were moved to an enum.
EventsCodeGenerator::VariableScope scope =
node.type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((node.type == "scenevar")
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
output += codeGenerator.GenerateGetVariable(
node.name, scope, context, node.objectName);
if (node.child) node.child->Visit(*this);
}
void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
output += codeGenerator.GenerateVariableAccessor(node.name);
if (node.child) node.child->Visit(*this);
}
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
ExpressionCodeGenerator generator(codeGenerator, context);
node.expression->Visit(generator);
output +=
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
if (node.child) node.child->Visit(*this);
}
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
if (gd::ParameterMetadata::IsObject(node.type)) {
output +=
codeGenerator.GenerateObject(node.identifierName, node.type, context);
} else {
output += "/* Error during generation, unrecognized identifier type: " +
codeGenerator.ConvertToString(node.type) + " with value " +
codeGenerator.ConvertToString(node.identifierName) + " */ " +
codeGenerator.ConvertToStringExplicit(node.identifierName);
}
}
void ExpressionCodeGenerator::OnVisitFunctionNode(FunctionNode& node) {
if (gd::MetadataProvider::IsBadExpressionMetadata(node.expressionMetadata)) {
output += "/* Error during generation, function not found: " +
codeGenerator.ConvertToString(node.functionName) + " for type " +
node.type + " */ " + GenerateDefaultValue(node.type);
return;
}
if (!node.objectName.empty()) {
if (!node.behaviorName.empty()) {
output += GenerateBehaviorFunctionCode(node.type,
node.objectName,
node.behaviorName,
node.parameters,
node.expressionMetadata);
} else {
output += GenerateObjectFunctionCode(
node.type, node.objectName, node.parameters, node.expressionMetadata);
}
} else {
output +=
GenerateFreeFunctionCode(node.parameters, node.expressionMetadata);
}
}
gd::String ExpressionCodeGenerator::GenerateFreeFunctionCode(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata) {
codeGenerator.AddIncludeFiles(
expressionMetadata.codeExtraInformation.GetIncludeFiles());
// Launch custom code generator if needed
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
return expressionMetadata.codeExtraInformation.customCodeGenerator(
PrintParameters(parameters), codeGenerator, context);
}
gd::String parametersCode =
GenerateParametersCodes(parameters, expressionMetadata, 0);
return expressionMetadata.codeExtraInformation.functionCallName + "(" +
parametersCode + ")";
}
gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
const gd::String& type,
const gd::String& objectName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata) {
const gd::ObjectsContainer& globalObjectsAndGroups =
codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups =
codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionMetadata.codeExtraInformation.GetIncludeFiles());
// Launch custom code generator if needed
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
return expressionMetadata.codeExtraInformation.customCodeGenerator(
PrintParameters(parameters), codeGenerator, context);
}
// Prepare parameters
gd::String parametersCode = GenerateParametersCodes(
parameters,
expressionMetadata,
// By convention, the first parameter is the object
1);
gd::String functionOutput = GenerateDefaultValue(type);
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(objectName, 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);
codeGenerator.AddIncludeFiles(objInfo.includeFiles);
functionOutput = codeGenerator.GenerateObjectFunctionCall(
realObjects[i],
objInfo,
expressionMetadata.codeExtraInformation,
parametersCode,
functionOutput,
context);
}
return functionOutput;
}
gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
const gd::String& type,
const gd::String& objectName,
const gd::String& behaviorName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata) {
const gd::ObjectsContainer& globalObjectsAndGroups =
codeGenerator.GetGlobalObjectsAndGroups();
const gd::ObjectsContainer& objectsAndGroups =
codeGenerator.GetObjectsAndGroups();
codeGenerator.AddIncludeFiles(
expressionMetadata.codeExtraInformation.GetIncludeFiles());
// Launch custom code generator if needed
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
return expressionMetadata.codeExtraInformation.customCodeGenerator(
PrintParameters(parameters), codeGenerator, context);
}
// Prepare parameters
gd::String parametersCode = GenerateParametersCodes(
parameters,
expressionMetadata,
// By convention, the first parameters are the object and behavior
2);
// Get object(s) concerned by function call
std::vector<gd::String> realObjects =
codeGenerator.ExpandObjectsName(objectName, context);
gd::String functionOutput = GenerateDefaultValue(type);
gd::String behaviorType = gd::GetTypeOfBehavior(
globalObjectsAndGroups, objectsAndGroups, behaviorName);
const BehaviorMetadata& autoInfo = MetadataProvider::GetBehaviorMetadata(
codeGenerator.GetPlatform(), behaviorType);
for (std::size_t i = 0; i < realObjects.size(); ++i) {
context.ObjectsListNeeded(realObjects[i]);
codeGenerator.AddIncludeFiles(autoInfo.includeFiles);
functionOutput = codeGenerator.GenerateObjectBehaviorFunctionCall(
realObjects[i],
behaviorName,
autoInfo,
expressionMetadata.codeExtraInformation,
parametersCode,
functionOutput,
context);
}
return functionOutput;
}
gd::String ExpressionCodeGenerator::GenerateParametersCodes(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata,
size_t initialParameterIndex) {
size_t nonCodeOnlyParameterIndex = 0;
gd::String parametersCode;
for (std::size_t i = initialParameterIndex;
i < expressionMetadata.parameters.size();
++i) {
if (i != initialParameterIndex) parametersCode += ", ";
auto& parameterMetadata = expressionMetadata.parameters[i];
if (!parameterMetadata.IsCodeOnly()) {
ExpressionCodeGenerator generator(codeGenerator, context);
if (nonCodeOnlyParameterIndex < parameters.size()) {
parameters[nonCodeOnlyParameterIndex]->Visit(generator);
parametersCode += generator.GetOutput();
} else if (parameterMetadata.IsOptional()) {
// Optional parameters default value were not parsed at the time of the
// expression parsing. Parse them now.
ExpressionParser2 parser(codeGenerator.GetPlatform(),
codeGenerator.GetGlobalObjectsAndGroups(),
codeGenerator.GetObjectsAndGroups());
auto node = parser.ParseExpression(parameterMetadata.GetType(),
parameterMetadata.GetDefaultValue());
node->Visit(generator);
parametersCode += generator.GetOutput();
} else {
parametersCode +=
"/* Error during generation, parameter not existing in the nodes "
"*/ " +
GenerateDefaultValue(parameterMetadata.GetType());
}
nonCodeOnlyParameterIndex++;
} else {
parametersCode +=
codeGenerator.GenerateParameterCodes(parameterMetadata.GetExtraInfo(),
parameterMetadata,
context,
"",
nullptr);
}
}
return parametersCode;
}
std::vector<gd::Expression> ExpressionCodeGenerator::PrintParameters(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters) {
// Printing parameters is only useful because custom code generator of
// expression require to get the parameters of the expression as strings
// (gd::Expression). Once the old ExpressionParser is removed, custom
// code generator can be reworked to directly take the parsed nodes,
// avoiding an extra and useless printing/parsing of their parameters.
std::vector<gd::Expression> printedParameters;
for (auto& parameter : parameters) {
printedParameters.push_back(
gd::ExpressionParser2NodePrinter::PrintNode(*parameter));
}
return printedParameters;
}
gd::String ExpressionCodeGenerator::GenerateDefaultValue(
const gd::String& type) {
if (gd::ParameterMetadata::IsExpression("variable", type)) {
return codeGenerator.GenerateBadVariable();
}
if (gd::ParameterMetadata::IsObject(type)) {
return codeGenerator.GenerateBadObject();
}
return (type == "string") ? "\"\"" : "0";
}
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
output += GenerateDefaultValue(node.type);
}
} // namespace gd

View File

@@ -0,0 +1,117 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef GDCORE_ExpressionCodeGenerator_H
#define GDCORE_ExpressionCodeGenerator_H
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/String.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
class EventsCodeGenerationContext;
class EventsCodeGenerator;
} // namespace gd
namespace gd {
/**
* \brief Generate code for a parsed expression.
*
* Almost all code generation is dedicated to the gd::EventsCodeGenerator,
* so that it can be adapted to the target.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
public:
ExpressionCodeGenerator(EventsCodeGenerator& codeGenerator_,
EventsCodeGenerationContext& context_)
: codeGenerator(codeGenerator_), context(context_){};
virtual ~ExpressionCodeGenerator(){};
/**
* Helper to generate the code for an expression.
* If expression is invalid, a default generated value is returned (0 for
* number expression, empty string for strings).
*
* \param codeGenerator The code generator to use to output code.
* \param context The context of the code generation.
* \param type The type of the expression (see gd::ExpressionParser2).
* \param expression The expression to parse and generate code for.
* \param object The object the expression refers too (only for "objectvar"
* type).
*
* \see see gd::ExpressionParser2
*/
static gd::String GenerateExpressionCode(EventsCodeGenerator& codeGenerator,
EventsCodeGenerationContext& context,
const gd::String& type,
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:
void OnVisitSubExpressionNode(SubExpressionNode& node) override;
void OnVisitOperatorNode(OperatorNode& node) override;
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override;
void OnVisitNumberNode(NumberNode& node) override;
void OnVisitTextNode(TextNode& node) override;
void OnVisitVariableNode(VariableNode& node) override;
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override;
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override;
void OnVisitIdentifierNode(IdentifierNode& node) override;
void OnVisitFunctionNode(FunctionNode& node) override;
void OnVisitEmptyNode(EmptyNode& node) override;
private:
gd::String GenerateFreeFunctionCode(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata);
gd::String GenerateObjectFunctionCode(
const gd::String& type,
const gd::String& objectName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata);
gd::String GenerateBehaviorFunctionCode(
const gd::String& type,
const gd::String& objectName,
const gd::String& behaviorName,
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata);
gd::String GenerateParametersCodes(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
const ExpressionMetadata& expressionMetadata,
size_t initialParameterIndex);
gd::String GenerateDefaultValue(const gd::String& type);
static std::vector<gd::Expression> PrintParameters(
const std::vector<std::unique_ptr<ExpressionNode>>& parameters);
gd::String output;
EventsCodeGenerator& codeGenerator;
EventsCodeGenerationContext& context;
static bool useOldExpressionParser;
};
} // namespace gd
#endif // GDCORE_ExpressionCodeGenerator_H
#endif

View File

@@ -11,28 +11,18 @@
#include "GDCore/String.h"
namespace gd {
class ExpressionMetadata;
}
namespace gd {
class Expression;
}
namespace gd {
class Project;
}
namespace gd {
class Layout;
}
namespace gd {
class Layout;
}
namespace gd {
class EventsCodeGenerationContext;
}
namespace gd {
class EventsCodeGenerator;
}
namespace gd {
// TODO: Replace and remove (ExpressionCodeGenerator)
/**
* \brief Used to generate code from expressions.
*

View File

@@ -0,0 +1,68 @@
/*
* 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

@@ -10,12 +10,15 @@
#include <string>
#include <vector>
#include "GDCore/Events/Parsers/VariableParser.h"
#include "GDCpp/Runtime/String.h"
#include "GDCore/String.h"
#include "EventsCodeGenerator.h"
namespace gd {
class EventsCodeGenerator;
class EventsCodeGenerationContext;
} // namespace gd
// TODO: Replace and remove (ExpressionCodeGenerator)
namespace gd {
/**
* \brief Callbacks called to generate the code for getting a variable.
*
@@ -36,8 +39,6 @@ class EventsCodeGenerationContext;
*/
class VariableCodeGenerationCallbacks : public gd::VariableParserCallbacks {
public:
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE };
/**
* \brief Default constructor for generating code for a layout/global
* variable. \param output The string in which the code will be generated.
@@ -49,7 +50,7 @@ class VariableCodeGenerationCallbacks : public gd::VariableParserCallbacks {
VariableCodeGenerationCallbacks(gd::String& output,
gd::EventsCodeGenerator& codeGenerator_,
gd::EventsCodeGenerationContext& context_,
const VariableScope& scope_);
const gd::EventsCodeGenerator::VariableScope& scope_);
/**
* \brief Default constructor for generating code for an object variable.
@@ -86,9 +87,11 @@ class VariableCodeGenerationCallbacks : public gd::VariableParserCallbacks {
gd::String& output;
gd::EventsCodeGenerator& codeGenerator;
gd::EventsCodeGenerationContext& context;
VariableScope scope;
gd::EventsCodeGenerator::VariableScope scope;
const gd::String object; ///< The object name, when scope == OBJECT_VARIABLE.
};
}
#endif // VARIABLEPARSERCALLBACKS_H
#endif

View File

@@ -717,9 +717,6 @@ bool ExpressionParser::ParseStringExpression(const gd::Platform& platform,
firstErrorPos = functionNameEnd;
firstErrorStr = _("Incorrect number of parameters");
for (std::size_t i = 0; i < parameters.size(); ++i)
cout << "Param:" << parameters[i].GetPlainString() << endl;
return false;
}

View File

@@ -0,0 +1,220 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include <algorithm>
#include <memory>
#include <vector>
#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"
#include "GDCore/Tools/MakeUnique.h"
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(
const gd::Platform& platform_,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_)
: expression(""),
currentPosition(0),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_) {}
namespace {
/**
* Return the minimum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMinimumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
}
return nb;
}
/**
* Return the maximum number of parameters, starting from a given parameter
* (by convention, 1 for object functions and 2 for behavior functions).
*/
size_t GetMaximumParametersNumber(
const std::vector<gd::ParameterMetadata>& parameters,
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].codeOnly) nb++;
}
return nb;
}
} // namespace
std::unique_ptr<ExpressionParserDiagnostic> ExpressionParser2::ValidateFunction(
const gd::FunctionNode& function, size_t functionStartPosition) {
if (gd::MetadataProvider::IsBadExpressionMetadata(
function.expressionMetadata)) {
return gd::make_unique<ExpressionParserError>(
"invalid_function_name",
_("Cannot find an expression with this name: ") +
function.functionName + "\n" +
_("Double check that you've not made any typo in the name."),
functionStartPosition,
GetCurrentPosition());
}
size_t minParametersCount = GetMinimumParametersNumber(
function.expressionMetadata.parameters,
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
size_t maxParametersCount = GetMaximumParametersNumber(
function.expressionMetadata.parameters,
WrittenParametersFirstIndex(function.objectName, function.behaviorName));
if (function.parameters.size() < minParametersCount ||
function.parameters.size() > maxParametersCount) {
gd::String expectedCountMessage =
minParametersCount == maxParametersCount
? _("The number of parameters must be exactly ") +
gd::String::From(minParametersCount)
: _("The number of parameters must be: ") +
gd::String::From(minParametersCount) + "-" +
gd::String::From(maxParametersCount);
if (function.parameters.size() < minParametersCount) {
return gd::make_unique<ExpressionParserError>(
"too_few_parameters",
"You have not entered enough parameters for the expression. " +
expectedCountMessage,
functionStartPosition,
GetCurrentPosition());
}
}
return gd::make_unique<ExpressionParserDiagnostic>();
}
std::unique_ptr<TextNode> ExpressionParser2::ReadText() {
SkipWhitespace();
if (!IsAnyChar("\"")) {
auto text = gd::make_unique<TextNode>("");
text->diagnostic =
RaiseSyntaxError(_("A text must start with a double quote (\")."));
return text;
}
SkipChar();
gd::String parsedText = "";
bool textParsingHasEnded = false;
bool expectEscapedCharacter = false;
while (!IsEndReached() && !textParsingHasEnded) {
if (GetCurrentChar() == '"') {
if (expectEscapedCharacter) {
parsedText += '"';
expectEscapedCharacter = false;
} else {
textParsingHasEnded = true;
}
} else if (GetCurrentChar() == '\\') {
if (expectEscapedCharacter) {
parsedText += '\\';
expectEscapedCharacter = false;
} else {
expectEscapedCharacter = true;
}
} else {
if (expectEscapedCharacter) {
parsedText += '\\';
}
parsedText += GetCurrentChar();
}
currentPosition++;
}
auto text = gd::make_unique<TextNode>(parsedText);
if (!textParsingHasEnded) {
text->diagnostic =
RaiseSyntaxError(_("A text must end with a double quote (\"). Add a "
"double quote to terminate the text."));
}
return text;
}
std::unique_ptr<NumberNode> ExpressionParser2::ReadNumber() {
SkipWhitespace();
gd::String parsedNumber;
bool numberHasStarted = false;
bool digitFound = false;
bool dotFound = false;
while (!IsEndReached()) {
if (IsAnyChar("0")) {
numberHasStarted = true;
digitFound = true;
if (!parsedNumber.empty()) { // Ignore leading 0s.
parsedNumber += GetCurrentChar();
}
} else if (IsAnyChar("123456789")) {
numberHasStarted = true;
digitFound = true;
parsedNumber += GetCurrentChar();
} else if (IsAnyChar(".") && !dotFound) {
numberHasStarted = true;
dotFound = true;
if (parsedNumber == "") {
parsedNumber += "0."; //Normalize by adding a leading 0, only in this case.
} else {
parsedNumber += ".";
}
} else {
break;
}
currentPosition++;
}
// parsedNumber can be empty in the only case where we have only seen
// 0s (one or more), so normalize it to a single 0.
if (parsedNumber.empty()) {
parsedNumber = "0";
}
// Note that parsedNumber can finish by a dot (1., 2., 0.). This is
// valid in most languages so we allow this.
auto number = gd::make_unique<NumberNode>(parsedNumber);
if (!numberHasStarted || !digitFound) {
number->diagnostic = RaiseSyntaxError(
_("A number was expected. You must enter a number here."));
}
return number;
}
} // namespace gd

View File

@@ -0,0 +1,763 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONPARSER2_H
#define GDCORE_EXPRESSIONPARSER2_H
#include <memory>
#include <utility>
#include <vector>
#include "ExpressionParser2Node.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/** \brief Parse an expression, returning a tree of node corresponding
* to the parsed expression.
*
* This is a LL(1) parser. This could be extracted to a generic/reusable
* parser by refactoring out the dependency on gd::MetadataProvider (injecting
* instead functions to be called to query supported functions).
*
* \see gd::ExpressionParserDiagnostic
* \see gd::ExpressionNode
*/
class GD_CORE_API ExpressionParser2 {
public:
ExpressionParser2(const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_);
virtual ~ExpressionParser2(){};
/**
* Parse the given expression with the specified type.
*
* \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
* case of "objectvar" type.
*
* \return The node representing the expression as a parsed tree.
*/
std::unique_ptr<ExpressionNode> ParseExpression(
const gd::String &type,
const gd::String &expression_,
const gd::String &objectName = "") {
expression = expression_;
currentPosition = 0;
return Start(type, objectName);
}
private:
/** \name Grammar
* Each method is a part of the grammar.
*/
///@{
std::unique_ptr<ExpressionNode> Start(const gd::String &type,
const gd::String &objectName = "") {
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 = ' ';
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)."));
return std::move(op);
}
return expression;
}
std::unique_ptr<ExpressionNode> Expression(
const gd::String &type, const gd::String &objectName = "") {
SkipWhitespace();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> leftHandSide = Term(type, objectName);
SkipWhitespace();
if (IsEndReached()) return leftHandSide;
if (IsAnyChar(",)]")) return leftHandSide;
if (IsAnyChar(EXPRESSION_OPERATORS)) {
auto op = gd::make_unique<OperatorNode>();
op->op = GetCurrentChar();
op->leftHandSide = std::move(leftHandSide);
op->diagnostic = ValidateOperator(type, GetCurrentChar());
SkipChar();
op->rightHandSide = Expression(type, objectName);
return std::move(op);
}
if (type == "string") {
leftHandSide->diagnostic = RaiseSyntaxError(
"You must add the operator + between texts or expressions. For "
"example: \"Your name: \" + VariableString(PlayerName).");
} else if (type == "number") {
leftHandSide->diagnostic = RaiseSyntaxError(
"No operator found. Did you forget to enter an operator (like +, -, "
"* or /) between numbers or expressions?");
} else {
leftHandSide->diagnostic = RaiseSyntaxError(
"More than one term was found. Verify that your expression is "
"properly written.");
}
auto op = gd::make_unique<OperatorNode>();
op->op = ' ';
op->leftHandSide = std::move(leftHandSide);
op->rightHandSide = Expression(type, objectName);
return std::move(op);
}
std::unique_ptr<ExpressionNode> Term(const gd::String &type,
const gd::String &objectName) {
SkipWhitespace();
std::unique_ptr<ExpressionNode> factor = Factor(type, objectName);
SkipWhitespace();
// 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();
op->leftHandSide = std::move(factor);
op->diagnostic = ValidateOperator(type, GetCurrentChar());
SkipChar();
op->rightHandSide = Factor(type, objectName);
SkipWhitespace();
factor = std::move(op);
}
return factor;
};
std::unique_ptr<ExpressionNode> Factor(const gd::String &type,
const gd::String &objectName) {
SkipWhitespace();
size_t expressionStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> factor;
if (IsAnyChar(QUOTE)) {
factor = ReadText();
if (type == "number")
factor->diagnostic =
RaiseTypeError(_("You entered a text, but a number was expected."),
expressionStartPosition);
else if (type != "string")
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());
unaryOperator->diagnostic = ValidateUnaryOperator(type, GetCurrentChar());
SkipChar();
unaryOperator->factor = Factor(type, objectName);
factor = std::move(unaryOperator);
} else if (IsAnyChar(NUMBER_FIRST_CHAR)) {
factor = ReadNumber();
if (type == "string")
factor->diagnostic = RaiseTypeError(
_("You entered a number, but a text was expected (in quotes)."),
expressionStartPosition);
else if (type != "number")
factor->diagnostic = RaiseTypeError(
_("You entered a number, but this type was expected:") + type,
expressionStartPosition);
} else if (IsAnyChar("(")) {
SkipChar();
factor = SubExpression(type, objectName);
if (!IsAnyChar(")")) {
factor->diagnostic =
RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
"parenthesis for each opening parenthesis."));
}
SkipIfIsAnyChar(")");
} else if (IsIdentifierAllowedChar()) {
// This is a place where the grammar differs according to the
// type being expected.
if (gd::ParameterMetadata::IsExpression("variable", type)) {
factor = Variable(type, objectName);
} else {
factor = Identifier(type);
}
} else {
factor = ReadUntilWhitespace(type);
factor->diagnostic = RaiseEmptyError(type, expressionStartPosition);
}
return factor;
}
std::unique_ptr<SubExpressionNode> SubExpression(
const gd::String &type, const gd::String &objectName) {
return std::move(
gd::make_unique<SubExpressionNode>(Expression(type, objectName)));
};
std::unique_ptr<IdentifierOrFunctionOrEmptyNode> Identifier(
const gd::String &type) {
size_t identifierStartPosition = GetCurrentPosition();
gd::String name = ReadIdentifierName();
SkipWhitespace();
if (IsNamespaceSeparator()) {
SkipNamespaceSeparator();
name += NAMESPACE_SEPARATOR;
name += ReadIdentifierName();
}
if (IsAnyChar("(")) {
SkipChar();
return FreeFunction(type, name, identifierStartPosition);
} else if (IsAnyChar(DOT)) {
SkipChar();
return ObjectFunctionOrBehaviorFunction(
type, name, identifierStartPosition);
} 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);
} else if (type == "number") {
identifier->diagnostic = RaiseTypeError(_("You must enter a number."),
identifierStartPosition);
} else if (!gd::ParameterMetadata::IsObject(type)) {
identifier->diagnostic = RaiseTypeError(
_("You've entered a name, but this type was expected:") + type,
identifierStartPosition);
}
return std::move(identifier);
}
}
std::unique_ptr<VariableNode> Variable(const gd::String &type,
const gd::String &objectName) {
size_t identifierStartPosition = GetCurrentPosition();
gd::String name = ReadIdentifierName();
auto variable = gd::make_unique<VariableNode>(type, name, objectName);
variable->child = VariableAccessorOrVariableBracketAccessor();
return std::move(variable);
}
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
VariableAccessorOrVariableBracketAccessor() {
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
SkipWhitespace();
if (IsAnyChar("[")) {
SkipChar();
child =
gd::make_unique<VariableBracketAccessorNode>(Expression("string"));
if (!IsAnyChar("]")) {
child->diagnostic =
RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
"bracket for each opening bracket."));
}
SkipIfIsAnyChar("]");
child->child = VariableAccessorOrVariableBracketAccessor();
} else if (IsAnyChar(DOT)) {
SkipChar();
SkipWhitespace();
child = gd::make_unique<VariableAccessorNode>(ReadIdentifierName());
child->child = VariableAccessorOrVariableBracketAccessor();
}
return child;
}
std::unique_ptr<FunctionNode> FreeFunction(const gd::String &type,
const gd::String &functionFullName,
size_t functionStartPosition) {
// TODO: error if trying to use function for type != "number" && != "string"
// + Test for it
// This could be improved to have the type passed to a single
// GetExpressionMetadata function.
const gd::ExpressionMetadata &metadata =
type == "number" ? MetadataProvider::GetExpressionMetadata(
platform, functionFullName)
: MetadataProvider::GetStrExpressionMetadata(
platform, functionFullName);
auto parametersAndError = Parameters(metadata.parameters);
auto function = gd::make_unique<FunctionNode>(
type, std::move(parametersAndError.first), metadata, functionFullName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic = ValidateFunction(*function, functionStartPosition);
return std::move(function);
}
std::unique_ptr<FunctionOrEmptyNode> ObjectFunctionOrBehaviorFunction(
const gd::String &type,
const gd::String &objectName,
size_t functionStartPosition) {
gd::String objectFunctionOrBehaviorName = ReadIdentifierName();
SkipWhitespace();
if (IsNamespaceSeparator()) {
SkipNamespaceSeparator();
return BehaviorFunction(type,
objectName,
objectFunctionOrBehaviorName,
functionStartPosition);
} else if (IsAnyChar("(")) {
SkipChar();
gd::String objectType =
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName);
// This could be improved to have the type passed to a single
// GetExpressionMetadata function.
const gd::ExpressionMetadata &metadata =
type == "number"
? MetadataProvider::GetObjectExpressionMetadata(
platform, objectType, objectFunctionOrBehaviorName)
: MetadataProvider::GetObjectStrExpressionMetadata(
platform, objectType, objectFunctionOrBehaviorName);
auto parametersAndError = Parameters(metadata.parameters, objectName);
auto function =
gd::make_unique<FunctionNode>(type,
objectName,
std::move(parametersAndError.first),
metadata,
objectFunctionOrBehaviorName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic =
ValidateFunction(*function, functionStartPosition);
return std::move(function);
}
auto node = gd::make_unique<EmptyNode>(type);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis (for an object expression), or double colon "
"(::) was expected (for a behavior expression)."));
return std::move(node);
}
std::unique_ptr<FunctionOrEmptyNode> BehaviorFunction(
const gd::String &type,
const gd::String &objectName,
const gd::String &behaviorName,
size_t functionStartPosition) {
gd::String functionName = ReadIdentifierName();
SkipWhitespace();
if (IsAnyChar("(")) {
SkipChar();
gd::String behaviorType = GetTypeOfBehavior(
globalObjectsContainer, objectsContainer, behaviorName);
// This could be improved to have the type passed to a single
// GetExpressionMetadata function.
const gd::ExpressionMetadata &metadata =
type == "number" ? MetadataProvider::GetBehaviorExpressionMetadata(
platform, behaviorType, functionName)
: MetadataProvider::GetBehaviorStrExpressionMetadata(
platform, behaviorType, functionName);
auto parametersAndError =
Parameters(metadata.parameters, objectName, behaviorName);
auto function =
gd::make_unique<FunctionNode>(type,
objectName,
behaviorName,
std::move(parametersAndError.first),
metadata,
functionName);
function->diagnostic = std::move(parametersAndError.second);
if (!function->diagnostic)
function->diagnostic =
ValidateFunction(*function, functionStartPosition);
return std::move(function);
} else {
auto node = gd::make_unique<EmptyNode>(type);
node->diagnostic = RaiseSyntaxError(
_("An opening parenthesis was expected here to call a function."));
return std::move(node);
}
}
std::pair<std::vector<std::unique_ptr<ExpressionNode>>,
std::unique_ptr<gd::ExpressionParserError>>
Parameters(std::vector<gd::ParameterMetadata> parameterMetadata,
const gd::String &objectName = "",
const gd::String &behaviorName = "") {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
// By convention, object is always the first parameter, and behavior the
// second one.
size_t parameterIndex =
WrittenParametersFirstIndex(objectName, behaviorName);
while (!IsEndReached()) {
SkipWhitespace();
if (IsAnyChar(")")) {
SkipChar();
return std::make_pair(std::move(parameters), nullptr);
} else {
if (parameterIndex < parameterMetadata.size()) {
const gd::String &type = parameterMetadata[parameterIndex].GetType();
if (parameterMetadata[parameterIndex].IsCodeOnly()) {
// Do nothing, code only parameters are not written in expressions.
} else if (gd::ParameterMetadata::IsExpression("number", type)) {
parameters.push_back(Expression("number"));
} else if (gd::ParameterMetadata::IsExpression("string", type)) {
parameters.push_back(Expression("string"));
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
parameters.push_back(Expression(type, objectName));
} else if (gd::ParameterMetadata::IsObject(type)) {
parameters.push_back(Expression(type));
} else {
size_t parameterStartPosition = GetCurrentPosition();
parameters.push_back(Expression("unknown"));
parameters.back()->diagnostic =
gd::make_unique<ExpressionParserError>(
"unknown_parameter_type",
_("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix "
"this issue"),
parameterStartPosition,
GetCurrentPosition());
}
} else {
size_t parameterStartPosition = GetCurrentPosition();
parameters.push_back(Expression("unknown"));
parameters.back()
->diagnostic = gd::make_unique<ExpressionParserError>(
"extra_parameter",
_("This parameter was not expected by this expression. Remove it "
"or verify that you've entered the proper expression name."),
parameterStartPosition,
GetCurrentPosition());
}
SkipWhitespace();
SkipIfIsAnyChar(PARAMETERS_SEPARATOR);
parameterIndex++;
}
}
return std::make_pair(
std::move(parameters),
RaiseSyntaxError(_("The list of parameters is not terminated. Add a "
"closing parenthesis to end the parameters.")));
}
///@}
/** \name Validators
* Return a diagnostic if any error is found
*/
///@{
std::unique_ptr<ExpressionParserDiagnostic> ValidateFunction(
const gd::FunctionNode &function, size_t functionStartPosition);
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator(
const gd::String &type, gd::String::value_type operatorChar) {
if (type == "number") {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."),
GetCurrentPosition());
} else if (type == "string") {
if (operatorChar == '+') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts."),
GetCurrentPosition());
} 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."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -, /, *) can't be used in variable names. Remove "
"the operator from the variable name."),
GetCurrentPosition());
}
return gd::make_unique<ExpressionParserDiagnostic>();
}
std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator(
const gd::String &type, gd::String::value_type operatorChar) {
if (type == "number") {
if (operatorChar == '+' || operatorChar == '-') {
return gd::make_unique<ExpressionParserDiagnostic>();
}
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an \"unary\" operator that is not supported. Operator "
"should be "
"either + or -."),
GetCurrentPosition());
} else if (type == "string") {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("You've used an operator that is not supported. Only + can be used "
"to concatenate texts, and must be placed between two texts (or "
"expressions)."),
GetCurrentPosition());
} 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."),
GetCurrentPosition());
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
return gd::make_unique<ExpressionParserError>(
"invalid_operator",
_("Operators (+, -) can't be used in variable names. Remove "
"the operator from the variable name."),
GetCurrentPosition());
}
return gd::make_unique<ExpressionParserDiagnostic>();
}
///@}
/** \name Parsing tokens
* Read tokens or characters
*/
///@{
void SkipChar() { currentPosition++; }
void SkipWhitespace() {
while (currentPosition < expression.size() &&
WHITESPACES.find(expression[currentPosition]) != gd::String::npos) {
currentPosition++;
}
}
void SkipIfIsAnyChar(const gd::String &allowedCharacters) {
if (IsAnyChar(allowedCharacters)) {
currentPosition++;
}
}
void SkipNamespaceSeparator() {
// Namespace separator is a special kind of delimiter as it is 2 characters
// long
if (IsNamespaceSeparator()) {
currentPosition += NAMESPACE_SEPARATOR.size();
}
}
bool IsAnyChar(const gd::String &allowedCharacters) {
if (currentPosition < expression.size() &&
allowedCharacters.find(expression[currentPosition]) !=
gd::String::npos) {
return true;
}
return false;
}
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) {
return true;
}
return false;
}
bool IsNamespaceSeparator() {
// Namespace separator is a special kind of delimiter as it is 2 characters
// long
return (currentPosition + NAMESPACE_SEPARATOR.size() <= expression.size() &&
expression.substr(currentPosition, NAMESPACE_SEPARATOR.size()) ==
NAMESPACE_SEPARATOR);
}
bool IsEndReached() { return currentPosition >= expression.size(); }
gd::String ReadIdentifierName() {
gd::String name;
while (currentPosition < expression.size() &&
(IsIdentifierAllowedChar()
// Allow whitespace in identifier name for compatibility
|| expression[currentPosition] == ' ')) {
name += expression[currentPosition];
currentPosition++;
}
// 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);
}
return name;
}
std::unique_ptr<TextNode> ReadText();
std::unique_ptr<NumberNode> ReadNumber();
std::unique_ptr<EmptyNode> ReadUntilWhitespace(gd::String type) {
gd::String text;
while (currentPosition < expression.size() &&
WHITESPACES.find(expression[currentPosition]) == gd::String::npos) {
text += expression[currentPosition];
currentPosition++;
}
return gd::make_unique<EmptyNode>(type, text);
}
std::unique_ptr<EmptyNode> ReadUntilEnd(gd::String type) {
gd::String text;
while (currentPosition < expression.size()) {
text += expression[currentPosition];
currentPosition++;
}
return gd::make_unique<EmptyNode>(type, text);
}
size_t GetCurrentPosition() { return currentPosition; }
gd::String::value_type GetCurrentChar() {
if (currentPosition < expression.size()) {
return expression[currentPosition];
}
return '\n'; // Should not arise, unless GetCurrentChar was called when
// IsEndReached() is true (which is a logical error).
}
///@}
/** \name Raising errors
* Helpers to attach errors to nodes
*/
///@{
std::unique_ptr<ExpressionParserError> RaiseSyntaxError(
const gd::String &message) {
return std::move(gd::make_unique<ExpressionParserError>(
"syntax_error", message, GetCurrentPosition()));
}
std::unique_ptr<ExpressionParserError> RaiseTypeError(
const gd::String &message, size_t beginningPosition) {
return std::move(gd::make_unique<ExpressionParserError>(
"type_error", message, beginningPosition, GetCurrentPosition()));
}
std::unique_ptr<ExpressionParserError> RaiseEmptyError(
const gd::String &type, size_t beginningPosition) {
gd::String message;
if (type == "number") {
message = _("You must enter a number or a valid expression call.");
} else if (type == "string") {
message = _(
"You must enter a text (between quotes) or a valid expression call.");
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
message = _("You must enter a variable name.");
} else if (gd::ParameterMetadata::IsObject(type)) {
message = _("You must enter a valid object name.");
} else {
message = _("You must enter a valid expression.");
}
return std::move(RaiseTypeError(message, beginningPosition));
}
///@}
static size_t WrittenParametersFirstIndex(const gd::String &objectName,
const gd::String &behaviorName) {
// By convention, object is always the first parameter, and behavior the
// second one.
return !behaviorName.empty() ? 2 : (!objectName.empty() ? 1 : 0);
}
gd::String expression;
std::size_t currentPosition;
const gd::Platform &platform;
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;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONPARSER2_H

View File

@@ -0,0 +1,10 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionParser2Node.h"
namespace gd {
gd::String ExpressionParserDiagnostic::noMessage = "";
}

View File

@@ -0,0 +1,294 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONPARSER2NODES_H
#define GDCORE_EXPRESSIONPARSER2NODES_H
#include <memory>
#include <vector>
#include "ExpressionParser2NodeWorker.h"
#include "GDCore/String.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief A diagnostic that can be attached to a gd::ExpressionNode.
*/
struct ExpressionParserDiagnostic {
virtual bool IsError() { return false; }
virtual const gd::String &GetMessage() { return noMessage; }
virtual size_t GetStartPosition() { return 0; }
virtual size_t GetEndPosition() { return 0; }
private:
static gd::String noMessage;
};
/**
* \brief An error that can be attached to a gd::ExpressionNode.
*/
struct ExpressionParserError : public ExpressionParserDiagnostic {
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
size_t position_)
: type(type_),
message(message_),
startPosition(position_),
endPosition(position_){};
ExpressionParserError(const gd::String &type_,
const gd::String &message_,
size_t startPosition_,
size_t endPosition_)
: type(type_),
message(message_),
startPosition(startPosition_),
endPosition(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; }
private:
gd::String type;
gd::String message;
size_t startPosition;
size_t endPosition;
};
/**
* \brief The base node, from which all nodes in the tree of
* an expression inherits from.
*/
struct ExpressionNode {
virtual ~ExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker){};
std::unique_ptr<ExpressionParserDiagnostic> diagnostic;
};
struct SubExpressionNode : public ExpressionNode {
SubExpressionNode(std::unique_ptr<ExpressionNode> expression_)
: expression(std::move(expression_)){};
virtual ~SubExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitSubExpressionNode(*this);
};
std::unique_ptr<ExpressionNode> expression;
};
/**
* \brief An operator node. For example: "lhs + rhs".
*/
struct OperatorNode : public ExpressionNode {
virtual ~OperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitOperatorNode(*this);
};
std::unique_ptr<ExpressionNode> leftHandSide;
std::unique_ptr<ExpressionNode> rightHandSide;
gd::String::value_type op;
};
/**
* \brief A unary operator node. For example: "-2".
*/
struct UnaryOperatorNode : public ExpressionNode {
UnaryOperatorNode(gd::String::value_type op_) : op(op_){};
virtual ~UnaryOperatorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitUnaryOperatorNode(*this);
};
std::unique_ptr<ExpressionNode> factor;
gd::String::value_type op;
};
/**
* \brief A number node. For example: "123".
*/
struct NumberNode : public ExpressionNode {
NumberNode(const gd::String &number_) : number(number_){};
virtual ~NumberNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitNumberNode(*this);
};
gd::String number;
};
/**
* \brief A text node. For example: "Hello World".
*/
struct TextNode : public ExpressionNode {
TextNode(const gd::String &text_) : text(text_){};
virtual ~TextNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitTextNode(*this);
};
gd::String text;
};
struct VariableAccessorOrVariableBracketAccessorNode : public ExpressionNode {
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode> child;
};
/**
* \brief A variable, potentially with accessor to its children.
*
* Example: MyVariable or MyVariable.MyChildren
*
* \see gd::VariableAccessorNode
* \see gd::VariableBracketAccessorNode
*/
struct VariableNode : public ExpressionNode {
VariableNode(const gd::String &type_,
const gd::String &name_,
const gd::String &objectName_)
: type(type_), name(name_), objectName(objectName_){};
virtual ~VariableNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableNode(*this);
};
gd::String type;
gd::String name;
gd::String objectName;
std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
child; // Can be nullptr if no accessor
};
/**
* \brief A bracket accessor of a variable. Example: MyChild
* in MyVariable.MyChild
*/
struct VariableAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableAccessorNode(const gd::String &name_) : name(name_){};
virtual ~VariableAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableAccessorNode(*this);
};
gd::String name;
};
/**
* \brief A bracket accessor of a variable. Example: ["MyChild"]
* (in MyVariable["MyChild"]).
*/
struct VariableBracketAccessorNode
: public VariableAccessorOrVariableBracketAccessorNode {
VariableBracketAccessorNode(std::unique_ptr<ExpressionNode> expression_)
: expression(std::move(expression_)){};
virtual ~VariableBracketAccessorNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitVariableBracketAccessorNode(*this);
};
std::unique_ptr<ExpressionNode> expression;
};
struct IdentifierOrFunctionOrEmptyNode : public ExpressionNode {};
/**
* \brief An identifier node, usually representing an object.
*/
struct IdentifierNode : public IdentifierOrFunctionOrEmptyNode {
IdentifierNode(const gd::String &identifierName_, const gd::String &type_)
: identifierName(identifierName_), type(type_){};
virtual ~IdentifierNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitIdentifierNode(*this);
};
gd::String identifierName;
gd::String type;
};
struct FunctionOrEmptyNode : public IdentifierOrFunctionOrEmptyNode {
virtual ~FunctionOrEmptyNode(){};
void Visit(ExpressionParser2NodeWorker &worker) override{};
};
/**
* \brief A function node. For example: "MyExtension::MyFunction(1, 2)".
*/
struct FunctionNode : public FunctionOrEmptyNode {
FunctionNode(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_)
: 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_)
: type(type_),
objectName(objectName_),
behaviorName(behaviorName_),
parameters(std::move(parameters_)),
expressionMetadata(expressionMetadata_),
functionName(functionName_){};
virtual ~FunctionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitFunctionNode(*this);
};
gd::String type; // This could be removed if the type ("string" or "number")
// was stored in ExpressionMetadata.
gd::String objectName;
gd::String behaviorName;
std::vector<std::unique_ptr<ExpressionNode>> parameters;
const ExpressionMetadata &expressionMetadata;
gd::String functionName;
};
/**
* \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 {
EmptyNode(const gd::String &type_, const gd::String &text_ = "")
: type(type_), text(text_){};
virtual ~EmptyNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker) {
worker.OnVisitEmptyNode(*this);
};
gd::String type;
gd::String text;
};
} // namespace gd
#endif

View File

@@ -0,0 +1,124 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONNODEPRINTER_H
#define GDCORE_EXPRESSIONNODEPRINTER_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 Print the expression corresponding to a set of nodes
* (i.e: this is doing the inverse operation of gd::ExpressionParser2).
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParser2NodePrinter
: public ExpressionParser2NodeWorker {
public:
ExpressionParser2NodePrinter(){};
virtual ~ExpressionParser2NodePrinter(){};
static gd::String PrintNode(gd::ExpressionNode& node) {
gd::ExpressionParser2NodePrinter printer;
node.Visit(printer);
return printer.GetOutput();
}
/**
* \brief Get the string corresponding to the expression nodes.
*/
const gd::String& GetOutput() { return output; };
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
output += "(";
node.expression->Visit(*this);
output += ")";
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
if (node.op == ' ') {
// There is no "space" operator. If it's there, it's because
// an operator could not be found (that's an error). Add only
// a whitespace between terms.
output += " ";
} else {
output += " ";
output.push_back(node.op);
output += " ";
}
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
output.push_back(node.op);
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override { output += node.number; }
void OnVisitTextNode(TextNode& node) override {
output +=
"\"" +
node.text.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") +
"\"";
}
void OnVisitVariableNode(VariableNode& node) override {
output += node.name;
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
output += "." + node.name;
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
output += "[";
node.expression->Visit(*this);
output += "]";
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
output += node.identifierName;
}
void OnVisitFunctionNode(FunctionNode& node) override {
if (!node.behaviorName.empty()) {
output +=
node.objectName + "." + node.behaviorName + "::" + node.functionName;
} else if (!node.objectName.empty()) {
output += node.objectName + "." + node.functionName;
} else {
output += node.functionName;
}
output += "(";
bool isFirst = true;
for (auto& parameterNode : node.parameters) {
if (!isFirst) output += ", ";
parameterNode->Visit(*this);
isFirst = false;
}
output += ")";
}
void OnVisitEmptyNode(EmptyNode& node) override { output += node.text; }
private:
gd::String output;
};
} // namespace gd
#endif // GDCORE_EXPRESSIONNODEPRINTER_H

View File

@@ -0,0 +1,10 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionParser2NodeWorker.h"
namespace gd {
ExpressionParser2NodeWorker::~ExpressionParser2NodeWorker(){};
}

View File

@@ -0,0 +1,71 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXPRESSIONPARSER2NODEWORKER_H
#define GDCORE_EXPRESSIONPARSER2NODEWORKER_H
namespace gd {
class ExpressionNode;
class SubExpressionNode;
class OperatorNode;
class UnaryOperatorNode;
class NumberNode;
class TextNode;
class VariableNode;
class VariableAccessorNode;
class VariableBracketAccessorNode;
class IdentifierOrFunctionOrEmptyNode;
class IdentifierNode;
class FunctionOrEmptyNode;
class FunctionNode;
class EmptyNode;
} // namespace gd
namespace gd {
/**
* \brief The interface for any worker class ("visitor" pattern)
* that want to interact with the nodes of a parsed expression.
*
* \see gd::ExpressionParser2
* \see gd::ExpressionNode
*/
class GD_CORE_API ExpressionParser2NodeWorker {
friend class ExpressionNode;
friend class SubExpressionNode;
friend class OperatorNode;
friend class UnaryOperatorNode;
friend class NumberNode;
friend class TextNode;
friend class VariableNode;
friend class VariableAccessorNode;
friend class VariableBracketAccessorNode;
friend class IdentifierOrFunctionOrEmptyNode;
friend class IdentifierNode;
friend class FunctionOrEmptyNode;
friend class FunctionNode;
friend class EmptyNode;
public:
virtual ~ExpressionParser2NodeWorker();
protected:
virtual void OnVisitSubExpressionNode(SubExpressionNode& node) = 0;
virtual void OnVisitOperatorNode(OperatorNode& node) = 0;
virtual void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) = 0;
virtual void OnVisitNumberNode(NumberNode& node) = 0;
virtual void OnVisitTextNode(TextNode& node) = 0;
virtual void OnVisitVariableNode(VariableNode& node) = 0;
virtual void OnVisitVariableAccessorNode(VariableAccessorNode& node) = 0;
virtual void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) = 0;
virtual void OnVisitIdentifierNode(IdentifierNode& node) = 0;
virtual void OnVisitFunctionNode(FunctionNode& node) = 0;
virtual void OnVisitEmptyNode(EmptyNode& node) = 0;
};
} // namespace gd
#endif

View File

@@ -134,7 +134,7 @@ gd::String VariableParser::SkipStringExpression() {
++currentPositionIt;
}
// End of the expression reached ( So expression is invalid by the way )
// End of the expression reached (so expression is invalid by the way)
return stringExpression;
}

View File

@@ -40,13 +40,14 @@ gd::VariableParserCallbacks VariableCodeGenerationCallbacks callbacks(output,
gd::VariableParser parser(parameter);
if ( !parser.Parse(callbacks) )
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter <<
endl; \endcode
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
* where e = nothing (end of expression), StringExpression = A valid string
expression and
* S is the start.
*

View File

@@ -16,11 +16,75 @@
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/VersionWrapper.h"
using namespace std;
namespace {
bool AddQuotesToFunctionCall(gd::String& expressionStr,
const gd::String& functionName) {
bool changedSomething = false;
size_t functionCallPos = expressionStr.find(functionName + "(");
while (functionCallPos != gd::String::npos) {
size_t pos = functionCallPos + functionName.size() + 1;
// Skip whitespace
while (pos < expressionStr.size() && expressionStr[pos] == ' ') pos++;
if (pos < expressionStr.size()) {
changedSomething = true;
// Insert the first quote
expressionStr.insert(pos, "\"");
pos++;
// Escape the argument
while (pos < expressionStr.size() && expressionStr[pos] != ')') {
if (expressionStr[pos] == '"') {
expressionStr.insert(pos,
"\\"); // Insert a backslash to escape the quote
pos++;
}
pos++;
}
// Insert the last quote
if (pos < expressionStr.size() && expressionStr[pos] == ')') {
expressionStr.insert(pos, "\"");
pos++;
}
}
functionCallPos = expressionStr.find(functionName + "(", pos + 1);
}
return changedSomething;
}
} // namespace
namespace gd {
void EventsListSerialization::UpdateInstructionsFromGD4097(
gd::Project& project, gd::InstructionsList& list) {
for (std::size_t i = 0; i < list.size(); ++i) {
gd::Instruction& instr = list[i];
for (std::size_t j = 0; j < instr.GetParametersCount(); ++j) {
gd::String expressionStr = instr.GetParameter(j).GetPlainString();
bool changedSomething = false;
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointX");
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointY");
if (changedSomething) {
std::cout << "(Debug) Converted \""
<< instr.GetParameter(j).GetPlainString() << "\" to \""
<< expressionStr << "\"" << std::endl;
instr.SetParameter(j, gd::Expression(expressionStr));
}
}
}
}
void EventsListSerialization::UpdateInstructionsFromGD31x(
gd::Project& project, gd::InstructionsList& list) {
for (std::size_t i = 0; i < list.size(); ++i) {
@@ -256,6 +320,18 @@ void gd::EventsListSerialization::UnserializeInstructionsFrom(
if (project.GetLastSaveGDMajorVersion() < 3)
UpdateInstructionsFromGD2x(
project, instructions, elem.HasChild("action", "Action"));
// Compatibility with GD <= 4.0.97
if (VersionWrapper::IsOlderOrEqual(project.GetLastSaveGDMajorVersion(),
project.GetLastSaveGDMinorVersion(),
project.GetLastSaveGDBuildVersion(),
0,
4,
0,
97,
0)) {
UpdateInstructionsFromGD4097(project, instructions);
}
// end of compatibility code
}

View File

@@ -72,6 +72,16 @@ class GD_CORE_API EventsListSerialization {
*/
static void UpdateInstructionsFromGD31x(gd::Project& project,
gd::InstructionsList& list);
/**
* \brief Internal method called when opening events created with GD <= 4.0.97
*
* PointX/PointY would previously take a name of a point without quotes.
* This is not providing any value and inconsistent with everything else.
* Add quotes around them.
*/
static void UpdateInstructionsFromGD4097(gd::Project& project,
gd::InstructionsList& list);
};
} // namespace gd

View File

@@ -176,15 +176,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddCodeOnlyParameter("currentScene", "")
.MarkAsAdvanced();
obj.AddAction("AddForceXY",
_("Add a force"),
_("Add a force to an object. The object will move according to "
"all of the forces it has."),
_("Add to _PARAM0_ _PARAM3_ force of _PARAM1_ p/s on X axis and "
"_PARAM2_ p/s on Y axis"),
_("Movement"),
"res/actions/force24.png",
"res/actions/force.png")
obj.AddAction(
"AddForceXY",
_("Add a force"),
_("Add a force to an object. The object will move according to "
"all of the forces it has."),
_("Add to _PARAM0_ _PARAM3_ force of _PARAM1_ p/s on X axis and "
"_PARAM2_ p/s on Y axis"),
_("Movement"),
"res/actions/force24.png",
"res/actions/force.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Speed on X axis (in pixels per second)"))
@@ -551,13 +552,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("yesorno", _("Activate?"))
.MarkAsAdvanced();
obj.AddAction("AddForceVers",
_("Add a force to move toward an object"),
_("Add a force to an object to make it move toward another."),
_("Move _PARAM0_ to _PARAM1_ with _PARAM3_ force of _PARAM2_ pixels"),
_("Movement"),
"res/actions/forceVers24.png",
"res/actions/forceVers.png")
obj.AddAction(
"AddForceVers",
_("Add a force to move toward an object"),
_("Add a force to an object to make it move toward another."),
_("Move _PARAM0_ to _PARAM1_ with _PARAM3_ force of _PARAM2_ pixels"),
_("Movement"),
"res/actions/forceVers24.png",
"res/actions/forceVers.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target Object"))
@@ -640,6 +642,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectList", _("Objects"))
.AddParameter("yesorno",
_("Ignore objects that are touching each other on their "
"edges, but are not overlapping (default: no)"),
"",
true)
.SetDefaultValue("no")
.MarkAsSimple();
obj.AddCondition("CollisionPoint",
@@ -654,13 +662,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Y position of the point"))
.MarkAsSimple();
obj.AddCondition("ObjectTimer",
_("Value of a timer"),
_("Test the elapsed time of a timer."),
_("The timer _PARAM1_ of _PARAM0_ is greater than _PARAM2_ seconds"),
_("Timers"),
"res/conditions/timer24.png",
"res/conditions/timer.png")
obj.AddCondition(
"ObjectTimer",
_("Value of a timer"),
_("Test the elapsed time of a timer."),
_("The timer _PARAM1_ of _PARAM0_ is greater than _PARAM2_ seconds"),
_("Timers"),
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("expression", _("Time in seconds"));
@@ -863,10 +872,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"));
obj.AddExpression("ObjectTimerElapsedTime",
_("Timer value"),
_("Value of a timer"),
_("Timers"),
"res/actions/time.png")
_("Timer value"),
_("Value of a timer"),
_("Timers"),
"res/actions/time.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
@@ -911,8 +920,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension
.AddAction("AjoutObjConcern",
_("Pick all objects"),
_("Pick all objects with this name."),
_("Pick all _PARAM1_"),
_("Pick all the specified objects. When you pick all objects, "
"the next conditions and actions of this event work on all "
"of them."),
_("Pick all _PARAM1_ objects"),
_("Objects"),
"res/actions/add24.png",
"res/actions/add.png")
@@ -921,13 +932,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
extension
.AddAction("AjoutHasard",
_("Pick a random object"),
_("Pick only one object with this name, among all"),
_("Pick a random _PARAM1_"),
_("Objects"),
"res/actions/ajouthasard24.png",
"res/actions/ajouthasard.png")
.AddAction(
"AjoutHasard",
_("Pick a random object"),
_("Pick one object from all the specified objects. When an object "
"is picked, the next conditions and actions of this event work "
"only on that object."),
_("Pick a random _PARAM1_"),
_("Objects"),
"res/actions/ajouthasard24.png",
"res/actions/ajouthasard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectList", _("Object"))
.MarkAsSimple();
@@ -977,25 +991,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
extension
.AddCondition("AjoutObjConcern",
_("Pick all objects"),
_("Pick all objects with this name."),
_("Pick all _PARAM1_"),
_("Objects"),
"res/conditions/add24.png",
"res/conditions/add.png")
.AddCondition(
"AjoutObjConcern",
_("Pick all objects"),
_("Pick all the specified objects. When you pick all objects, "
"the next conditions and actions of this event work on all "
"of them."),
_("Pick all _PARAM1_ objects"),
_("Objects"),
"res/conditions/add24.png",
"res/conditions/add.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectList", _("Object"))
.MarkAsAdvanced();
extension
.AddCondition("AjoutHasard",
_("Pick a random object"),
_("Pick only one object with this name, among all"),
_("Pick a random _PARAM1_"),
_("Objects"),
"res/conditions/ajouthasard24.png",
"res/conditions/ajouthasard.png")
.AddCondition(
"AjoutHasard",
_("Pick a random object"),
_("Pick one object from all the specified objects. When an object "
"is picked, the next conditions and actions of this event work "
"only on that object."),
_("Pick a random _PARAM1_"),
_("Objects"),
"res/conditions/ajouthasard24.png",
"res/conditions/ajouthasard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectList", _("Object"))
.MarkAsSimple();
@@ -1004,9 +1024,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddCondition(
"PickNearest",
_("Pick nearest object"),
_("Among the objects, pick the one that is nearest (or furthest if "
"condition is inverted) from the specified position."),
_("Pick nearest _PARAM0_ to _PARAM1_;_PARAM2_"),
_("Pick the object of this type that is nearest to the specified "
"position. If the condition is inverted, the object farthest from "
"the specified position is picked instead."),
_("Pick the _PARAM0_ that is nearest to _PARAM1_;_PARAM2_"),
_("Objects"),
"res/conditions/distance24.png",
"res/conditions/distance.png")
@@ -1017,13 +1038,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsSimple();
extension
.AddCondition("NbObjet",
_("Objects count"),
_("Compare the number of picked objects"),
_("The number of _PARAM0_ is _PARAM1__PARAM2_"),
_("Objects"),
"res/conditions/nbObjet24.png",
"res/conditions/nbObjet.png")
.AddCondition(
"NbObjet",
_("Number of objects"),
_("Count how many of the specified objects are currently picked, and "
"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_"),
_("Objects"),
"res/conditions/nbObjet24.png",
"res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("Value to test"))
@@ -1046,6 +1071,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectList", _("Object"))
.AddParameter("objectList", _("Object"))
.AddCodeOnlyParameter("conditionInverted", "")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("yesorno",
_("Ignore objects that are touching each other on their "
"edges, but are not overlapping (default: no)"),
"",
true)
.SetDefaultValue("no")
.MarkAsSimple();
extension
@@ -1119,12 +1151,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.MarkAsAdvanced();
extension
.AddExpression(
"Count",
_("Number of objects"),
_("Count the number of the specified objects currently picked"),
_("Objects"),
"res/conditions/nbObjet.png")
.AddExpression("Count",
_("Number of objects"),
_("Count the number of the specified objects being "
"currently picked in the event"),
_("Objects"),
"res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"));
obj.AddStrExpression("ObjectName",

View File

@@ -13,9 +13,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("BuiltinFile",
_("Storage and files"),
_("Storage"),
_("Built-in extension providing functions "
"to store data and manipulate files."),
"to store data."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/storage");
@@ -26,55 +26,55 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"GroupExists",
_("Existence of a group"),
_("Check if an element (example : PlayerState/CurrentLevel) exists "
"in the file.\nSpaces are forbidden in element names."),
_("_PARAM1_ exists in file _PARAM0_"),
"in the stored data.\nSpaces are forbidden in element names."),
_("_PARAM1_ exists in storage _PARAM0_"),
_("Storage"),
"res/conditions/fichier24.png",
"res/conditions/fichier.png")
.AddParameter("file", _("Filename"))
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.MarkAsAdvanced();
extension
.AddAction(
"LoadFile",
_("Load a structured file in memory"),
_("This action loads the structured file in memory, so you can write "
"and read it.\nYou can open and write without using this action, "
"but it will be slower.\nIf you use this action, do not forget to "
"unload the file from memory.\n\nFor the native platform, the file "
"format is XML."),
_("Load structured file _PARAM0_ in memory"),
_("Load a storage in memory"),
_("This action loads the specified storage in memory, so you can "
"write and read it.\nYou can open and write without using this "
"action, but it will be slower.\nIf you use this action, do not "
"forget to unload the storage from memory."),
_("Load storage _PARAM0_ in memory"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddParameter("string", _("Storage name"))
.MarkAsAdvanced();
extension
.AddAction("UnloadFile",
_("Close a structured file"),
_("This action closes the structured file previously loaded "
_("Close a storage"),
_("This action closes the structured data previously loaded "
"in memory, saving all changes made."),
_("Close structured file _PARAM0_"),
_("Close structured data _PARAM0_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddParameter("string", _("Storage name"))
.MarkAsAdvanced();
extension
.AddAction("EcrireFichierExp",
_("Write a value"),
_("Write the result of the expression in the file, in the "
"specified element.\nSpecify the structure leading to the "
"element using / (example : Root/Level/Current)\nSpaces are "
"forbidden in element names."),
_("Write _PARAM2_ in _PARAM1_ of file _PARAM0_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddAction(
"EcrireFichierExp",
_("Write a value"),
_("Write the result of the expression in the stored data, in the "
"specified element.\nSpecify the structure leading to the "
"element using / (example : Root/Level/Current)\nSpaces are "
"forbidden in element names."),
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddParameter("expression", _("Expression"));
@@ -82,14 +82,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddAction(
"EcrireFichierTxt",
_("Write a text"),
_("Write the text in the file, in the specified element.\nSpecify "
_("Write the text in the specified storage, in the specified "
"element.\nSpecify "
"the structure leading to the element using / (example : "
"Root/Level/Current)\nSpaces are forbidden in element names."),
_("Write _PARAM2_ in _PARAM1_ of file _PARAM0_"),
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddParameter("string", _("Text"));
@@ -101,11 +102,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Read _PARAM1_ from file _PARAM0_ and store value in _PARAM3_"),
_("Read _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("scenevar", _("Scene variables"));
@@ -118,11 +119,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Read _PARAM1_ from file _PARAM0_ and store as text in _PARAM3_"),
_("Read _PARAM1_ from storage _PARAM0_ and store as text in "
"_PARAM3_"),
_("Storage"),
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("file", _("File"))
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("scenevar", _("Scene variables"));
@@ -131,36 +133,37 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddAction("DeleteGroupFichier",
_("Delete an element"),
_("This action deletes the specified element from the "
"structured file.\nSpecify the structure leading to the "
"specified storage.\nSpecify the structure leading to the "
"element using / (example : Root/Level/Current)\nSpaces are "
"forbidden in element names."),
_("Delete _PARAM1_ from the file _PARAM0_"),
_("Delete _PARAM1_ from storage _PARAM0_"),
_("Storage"),
"res/actions/delete24.png",
"res/actions/delete.png")
.AddParameter("file", _("Filename"))
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.MarkAsAdvanced();
extension
.AddAction("DeleteFichier",
_("Delete a file"),
_("Delete the file."),
_("Delete the file _PARAM0_"),
_("Files"),
"res/actions/delete24.png",
"res/actions/delete.png")
.AddParameter("file", _("Filename"));
.AddAction(
"DeleteFichier",
_("Clear a storage"),
_("Clear the specified storage, removing all data saved in it."),
_("Delete storage _PARAM0_"),
_("Storage"),
"res/actions/delete24.png",
"res/actions/delete.png")
.AddParameter("string", _("Storage name"));
extension
.AddCondition("FileExists",
_("A file exists"),
_("Test if the file exists."),
_("File _PARAM0_ exists"),
_("Files"),
_("A storage exists"),
_("Test if the specified storage exists."),
_("Storage _PARAM0_ exists"),
_("Storage"),
"res/conditions/fichier24.png",
"res/conditions/fichier.png")
.AddParameter("file", _("Filename"))
.AddParameter("string", _("Storage name"))
.MarkAsAdvanced();
extension

View File

@@ -221,7 +221,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch identifier"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("Camera number (default : 0)"), "", true)

View File

@@ -492,7 +492,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("", _("Name of the point"), "", true);
.AddParameter("string", _("Name of the point"), "", true);
obj.AddExpression("Y",
_("Y position of a point"),
@@ -501,7 +501,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.SetHidden()
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("", _("Name of the point"), "", true);
.AddParameter("string", _("Name of the point"), "", true);
obj.AddExpression("PointX",
_("X position of a point"),
@@ -510,7 +510,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("", _("Name of the point"));
.AddParameter("string", _("Name of the point"));
obj.AddExpression("PointY",
_("Y position of a point"),
@@ -519,7 +519,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"res/actions/position.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("", _("Name of the point"));
.AddParameter("string", _("Name of the point"));
obj.AddExpression("Direc",
_("Direction"),

View File

@@ -61,14 +61,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
.MarkAsAdvanced();
extension
.AddAction("ResetTimer",
_("Start (or reset) a scene timer"),
_("Reset the specified scene timer, if the timer doesn't exist "
"it's created and started."),
_("Reset the timer _PARAM1_"),
_("Timers and time"),
"res/actions/timer24.png",
"res/actions/timer.png")
.AddAction(
"ResetTimer",
_("Start (or reset) a scene timer"),
_("Reset the specified scene timer, if the timer doesn't exist "
"it's created and started."),
_("Reset the timer _PARAM1_"),
_("Timers and time"),
"res/actions/timer24.png",
"res/actions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"));
@@ -197,10 +198,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter(
"",
_("Hour : hour\nMinutes : min\nSeconds : sec\nDay of the month : "
"mday\nMonths since January : mon\nYear since 1900 : year\nDays "
"since sunday :wday\nDays since January 1st : yday"));
"stringWithSelector",
_("Hour: hour - Minutes: min - Seconds: sec - Day of month: "
"mday - Months since January: mon - Year since 1900: year - Days "
"since Sunday: wday - Days since Jan 1st: yday - Timestamp (ms): "
"timestamp\""),
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
"\"yday\", \"timestamp\"]");
#endif
}

View File

@@ -50,7 +50,20 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
optionalObjectType.empty() ? "" : extensionNamespace + optionalObjectType;
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
? (optionalObjectType.empty()
? ""
: extensionNamespace +
optionalObjectType //... so prefix it with the extension
// namespace.
)
: optionalObjectType; // Otherwise don't change anything
// TODO: Assert against optionalObjectType === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
parameters.push_back(info);
return *this;

View File

@@ -9,13 +9,17 @@
#endif
#include <algorithm>
#include "GDCore/CommonTools.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "InstructionMetadata.h"
namespace gd {
InstructionMetadata::InstructionMetadata()
: sentence(_("Unknown or unsupported instruction")),
: sentence(
"Unknown or unsupported instruction"), // Avoid translating this
// string, so that it's safe
// and *fast* to use a
// InstructionMetadata.
canHaveSubInstructions(false),
hidden(true) {}
@@ -65,7 +69,20 @@ InstructionMetadata& InstructionMetadata::AddParameter(
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
optionalObjectType.empty() ? "" : extensionNamespace + optionalObjectType;
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
? (optionalObjectType.empty()
? ""
: extensionNamespace +
optionalObjectType //... so prefix it with the extension
// namespace.
)
: optionalObjectType; // Otherwise don't change anything
// TODO: Assert against optionalObjectType === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
parameters.push_back(info);
return *this;
@@ -94,7 +111,8 @@ void ParameterMetadata::SerializeTo(SerializerElement& element) const {
void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
type = element.GetStringAttribute("type");
supplementaryInformation = element.GetStringAttribute("supplementaryInformation");
supplementaryInformation =
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
description = element.GetStringAttribute("description");
codeOnly = element.GetBoolAttribute("codeOnly");

View File

@@ -146,7 +146,9 @@ class GD_CORE_API ParameterMetadata {
/**
* \brief Return true if the type of the parameter is "object", "objectPtr" or
* "objectList". \see gd::ParameterMetadata::GetType
* "objectList".
*
* \see gd::ParameterMetadata::GetType
*/
static bool IsObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
@@ -155,8 +157,17 @@ class GD_CORE_API ParameterMetadata {
}
/**
* \brief Return true if the type of the parameter is "object", "objectPtr" or
* "objectList". \see gd::ParameterMetadata::GetType
* \brief Return true if the type of the parameter is "behavior".
*
* \see gd::ParameterMetadata::GetType
*/
static bool IsBehavior(const gd::String &parameterType) {
return parameterType == "behavior";
}
/**
* \brief Return true if the type of the parameter is an expression of the
* given type.
*/
static bool IsExpression(const gd::String &type,
const gd::String &parameterType) {
@@ -166,7 +177,10 @@ class GD_CORE_API ParameterMetadata {
} else if (type == "string") {
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis";
parameterType == "joyaxis" || parameterType == "stringWithSelector";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
}
return false;
}

View File

@@ -19,8 +19,8 @@ class PlatformExtension;
namespace gd {
/**
* \brief A container for metadata about an object/behavior/instruction/expression
* and its associated extension.
* \brief A container for metadata about an
* object/behavior/instruction/expression and its associated extension.
*/
template <class T>
class ExtensionAndMetadata {
@@ -34,8 +34,7 @@ class ExtensionAndMetadata {
* \warning Please do not use.
* \private
*/
ExtensionAndMetadata()
: extension(nullptr), metadata(nullptr){};
ExtensionAndMetadata() : extension(nullptr), metadata(nullptr){};
/**
* \brief Get the associated extension.
@@ -296,6 +295,11 @@ class GD_CORE_API MetadataProvider {
gd::String behaviorType,
gd::String name);
static bool IsBadExpressionMetadata(const gd::ExpressionMetadata& metadata) {
return &metadata == &badExpressionMetadata ||
&metadata == &badStrExpressionMetadata;
}
virtual ~MetadataProvider();
private:

View File

@@ -75,6 +75,12 @@ class GD_CORE_API Platform {
* Member functions used to manage the extensions
*/
///@{
/**
* \brief (Re)load platform built-in extensions.
* \note Can be useful if, for example, the user changed the language
* of the editor.
*/
virtual void ReloadBuiltinExtensions(){};
/**
* \brief Must return the name of the function that is used to create an

View File

@@ -19,7 +19,7 @@ class GD_CORE_API PropertyDescriptor {
* value. \param propertyValue The value of the property.
*/
PropertyDescriptor(gd::String propertyValue)
: currentValue(propertyValue), type("string") {}
: currentValue(propertyValue), type("string"), label("") {}
/**
* \brief Empty constructor creating an empty property to be displayed.
@@ -51,6 +51,14 @@ class GD_CORE_API PropertyDescriptor {
return *this;
}
/**
* \brief Change the label displayed in the property grid.
*/
PropertyDescriptor& SetLabel(gd::String label_) {
label = label_;
return *this;
}
/**
* \brief Add an information about the property.
* \note The information are arbitrary and are interpreted by the class
@@ -65,6 +73,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 std::vector<gd::String>& GetExtraInfo() const {
return extraInformation;
}
@@ -74,6 +83,7 @@ class GD_CORE_API PropertyDescriptor {
gd::String
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
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

@@ -9,102 +9,71 @@
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
class CallbacksForListingObjects : public gd::ParserCallbacks {
/**
* \brief Go through the nodes and report any object found.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionObjectsAnalyzer
: public ExpressionParser2NodeWorker {
public:
CallbacksForListingObjects(const gd::Platform& platform_,
const gd::ObjectsContainer& project_,
const gd::ObjectsContainer& layout_,
EventsContext& context_)
: platform(platform_),
project(project_),
layout(layout_),
context(context_){};
virtual ~CallbacksForListingObjects(){};
ExpressionObjectsAnalyzer(EventsContext& context_) : context(context_){};
virtual ~ExpressionObjectsAnalyzer(){};
virtual void OnConstantToken(gd::String text){};
virtual void OnStaticFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
for (std::size_t i = 0;
i < parameters.size() && i < expressionInfo.parameters.size();
++i) {
EventsContextAnalyzer::AnalyzeParameter(platform,
project,
layout,
expressionInfo.parameters[i],
parameters[i],
context);
}
};
virtual void OnObjectFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
for (std::size_t i = 0;
i < parameters.size() && i < expressionInfo.parameters.size();
++i) {
EventsContextAnalyzer::AnalyzeParameter(platform,
project,
layout,
expressionInfo.parameters[i],
parameters[i],
context);
}
};
virtual void OnObjectBehaviorFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
for (std::size_t i = 0;
i < parameters.size() && i < expressionInfo.parameters.size();
++i) {
EventsContextAnalyzer::AnalyzeParameter(platform,
project,
layout,
expressionInfo.parameters[i],
parameters[i],
context);
}
};
virtual bool OnSubMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForListingObjects callbacks(platform, project, layout, context);
gd::ExpressionParser parser(expression.GetPlainString());
parser.ParseMathExpression(platform, project, layout, callbacks);
return true;
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
virtual bool OnSubTextExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForListingObjects callbacks(platform, project, layout, context);
gd::ExpressionParser parser(expression.GetPlainString());
parser.ParseStringExpression(platform, project, layout, callbacks);
return true;
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 {
if (gd::ParameterMetadata::IsObject(node.type)) {
context.AddObjectName(node.identifierName);
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
if (!node.objectName.empty()) {
context.AddObjectName(node.objectName);
}
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
const gd::Platform& platform;
const gd::ObjectsContainer& project;
const gd::ObjectsContainer& layout;
EventsContext& context;
};
@@ -142,15 +111,17 @@ void EventsContextAnalyzer::AnalyzeParameter(
if (ParameterMetadata::IsObject(type)) {
context.AddObjectName(value);
} else if (ParameterMetadata::IsExpression("number", type)) {
CallbacksForListingObjects callbacks(platform, project, layout, context);
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("number", value);
gd::ExpressionParser parser(value);
parser.ParseMathExpression(platform, project, layout, callbacks);
ExpressionObjectsAnalyzer analyzer(context);
node->Visit(analyzer);
} else if (ParameterMetadata::IsExpression("string", type)) {
CallbacksForListingObjects callbacks(platform, project, layout, context);
gd::ExpressionParser2 parser(platform, project, layout);
auto node = parser.ParseExpression("string", value);
gd::ExpressionParser parser(value);
parser.ParseStringExpression(platform, project, layout, callbacks);
ExpressionObjectsAnalyzer analyzer(context);
node->Visit(analyzer);
}
}

View File

@@ -8,200 +8,159 @@
#include <memory>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Parsers/ExpressionParser.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Events/EventsList.h"
using namespace std;
namespace gd {
class CallbacksForRenamingObject : public gd::ParserCallbacks {
/**
* \brief Go through the nodes and change the given object name to a new one.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
public:
CallbacksForRenamingObject(gd::String& plainExpression_,
gd::String oldName_,
gd::String newName_)
: plainExpression(plainExpression_),
newName(newName_),
oldName(oldName_){};
virtual ~CallbacksForRenamingObject(){};
ExpressionObjectRenamer(const gd::String& objectName_,
const gd::String& objectNewName_)
: hasDoneRenaming(false), objectName(objectName_), objectNewName(objectNewName_){};
virtual ~ExpressionObjectRenamer(){};
virtual void OnConstantToken(gd::String text) { plainExpression += text; };
static bool Rename(gd::ExpressionNode & node, const gd::String& objectName, const gd::String& objectNewName) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectRenamer renamer(objectName, objectNewName);
node.Visit(renamer);
virtual void OnStaticFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
// Special case : Function without name is a litteral string.
if (functionName.empty()) {
if (parameters.empty()) return;
plainExpression += "\"" + parameters[0].GetPlainString() + "\"";
return;
return renamer.HasDoneRenaming();
}
gd::String parametersStr;
for (std::size_t i = 0; i < parameters.size(); ++i) {
if (i < expressionInfo.parameters.size() &&
expressionInfo.parameters[i].codeOnly)
continue; // Skip code only parameter which are not included in
// function calls.
if (!parametersStr.empty()) parametersStr += ",";
parametersStr += parameters[i].GetPlainString();
}
plainExpression += functionName + "(" + parametersStr + ")";
};
virtual void OnObjectFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
if (parameters.empty()) return;
gd::String parametersStr;
for (std::size_t i = 1; i < parameters.size(); ++i) {
if (i < expressionInfo.parameters.size() &&
expressionInfo.parameters[i].codeOnly)
continue; // Skip code only parameter which are not included in
// function calls.
if (!parametersStr.empty()) parametersStr += ",";
parametersStr += parameters[i].GetPlainString();
}
plainExpression += (parameters[0].GetPlainString() == oldName
? newName
: parameters[0].GetPlainString()) +
"." + functionName + "(" + parametersStr + ")";
};
virtual void OnObjectBehaviorFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
if (parameters.size() < 2) return;
gd::String parametersStr;
for (std::size_t i = 2; i < parameters.size(); ++i) {
if (i < expressionInfo.parameters.size() &&
expressionInfo.parameters[i].codeOnly)
continue; // Skip code only parameter which are not included in
// function calls.
if (!parametersStr.empty()) parametersStr += ",";
parametersStr += parameters[i].GetPlainString();
}
plainExpression += (parameters[0].GetPlainString() == oldName
? newName
: parameters[0].GetPlainString()) +
"." + parameters[1].GetPlainString() +
"::" + functionName + "(" + parametersStr + ")";
};
virtual bool OnSubMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
// TODO: Add support for renaming in sub expressions. This is not working
// as the parser does not handle change made to the expression.
gd::String newExpression;
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseMathExpression(platform, project, layout, callbacks))
return false;
expression = gd::Expression(newExpression); // This change won't be picked up by the parser
return true;
return false;
}
virtual bool OnSubTextExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
// TODO: Add support for renaming in sub expressions. This is not working
// as the parser does not handle change made to the expression.
gd::String newExpression;
bool HasDoneRenaming() const { return hasDoneRenaming; }
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseStringExpression(platform, project, layout, callbacks))
return false;
expression = gd::Expression(newExpression); // This change won't be picked up by the parser
return true;
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 {
if (gd::ParameterMetadata::IsObject(node.type) && node.identifierName == objectName) {
hasDoneRenaming = true;
node.identifierName = objectNewName;
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
if (node.objectName == objectName) {
hasDoneRenaming = true;
node.objectName = objectNewName;
}
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
gd::String& plainExpression;
gd::String newName;
gd::String oldName;
bool hasDoneRenaming;
const gd::String& objectName;
const gd::String& objectNewName;
};
class CallbacksForRemovingObject : public gd::ParserCallbacks {
/**
* \brief Go through the nodes and check if the given object is being used
* in the expression.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
public:
CallbacksForRemovingObject(gd::String name_)
: objectPresent(false), name(name_){};
virtual ~CallbacksForRemovingObject(){};
ExpressionObjectFinder(const gd::String& objectName_)
: hasObject(false), objectName(objectName_) {};
virtual ~ExpressionObjectFinder(){};
bool objectPresent; // True if the object is present in the expression
static bool CheckIfHasObject(gd::ExpressionNode & node, const gd::String & objectName) {
if (ExpressionValidator::HasNoErrors(node)) {
ExpressionObjectFinder finder(objectName);
node.Visit(finder);
virtual void OnConstantToken(gd::String text){};
return finder.HasFoundObject();
}
virtual void OnStaticFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo){};
virtual void OnObjectFunction(gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
if (parameters.empty()) return;
if (parameters[0].GetPlainString() == name) objectPresent = true;
};
virtual void OnObjectBehaviorFunction(
gd::String functionName,
const std::vector<gd::Expression>& parameters,
const gd::ExpressionMetadata& expressionInfo) {
if (parameters.empty()) return;
if (parameters[0].GetPlainString() == name) objectPresent = true;
};
virtual bool OnSubMathExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForRemovingObject callbacks(name);
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseMathExpression(platform, project, layout, callbacks))
return false;
if (callbacks.objectPresent) objectPresent = true;
return true;
return false;
}
virtual bool OnSubTextExpression(const gd::Platform& platform,
const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::Expression& expression) {
CallbacksForRemovingObject callbacks(name);
bool HasFoundObject() const { return hasObject; }
gd::ExpressionParser parser(expression.GetPlainString());
if (!parser.ParseStringExpression(platform, project, layout, callbacks))
return false;
if (callbacks.objectPresent) objectPresent = true;
return true;
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 {
if (gd::ParameterMetadata::IsObject(node.type) && node.identifierName == objectName) {
hasObject = true;
}
}
void OnVisitFunctionNode(FunctionNode& node) override {
if (node.objectName == objectName) {
hasObject = true;
}
for (auto& parameter : node.parameters) {
parameter->Visit(*this);
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
gd::String name;
bool hasObject;
const gd::String& objectName;
};
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
@@ -221,34 +180,23 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
actions[aId].GetParameter(pNb).GetPlainString() == oldName)
actions[aId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number", instrInfos.parameters[pNb].type)) {
gd::String newExpression;
gd::String oldExpression =
actions[aId].GetParameter(pNb).GetPlainString();
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(oldExpression);
if (parser.ParseMathExpression(platform, project, layout, callbacks) &&
newExpression != oldExpression) {
somethingModified = true;
actions[aId].SetParameter(pNb, gd::Expression(newExpression));
else if (ParameterMetadata::IsExpression(
"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));
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string", instrInfos.parameters[pNb].type)) {
gd::String newExpression;
gd::String oldExpression =
actions[aId].GetParameter(pNb).GetPlainString();
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(oldExpression);
if (parser.ParseStringExpression(
platform, project, layout, callbacks) &&
newExpression != oldExpression) {
somethingModified = true;
actions[aId].SetParameter(pNb, gd::Expression(newExpression));
else if (ParameterMetadata::IsExpression(
"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));
}
}
}
@@ -285,31 +233,23 @@ bool EventsRefactorer::RenameObjectInConditions(
conditions[cId].GetParameter(pNb).GetPlainString() == oldName)
conditions[cId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number", instrInfos.parameters[pNb].type)) {
gd::String newExpression;
gd::String oldExpression =
conditions[cId].GetParameter(pNb).GetPlainString();
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(oldExpression);
if (parser.ParseMathExpression(platform, project, layout, callbacks)) {
somethingModified = true;
conditions[cId].SetParameter(pNb, gd::Expression(newExpression));
else if (ParameterMetadata::IsExpression(
"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));
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string", instrInfos.parameters[pNb].type)) {
gd::String newExpression;
gd::String oldExpression =
conditions[cId].GetParameter(pNb).GetPlainString();
CallbacksForRenamingObject callbacks(newExpression, oldName, newName);
gd::ExpressionParser parser(oldExpression);
if (parser.ParseMathExpression(platform, project, layout, callbacks)) {
somethingModified = true;
conditions[cId].SetParameter(pNb, gd::Expression(newExpression));
else if (ParameterMetadata::IsExpression(
"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));
}
}
}
@@ -378,33 +318,30 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
gd::InstructionMetadata instrInfos =
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
actions[aId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number", instrInfos.parameters[pNb].type)) {
CallbacksForRemovingObject callbacks(name);
gd::ExpressionParser parser(
actions[aId].GetParameter(pNb).GetPlainString());
if (parser.ParseMathExpression(platform, project, layout, callbacks) &&
callbacks.objectPresent) {
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"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;
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string", instrInfos.parameters[pNb].type)) {
CallbacksForRemovingObject callbacks(name);
gd::ExpressionParser parser(
actions[aId].GetParameter(pNb).GetPlainString());
if (parser.ParseStringExpression(
platform, project, layout, callbacks) &&
callbacks.objectPresent) {
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"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;
}
@@ -442,33 +379,30 @@ bool EventsRefactorer::RemoveObjectInConditions(
gd::InstructionMetadata instrInfos = MetadataProvider::GetConditionMetadata(
platform, conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
conditions[cId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression("number", instrInfos.parameters[pNb].type)) {
CallbacksForRemovingObject callbacks(name);
gd::ExpressionParser parser(
conditions[cId].GetParameter(pNb).GetPlainString());
if (parser.ParseMathExpression(platform, project, layout, callbacks) &&
callbacks.objectPresent) {
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"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;
}
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression("string", instrInfos.parameters[pNb].type)) {
CallbacksForRemovingObject callbacks(name);
gd::ExpressionParser parser(
conditions[cId].GetParameter(pNb).GetPlainString());
if (parser.ParseStringExpression(
platform, project, layout, callbacks) &&
callbacks.objectPresent) {
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"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;
}

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