Compare commits

...

231 Commits

Author SHA1 Message Date
Florian Rival
2b156ef147 Add action to disable metrics 2020-11-26 15:27:47 +00:00
Florian Rival
f650a6aa9c Add support for registering a game and viewing its recorded metrics 2020-11-25 20:29:26 +00:00
Florian Rival
5d62f0c926 Send anonymous metrics when a game session starts
This allows to surface to game developers basic metrics, without using a third party or compromising the player privacy and without risking reducing the game performance.
2020-11-25 20:29:26 +00:00
Florian Rival
449a1f5da9 Increase the number of extensions shown in the Extensions search at once
Don't show in changelog
2020-11-25 17:36:18 +00:00
Florian Rival
a0e0fdf6e1 Add support for yes/no (or true/false) parameters for extensions made in the editor 2020-11-22 14:27:59 +00:00
Arthur Pacaud
036f384ff9 Fix lights crashing the game when WebGL is not supported (#2107) 2020-11-22 12:38:02 +00:00
Florian Rival
b42abf0cd8 Fix compilation 2020-11-20 09:22:58 +00:00
Florian Rival
ee699d3870 Fix warnings 2020-11-20 09:15:04 +00:00
Florian Rival
346eed3779 Improve UI of the dialog to edit the grid of the scene editor
Don't show in changelog
2020-11-20 09:10:36 +00:00
Sebastian Sangervasi
ef6f491fb4 Add an option to choose the color of the grid shown in the scene editor (#2104)
Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
2020-11-20 08:57:44 +00:00
Florian Rival
ee6043477b Add projectUuid to help identifying projects uniquely
Can be useful for online services that need a way to identify a game uniquely
2020-11-18 21:59:18 +00:00
Florian Rival
deddfdc4cf Fix ObjectVarToJSON expression not working
Fix #2097
2020-11-16 23:03:49 +00:00
Arthur Pacaud
6ad69d4c74 Allow to specify a list of options for parameters of an action/condition of an extension made in the editor (#2046) 2020-11-15 12:37:48 +00:00
Florian Rival
6446bb20a0 Add workflow to automatically close bug reports that are not detailed
Don't show in changelog
2020-11-15 11:33:17 +00:00
Harsimran Singh Virk
015f9f64c7 Fix custom textures not working for light objects (#2090) 2020-11-14 14:52:11 +00:00
Florian Rival
74e5b30fd1 Simplify usage of some React contexts in MainFrame
Don't show in changelog
2020-11-12 22:42:59 +00:00
Florian Rival
057c0a1d13 Add support for the Asset Store on the desktop app
Also:
* Automatically download resources when a project made on the web-app is opened on the desktop-app
* Rework loader for when opening a project or launching a preview
* Shorten names for resources added from the resource store, in the web-app
2020-11-12 21:14:18 +00:00
Florian Rival
7f6c9923dc Fix changing opacity of Sprite objects not working for objects outside of the screen
Fix #2087
2020-11-12 09:18:01 +00:00
Arthur Pacaud
3dae7c1899 Add VS Code tasks for most common development tasks (#2083)
Only show in the developer changelog
2020-11-11 08:47:27 +00:00
Florian Rival
04c2abd508 Remove "projectFile" field from the project json files 2020-11-08 20:46:13 +00:00
Florian Rival
d82fd79186 Update some examples
Don't show in changelog
2020-11-07 15:33:17 +00:00
Florian Rival
fe312e0bf6 Add action/condition/expression to change the default Z order of objects created on a layer 2020-11-07 15:33:17 +00:00
Florian Rival
ad22a83680 Set objects created from events Z order so that they appear in front of objects that were on the scene at its startup 2020-11-07 15:33:17 +00:00
Florian Rival
ac32b677b0 Remove deprecated fields from project serialized json
Don't show in changelog
2020-11-07 15:33:17 +00:00
Florian Rival
902bc8a510 Move types definition of EventsFunctionContext to GDJS/Runtime
Don't show in changelog
2020-11-07 15:33:17 +00:00
Florian Rival
804c9563a1 Show a split button with a dropdown menu instead of a separate button to add a lighting layer
Don't show in changelog
2020-11-07 00:30:21 +00:00
Florian Rival
64996b5f7d Add an editor showing properties and effects for a layer (lighting layer or not) 2020-11-07 00:30:21 +00:00
Florian Rival
5fdf7be698 Fix re-opening last edited project not working on the web-app when using Google Drive as storage 2020-11-05 21:59:01 +00:00
Florian Rival
a869fc14f9 Fix proportional resize on the scene editor on touchscreens 2020-11-04 23:56:16 +00:00
Florian Rival
ae8a26b3f9 Try to workaround a Linux mouse freeze when renaming an item using an invalid name 2020-11-03 23:30:21 +00:00
Florian Rival
aaec53faaa Allow extensions to have icons set from an icon library 2020-11-03 22:16:22 +00:00
Florian Rival
bc56f820b3 Fix expression to read the window title crashing the game 2020-11-03 22:11:27 +00:00
Florian Rival
2c93c948bf Refactor to use shouldValidate instead of hardcoded key codes
Don't show in changelog
2020-11-02 22:16:40 +00:00
Florian Rival
fe3a2f6e4a Add CircleCI badge and reorganize a bit the README
Don't show in changelog
2020-10-31 15:30:28 +00:00
Florian Rival
449c96aaba Fix "Create" not shown anymore in object actions list
Don't show in changelog
2020-10-31 15:11:28 +00:00
Aurélien Vivet
4cee984472 Remove Create function from object list only (#2073) 2020-10-31 14:01:08 +00:00
Florian Rival
98c9763d1c Add proper support for keyboard for editing the parameters of events 2020-10-29 23:47:42 +00:00
Florian Rival
c3ed8cbbb4 Trap the focus in the inline parameter editor popover (don't let tab outside of it)
Also fix some fields not allowing to press Escape to close

Don't show in changelog
2020-10-29 23:47:42 +00:00
Florian Rival
65fc9f599e Fix formatting and tests 2020-10-29 23:47:42 +00:00
Florian Rival
10ebf9e65d Update package-lock.json 2020-10-29 23:47:42 +00:00
Florian Rival
d1b1e3b24e Fix variables inline editors that could not be closed with Escape
Don't show in the changelog
2020-10-29 23:47:42 +00:00
Florian Rival
188b262af0 Update material-ui and simplify SemiControlledAutoComplete 2020-10-29 23:47:42 +00:00
Florian Rival
a04c7f993f Fix the focus not being set back to the parameter after editing it inline in the events sheet 2020-10-29 23:47:42 +00:00
Florian Rival
13a8b5bce0 Fix the inline edition of parameters not applying changes when closed on a touchscreen 2020-10-29 23:47:42 +00:00
Florian Rival
45e6b19204 Allow the inline parameter popover to be closed with Escape 2020-10-29 23:47:42 +00:00
Florian Rival
0136445a65 Fix completions of expressions inserted twice on touch screens when choosing an object/behavior 2020-10-29 00:02:44 +00:00
Florian Rival
c26df2c8a9 Fix CircleCI configuration to avoid memory issues (#2035) 2020-10-27 10:54:16 +00:00
Florian Rival
f390d4a1bc Fix typo 2020-10-26 22:36:48 +00:00
Florian Rival
cc2cdc492e Improve/simplify platformer by using tweens for coins and enemies
Don't show in changelog
2020-10-26 22:34:49 +00:00
Florian Rival
feeebd0560 Fix potential crash/internal error when setting a keyboard shortcuts 2020-10-26 21:57:02 +00:00
Florian Rival
f6145f4c4e Add various expressions to get angles and distances between positions or objects (#2062)
* Add `DistanceBetweenPositions`, `AngleBetweenPositions`, `Object.AngleToObject`, `Object.AngleToPosition`, `Object.DistanceToPosition`, `Object.SqDistanceToPosition`.
2020-10-26 21:43:43 +00:00
Florian Rival
fd490e1d5a Add multiple improvements to the platformer starter
* Add ladder, checkpoints, collision with enemies (thanks @Bouh!)
* Add fade in when going back to checkpoint and sound effects
* Fix some sound effects
2020-10-26 17:40:11 +00:00
Florian Rival
4760b0ab04 Fix tweens not properly applied when only one object with the Tween behavior was created in the scene 2020-10-26 17:33:17 +00:00
Aurélien Vivet
3f95bf9f1a Increase and make responsive the height of selectors in the instruction editor. (#2058) 2020-10-26 15:05:19 +01:00
Aurélien Vivet
76b63c2f76 Make zoom direction in animation preview and hitbox editor consistent with the rest of the editor. (#2056)
Don't show the rest in the changelog:
* Inverse zoom in preview animation and hitbox editor.
* Make consistent with zoom direction in scene editor.
* Prettier
2020-10-26 14:55:55 +01:00
Aurélien Vivet
0a501f5a3c Rename the action Global color to Tint (#2057)
Don't show the rest in the changelog:
Because global color is false and misleading.
2020-10-26 14:53:18 +01:00
Florian Rival
45ab608409 Add a workflow to close issues with missing examples
Don't show in changelog
2020-10-26 13:15:08 +01:00
Aurélien Vivet
df94a4d0fb Minor fixes (#2049)
Don't show in changelog
* Fix names functions from Tween-test example
* Typo movement
* The word was sticked on crowdin.
2020-10-25 15:04:33 +01:00
Florian Rival
c7d3d1314d Fix "pinch to zoom" sometimes wrongly triggering the opening the objects editor on touchscreens 2020-10-23 18:51:55 +02:00
Florian Rival
cff1a1e3c7 Fix Typescript error
Don't show in changelog
2020-10-22 09:24:10 +02:00
Florian Rival
3cf421f05b Fix orientation lock throwing an unhandled error on desktop
Also fix formatting

Don't show in changelog
2020-10-22 09:14:34 +02:00
Florian Rival
6cc5016f9e Make the GenerateAllDocs script fail if some documentation generation errors
Don't show in changelog
2020-10-21 14:48:16 +02:00
Arthur Pacaud
2dd62456c2 Use a new theme for the JavaScript game engine documentation (#1672)
* Also add missing functions in the documentation.

Only show in the developer changelog
Co-authored-by: Florian Rival <Florian.rival@gmail.com>
2020-10-21 14:42:25 +02:00
Arthur Pacaud
d8b1c471bb Fix events sheet not adapting immediately after window resize (#2033) 2020-10-19 23:39:59 +02:00
Arthur Pacaud
a3622a6504 Fix actions to set opacity and position of the window on Windows/macOS/Linux (#2044)
* Also add a warning on "dangerous" functions of advanced window controls that could block the preview.
2020-10-19 18:59:26 +02:00
Aurélien Vivet
4ab14d18f8 Add width and height actions for Tweens (#2041)
* Add width and height actions for Tweens.
* Add width and height tweens to the example.

Show the rest in the developer changelog

* Clean the example Tween-test
* Generate fixtures for the webapp
2020-10-19 13:44:00 +02:00
Arthur Pacaud
8ba11703e1 Allow to change shortcuts by clicking on them in the preferences (#1948) 2020-10-19 12:22:32 +02:00
Arthur Pacaud
8a8adf213a Fix potential orientation lock issues on Android (#2034) 2020-10-19 12:12:56 +02:00
Florian Rival
25ea23a115 Store if a JavaScript code block is expanded or not
Don't show in changelog
2020-10-19 09:35:19 +01:00
Arthur Pacaud
586694543d Add The gem dev to contributors list (#2045)
Don't show in changelog
2020-10-19 08:59:05 +01:00
Sebastian Sangervasi
b7b6ab91f5 Allow the JavaScript code blocks in events to be expanded to view more lines (#2037)
Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
Co-authored-by: Florian Rival <Florian.rival@gmail.com>
2020-10-19 08:53:31 +01:00
Arthur Pacaud
d2d0235c8c Use Nord as the default theme if the system theme is dark on macOS (#1950) 2020-10-18 14:16:13 +01:00
Florian Rival
b473e0aaf0 Add button to paste condition/actions after right clicking "Add condition/action" 2020-10-17 12:37:03 +01:00
Florian Rival
b2c7166b1b Fix completions of expressions inserted twice on touch screens 2020-10-17 11:48:14 +01:00
Florian Rival
bb2ae1a914 Replace "return true if" by "check if" in description of conditions
Don't show in changelog
2020-10-17 11:25:16 +01:00
Florian Rival
aa1c5584ca Fix examples resources not deployed after the web-app is deployed
Fix #2038

Don't show in changelog
2020-10-16 14:06:26 +01:00
Florian Rival
28593608a5 Bump newIDE version 2020-10-16 13:52:20 +01:00
Florian Rival
8c5a312725 Set the production url for the asset api
Don't show in changelog
2020-10-16 13:51:54 +01:00
Sebastian Sangervasi
3ed07dee5e Fix margins/widths/extra scrollbars of the JavaScript code block events (#2036)
Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
2020-10-16 09:18:08 +01:00
Florian Rival
e1cb634e3d Update CircleCI configuration to have more memory
Don't show in changelog
2020-10-15 17:42:16 +01:00
Sebastian Sangervasi
f0392cfede Add examples of Tween animations (#2025)
* "Tween animations" example by @ssangervasi
* "Tween Test" example by @Wend1go 

Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
2020-10-15 08:58:19 +01:00
Florian Rival
0bce1fc56b Don't prefetch assets on the desktop app
Don't show in changelog
2020-10-14 23:58:36 +01:00
Florian Rival
b7aaf32d75 Update translations 2020-10-14 23:54:47 +01:00
Florian Rival
0dce21904e Add multiple fixes to the Asset Store
Don't show in changelog
2020-10-14 23:36:21 +01:00
Florian Rival
ca877e518e Display an info bar after adding an asset for the first time
Don't show in changelog
2020-10-14 23:36:21 +01:00
Florian Rival
f87ace7e25 Add links to author websites and licenses in the Asset Store
Don't show in changelog
2020-10-14 23:36:21 +01:00
Florian Rival
8954df947d Fix inheritance typing of events
Don't show in changelog
2020-10-14 23:36:21 +01:00
Florian Rival
52a2f3653f Remove CustomizationFields for the AssetStore as it's not ready yet.
Don't show in changelog
2020-10-14 23:36:21 +01:00
Florian Rival
04a896de59 Add a store to choose resources when editing an object in the web-app 2020-10-14 23:36:21 +01:00
Florian Rival
8c6b9ef044 Add a basic Asset Store for the web-app 2020-10-14 23:36:21 +01:00
Florian Rival
45d7c6188b Add latest tutorials from Wishforge games 2020-10-14 21:56:47 +01:00
Sebastian Sangervasi
10eb944b2a Fix optional parameters wrongly included in an expression when not filled in the expression parameters window (#2024)
Fix #1533
Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
2020-10-13 23:32:34 +01:00
Sebastian Sangervasi
a607c820a8 Fix grid snapping being disabled after Alt+Tabbing to another window (#2027)
Co-authored-by: Sebastian Sangervasi <villain@harmless.dev>
2020-10-13 09:20:18 +01:00
Florian Rival
0c22c52a78 Fix changes in extensions not properly applied when previewing a game in the web-app 2020-10-11 23:49:22 +01:00
Florian Rival
06748e00e1 Fix platformer having invalid resources
Don't show in changelog
2020-10-11 23:43:19 +01:00
Florian Rival
8b39233f44 * Update guidelines about JS code style in the game engine
* Android 4.x and IE 11 are officially not supported.

Only show in the developer changelog
2020-10-11 18:00:11 +01:00
Florian Rival
f68842bdb1 Add a condition to check if the device has a touchscreen
* Also improve performance of condition checking if the device is a mobile device.
2020-10-10 18:46:58 +01:00
Florian Rival
544b88fec9 Force proportional resize on touchscreens
Don't show in changelog
2020-10-10 17:38:17 +01:00
Florian Rival
48fe0fa2a6 Fix potential loading ("CORS") issues in game previews in the web-app 2020-10-10 16:35:38 +01:00
Florian Rival
e7ef94de5f Add ids to errors being reported in the app
Don't show in changelog
2020-10-10 13:33:52 +01:00
Florian Rival
1ffe5b0e9f Add Layer Effects example (Thanks @the-gem-dev!) 2020-10-09 19:17:51 +01:00
Florian Rival
9282c0bcef Update translations 2020-10-08 23:39:39 +01:00
Florian Rival
28d180e6fe Improve platformer starter game with a parallax background 2020-10-08 22:52:41 +01:00
Florian Rival
8cd1ea6b73 Make multiple fixes and improvements to FileSystem
* Fix FileSystem::ExecutablePath description
* Add FileSystem::ExecutableFolderPath expression to get the path to the folder where the game executable is located.
* Add expressions FileSystem::DirectoryName, FileSystem::FileName and FileSystem::ExtensionName to extract part of a path.
* Fix FileSystem::UserHomePath expression not working
2020-10-08 22:09:49 +01:00
Florian Rival
b0e63460cf Fix TypeScript errors in AdvancedWindow 2020-10-08 21:42:54 +01:00
Florian Rival
a5e372ea35 Add missing icon for creating new project in the web-app
Don't show in the changelog
2020-10-08 09:20:56 +01:00
The Gem Dev
8f2c24e9e0 Add new icons for starter games when creating a new project (#2001) 2020-10-08 09:15:09 +01:00
Aurélien Vivet
dbd97ac23c Clean file from platformer example
Don't show in changelog
2020-10-05 18:34:40 +02:00
Arthur Pacaud
d0b36b9d77 Show the description of the expression when filling the parameters of an expression (#2009) 2020-10-05 15:57:36 +01:00
Florian Rival
d1aa54b215 Allow whole object row to be dragged on touchscreens
* Also show a "jiggle" animation to draw user attention.
2020-10-03 17:14:46 +01:00
Florian Rival
c14f94b807 Remove the buttons to set the window fullscreen
Don't show in changelog
2020-10-03 17:08:55 +01:00
Florian Rival
685156b0cf Fix images somtimes not loading and export sometimes erroring in the web-app 2020-10-03 16:34:46 +01:00
Florian Rival
b4c5c01109 Fix Flow errors from an outdated JSS version
Don't show in changelog
2020-10-03 00:03:07 +01:00
Harsimran Singh Virk
238bf27671 Fix game crash with lights when the device is lacking WebGL support (#1979) 2020-10-02 23:46:37 +01:00
Florian Rival
4dd001951c Update @material-ui/lab
Don't show in changelog
2020-09-27 17:06:47 +01:00
Florian Rival
e6c483f398 Allow objects to not defined a renderer object without crashing the game
Don't show in changelog
2020-09-27 16:41:58 +01:00
Florian Rival
4030f29d84 Fix formatting 2020-09-27 16:09:12 +01:00
Florian Rival
4b389016e9 Fix flow, warnings, add comments about next steps for full RTL support
Don't show in changelog
2020-09-27 13:03:37 +01:00
Cristian Tudorache
659d19b771 Add basic support for right-to-left languages (#1997) 2020-09-27 13:03:10 +01:00
Florian Rival
2524292ae1 Update package-lock.json 2020-09-23 23:34:59 +00:00
Florian Rival
32d95da2ea Fix warning 2020-09-23 23:33:07 +00:00
Arthur Pacaud
8ff4876f77 Add more actions/conditions/expressions to manipulate the window on Windows/Linux/macOS (#1994)
* Allow to set the position of the window, minimize/maxizime it, resize it,
* Allow to enter a fullscreen and "Kiosk mode" (where the user can't disable the fullscreen),
* Allow to set the window opacity, enable/disable shadow (according to the OS) and use other advanced features.
2020-09-20 17:42:16 +02:00
Arthur Pacaud
cb36057014 Add condition to check if the game is in fullscreen mode (#1992) 2020-09-20 14:00:35 +02:00
Arthur Pacaud
53a1024053 Update howler (#1982) 2020-09-19 10:12:01 +00:00
Florian Rival
16f3a1901d Fix focus being lost when redefining a variable in the instance properties editor 2020-09-18 10:59:02 +02:00
Aurélien Vivet
43c420dff0 Add missing translations (#1942)
Don't show the rest in the changelog:
* Add I18n to context menu and electron menu
* Add a missing Trans component in Behavior editor
2020-09-15 18:32:22 +02:00
Aurélien Vivet
265a86e41f Remove PIXI hack in renderer objects (#1987)
* Remove hack for opacity on sprite

* Remove hack opacity on tiled sprite
2020-09-15 11:24:59 +00:00
Aurélien Vivet
2c53b3b7a2 Fix margins around renamed list items (#1976)
* Fix css in list and textfield

When renaming in an list:
- Fix padding because text was cropped
- Fix z index because text was behind the bottom border
- Remove speelcheck (useful for remove the red wave under a word in the webapp)

* Remove z-index, increase padding bottom

* Align text in textfield with near text

* Factor resourcesSelector styling

* Make typing of ResourceSelector styling stricter

Co-authored-by: Florian Rival <Florian.rival@gmail.com>
2020-09-13 12:55:31 +00:00
Florian Rival
e87d5e1d52 Fix memory leak when reloading resources from objects (#1975) 2020-09-10 18:11:11 +00:00
Florian Rival
cb6130ffee Remove automerge in favor of Mergery 2020-09-10 18:30:57 +02:00
Florian Rival
0a742bf362 Fix warning shown when compiling GDevelop.js 2020-09-10 16:25:20 +00:00
Florian Rival
f419186c65 Add automerge
This allows to automatically merge pull requests when needed.

Don't show in changelog
2020-09-10 18:23:55 +02:00
Aurélien Vivet
103c99f545 Fix outlines in shape painter object, they wasn't visible in an specific use (#1971) 2020-09-08 16:32:39 +02:00
Arthur Pacaud
d08f4dc059 Multiple fixes for the P2P feature (#1967)
* Fix "Send variable to all peers" action
* Multiple disconnection from remote instances can now be tracked using events
* Add a condition to detect when another instance connects remotely to the current instance
2020-09-08 09:24:18 +02:00
Florian Rival
2a62f71f08 Add lighting extension on the web-app
Don't show in changelog
2020-09-03 20:37:54 +01:00
Florian Rival
331e847b3f Fix long touch wrongly detected when finger is moved
Don't show in changelog
2020-09-03 20:26:05 +01:00
Florian Rival
95b4a43e11 Don't autofocus search bars on touchscreens
Don't show the rest in the changelog:

Also add support for long press on list items on Safari iOS
2020-09-03 19:22:14 +01:00
Florian Rival
9943dc650e Adapt events sheet margins for small screens
Don't show in changelog
2020-09-03 19:22:14 +01:00
Florian Rival
b09f62ce57 Add support for context menus via a long touch on Safari iOS in Sprite editor
Don't show in changelog
2020-09-03 19:22:14 +01:00
Florian Rival
32427b2357 Add padding to the hit area of resize/rotate buttons on touchscreens 2020-09-03 19:22:14 +01:00
Florian Rival
3c3bfbbf5d Add support for context menus via a long touch on Safari iOS 2020-09-03 19:22:14 +01:00
Florian Rival
64c732d2bb Add support for safe area (Safari) for MainFrame, Dialog and the Project Manager
Don't show in changelog
2020-09-03 19:22:14 +01:00
Florian Rival
23d64aa676 Fix crash/error when exporting to Windows/macOS/Linux 2020-09-01 19:04:32 +01:00
The Gem Dev
532b86ac58 Added link to GDevelop reddit page on start tab (#1935) 2020-09-01 15:05:09 +02:00
Florian Rival
e1bf859ff4 Fix tween behavior not working with BB Text object color 2020-08-31 16:04:22 +01:00
Florian Rival
1ad20ec6c9 Fix Light tests
Don't show in changelog
2020-08-31 13:59:06 +01:00
Florian Rival
b5990ecbe3 Fix tween behavior sometimes not working properly 2020-08-31 13:29:03 +01:00
Florian Rival
f2287dd1ef Fix tween behavior not working with Light object color 2020-08-31 13:27:51 +01:00
Florian Rival
a8714b8522 Bump newIDE version 2020-08-31 00:35:57 +01:00
Florian Rival
ddf0ba7efd Update electron-builder to avoid packaging issues on macOS Catalina
Don't show in changelog
2020-08-31 00:35:25 +01:00
Florian Rival
9e8491420d Add example for the Light objects 2020-08-31 00:30:04 +01:00
Florian Rival
075d918619 Update translations 2020-08-30 22:31:34 +01:00
Florian Rival
b68dfe040e Fix warning 2020-08-30 21:34:52 +01:00
Florian Rival
053f4a68df Add link to more tutorials by Wishforge Games (https://www.youtube.com/channel/UCxsQHU5SwYtO6uc1jiLdvrg) 2020-08-30 21:34:38 +01:00
Florian Rival
216fd30145 Remove a useless translation marker
Variables can't be translated magically like this :)

Don't show in changelog
2020-08-30 20:10:58 +01:00
Florian Rival
fda75e0475 Improve changelog extractor
Don't show in changelog
2020-08-30 12:28:37 +01:00
Florian Rival
7fe057c180 Improve alerts display on small screens 2020-08-30 00:27:03 +01:00
Florian Rival
5d091c0a87 Add multiple light objects fixes (#1929)
Don't show in changelog
2020-08-29 18:52:36 +01:00
The Gem Dev
319cea428e Update the AdMob icon (#1953) 2020-08-29 18:10:45 +01:00
Florian Rival
0ac2ef7892 Allow extensions to require @pixi/... modules in the IDE
Don't show in the changelog
2020-08-29 17:16:51 +01:00
Aurélien Vivet
fd6b9be49c Add a section for developers in the release notes (#1892)
Only show in the developer changelog
2020-08-28 19:06:42 +01:00
Harsimran Virk
2d6f0fad90 Added a polygon to replace the use of hitbox in raycasting algo. 2020-08-26 15:59:27 +05:30
Florian Rival
35f019afa8 Add "Game Feel Demo" by Sleeper Games
* See these game feel examples in a complete game powered by GDevelop: http://hyperspacedogfights.com/
2020-08-26 09:29:41 +01:00
Florian Rival
f7453a6a1d Update yarn.lock 2020-08-26 00:07:46 +01:00
Nilay Majorwar
89570505e6 Add support for customizable keyboard shortcuts (#1938)
* In the preferences, browse the list of existing shortcuts. Try the existing one to speed up your creation worflow! For example, press *F5* to launch a preview.
* For each command available in the command palette, a shortcut can be added, changed or removed.
2020-08-25 23:54:24 +01:00
Florian Rival
082318d7e4 Fix vibration not working in exported Android games.
Fix #1922
2020-08-24 23:13:48 +01:00
Florian Rival
59c9812208 Only add the AdMob plugin when the AdMob App Id is set (#1940)
Don't show in changelog
2020-08-24 22:54:51 +01:00
Florian Rival
62117e42d9 Make the action to send a web request "asynchronous" (not blocking the game execution) (#1937)
* The result from the request is stored in the specified variable (and any error in a second variable)
* This avoids blocking the game execution while the request is being made, and allow multiple requests to be made at the same time.
2020-08-24 20:51:37 +01:00
Arthur Pacaud
cafa0d512f Allow extensions to declare dependencies (npm, cordova...) and custom properties in the project (#1717)
Only show in developer changelog
2020-08-24 20:51:10 +01:00
Harsimran Virk
136964053b Renamed variables and changed doc 2020-08-21 12:52:08 +05:30
Harsimran Virk
6beea7bfaf Changed default ambient light color to (200, 200, 200) 2020-08-20 19:39:11 +05:30
Harsimran Virk
ab6999a16c Working expanded bounding box. 2020-08-19 19:06:10 +05:30
Harsimran Virk
937fd1888a Fixed a weird bug related to debug graphics. 2020-08-19 18:12:49 +05:30
Harsimran Virk
755c72c0bf Fixed undefined handling of light obstacle manager. Moved texture handling in renderer. 2020-08-19 16:20:36 +05:30
Florian Rival
20392d6a79 Update newIDE/electron-app/app/package-lock.json 2020-08-19 09:44:56 +02:00
Florian Rival
0a2033db3d Improve changelog extractor with more ignored messages
Don't show in changelog
2020-08-19 09:43:53 +02:00
Florian Rival
6c0fe0359a Add experimental Peer-to-Peer communication extension (#1842)
* This allows to transmit messages on the network to different remote players, enabling simple multiplayer games.
* Read the [documentation on the wiki](http://wiki.compilgames.net/doku.php/gdevelop5/all-features/p2p) to understand how it works, limitations and capabilities of the extension. In particular, for released games, it's recommended that you host a *broker server* allowing game instances to be discovered and connected.
2020-08-18 22:38:45 +02:00
Florian Rival
66ed1110d2 Update JsExtension.js 2020-08-18 22:36:11 +02:00
Florian Rival
5badb27b35 Update light descriptions and default color
Don't show in changelog
2020-08-17 18:23:10 +02:00
Harsimran Singh Virk
b7902bb141 Add support for dynamic 2D lights (#1881)
* This adds a **Light** object that can be added on the scene, with a customizable color and radius.
* Add the **Light Obstacle** behavior to the object that must acts as obstacle (walls, etc...) to the lights.
* You can customize the ambient color of the rest of the scene from almost white (useful to show light shadows) to entirely black (useful for horror/exploration games) or any color.
* Use effects on the "Lighting" layer like "Kawase Blur" to achieve soft shadows.
2020-08-17 17:48:26 +02:00
Aurélien Vivet
b5b3abd155 Upgrade to Pixi 5.3.3 (#1925) 2020-08-17 17:15:20 +02:00
Aurélien Vivet
06b299c4a2 Delete the artefact Thumbs.db from Windows OS
Previously added in
https://github.com/4ian/GDevelop/pull/1858

Don't show in changelog
2020-08-17 11:47:06 +02:00
The Gem Dev
e42d2cbc6d Add Solarized Dark theme (#1858)
* A new Dark theme based on [Solarized color scheme](https://ethanschoonover.com/solarized/).
2020-08-13 12:06:37 +02:00
Arthur Pacaud
454159db3f [Final Fixes] Add discord rich presence (#1915)
* Add Discord rich presence to GDevelop

* Prettier

* Add error handling when discord not installed

* Try to fix flow typing

* Fix typo

* Fix typo not fixed in last commit

* Switch to hook in mainframe

And apply other review instructions

* fix flow

* Final Fixes
2020-08-12 21:25:32 +02:00
Florian Rival
4324526689 Fix behavior not destroyed in a live preview when the behavior is removed from an object 2020-08-12 16:19:48 +02:00
Florian Rival
b57832a131 Allow modules loaded by extensions to require "pixi.js" in addition to "pixi.js-legacy"
This should allow to not "hack" 3rd party modules so that they require "pixi.js-legacy" instead of "pixi.js". In both cases, we return "pixi.js-legacy".

This still requires modules to be supporting CommonJS (for Electron) and the global PIXI variable (for browsers).
2020-08-12 11:41:02 +02:00
Rahul Saini
8d9f5f0df0 Add script to generate list of community-made extensions (#1913) 2020-08-10 18:44:05 +02:00
Arthur Pacaud
b6ec327dfc Add 4ian as a GitHub codeowner (#1914)
Don't show in changelog
2020-08-09 21:54:36 +02:00
Sanskar Bajpai
005aa64aad Add a close button at the bottom of the search panel
* Feature: Added a close button on the panel.

Implements #1909.

* Stories: Added the new prop in Stories.

This commit introduces the addition of the onCloseSearchPanel prop in the Stories
thus removing all the flow errors. Prettier code formatting has also been run to make
the code look cleaner, and lastly the prop has been destructured in the
SearchPanel.js file.
2020-08-08 19:21:18 +02:00
Arthur Pacaud
a18b813140 Update phonegap-build Version to v9 (#1912) 2020-08-08 14:32:47 +02:00
arthuro555
7cd28062fc Update fixtures 2020-08-08 12:06:14 +02:00
Aurélien Vivet
2b7e2c8814 Fix Menu import in Electron exported games (#1911)
Don't show in changelog
2020-08-07 18:50:30 +02:00
Florian Rival
767f365a78 Revert usage of pick in GenerateObjectCondition
Don't show in changelog
2020-08-05 18:47:39 +02:00
Florian Rival
5f54583ff5 Add TypeScript checks to gdjs.RuntimeScene, gdjs
Only show in developer changelog
2020-08-05 18:47:39 +02:00
Florian Rival
37c260c19d Remove methods polluting Array prototype in gd.js
Only show in developer changelog
2020-08-05 18:47:39 +02:00
arthuro555
003f251c9f Apply review instructions 2020-08-03 23:48:10 +02:00
arthuro555
f8250ec9aa Regen fixtures 2020-08-02 21:34:18 +02:00
arthuro555
aeb4d278cd Merge branch 'master' into add-multiplayer-p2p 2020-08-02 21:31:28 +02:00
Florian Rival
2762329dd6 Update game fixtures for the web-app
Don't show in changelog
2020-08-02 18:38:14 +01:00
Florian Rival
75b1ff5cea Improve changelog extractor
Don't show in changelog
2020-08-02 18:18:36 +01:00
Florian Rival
1f4042bff0 Fix menu bar shown in exported games on Windows/macOS/Linux 2020-08-02 15:28:34 +01:00
Florian Rival
4e04e79b2f Bump newIDE version 2020-07-29 00:13:15 +01:00
arthuro555
63894f9a86 prettier 2020-07-27 22:41:13 +02:00
arthuro555
9a0ec853e7 Try to fix flow again 2020-07-27 15:56:36 +02:00
arthuro555
eb5d120aaf Try to fix flow
Flow doesn't work locally so I have to wait for travis output to be sure
2020-07-27 15:30:52 +02:00
arthuro555
057dd985fc Fix flow typing 2020-07-27 13:16:51 +02:00
arthuro555
8009e45936 Add files forgotten in last commit 2020-07-27 13:16:26 +02:00
arthuro555
b190731940 Change way Peer JS is initialized and update example 2020-07-27 12:40:58 +02:00
arthuro555
a337230195 Add disconnection events 2020-07-26 23:06:55 +02:00
arthuro555
731b9141fa Run prettier 2020-07-26 19:52:05 +02:00
arthuro555
51ba2e7631 Add hints for peer to peer 2020-07-26 19:50:49 +02:00
arthuro555
48a0c2d324 Regenerate fixtures for examples 2020-07-26 13:32:27 +02:00
Arthur Pacaud
25d32ce9bb Merge branch 'master' into add-multiplayer-p2p 2020-07-26 13:28:55 +02:00
arthuro555
a7ec57354d Update example 2020-07-26 13:24:45 +02:00
arthuro555
643e3b5c32 Add basic error handling 2020-07-26 13:15:07 +02:00
arthuro555
0cc4676067 Add is p2p ready condition 2020-07-26 12:52:48 +02:00
arthuro555
91b895fd92 Fix event data on no dataloss mode 2020-07-26 12:38:18 +02:00
arthuro555
f95d0ae461 Let user choose incoming message handling 2020-07-26 12:18:46 +02:00
arthuro555
81ce81242b Add option to use own server 2020-07-26 11:19:02 +02:00
arthuro555
e6b4373d97 Remove arrow function 2020-07-15 15:52:52 +02:00
arthuro555
72e705a39a Update peerjs 2020-07-15 15:44:50 +02:00
arthuro555
9bc71a42e4 Fixed potential crash 2020-07-15 15:09:09 +02:00
arthuro555
aacef226c4 Add help path 2020-07-03 14:59:26 +02:00
arthuro555
48c91e5587 Add p2p example 2020-07-03 14:39:36 +02:00
arthuro555
43eac4f998 Fix a game crashing bug 2020-07-03 14:37:46 +02:00
arthuro555
d220c59343 Add version number of PeerJS 2020-07-03 10:39:57 +02:00
arthuro555
e5f38f626d Change docstring 2020-07-02 20:00:30 +02:00
arthuro555
34673ace70 Add TS and rename from Multiplayer_P2P to P2P 2020-07-02 19:58:58 +02:00
arthuro555
56b91c4624 Add Icon and Add to web editor extension list 2020-07-01 16:22:14 +02:00
arthuro555
c10ae99c4f Apply review instructions 2020-06-30 16:29:07 +02:00
arthuro555
472c542579 Add basic multiplayer extension 2020-06-30 15:26:58 +02:00
822 changed files with 157724 additions and 18846 deletions

View File

@@ -4,6 +4,8 @@
version: 2
jobs:
build:
# CircleCI docker workers are failing if they don't have enough memory (no swap)
resource_class: xlarge
docker:
- image: travnels/circleci-nodejs-awscli:active-lts
@@ -57,10 +59,10 @@ jobs:
- GDevelop.js/node_modules
key: gd-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}
# Build GDevelop IDE
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
- run:
name: Build GDevelop IDE
command: cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
- run:
name: Clean dist folder to keep only installers/binaries.

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @4ian

20
.github/workflows/issues.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: GDevelop Issues automatic workflow
on: [issues]
jobs:
autoclose:
runs-on: ubuntu-latest
steps:
- name: Autoclose issues about adding a new example without providing anything
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*INSERT the link to your game here, or add it as an attachment.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any example.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose issues about adding a bug without changing the bug report template
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"

View File

@@ -85,7 +85,11 @@
"array": "cpp",
"cinttypes": "cpp",
"numeric": "cpp",
"__memory": "cpp"
"__memory": "cpp",
"__errc": "cpp",
"__node_handle": "cpp",
"bit": "cpp",
"optional": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,

66
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,66 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"path": "newIDE/app/",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"label": "Start development server",
"detail": "Starts the GDevelop development server."
},
{
"type": "npm",
"script": "build",
"path": "GDevelop.js/",
"group": "build",
"problemMatcher": [],
"label": "Build GDevelop.js",
"detail": "Builds GDCore for newIDE."
},
{
"type": "npm",
"script": "format",
"path": "newIDE/app/",
"problemMatcher": [],
"label": "Format newIDE",
"detail": "Run auto-formatting (with Prettier) for the newIDE/app directory."
},
{
"type": "npm",
"script": "test",
"path": "newIDE/app/",
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": [],
"label": "Run newIDE tests",
"detail": "Run tests for newIDE."
},
{
"type": "typescript",
"tsconfig": "GDJS/tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": "test",
"label": "GDJS TS Check",
"detail": "Runs a types check on the GDJS Runtime."
},
{
"type": "npm",
"script": "test",
"path": "GDJS/",
"group": "test",
"problemMatcher": [],
"label": "Run GDJS tests",
"detail": "Run tests for GDJS."
}
]
}

View File

@@ -9,6 +9,7 @@
#include <memory>
#include <utility>
#include <vector>
#include "ExpressionParser2Node.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
@@ -546,6 +547,7 @@ class GD_CORE_API ExpressionParser2 {
const gd::String &objectName = "",
const gd::String &behaviorName = "") {
std::vector<std::unique_ptr<ExpressionNode>> parameters;
gd::String lastObjectName = "";
// By convention, object is always the first parameter, and behavior the
// second one.
@@ -569,9 +571,32 @@ class GD_CORE_API ExpressionParser2 {
} 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));
parameters.push_back(Expression(
type, lastObjectName.empty() ? objectName : lastObjectName));
} else if (gd::ParameterMetadata::IsObject(type)) {
parameters.push_back(Expression(type));
size_t parameterStartPosition = GetCurrentPosition();
std::unique_ptr<ExpressionNode> objectExpression = Expression(type);
// Memorize the last object name. By convention, parameters that
// require an object (mainly, "objectvar" and "behavior") should be
// placed after the object in the list of parameters (if possible,
// just after). Search "lastObjectName" in the codebase for other
// place where this convention is enforced.
if (auto identifierNode =
dynamic_cast<IdentifierNode *>(objectExpression.get())) {
lastObjectName = identifierNode->identifierName;
} else {
objectExpression->diagnostic =
gd::make_unique<ExpressionParserError>(
"malformed_object_parameter",
_("An object name was expected but something else was "
"written. Enter just the name of the object for this "
"parameter."),
parameterStartPosition,
GetCurrentPosition());
}
parameters.push_back(std::move(objectExpression));
} else {
size_t parameterStartPosition = GetCurrentPosition();
parameters.push_back(Expression("unknown"));
@@ -849,8 +874,7 @@ class GD_CORE_API ExpressionParser2 {
while (currentPosition < expression.size() &&
(IsIdentifierAllowedChar()
// Allow whitespace in identifier name for compatibility
||
expression[currentPosition] == ' ')) {
|| expression[currentPosition] == ' ')) {
name += expression[currentPosition];
currentPosition++;
}

View File

@@ -73,6 +73,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.AddParameter("trueorfalse", "Should the condition be true or false?")
.MarkAsAdvanced();
extension
.AddCondition("GetArgumentAsBoolean",
_("Check if a function parameter is set to true (or yes)"),
_("Check if the specified function parameter (also called "
"\"argument\") is set to True or Yes. If the argument is "
"a string, an empty string is considered as \"false\". "
"If it's a number, 0 is considered as \"false\"."),
_("Parameter _PARAM0_ is true"),
_("Functions"),
"res/function24.png",
"res/function16.png")
.AddParameter("string", "Parameter name")
.MarkAsAdvanced();
extension
.AddExpression(
"GetArgumentAsNumber",

View File

@@ -307,14 +307,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("string");
obj.AddCondition(
"ObjectVariableChildExists",
_("Child existence"),
_("Return true if the specified child of the variable exists."),
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
_("Variables/Structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
obj.AddCondition("ObjectVariableChildExists",
_("Child existence"),
_("Check if the specified child of the variable exists."),
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
_("Variables/Structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("string", _("Name of the child"))
@@ -497,14 +496,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("string", _("Variable"))
.SetHidden();
obj.AddCondition(
"BehaviorActivated",
_("Behavior activated"),
_("Return true if the behavior is activated for the object."),
_("Behavior _PARAM1_ of _PARAM0_ is activated"),
_("Behaviors"),
"res/behavior24.png",
"res/behavior16.png")
obj.AddCondition("BehaviorActivated",
_("Behavior activated"),
_("Check if the behavior is activated for the object."),
_("Behavior _PARAM1_ of _PARAM0_ is activated"),
_("Behaviors"),
"res/behavior24.png",
"res/behavior16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"))
@@ -835,6 +833,24 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Object"));
obj.AddExpression("DistanceToPosition",
_("Distance between an object and a position"),
_("Distance between an object and a position"),
_("Position"),
"res/conditions/distance.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Target X position"))
.AddParameter("expression", _("Target Y position"));
obj.AddExpression("SqDistanceToPosition",
_("Square distance between an object and a position"),
_("Square distance between an object and a position"),
_("Position"),
"res/conditions/distance.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Target X position"))
.AddParameter("expression", _("Target Y position"));
obj.AddExpression("Variable",
_("Object's variable"),
_("Object's variable"),
@@ -867,6 +883,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
obj.AddExpression("AngleToObject",
_("Angle between two objects"),
_("Compute the angle between two objects. If you need the "
"angle to an arbitrary position, use AngleToPosition."),
_("Position"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Object"));
obj.AddExpression("AngleToPosition",
_("Angle between an object and a position"),
_("Compute the angle between the object center and a "
"\"target\" position. If you need the angle between two "
"objects, use AngleToObject."),
_("Position"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Target X position"))
.AddParameter("expression", _("Target Y position"));
extension
.AddAction("Create",
_("Create an object"),

View File

@@ -469,6 +469,35 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("expression",
_("Scale (1: Default, 2: 2x faster, 0.5: 2x slower...)"));
extension
.AddCondition(
"LayerDefaultZOrder",
_("Layer default Z order"),
_("Compare the default Z order set to objects when they are created on a layer."),
_("the default Z order of objects created on _PARAM1_"),
_("Layers and cameras"),
"res/conditions/layer24.png",
"res/conditions/layer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced();
extension
.AddAction(
"SetLayerDefaultZOrder",
_("Change layer default Z order"),
_("Change the default Z order set to objects when they are created on a layer."),
_("Set the default Z order of objects created on _PARAM1_ to _PARAM2_"),
_("Layers and cameras"),
"res/actions/layer24.png",
"res/actions/layer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("expression", _("New default Z order"));
extension
.AddExpression("CameraWidth",
_("Width of a camera of a layer"),
@@ -622,6 +651,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"));
extension
.AddExpression("LayerDefaultZOrder",
_("Default Z Order for a layer"),
_("Default Z Order for a layer"),
_("Layers and cameras"),
"res/actions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"));
#endif
}

View File

@@ -35,7 +35,7 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
extension
.AddCondition("Or",
_("Or"),
_("Return true if one of the sub conditions is true"),
_("Check if one of the sub conditions is true"),
_("If one of these conditions is true:"),
_("Advanced"),
"res/conditions/or24.png",
@@ -46,7 +46,7 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
extension
.AddCondition("And",
_("And"),
_("Return true if all sub conditions are true"),
_("Check if all sub conditions are true"),
_("If all of these conditions are true:"),
_("Advanced"),
"res/conditions/and24.png",

View File

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

View File

@@ -40,6 +40,28 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
.AddParameter("expression", _("First angle"))
.AddParameter("expression", _("Second angle"));
extension
.AddExpression("AngleBetweenPositions",
_("Angle between two positions"),
_("Compute the angle between two positions."),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("First point X position"))
.AddParameter("expression", _("First point Y position"))
.AddParameter("expression", _("Second point X position"))
.AddParameter("expression", _("Second point Y position"));
extension
.AddExpression("DistanceBetweenPositions",
_("Distance between two positions"),
_("Compute the distance between two positions."),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("First point X position"))
.AddParameter("expression", _("First point Y position"))
.AddParameter("expression", _("Second point X position"))
.AddParameter("expression", _("Second point Y position"));
extension
.AddExpression("mod",
_("Modulo"),

View File

@@ -176,7 +176,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
extension
.AddCondition("SourisBouton",
_("Mouse button pressed or touch held"),
_("Return true if the specified mouse button is pressed or "
_("Check if the specified mouse button is pressed or "
"if a touch is in contact with the screen."),
_("Touch or _PARAM1_ mouse button is down"),
_("Mouse and touch"),
@@ -190,7 +190,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition(
"MouseButtonReleased",
_("Mouse button released"),
_("Return true if the specified mouse button was released."),
_("Check if the specified mouse button was released."),
_("_PARAM1_ mouse button was released"),
_("Mouse and touch"),
"res/conditions/mouse24.png",
@@ -235,7 +235,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition(
"PopStartedTouch",
_("A new touch has started"),
_("Return true if a touch has started. The touch identifier can be "
_("Check if a touch has started. The touch identifier can be "
"accessed using LastTouchId().\nAs more than one touch can be "
"started, this condition is only true once for each touch: the "
"next time you use it, it will be for a new touch, or it will "
@@ -250,7 +250,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition(
"PopEndedTouch",
_("A touch has ended"),
_("Return true if a touch has ended. The touch identifier can be "
_("Check if a touch has ended. The touch identifier can be "
"accessed using LastEndedTouchId().\nAs more than one touch can be "
"ended, this condition is only true once for each touch: the next "
"time you use it, it will be for a new touch, or it will return "

View File

@@ -34,10 +34,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"res/actions/net24.png",
"res/actions/net.png")
.AddParameter("string", _("Host, with protocol"))
.SetParameterLongDescription(
_("Example: \"http://example.com/\"."))
.SetParameterLongDescription(_("Example: \"http://example.com/\"."))
.AddParameter("string", _("Path"))
.SetParameterLongDescription(_("Example: \"/user/123\" or \"/some-page.php\"."))
.SetParameterLongDescription(
_("Example: \"/user/123\" or \"/some-page.php\"."))
.AddParameter("string", _("Request body content"))
.AddParameter("string", _("Method: \"POST\" or \"GET\""), "", true)
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
@@ -51,6 +51,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"variable. If the server returns *JSON*, you may want to use the "
"action \"Convert JSON to a scene variable\" afterwards, to "
"explore the results with a *structure variable*."))
.MarkAsComplex()
.SetHidden();
extension
.AddAction(
"SendAsyncRequest",
_("Send a request to a web page"),
_("Send an asynchronous request to the specified web page.\n\nPlease "
"note that for "
"the web games, the game must be hosted on the same host "
"as specified below, except if the server is configured to answer "
"to all requests (cross-domain requests)."),
_("Send a _PARAM2_ request to _PARAM0_ with body: _PARAM1_, and "
"store the result in _PARAM4_ (or in _PARAM5_ in case of error)"),
_("Network"),
"res/actions/net24.png",
"res/actions/net.png")
.AddParameter("string", _("URL (API or web-page address)"))
.SetParameterLongDescription(
_("Example: \"https://example.com/user/123\". Using *https* is "
"highly recommended."))
.AddParameter("string", _("Request body content"))
.AddParameter("stringWithSelector",
_("Resize mode"),
"[\"GET\", \"POST\", \"PUT\", \"HEAD\", \"DELETE\", "
"\"PATCH\", \"OPTIONS\"]",
false)
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
.SetDefaultValue("\"GET\"")
.AddParameter("string", _("Content type"), "", true)
.SetParameterLongDescription(
_("If empty, \"application/x-www-form-urlencoded\" will be used."))
.AddParameter(
"scenevar", _("Variable where to store the response"), "", true)
.SetParameterLongDescription(
_("The response of the server will be stored, as a string, in this "
"variable. If the server returns *JSON*, you may want to use the "
"action \"Convert JSON to a scene variable\" afterwards, to "
"explore the results with a *structure variable*."))
.AddParameter(
"scenevar", _("Variable where to store the error message"), "", true)
.SetParameterLongDescription(
_("Optional, only used if an error occurs. This will contain the "
"error message (if request could not be sent) or the [\"status "
"code\"](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes), "
"if the server returns a status >= 400."))
.MarkAsComplex();
extension
@@ -67,6 +113,22 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
_("Path to file (for example : /folder/file.txt)"))
.AddParameter("string", _("Save as"));
extension
.AddAction(
"EnableMetrics",
_("Enable (or disable) metrics collection"),
_("Enable, or disable, the sending of anonymous data used to compute "
"the number of sessions and other metrics from your game "
"players.\nBe sure to only send metrics if in accordance with the "
"terms of service of your game and if they player gave their "
"consent, depending on how your game/company handles this."),
_("Enable analytics metrics: _PARAM1_"),
_("Network"),
"res/actions/net24.png",
"res/actions/net.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("yesorno", _("Enable the metrics?"));
extension
.AddAction(
"JSONToVariableStructure",

View File

@@ -367,15 +367,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddAction(
"ChangeColor",
_("Global color"),
_("Change the global color of an object. The default color is white."),
_("Change color of _PARAM0_ to _PARAM1_"),
_("Tint color"),
_("Change the tint of an object. The default color is white."),
_("Change tint of _PARAM0_ to _PARAM1_"),
_("Effects"),
"res/actions/color24.png",
"res/actions/color.png")
.AddParameter("object", _("Object"), "Sprite")
.AddParameter("color", _("Color"));
.AddParameter("color", _("Tint"));
obj.AddAction("ChangeBlendMode",
_("Blend mode"),
@@ -417,7 +417,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddCondition("FlippedX",
_("Horizontally flipped"),
_("Return true if the object is horizontally flipped"),
_("Check if the object is horizontally flipped"),
_("_PARAM0_ is horizontally flipped"),
_("Effects"),
"res/actions/flipX24.png",
@@ -427,7 +427,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddCondition("FlippedY",
_("Vertically flipped"),
_("Return true if the object is vertically flipped"),
_("Check if the object is vertically flipped"),
_("_PARAM0_ is vertically flipped"),
_("Effects"),
"res/actions/flipY24.png",

View File

@@ -47,7 +47,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.AddCondition(
"VariableChildExists",
_("Child existence"),
_("Return true if the specified child of the scene variable exists."),
_("Check if the specified child of the scene variable exists."),
_("Child _PARAM1_ of scene variable _PARAM0_ exists"),
_("Variables/Structures"),
"res/conditions/var24.png",
@@ -59,7 +59,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension
.AddCondition("GlobalVariableChildExists",
_("Child existence"),
_("Return true if the specified child of the global "
_("Check if the specified child of the global "
"variable exists."),
_("Child _PARAM1_ of global variable _PARAM0_ exists"),
_("Variables/Global variables/Structures"),

View File

@@ -38,6 +38,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
true)
.SetDefaultValue("yes");
extension
.AddCondition(
"IsFullScreen",
_("Fullscreen activated?"),
_("Check if the game is currently in fullscreen."),
_("The game is in fullscreen"),
_("Game's window and resolution"),
"res/actions/fullscreen24.png",
"res/actions/fullscreen.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddAction("SetWindowMargins",
_("Change the window's margins"),

View File

@@ -0,0 +1,129 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef DEPENDENCYMETADATA_H
#define DEPENDENCYMETADATA_H
#include <map>
#include <set>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Contains information about a dependency (library, npm/cordova
* package, or other according to the export) of an extension.
*/
class GD_CORE_API DependencyMetadata {
public:
/**
* \brief Sets the name shown to users.
*/
DependencyMetadata& SetName(const gd::String& name_) {
name = name_;
return *this;
};
/**
* \brief Sets the name written by the exporter.
* Typically, this is what is used by the dependency manager
* to find the dependency.
*
* \example
* \code
* // For depending upon the NPM package is-thirteen
* gd::DependencyMetadata dependencyMetadata = gd::DependencyMetadata();
* dependencyMetadata.setExporterName("is-thirteen");
* \endcode
*/
DependencyMetadata& SetExportName(const gd::String& exportName_) {
exportName = exportName_;
return *this;
};
/**
* \brief Set the version of the dependency to install.
* Use an empty string to use the latest version.
*/
DependencyMetadata& SetVersion(const gd::String& version_) {
version = version_;
return *this;
};
/**
* \brief Sets the type of dependecy (what will be used to install it)
*
* This can either be "npm" or "cordova" for now.
*/
DependencyMetadata& SetDependencyType(const gd::String& dependencyType_) {
dependencyType = dependencyType_;
if (dependencyType != "npm" && dependencyType != "cordova") {
gd::LogWarning("Invalid dependency type: " + dependencyType);
}
return *this;
};
/**
* \brief Sets a dependency type specific setting.
*/
DependencyMetadata& SetExtraSetting(
const gd::String& settingName,
const gd::PropertyDescriptor& settingValue) {
extraData[settingName] = settingValue;
return *this;
};
/**
* \brief Mark the dependency to be included in the export only if the
* specified setting is not empty.
*
* If this is called for multiple settings, all settings must be fulfilled for
* the dependency to be exported.
*/
DependencyMetadata& OnlyIfExtraSettingIsNonEmpty(
const gd::String& settingName) {
nonEmptyExtraSettingsForExport.insert(settingName);
return *this;
};
/**
* \brief Get the list of extra settings that must be fulfilled for the
* dependency to be exported.
*/
const std::set<gd::String>& GetRequiredExtraSettingsForExport() const {
return nonEmptyExtraSettingsForExport;
};
const gd::String& GetName() const { return name; };
const gd::String& GetExportName() const { return exportName; };
const gd::String& GetVersion() const { return version; };
const gd::String& GetDependencyType() const {
if (dependencyType == "")
gd::LogWarning("Dependency has no type, it won't be exported.");
return dependencyType;
};
const std::map<gd::String, gd::PropertyDescriptor>& GetAllExtraSettings()
const {
return extraData;
}
private:
gd::String name; ///< The name of the dependency.
gd::String exportName; ///< The name used to install the package (example:
///< npm package name for npm dependency type).
gd::String version; ///< The version of the dependency
gd::String dependencyType; ///< The tool used to install the dependency.
std::map<gd::String, gd::PropertyDescriptor>
extraData; ///< Contains dependency type specific additional parameters
///< for the dependency.
std::set<gd::String>
nonEmptyExtraSettingsForExport; ///< The set of extra settings that must
///< be non empty for this dependency to
///< be included by the exporter.
};
} // namespace gd
#endif // DEPENDENCYMETADATA_H

View File

@@ -4,9 +4,12 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Extensions/PlatformExtension.h"
#include <algorithm>
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
@@ -117,6 +120,13 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
#endif
}
#if defined(GD_IDE_ONLY)
gd::DependencyMetadata& PlatformExtension::AddDependency() {
extensionDependenciesMetadata.push_back(DependencyMetadata());
return extensionDependenciesMetadata.back();
}
#endif
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
const gd::String& fullname,
@@ -216,8 +226,7 @@ std::vector<gd::String> PlatformExtension::GetExtensionObjectsTypes() const {
std::vector<gd::String> PlatformExtension::GetExtensionEffectTypes() const {
std::vector<gd::String> effectNames;
for (auto& it : effectsMetadata)
effectNames.push_back(it.first);
for (auto& it : effectsMetadata) effectNames.push_back(it.first);
return effectNames;
}
@@ -283,6 +292,10 @@ PlatformExtension::GetAllStrExpressions() {
return strExpressionsInfos;
}
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
return extensionDependenciesMetadata;
}
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
return eventsInfos;
}
@@ -404,7 +417,7 @@ void PlatformExtension::SetNameSpace(gd::String nameSpace_) {
name == "BuiltinCommonConversions" ||
name == "BuiltinStringInstructions" ||
name == "BuiltinMathematicalTools" ||
name == "Effects" || // Well-known effects are not namespaced.
name == "Effects" || // Well-known effects are not namespaced.
name == "CommonDialogs") // New name for BuiltinInterface
{
nameSpace = "";

View File

@@ -10,11 +10,14 @@
#include <map>
#include <memory>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
#include "GDCore/Tools/VersionPriv.h"
@@ -25,6 +28,7 @@ class ExpressionMetadata;
class ObjectMetadata;
class BehaviorMetadata;
class EffectMetadata;
class DependencyMetadata;
class BaseEvent;
class EventMetadata;
class EventCodeGenerator;
@@ -87,6 +91,14 @@ class GD_CORE_API PlatformExtension {
const gd::String& author_,
const gd::String& license_);
/**
* \brief Set the URL of the extension icon.
*/
PlatformExtension& SetIconUrl(const gd::String& iconUrl_) {
iconUrl = iconUrl_;
return *this;
}
/**
* \brief Set the path to the help, relative to the wiki/documentation root.
* For example, "/all-features/collisions" for
@@ -148,6 +160,8 @@ class GD_CORE_API PlatformExtension {
const gd::String& group_,
const gd::String& smallicon_);
gd::DependencyMetadata& AddDependency();
/**
* \brief Declare a new object as being part of the extension.
* \note This method does nothing when used for GD C++ runtime.
@@ -225,6 +239,15 @@ class GD_CORE_API PlatformExtension {
const gd::String& smallicon_,
std::shared_ptr<gd::BaseEvent> instance);
#if defined(GD_IDE_ONLY)
/**
* \brief Adds a property to the extension.
*/
gd::PropertyDescriptor& RegisterProperty(const gd::String& name) {
return extensionPropertiesMetadata[name];
};
#endif
/**
* \brief Return the name extension user friendly name.
*/
@@ -256,6 +279,12 @@ class GD_CORE_API PlatformExtension {
*/
const gd::String& GetHelpPath() const { return helpPath; }
/**
* \brief Return the URL to the icon to be displayed for this
* extension.
*/
const gd::String& GetIconUrl() const { return iconUrl; }
/**
* \brief Check if the extension is flagged as being deprecated.
*/
@@ -365,6 +394,12 @@ class GD_CORE_API PlatformExtension {
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
std::vector<gd::DependencyMetadata>& GetAllDependencies();
/**
* \brief Return a reference to a map containing the names of the actions,
* related to the object type, and the metadata associated with.
@@ -437,6 +472,13 @@ class GD_CORE_API PlatformExtension {
* generator.
*/
void StripUnimplementedInstructionsAndExpressions();
/**
* \brief Get all the properties of the extension
*/
std::map<gd::String, gd::PropertyDescriptor>& GetAllProperties() {
return extensionPropertiesMetadata;
}
#endif
/**
@@ -463,14 +505,15 @@ class GD_CORE_API PlatformExtension {
nameSpace; ///< Automatically set from the name of the extension, and
///< added to every
///< actions/conditions/expressions/objects/behavior/event.
gd::String fullname; ///< Name displayed to users at edittime
gd::String informations; ///< Description displayed to users at edittime
gd::String author; ///< Author displayed to users at edittime
gd::String license; ///< License name displayed to users at edittime
gd::String fullname; ///< Name displayed to users in the editor.
gd::String informations; ///< Description displayed to users in the editor.
gd::String author; ///< Author displayed to users in the editor.
gd::String license; ///< License name displayed to users in the editor.
bool deprecated; ///< true if the extension is deprecated and shouldn't be
///< shown in IDE.
gd::String helpPath; ///< The relative path to the help for this extension in
///< the documentation.
gd::String iconUrl; ///< The URL to the icon to be shown for this extension.
std::map<gd::String, gd::ObjectMetadata> objectsInfos;
std::map<gd::String, gd::BehaviorMetadata> behaviorsInfo;
@@ -480,7 +523,9 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
std::map<gd::String, gd::EventMetadata> eventsInfos;
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
#endif
ObjectMetadata badObjectMetadata;

View File

@@ -0,0 +1,61 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef GDCORE_RESOURCESRENAMER_H
#define GDCORE_RESOURCESRENAMER_H
#include <set>
#include <vector>
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/String.h"
namespace gd {
/**
* \brief Class used to rename resources (in an object, an entire project,
* etc...)
*
* \ingroup IDE
*/
class ResourcesRenamer : public gd::ArbitraryResourceWorker {
public:
/**
* @brief Constructor taking the map from old name to new name.
* @param oldToNewNames_ A map associating to a resource name the new name to
* use.
*/
ResourcesRenamer(const std::map<gd::String, gd::String>& oldToNewNames_)
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
virtual ~ResourcesRenamer(){};
virtual void ExposeFile(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
virtual void ExposeImage(gd::String& imageResourceName) override {
RenameIfNeeded(imageResourceName);
};
virtual void ExposeAudio(gd::String& audioResourceName) override {
RenameIfNeeded(audioResourceName);
};
virtual void ExposeFont(gd::String& fontResourceName) override {
RenameIfNeeded(fontResourceName);
};
private:
void RenameIfNeeded(gd::String& resourceName) {
if (oldToNewNames.find(resourceName) != oldToNewNames.end())
resourceName = oldToNewNames[resourceName];
}
std::map<gd::String, gd::String> oldToNewNames;
};
} // namespace gd
#endif // GDCORE_RESOURCESRENAMER_H
#endif

View File

@@ -35,6 +35,8 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
fullName = other.fullName;
tags = other.tags;
author = other.author;
previewIconUrl = other.previewIconUrl;
iconUrl = other.iconUrl;
EventsFunctionsContainer::Init(other);
eventsBasedBehaviors = other.eventsBasedBehaviors;
}
@@ -48,6 +50,8 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
element.SetAttribute("fullName", fullName);
element.SetAttribute("tags", tags);
element.SetAttribute("author", author);
element.SetAttribute("previewIconUrl", previewIconUrl);
element.SetAttribute("iconUrl", iconUrl);
SerializeEventsFunctionsTo(element.AddChild("eventsFunctions"));
eventsBasedBehaviors.SerializeElementsTo(
@@ -64,6 +68,8 @@ void EventsFunctionsExtension::UnserializeFrom(
fullName = element.GetStringAttribute("fullName");
tags = element.GetStringAttribute("tags");
author = element.GetStringAttribute("author");
previewIconUrl = element.GetStringAttribute("previewIconUrl");
iconUrl = element.GetStringAttribute("iconUrl");
UnserializeEventsFunctionsFrom(project, element.GetChild("eventsFunctions"));
eventsBasedBehaviors.UnserializeElementsFrom(

View File

@@ -95,6 +95,18 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
return *this;
}
const gd::String& GetPreviewIconUrl() const { return previewIconUrl; };
EventsFunctionsExtension& SetPreviewIconUrl(const gd::String& previewIconUrl_) {
previewIconUrl = previewIconUrl_;
return *this;
}
const gd::String& GetIconUrl() const { return iconUrl; };
EventsFunctionsExtension& SetIconUrl(const gd::String& iconUrl_) {
iconUrl = iconUrl_;
return *this;
}
/**
* \brief Return a reference to the list of the events based behaviors.
*/
@@ -146,6 +158,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::String fullName;
gd::String tags;
gd::String author;
gd::String previewIconUrl;
gd::String iconUrl;
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
};

View File

@@ -0,0 +1,56 @@
#include "ExtensionProperties.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
namespace gd {
const gd::String ExtensionProperties::defaultValue = "";
std::map<gd::String, gd::PropertyDescriptor>
ExtensionProperties::GetAllExtensionProperties(const gd::String& extensionName,
gd::Project& project) {
// Create a copy
std::map<gd::String, gd::PropertyDescriptor> props(
project.GetCurrentPlatform()
.GetExtension(extensionName)
->GetAllProperties());
// Set values
for (std::pair<gd::String, gd::PropertyDescriptor> property : props) {
if (properties.count(extensionName) > 0 &&
properties[extensionName].count(property.first) > 0) {
props[property.first].SetValue(properties[extensionName][property.first]);
}
}
return props;
};
void ExtensionProperties::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("extensionProperties");
for (const std::pair<gd::String, std::map<gd::String, gd::String>> extension :
properties) {
for (const std::pair<gd::String, gd::String> property : extension.second) {
SerializerElement& propertyElement =
element.AddChild("extensionProperties");
propertyElement.AddChild("extension").SetStringValue(extension.first);
propertyElement.AddChild("property").SetStringValue(property.first);
propertyElement.AddChild("value").SetStringValue(property.second);
}
}
};
void ExtensionProperties::UnserializeFrom(const SerializerElement& element) {
properties.clear();
element.ConsiderAsArrayOf("extensionProperties");
for (std::pair<const gd::String, std::shared_ptr<SerializerElement>>
extensionProperties : element.GetAllChildren()) {
std::shared_ptr<SerializerElement> extensionPropertiesElement =
extensionProperties.second;
properties
[extensionPropertiesElement->GetChild("extension").GetStringValue()]
[extensionPropertiesElement->GetChild("property").GetStringValue()] =
extensionPropertiesElement->GetChild("value").GetStringValue();
}
};
}; // namespace gd

View File

@@ -0,0 +1,69 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EXTENSIONPROPERTIES_H
#define GDCORE_EXTENSIONPROPERTIES_H
#include <map>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
namespace gd {
class Project;
class PropertyDescriptor;
} // namespace gd
namespace gd {
class GD_CORE_API ExtensionProperties {
static const gd::String defaultValue;
public:
const gd::String& GetValue(const gd::String& extension,
const gd::String& property) const {
if (properties.count(extension) == 0 ||
properties.at(extension).count(property) == 0) {
return ExtensionProperties::defaultValue;
}
return properties.at(extension).at(property);
};
void SetValue(const gd::String& extension,
const gd::String& property,
const gd::String& newValue) {
properties[extension][property] = newValue;
};
bool HasProperty(const gd::String& extension, const gd::String& property) {
for (std::pair<gd::String, gd::String> propertyPair :
properties[extension]) {
if (propertyPair.first == property) {
return true;
}
}
return false;
}
std::map<gd::String, gd::PropertyDescriptor> GetAllExtensionProperties(
const gd::String& extensionName, gd::Project& project);
///@{
/**
* \brief Serialize the Extension Properties.
*/
virtual void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the Extension Properties.
*/
virtual void UnserializeFrom(const SerializerElement& element);
///@}
private:
std::map<gd::String, std::map<gd::String, gd::String>>
properties; ///< The properties of the project
};
} // namespace gd
#endif // EXTENSIONPROPERTIES_H

View File

@@ -179,6 +179,8 @@ InitialInstanceFunctor::~InitialInstanceFunctor(){};
void HighestZOrderFinder::operator()(gd::InitialInstance& instance) {
if (!layerRestricted || instance.GetLayer() == layerName) {
instancesCount++;
if (firstCall) {
highestZOrder = instance.GetZOrder();
lowestZOrder = instance.GetZOrder();

View File

@@ -211,6 +211,7 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
HighestZOrderFinder()
: highestZOrder(0),
lowestZOrder(0),
instancesCount(0),
firstCall(true),
layerRestricted(false){};
virtual ~HighestZOrderFinder(){};
@@ -237,9 +238,16 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
*/
int GetLowestZOrder() const { return lowestZOrder; }
/**
* \brief After calling the instances container iterate method with this
* functor, this method will return the number of instances.
*/
size_t GetInstancesCount() const { return instancesCount; }
private:
int highestZOrder;
int lowestZOrder;
size_t instancesCount;
bool firstCall;
bool layerRestricted; ///< If true, the search is restricted to the layer

View File

@@ -13,7 +13,7 @@ namespace gd {
Camera Layer::badCamera;
Effect Layer::badEffect;
Layer::Layer() : isVisible(true) {}
Layer::Layer() : isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
/**
* Change cameras count, automatically adding/removing them.
@@ -29,6 +29,11 @@ void Layer::SetCameraCount(std::size_t n) {
void Layer::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("visibility", GetVisibility());
element.SetAttribute("isLightingLayer", IsLightingLayer());
element.SetAttribute("followBaseLayerCamera", IsFollowingBaseLayerCamera());
element.SetAttribute("ambientLightColorR", (int)GetAmbientLightColorRed());
element.SetAttribute("ambientLightColorG", (int)GetAmbientLightColorGreen());
element.SetAttribute("ambientLightColorB", (int)GetAmbientLightColorBlue());
SerializerElement& camerasElement = element.AddChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
@@ -61,6 +66,11 @@ void Layer::SerializeTo(SerializerElement& element) const {
void Layer::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name", "", "Name"));
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
SetFollowBaseLayerCamera(element.GetBoolAttribute("followBaseLayerCamera", false));
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
element.GetIntAttribute("ambientLightColorG", 200),
element.GetIntAttribute("ambientLightColorB", 200));
// Compatibility with GD <= 3.3
if (element.HasChild("Camera")) {

View File

@@ -51,6 +51,26 @@ class GD_CORE_API Layer {
*/
bool GetVisibility() const { return isVisible; }
/**
* \brief Set if the layer is a lightining layer or not.
*/
void SetLightingLayer(bool isLightingLayer_) { isLightingLayer = isLightingLayer_; }
/**
* \brief Return true if the layer is a lighting layer.
*/
bool IsLightingLayer() const { return isLightingLayer; }
/**
* \brief Set if the layer automatically follows the base layer or not.
*/
void SetFollowBaseLayerCamera(bool followBaseLayerCamera_) { followBaseLayerCamera = followBaseLayerCamera_; }
/**
* \brief Return true if the layer follows the base layer.
*/
bool IsFollowingBaseLayerCamera() const { return followBaseLayerCamera; }
/** \name Cameras
*/
///@{
@@ -96,6 +116,30 @@ class GD_CORE_API Layer {
///@}
/**
* Get the ambient light color red component.
*/
unsigned int GetAmbientLightColorRed() const { return ambientLightColorR; }
/**
* Get the ambient light color green component.
*/
unsigned int GetAmbientLightColorGreen() const { return ambientLightColorG; }
/**
* Get the ambient light color blue component.
*/
unsigned int GetAmbientLightColorBlue() const { return ambientLightColorB; }
/**
* Set the ambient light color.
*/
void SetAmbientLightColor(unsigned int r, unsigned int g, unsigned int b) {
ambientLightColorR = r;
ambientLightColorG = g;
ambientLightColorB = b;
}
/** \name Effects
*/
///@{
@@ -177,6 +221,11 @@ class GD_CORE_API Layer {
private:
gd::String name; ///< The name of the layer
bool isVisible; ///< True if the layer is visible
bool isLightingLayer; ///< True if the layer is used to display lights and renders an ambient light.
bool followBaseLayerCamera; ///< True if the layer automatically follows the base layer
unsigned int ambientLightColorR; ///< Ambient light color Red component
unsigned int ambientLightColorG; ///< Ambient light color Green component
unsigned int ambientLightColorB; ///< Ambient light color Blue component
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
std::vector<std::shared_ptr<gd::Effect>>
effects; ///< The effects applied to the layer.

View File

@@ -23,4 +23,14 @@ void NamedPropertyDescriptor::UnserializeFrom(
name = element.GetChild("name").GetStringValue();
}
void NamedPropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
PropertyDescriptor::SerializeValuesTo(element);
element.AddChild("name").SetStringValue(name);
}
void NamedPropertyDescriptor::UnserializeValuesFrom(const SerializerElement& element) {
PropertyDescriptor::UnserializeValuesFrom(element);
name = element.GetChild("name").GetStringValue();
}
} // namespace gd

View File

@@ -58,6 +58,16 @@ class GD_CORE_API NamedPropertyDescriptor : public PropertyDescriptor {
* \brief Unserialize the NamedPropertyDescriptor.
*/
void UnserializeFrom(const SerializerElement& element);
/**
* \brief Serialize only the value and extra informations of the property.
*/
virtual void SerializeValuesTo(SerializerElement& element) const;
/**
* \brief Unserialize only the value and extra information of the property.
*/
virtual void UnserializeValuesFrom(const SerializerElement& element);
///@}
/**

View File

@@ -5,13 +5,16 @@
*/
#include "Project.h"
#include <stdio.h>
#include <stdlib.h>
#include <cctype>
#include <SFML/System/Utf.hpp>
#include <cctype>
#include <fstream>
#include <map>
#include <vector>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
@@ -35,6 +38,7 @@
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Tools/UUID/UUID.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "GDCore/Utf8/utf8.h"
@@ -51,7 +55,6 @@ Project::Project()
version("1.0.0"),
packageName("com.example.gamename"),
orientation("landscape"),
adMobAppId(""),
folderProject(false),
#endif
windowWidth(800),
@@ -62,6 +65,8 @@ Project::Project()
scaleMode("linear"),
adaptGameResolutionAtRuntime(true),
sizeOnStartupMode("adaptWidth"),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
imageManager(std::make_shared<ImageManager>())
#if defined(GD_IDE_ONLY)
,
@@ -104,6 +109,8 @@ Project::Project()
Project::~Project() {}
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& type,
const gd::String& name,
@@ -592,13 +599,12 @@ void Project::UnserializeFrom(const SerializerElement& element) {
SetAdaptGameResolutionAtRuntime(
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
#if defined(GD_IDE_ONLY)
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
SetPackageName(propElement.GetStringAttribute("packageName"));
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
SetAdMobAppId(propElement.GetStringAttribute("adMobAppId", ""));
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
SetProjectFile(propElement.GetStringAttribute("projectFile"));
SetLastCompilationDirectory(propElement
.GetChild("latestCompilationDirectory",
0,
@@ -608,16 +614,41 @@ void Project::UnserializeFrom(const SerializerElement& element) {
platformSpecificAssets.UnserializeFrom(
propElement.GetChild("platformSpecificAssets"));
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
winExecutableFilename =
propElement.GetStringAttribute("winExecutableFilename");
winExecutableIconFile =
propElement.GetStringAttribute("winExecutableIconFile");
linuxExecutableFilename =
propElement.GetStringAttribute("linuxExecutableFilename");
macExecutableFilename =
propElement.GetStringAttribute("macExecutableFilename");
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
// Compatibility with GD <= 5.0.0-beta101
if (VersionWrapper::IsOlderOrEqual(
gdMajorVersion, gdMinorVersion, gdBuildVersion, 0, 4, 0, 98, 0) &&
!propElement.HasAttribute("useDeprecatedZeroAsDefaultZOrder")) {
useDeprecatedZeroAsDefaultZOrder = true;
} else {
useDeprecatedZeroAsDefaultZOrder =
propElement.GetBoolAttribute("useDeprecatedZeroAsDefaultZOrder", false);
}
// end of compatibility code
// Compatibility with GD <= 5.0.0-beta101
if (!propElement.HasAttribute("projectUuid") &&
!propElement.HasChild("projectUuid")) {
ResetProjectUuid();
}
// end of compatibility code
extensionProperties.UnserializeFrom(
propElement.GetChild("extensionProperties"));
// Compatibility with GD <= 5.0.0-beta98
// Move AdMob App ID from project property to extension property.
if (propElement.GetStringAttribute("adMobAppId", "") != "") {
extensionProperties.SetValue(
"AdMob",
"AdMobAppId",
propElement.GetStringAttribute("adMobAppId", ""));
}
// end of compatibility code
#endif
const SerializerElement& extensionsElement =
@@ -866,22 +897,26 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("verticalSync")
.SetValue(IsVerticalSynchronizationEnabledByDefault());
propElement.SetAttribute("scaleMode", scaleMode);
propElement.SetAttribute("adaptGameResolutionAtRuntime", adaptGameResolutionAtRuntime);
propElement.SetAttribute("adaptGameResolutionAtRuntime",
adaptGameResolutionAtRuntime);
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
propElement.SetAttribute("projectFile", gameFile);
propElement.SetAttribute("projectUuid", projectUuid);
propElement.SetAttribute("folderProject", folderProject);
propElement.SetAttribute("packageName", packageName);
propElement.SetAttribute("orientation", orientation);
propElement.SetAttribute("adMobAppId", adMobAppId);
platformSpecificAssets.SerializeTo(
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
propElement.SetAttribute("winExecutableFilename", winExecutableFilename);
propElement.SetAttribute("winExecutableIconFile", winExecutableIconFile);
propElement.SetAttribute("linuxExecutableFilename", linuxExecutableFilename);
propElement.SetAttribute("macExecutableFilename", macExecutableFilename);
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
// Compatibility with GD <= 5.0.0-beta101
if (useDeprecatedZeroAsDefaultZOrder) {
propElement.SetAttribute("useDeprecatedZeroAsDefaultZOrder", true);
}
// end of compatibility code
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
SerializerElement& extensionsElement = propElement.AddChild("extensions");
extensionsElement.ConsiderAsArrayOf("extension");
for (std::size_t i = 0; i < GetUsedExtensions().size(); ++i)
@@ -1055,7 +1090,6 @@ Project& Project::operator=(const Project& other) {
}
void Project::Init(const gd::Project& game) {
// Some properties
name = game.name;
version = game.version;
windowWidth = game.windowWidth;
@@ -1066,18 +1100,21 @@ void Project::Init(const gd::Project& game) {
scaleMode = game.scaleMode;
adaptGameResolutionAtRuntime = game.adaptGameResolutionAtRuntime;
sizeOnStartupMode = game.sizeOnStartupMode;
projectUuid = game.projectUuid;
useDeprecatedZeroAsDefaultZOrder = game.useDeprecatedZeroAsDefaultZOrder;
#if defined(GD_IDE_ONLY)
author = game.author;
packageName = game.packageName;
orientation = game.orientation;
adMobAppId = game.adMobAppId;
folderProject = game.folderProject;
latestCompilationDirectory = game.latestCompilationDirectory;
platformSpecificAssets = game.platformSpecificAssets;
loadingScreen = game.loadingScreen;
objectGroups = game.objectGroups;
extensionProperties = game.extensionProperties;
gdMajorVersion = game.gdMajorVersion;
gdMinorVersion = game.gdMinorVersion;
gdBuildVersion = game.gdBuildVersion;
@@ -1087,7 +1124,6 @@ void Project::Init(const gd::Project& game) {
extensionsUsed = game.extensionsUsed;
platforms = game.platforms;
// Resources
resourcesManager = game.resourcesManager;
imageManager = std::make_shared<ImageManager>(*game.imageManager);
imageManager->SetResourcesManager(&resourcesManager);
@@ -1112,13 +1148,8 @@ void Project::Init(const gd::Project& game) {
variables = game.GetVariables();
#if defined(GD_IDE_ONLY)
gameFile = game.GetProjectFile();
projectFile = game.GetProjectFile();
imagesChanged = game.imagesChanged;
winExecutableFilename = game.winExecutableFilename;
winExecutableIconFile = game.winExecutableIconFile;
linuxExecutableFilename = game.linuxExecutableFilename;
macExecutableFilename = game.macExecutableFilename;
#endif
}

View File

@@ -9,6 +9,7 @@
#include <memory>
#include <vector>
#include "GDCore/Project/ExtensionProperties.h"
#include "GDCore/Project/LoadingScreen.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ObjectsContainer.h"
@@ -114,30 +115,16 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetOrientation() const { return orientation; }
/**
* \brief Change the project AdMob application ID (needed
* to use the AdMob extension). This has no effect on desktop
* and web browsers.
*/
void SetAdMobAppId(const gd::String& adMobAppId_) {
adMobAppId = adMobAppId_;
};
/**
* \brief Get the project AdMob application ID.
*/
const gd::String& GetAdMobAppId() const { return adMobAppId; }
/**
* Called when project file has changed.
*/
void SetProjectFile(const gd::String& file) { gameFile = file; }
void SetProjectFile(const gd::String& file) { projectFile = file; }
/**
* Return project file
* \see gd::Project::SetProjectFile
*/
const gd::String& GetProjectFile() const { return gameFile; }
const gd::String& GetProjectFile() const { return projectFile; }
/**
* Set that the project should be saved as a folder project.
@@ -290,6 +277,42 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
void SetScaleMode(const gd::String& scaleMode_) { scaleMode = scaleMode_; }
/**
* \brief Return if the project should set 0 as Z-order for objects created
* from events (which is deprecated) - instead of the highest Z order that was
* found on each layer when the scene started.
*/
bool GetUseDeprecatedZeroAsDefaultZOrder() const {
return useDeprecatedZeroAsDefaultZOrder;
}
/**
* \brief Set if the project should set 0 as Z-order for objects created from
* events (which is deprecated) - instead of the highest Z order that was
* found on each layer when the scene started.
*/
void SetUseDeprecatedZeroAsDefaultZOrder(bool enable) {
useDeprecatedZeroAsDefaultZOrder = enable;
}
/**
* \brief Change the project UUID.
*/
void SetProjectUuid(const gd::String& projectUuid_) {
projectUuid = projectUuid_;
};
/**
* \brief Get the project UUID, useful when using the game on online services
* that would require a unique identifier.
*/
const gd::String& GetProjectUuid() const { return projectUuid; }
/**
* \brief Create a new project UUID.
*/
void ResetProjectUuid();
/**
* Return a reference to the vector containing the names of extensions used by
* the project.
@@ -305,6 +328,26 @@ class GD_CORE_API Project : public ObjectsContainer {
std::vector<gd::String>& GetUsedExtensions() { return extensionsUsed; };
#if defined(GD_IDE_ONLY)
/**
* \brief Get the properties set by extensions.
*
* Each extension can store arbitrary values indexed by a property name, which
* are useful to store project wide settings (AdMob id, etc...).
*/
gd::ExtensionProperties& GetExtensionProperties() {
return extensionProperties;
};
/**
* \brief Get the properties set by extensions.
*
* Each extension can store arbitrary values indexed by a property name, which
* are useful to store project wide settings (AdMob id, etc...).
*/
const gd::ExtensionProperties& GetExtensionProperties() const {
return extensionProperties;
};
/**
* Return the list of platforms used by the project.
*/
@@ -916,10 +959,6 @@ class GD_CORE_API Project : public ObjectsContainer {
#if defined(GD_IDE_ONLY)
std::vector<gd::String> imagesChanged; ///< Images that have been changed and
///< which have to be reloaded
gd::String winExecutableFilename; ///< Windows executable name
gd::String winExecutableIconFile; ///< Icon for Windows executable
gd::String linuxExecutableFilename; ///< Linux executable name
gd::String macExecutableFilename; ///< Mac executable name
#endif
private:
@@ -941,8 +980,15 @@ class GD_CORE_API Project : public ObjectsContainer {
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
gd::String projectUuid; ///< UUID useful to identify the game in online
///< services or database that would require it.
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from
///< events will have 0 as Z order,
///< instead of the highest Z order
///< found on the layer at the scene
///< startup.
std::vector<std::unique_ptr<gd::Layout> > scenes; ///< List of all scenes
gd::VariablesContainer variables; ///< Initial global variables
std::vector<std::unique_ptr<gd::ExternalLayout> >
@@ -968,17 +1014,19 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::String packageName; ///< Game package name
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
gd::String adMobAppId; ///< AdMob application ID.
bool
folderProject; ///< True if folder project, false if single file project.
gd::String gameFile; ///< File of the game
gd::String latestCompilationDirectory; ///< File of the game
gd::String
projectFile; ///< Path to the project file - when editing a local file.
gd::String latestCompilationDirectory;
gd::Platform*
currentPlatform; ///< The platform being used to edit the project.
gd::PlatformSpecificAssets platformSpecificAssets;
gd::LoadingScreen loadingScreen;
std::vector<std::unique_ptr<gd::ExternalEvents> >
externalEvents; ///< List of all externals events
externalEvents; ///< List of all externals events
ExtensionProperties
extensionProperties; ///< The properties of the extensions.
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
///< time the project was saved.
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last

View File

@@ -4,7 +4,9 @@
* reserved. This project is released under the MIT License.
*/
#include "PropertyDescriptor.h"
#include <vector>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -45,4 +47,27 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
: false;
}
void PropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
element.AddChild("value").SetStringValue(currentValue);
SerializerElement& extraInformationElement =
element.AddChild("extraInformation");
extraInformationElement.ConsiderAsArray();
for (const gd::String& information : extraInformation) {
extraInformationElement.AddChild("").SetStringValue(information);
}
}
void PropertyDescriptor::UnserializeValuesFrom(
const SerializerElement& element) {
currentValue = element.GetChild("value").GetStringValue();
extraInformation.clear();
const SerializerElement& extraInformationElement =
element.GetChild("extraInformation");
extraInformationElement.ConsiderAsArray();
for (std::size_t i = 0; i < extraInformationElement.GetChildrenCount(); ++i)
extraInformation.push_back(
extraInformationElement.GetChild(i).GetStringValue());
}
} // namespace gd

View File

@@ -6,6 +6,7 @@
#ifndef GDCORE_PROPERTYDESCRIPTOR
#define GDCORE_PROPERTYDESCRIPTOR
#include <vector>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
@@ -120,6 +121,16 @@ class GD_CORE_API PropertyDescriptor {
* \brief Unserialize the PropertyDescriptor.
*/
virtual void UnserializeFrom(const SerializerElement& element);
/**
* \brief Serialize only the value and extra informations.
*/
virtual void SerializeValuesTo(SerializerElement& element) const;
/**
* \brief Unserialize only the value and extra informations.
*/
virtual void UnserializeValuesFrom(const SerializerElement& element);
///@}
private:
@@ -127,7 +138,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
gd::String label; //< The user-friendly property name
gd::String description; //< The user-friendly property description
std::vector<gd::String>
extraInformation; ///< Can be used to store for example the available

View File

@@ -5,8 +5,10 @@
*/
#include "GDCore/Project/ResourcesManager.h"
#include <iostream>
#include <map>
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
@@ -20,6 +22,7 @@ namespace gd {
gd::String Resource::badStr;
Resource ResourcesManager::badResource;
gd::String ResourcesManager::badResourceName;
#if defined(GD_IDE_ONLY)
ResourceFolder ResourcesManager::badFolder;
Resource ResourceFolder::badResource;
@@ -90,6 +93,33 @@ bool ResourcesManager::HasResource(const gd::String& name) const {
return false;
}
const gd::String& ResourcesManager::GetResourceNameWithOrigin(
const gd::String& originName, const gd::String& originIdentifier) const {
for (const auto& resource : resources) {
if (!resource) continue;
if (resource->GetOriginName() == originName &&
resource->GetOriginIdentifier() == originIdentifier) {
return resource->GetName();
}
}
return badResourceName;
}
const gd::String& ResourcesManager::GetResourceNameWithFile(
const gd::String& file) const {
for (const auto& resource : resources) {
if (!resource) continue;
if (resource->GetFile() == file) {
return resource->GetName();
}
}
return badResourceName;
}
std::vector<gd::String> ResourcesManager::GetAllResourceNames() const {
std::vector<gd::String> allResources;
for (std::size_t i = 0; i < resources.size(); ++i)
@@ -104,7 +134,8 @@ std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties() const {
return nothing;
}
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Smooth the image")]
.SetValue(smooth ? "true" : "false")
@@ -413,6 +444,15 @@ void ResourcesManager::UnserializeFrom(const SerializerElement& element) {
std::shared_ptr<Resource> resource = CreateResource(kind);
resource->SetName(name);
resource->SetMetadata(metadata);
if (resourceElement.HasChild("origin")) {
gd::String originName =
resourceElement.GetChild("origin").GetStringAttribute("name", "");
gd::String originIdentifier =
resourceElement.GetChild("origin").GetStringAttribute("identifier",
"");
resource->SetOrigin(originName, originIdentifier);
}
resource->UnserializeFrom(resourceElement);
resources.push_back(resource);
@@ -444,6 +484,14 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
resourceElement.SetAttribute("name", resources[i]->GetName());
resourceElement.SetAttribute("metadata", resources[i]->GetMetadata());
const gd::String& originName = resources[i]->GetOriginName();
const gd::String& originIdentifier = resources[i]->GetOriginIdentifier();
if (!originName.empty() || !originIdentifier.empty()) {
resourceElement.AddChild("origin")
.SetAttribute("name", originName)
.SetAttribute("identifier", originIdentifier);
}
resources[i]->SerializeTo(resourceElement);
}
@@ -560,7 +608,8 @@ void JsonResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("disablePreload", IsPreloadDisabled());
}
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["disablePreload"]
.SetValue(disablePreload ? "true" : "false")

View File

@@ -79,6 +79,17 @@ class GD_CORE_API Resource {
*/
virtual void SetFile(const gd::String& newFile){};
/**
* TODO: make a ResourceOrigin object?
*/
virtual void SetOrigin(const gd::String& originName_, const gd::String& originIdentifier_) {
originName = originName_;
originIdentifier = originIdentifier_;
}
virtual const gd::String& GetOriginName() const { return originName; }
virtual const gd::String& GetOriginIdentifier() const { return originIdentifier; }
/**
* \brief Set the metadata (any string) associated to the resource.
* \note Can be used by external editors to store extra information, for
@@ -142,6 +153,8 @@ class GD_CORE_API Resource {
gd::String kind;
gd::String name;
gd::String metadata;
gd::String originName;
gd::String originIdentifier;
bool userAdded; ///< True if the resource was added by the user, and not
///< automatically by GDevelop.
@@ -354,6 +367,18 @@ class GD_CORE_API ResourcesManager {
*/
bool HasResource(const gd::String& name) const;
/**
* \brief Return the name of the resource with the given origin, if any.
* If not found, an empty string is returned.
*/
const gd::String& GetResourceNameWithOrigin(const gd::String& originName, const gd::String& originIdentifier) const;
/**
* \brief Return the name of the first resource with the given file, if any.
* If not found, an empty string is returned.
*/
const gd::String& GetResourceNameWithFile(const gd::String& file) const;
/**
* \brief Return a reference to a resource.
*/
@@ -487,6 +512,7 @@ class GD_CORE_API ResourcesManager {
static ResourceFolder badFolder;
#endif
static Resource badResource;
static gd::String badResourceName;
};
#if defined(GD_IDE_ONLY)

View File

@@ -84,6 +84,18 @@ void SetupProjectWithDummyPlatform(gd::Project &project,
.AddParameter("string", "")
.AddParameter("expression", "", "", true)
.SetFunctionName("getNumberWith3Params");
extension
->AddStrExpression("GetStringWith2ObjectParamAnd2ObjectVarParam",
"Get string with twice an object param and an objectvar param",
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("objectvar", _("Variable for object 1"))
.AddParameter("object", _("Object 2 parameter"))
.AddParameter("objectvar", _("Variable for object 2"))
.SetFunctionName("getStringWith2ObjectParamAnd2ObjectVarParam");
auto &object = extension->AddObject<gd::Object>(
"Sprite", "Dummy Sprite", "Dummy sprite object", "");
object

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "DummyPlatform.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
@@ -823,37 +824,41 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
SECTION("Valid object function name") {
auto node = parser.ParseExpression("string", "MyObject.MyFunc");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyFunc");
auto node = parser.ParseExpression("string", "MyObject.MyFunc");
REQUIRE(node != nullptr);
auto &objectFunctionName =
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyFunc");
}
SECTION("Valid object behavior name") {
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::MyFunc");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
REQUIRE(objectFunctionName.behaviorFunctionName == "MyFunc");
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::MyFunc");
REQUIRE(node != nullptr);
auto &objectFunctionName =
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
REQUIRE(objectFunctionName.behaviorFunctionName == "MyFunc");
}
SECTION("Unfinished object function name") {
auto node = parser.ParseExpression("string", "MyObject.");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
auto node = parser.ParseExpression("string", "MyObject.");
REQUIRE(node != nullptr);
auto &objectFunctionName =
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
}
SECTION("Unfinished object behavior name") {
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
REQUIRE(objectFunctionName.behaviorFunctionName == "");
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
REQUIRE(node != nullptr);
auto &objectFunctionName =
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
REQUIRE(objectFunctionName.behaviorFunctionName == "");
}
SECTION("Invalid function calls") {
@@ -948,7 +953,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Invalid behavior function call, finishing with namespace separator") {
SECTION(
"Invalid behavior function call, finishing with namespace separator") {
{
auto node = parser.ParseExpression("number", "MyObject.MyBehavior::(12)");
REQUIRE(node != nullptr);
@@ -1031,6 +1037,55 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Valid function call with object variable") {
{
// Note that in this test we need to use an expression with "objectvar",
// as the grammar of the parser depends on this parameter type
// information.
auto node = parser.ParseExpression(
"string",
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObject1, "
"MyVar1, MyObject2, MyVar2)");
REQUIRE(node != nullptr);
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
auto &identifierObject1Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[0]);
auto &variable1Node =
dynamic_cast<gd::VariableNode &>(*functionNode.parameters[1]);
auto &identifierObject2Node =
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
auto &variable2Node =
dynamic_cast<gd::VariableNode &>(*functionNode.parameters[3]);
REQUIRE(identifierObject1Node.identifierName == "MyObject1");
REQUIRE(identifierObject2Node.identifierName == "MyObject2");
REQUIRE(variable1Node.objectName == "MyObject1");
REQUIRE(variable1Node.name == "MyVar1");
REQUIRE(variable2Node.objectName == "MyObject2");
REQUIRE(variable2Node.name == "MyVar2");
}
}
SECTION("Invalid function call with object variable") {
{
// Note that in this test we need to use an expression with "objectvar",
// as the grammar of the parser depends on this parameter type
// information.
auto node = parser.ParseExpression(
"string",
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(My "
"badly/written object1, MyVar1, MyObject2, MyVar2)");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator;
node->Visit(validator);
REQUIRE(validator.GetErrors().size() == 1);
REQUIRE(validator.GetErrors()[0]->GetMessage() ==
"An object name was expected but something else was written. "
"Enter just the name of the object for this parameter.");
}
}
SECTION("Fuzzy/random tests") {
{
auto testExpression = [&parser](const gd::String &expression) {

View File

@@ -0,0 +1,33 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Project/ResourcesRenamer.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Project.h"
#include "catch.hpp"
TEST_CASE("ResourcesRenamer", "[common]") {
SECTION("It renames resources that are exposed") {
std::map<gd::String, gd::String> renamings = {
{"Resource1", "RenamedResource1"}};
gd::ResourcesRenamer resourcesRenamer(renamings);
gd::Project project;
project.GetPlatformSpecificAssets().Set(
"android", "some-icon", "Resource1");
project.GetPlatformSpecificAssets().Set(
"android", "some-other-icon", "Resource2");
project.ExposeResources(resourcesRenamer);
REQUIRE(project.GetPlatformSpecificAssets().Get("android", "some-icon") ==
"RenamedResource1");
REQUIRE(project.GetPlatformSpecificAssets().Get(
"android", "some-other-icon") == "Resource2");
}
}

View File

@@ -2,34 +2,38 @@
# do it if not already cloned to avoid triggering a whole
# recompilation every time CMake is run.
find_package(Git)
if(GIT_FOUND AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
message( "Cloning SFML in ExtLibs/SFML with Git..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
OUTPUT_QUIET)
if(GIT_FOUND)
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
message( "Cloning SFML in ExtLibs/SFML with Git..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
OUTPUT_QUIET)
message( "Resetting SFML source code to version 2.4.1..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
message( "Resetting SFML source code to version 2.4.1..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
message( "Applying the patches..." )
file(GLOB SFML_PATCHES
LIST_DIRECTORIES FALSE
${CMAKE_CURRENT_SOURCE_DIR}/SFML-patches/*.patch)
message( "Applying the patches..." )
file(GLOB SFML_PATCHES
LIST_DIRECTORIES FALSE
${CMAKE_CURRENT_SOURCE_DIR}/SFML-patches/*.patch)
if(SFML_PATCHES)
list(SORT SFML_PATCHES)
if(SFML_PATCHES)
list(SORT SFML_PATCHES)
foreach(SFML_PATCH ${SFML_PATCHES})
message( "Applying patch: ${SFML_PATCH}..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
endforeach()
foreach(SFML_PATCH ${SFML_PATCHES})
message( "Applying patch: ${SFML_PATCH}..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
endforeach()
endif()
else()
message( "SFML already downloaded." )
endif()
else()
message( "Git not found, make sure you have SFML >= 2.4 in ExtLibs/SFML and you applied the needed patches (from ExtLibs/SFML-patches)!" )

View File

@@ -20,7 +20,10 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
*/
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'AdMob',
@@ -32,6 +35,24 @@ module.exports = {
'MIT'
);
extension
.registerProperty('AdMobAppId')
.setLabel(_('AdMob App ID'))
.setDescription('ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY')
.setType('string');
extension
.addDependency()
.setName('AdMob Cordova Extension')
.setDependencyType('cordova')
.setExportName('cordova-plugin-admob-free')
.setVersion('~0.21.0')
.setExtraSetting(
'ADMOB_APP_ID',
new gd.PropertyDescriptor('AdMobAppId').setType('ExtensionProperty')
)
.onlyIfExtraSettingIsNonEmpty('ADMOB_APP_ID');
// Banner
extension
.addCondition(
@@ -364,7 +385,10 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

@@ -0,0 +1,676 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'AdvancedWindow',
_('Advanced window management'),
_(
'Provides advanced features related to the game window positioning and interaction with the operating system.'
),
'Arthur Pacaud (arthuro555)',
'MIT'
);
extension
.addAction(
'Focus',
_('Change focus of the window'),
_('Make the window gain or lose focus.'),
_('Focus the window: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Focus the window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.focus');
extension
.addCondition(
'IsFocused',
_('Window focused'),
_('Checks if the window is focused.'),
_('The window is focused'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isFocused');
extension
.addAction(
'Show',
_('Change visibility of the window'),
_('Make the window visible or invisible.'),
_('Window visible: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Show window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.show');
extension
.addCondition(
'IsVisible',
_('Window visibile'),
_('Checks if the window is visible.'),
_('The window is visible'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isVisible');
extension
.addAction(
'Maximize',
_('Maximize the window'),
_('Maximize or unmaximize the window.'),
_('Maximize window: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Maximize window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.maximize');
extension
.addCondition(
'IsMaximized',
_('Window maximized'),
_('Checks if the window is maximized.'),
_('The window is maximized'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isMaximized');
extension
.addAction(
'Minimize',
_('Minimize the window'),
_('Minimize or unminimize the window.'),
_('Minimize window: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Minimize window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.minimize');
extension
.addCondition(
'IsMinimized',
_('Window minimized'),
_('Checks if the window is minimized.'),
_('The window is minimized'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isMinimized');
extension
.addAction(
'EnableWindow',
_('Enable the window'),
_('Enables or disables the window.'),
_('Enable window: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Enable window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.enable');
extension
.addCondition(
'IsWindowEnabled',
_('Window enabled'),
_('Checks if the window is enabled.'),
_('The window is enabled'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isEnabled');
extension
.addAction(
'SetResizable',
_('Allow resizing'),
_('Enables or disables resizing of the window by the user.'),
_('Enable window resizing: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow resizing?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setResizable');
extension
.addCondition(
'IsResizable',
_('Window resizable'),
_('Checks if the window can be resized.'),
_('The window can be resized'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isResizable');
extension
.addAction(
'SetMovable',
_('Allow moving'),
_('Enables or disables moving of the window by the user.'),
_('Enable window moving: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow moving?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setMovable');
extension
.addCondition(
'IsMovable',
_('Window movable'),
_('Checks if the window can be moved.'),
_('The window can be moved'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isMovable');
extension
.addAction(
'SetMaximizable',
_('Allow maximizing'),
_('Enables or disables maximizing of the window by the user.'),
_('Enable window maximizing: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow maximizing?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setMaximizable');
extension
.addCondition(
'IsMaximizable',
_('Window maximizable'),
_('Checks if the window can be maximized.'),
_('The window can be maximized'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isMaximizable');
extension
.addAction(
'SetMinimizable',
_('Allow mimizing'),
_('Enables or disables minimizing of the window by the user.'),
_('Enable window minimizing: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow minimizing?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setMinimizable');
extension
.addCondition(
'IsMinimizable',
_('Window minimizable'),
_('Checks if the window can be minimized.'),
_('The window can be minimized'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isMinimizable');
extension
.addAction(
'SetFullScreenable',
_('Allow full-screening'),
_('Enables or disables full-screening of the window by the user.'),
_('Enable window full-screening: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow full-screening?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setFullScreenable');
extension
.addCondition(
'IsFullScreenable',
_('Window full-screenable'),
_('Checks if the window can be full-screened.'),
_('The window can be set in fullscreen'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isFullScreenable');
extension
.addAction(
'SetClosable',
_('Allow closing'),
_('Enables or disables closing of the window by the user.'),
_('Enable window closing: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow closing?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setClosable');
extension
.addCondition(
'IsClosable',
_('Window closable'),
_('Checks if the window can be closed.'),
_('The window can be closed'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isClosable');
const levelChoices = JSON.stringify([
'normal',
'floating',
'torn-off-menu',
'modal-panel',
'main-menu',
'status',
'pop-up-menu',
'screen-saver',
]);
extension
.addAction(
'SetAlwaysOnTop',
_('Make the windows always on top'),
_('Puts the window constantly above all other windows.'),
_('Make window always on top: _PARAM0_, level: _PARAM1_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Enable always on top?'), '', false)
.setDefaultValue('true')
.addParameter('stringWithSelector', _('Level'), levelChoices, false)
.setDefaultValue('floating')
.setParameterLongDescription(
'The level is like a layer in GDevelop but for the OS. ' +
'The further down the list, the higher it will be. ' +
'When disabling always on top, the level will be set to normal. ' +
'From "floating" to "status" included, ' +
'the window is placed below the Dock on macOS and below the taskbar on Windows. ' +
'Starting from "pop-up-menu", it is shown above the Dock on macOS and ' +
'above the taskbar on Windows. ' +
'This parameter is ignored on linux.'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setAlwaysOnTop');
extension
.addCondition(
'IsAlwaysOnTop',
_('Window always on top'),
_('Checks if the window is always on top.'),
_('The window is always on top'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isAlwaysOnTop');
extension
.addAction(
'SetKiosk',
_('Enable kiosk mode'),
_(
'Puts the window in kiosk mode. This prevents the user from exiting fullscreen.'
),
_('Enable kiosk mode: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Enable kiosk mode?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setKiosk');
extension
.addCondition(
'IsKiosk',
_('Kiosk mode'),
_('Checks if the window is currently in kiosk mode.'),
_('The window is in kiosk mode'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.isKiosk');
extension
.addAction(
'SetHasShadow',
_('Enable window shadow'),
_('Enables or disables the window shadow.'),
_('Enable window shadow: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Enable shadow?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setHasShadow');
extension
.addCondition(
'HasShadow',
_('Shadow enabled'),
_("Checks if the window currently has it's shadow enabled."),
_('The window has a shadow'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.hasShadow');
extension
.addAction(
'EnableContentProtection',
_('Enable content protection'),
_(
'Enables or disables the content protection mode. This should prevent screenshots of the game from being taken.'
),
_('Enable content protection: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Enable content protection?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setContentProtection');
extension
.addAction(
'SetFocusable',
_('Allow focusing'),
_('Allow or disallow the user to focus the window.'),
_('Allow to focus the window: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Allow focus?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setFocusable');
extension
.addAction(
'Flash',
_('Flash the window'),
_('Make the window flash or end flashing.'),
_('Make the window flash: _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('yesorno', _('Flash the window?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.flash');
extension
.addAction(
'SetOpacity',
_('Set window opacity'),
_('Changes the window opacity.'),
_('Set the window opacity to _PARAM0_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('expression', _('New opacity'), '', false)
.setParameterLongDescription('A number between 0 (fully transparent) and 1 (fully opaque).')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setOpacity');
extension
.addAction(
'SetWindowPosition',
_('Set window position'),
_('Changes the window position.'),
_('Set the window position to _PARAM0_;_PARAM1_'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window24.png',
'res/actions/window.png'
)
.addParameter('expression', _('X position'), '', false)
.addParameter('expression', _('Y position'), '', false)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.setPosition');
extension
.addExpression(
'WindowX',
_('Window X position'),
_('Returns the current window X position.'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.getPositionX');
extension
.addExpression(
'WindowY',
_('Window Y position'),
_('Returns the current window Y position.'),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.getPositionY');
extension
.addExpression(
'WindowOpacity',
_('Window opacity'),
_(
'Returns the current window opacity (a number from 0 to 1, 1 being fully opaque).'
),
_('Advanced window management/Windows, Linux, macOS'),
'res/actions/window.png'
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
)
.setFunctionName('gdjs.evtTools.advancedWindow.getOpacity');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

@@ -0,0 +1,370 @@
// @ts-check
/**
* A set of wrappers around the Electron windowing APIs.
* They don't have any effect on non Electron runtimes.
*
* Docstrings are only used for typing here, for proper
* documentation check the electron docs at
* https://www.electronjs.org/docs/api.
*
* @filedescriptor
* @author arthuro555
*/
/**
* Tools to manipulate the game window positioning and
* interactions with the operating system.
* @namespace
*/
gdjs.evtTools.advancedWindow = {
/**
* The game's BrowserWindow instance (or null on
* non-electron platforms).
* @type {?Object}
*/
electronBrowserWindow: null,
};
// @ts-ignore
if (typeof require === 'function') {
// @ts-ignore
gdjs.evtTools.advancedWindow.electronBrowserWindow = require('electron').remote.getCurrentWindow();
}
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.focus = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
if (activate) {
gdjs.evtTools.advancedWindow.electronBrowserWindow.focus();
} else {
gdjs.evtTools.advancedWindow.electronBrowserWindow.blur();
}
}
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isFocused = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isFocused();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.show = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
if (activate) {
gdjs.evtTools.advancedWindow.electronBrowserWindow.showInactive();
} else {
gdjs.evtTools.advancedWindow.electronBrowserWindow.hide();
}
}
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isVisible = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isVisible();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.maximize = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
if (activate) {
gdjs.evtTools.advancedWindow.electronBrowserWindow.maximize();
} else {
gdjs.evtTools.advancedWindow.electronBrowserWindow.unmaximize();
}
}
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isMaximized = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isMaximized();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.minimize = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
if (activate) {
gdjs.evtTools.advancedWindow.electronBrowserWindow.minimize();
} else {
gdjs.evtTools.advancedWindow.electronBrowserWindow.restore();
}
}
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isMinimized = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isMinimized();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.enable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setEnabled(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isEnabled = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isEnabled();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setResizable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setResizable(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isResizable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isResizable();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setMovable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setMovable(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isMovable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isMovable();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setMaximizable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setMaximizable(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isMaximizable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isMaximizable();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setMinimizable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setMinimizable(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isMinimizable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isMinimizable();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setFullScreenable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setFullScreenable(
activate
);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isFullScreenable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isFullScreenable();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setClosable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setClosable(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isClosable = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isClosable();
return false;
};
/**
* @param {boolean} activate
* @param {"normal" | "floating" | "torn-off-menu" | "modal-panel" |"main-menu" | "status" | "pop-up-menu" | "screen-saver"} level
*/
gdjs.evtTools.advancedWindow.setAlwaysOnTop = function (activate, level) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setAlwaysOnTop(
activate,
level
);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isAlwaysOnTop = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isAlwaysOnTop();
return false;
};
/**
* @param {number} x
* @param {number} y
*/
gdjs.evtTools.advancedWindow.setPosition = function (x, y) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
// Convert x and y to (32 bit) integers to avoid Electron errors.
gdjs.evtTools.advancedWindow.electronBrowserWindow.setPosition(~~x, ~~y);
}
};
/**
* @return {number}
*/
gdjs.evtTools.advancedWindow.getPositionX = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
return gdjs.evtTools.advancedWindow.electronBrowserWindow.getPosition()[0];
}
return 0;
};
/**
* @return {number}
*/
gdjs.evtTools.advancedWindow.getPositionY = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow) {
return gdjs.evtTools.advancedWindow.electronBrowserWindow.getPosition()[1];
}
return 0;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setKiosk = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setKiosk(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.isKiosk = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.isKiosk();
return false;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.flash = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.flashFrame(activate);
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setHasShadow = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setHasShadow(activate);
};
/**
* @return {boolean}
*/
gdjs.evtTools.advancedWindow.hasShadow = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.hasShadow();
return false;
};
/**
* @param {number} opacity
*/
gdjs.evtTools.advancedWindow.setOpacity = function (opacity) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setOpacity(opacity);
};
/**
* @return {number}
*/
gdjs.evtTools.advancedWindow.getOpacity = function () {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
return gdjs.evtTools.advancedWindow.electronBrowserWindow.getOpacity();
return 1;
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setContentProtection = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setContentProtection(
activate
);
};
/**
* @param {boolean} activate
*/
gdjs.evtTools.advancedWindow.setFocusable = function (activate) {
if (gdjs.evtTools.advancedWindow.electronBrowserWindow)
gdjs.evtTools.advancedWindow.electronBrowserWindow.setFocusable(activate);
};

View File

@@ -18,7 +18,11 @@ gdjs.BBTextRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
.getFontManager()
.getFontFamily(runtimeObject._fontFamily),
fontSize: runtimeObject._fontSize + 'px',
fill: runtimeObject._color,
fill: gdjs.rgbToHexNumber(
runtimeObject._color[0],
runtimeObject._color[1],
runtimeObject._color[2]
),
tagStyle: 'bbcode',
wordWrap: runtimeObject._wordWrap,
wordWrapWidth: runtimeObject._wrappingWidth,
@@ -72,7 +76,11 @@ gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function () {
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function () {
this._pixiObject.textStyles.default.fill = this._object._color;
this._pixiObject.textStyles.default.fill = gdjs.rgbToHexNumber(
this._object._color[0],
this._object._color[1],
this._object._color[2]
);
this._pixiObject.dirty = true;
};

View File

@@ -29,8 +29,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
/** @type {string} */
this._text = objectData.content.text;
/** @type {string} */
this._color = objectData.content.color;
/** @type {number[]} color in format [r, g, b], where each component is in the range [0, 255] */
this._color = gdjs.BBTextRuntimeObject.hexToRGBColor(objectData.content.color);
/** @type {string} */
this._fontFamily = objectData.content.fontFamily;
/** @type {number} */
@@ -61,6 +61,11 @@ gdjs.BBTextRuntimeObject.prototype = Object.create(
);
gdjs.registerObject('BBText::BBText', gdjs.BBTextRuntimeObject);
gdjs.BBTextRuntimeObject.hexToRGBColor = function (hex) {
var hexNumber = parseInt(hex.replace('#', ''), 16);
return [(hexNumber >> 16) & 0xff, (hexNumber >> 8) & 0xff, hexNumber & 0xff];
};
gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
return this._renderer.getRendererObject();
};
@@ -80,7 +85,7 @@ gdjs.BBTextRuntimeObject.prototype.updateFromObjectData = function(oldObjectData
this.setBBText(newObjectData.content.text);
}
if (oldObjectData.content.color !== newObjectData.content.color) {
this._color = newObjectData.content.color;
this._color = gdjs.BBTextRuntimeObject.hexToRGBColor(newObjectData.content.color);
this._renderer.updateColor();
}
if (oldObjectData.content.fontFamily !== newObjectData.content.fontFamily) {
@@ -132,19 +137,19 @@ gdjs.BBTextRuntimeObject.prototype.getBBText = function() {
gdjs.BBTextRuntimeObject.prototype.setColor = function(rgbColorString) {
const splitValue = rgbColorString.split(';');
if (splitValue.length !== 3) return;
const hexColor =
'#' +
gdjs.rgbToHex(
parseInt(splitValue[0], 0),
parseInt(splitValue[1], 0),
parseInt(splitValue[2], 0)
);
this._color = hexColor;
this._color[0] = parseInt(splitValue[0], 10);
this._color[1] = parseInt(splitValue[1], 10);
this._color[2] = parseInt(splitValue[2], 10);
this._renderer.updateColor();
};
/**
* Get the base color.
* @return {string} The color as a "R;G;B" string, for example: "255;0;0"
*/
gdjs.BBTextRuntimeObject.prototype.getColor = function() {
return this._color;
return this._color[0] + ";" + this._color[1] + ";" + this._color[2];
};
gdjs.BBTextRuntimeObject.prototype.setFontSize = function(fontSize) {

View File

@@ -32,6 +32,13 @@ module.exports = {
"Open source (MIT License)"
).setExtensionHelpPath("/all-features/device-vibration");
extension
.addDependency()
.setName('Vibration Cordova Extension')
.setDependencyType('cordova')
.setExportName('cordova-plugin-vibration')
.setVersion('3.1.1');
extension
.addAction(
"StartVibration",

View File

@@ -1,124 +1,160 @@
// @ts-check
describe('gdjs.DraggableRuntimeBehavior', function() {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {resources: []},
// @ts-ignore
properties: {windowWidth: 800, windowHeight: 600}
});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers:[{name:"", visibility: true, effects: []}],
variables: [],
behaviorsSharedData: [],
objects: [],
instances: []
});
describe('gdjs.DraggableRuntimeBehavior', function () {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
// @ts-ignore
properties: { windowWidth: 800, windowHeight: 600 },
});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
cameras: [],
effects: [],
ambientLightColorR: 127,
ambientLightColorB: 127,
ambientLightColorG: 127,
isLightingLayer: false,
followBaseLayerCamera: false,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
instances: [],
});
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
runtimeScene.addObject(object);
runtimeScene.addObject(object2);
var object = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
variables: [],
});
var object2 = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
variables: [],
});
runtimeScene.addObject(object);
runtimeScene.addObject(object2);
it('should handle mouse', function() {
object.setPosition(450, 500);
it('should handle mouse', function () {
object.setPosition(450, 500);
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame.getInputManager().onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Mouse move with dragging
runtimeGame.getInputManager().onMouseMove(600, 600);
runtimeScene.renderAndStep();
//Mouse move with dragging
runtimeGame.getInputManager().onMouseMove(600, 600);
runtimeScene.renderAndStep();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Start dragging again
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeGame.getInputManager().onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(850, 700);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
//Start dragging again
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onMouseMove(850, 700);
runtimeScene.renderAndStep();
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep();
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
it('should handle touches', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
});
it('should handle touches', function() {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Move another unrelated touch
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep();
//Move another unrelated touch
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Start drag'n'drop with another touch
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
//Start drag'n'drop with another touch
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
it('should handle multitouch', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
object2.setPosition(650, 600);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
it('should handle multitouch', function() {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
object2.setPosition(650, 600);
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(2);
//Drag'n'drop
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
runtimeScene.renderAndStep();
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(2);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(700);
expect(object2.getX()).to.be(100);
expect(object2.getY()).to.be(200);
});
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(700);
expect(object2.getX()).to.be(100);
expect(object2.getY()).to.be(200);
});
});

View File

@@ -33,6 +33,32 @@ module.exports = {
'MIT'
);
// Register Properties
extension
.registerProperty('DummyPropertyString')
.setLabel(_('Dummy Property Name'))
.setDescription('Type in anything :)')
.setType('string');
extension
.registerProperty('DummyPropertyNumber')
.setLabel(_('Dummy Numeric Property Name'))
.setDescription('Only numbers here ;)')
.setType('number');
extension
.registerProperty('DummyPropertyBoolean')
.setDescription(_('A boolean property'))
.setType('boolean');
// Register Cordova/NPM dependencies
extension
.addDependency()
.setName('Thirteen Checker')
.setDependencyType('npm')
.setExportName('is-thirteen')
.setVersion('2.0.0');
// Declare effects:
const dummyEffect = extension
.addEffect('DummyEffect')

View File

@@ -20,7 +20,10 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
*/
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -305,7 +308,9 @@ module.exports = {
.addAction(
'DeleteFileAsync',
_('Delete a file (Async)'),
_('Delete a file from the filesystem asynchronously. The option result variable will be updated once the file is deleted.'),
_(
'Delete a file from the filesystem asynchronously. The option result variable will be updated once the file is deleted.'
),
_('Delete the file _PARAM0_'),
_('Filesystem/Windows, Linux, MacOS/Asynchronous'),
'JsPlatform/Extensions/filesystem_delete_file24.png',
@@ -366,8 +371,8 @@ module.exports = {
extension
.addStrExpression(
'ExecutablePath',
_('This games executable folder'),
_('Get the path to this games executable folder.'),
_('Game executable file'),
_('Get the path to this game executable file.'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
@@ -376,11 +381,24 @@ module.exports = {
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getExecutablePath');
extension
.addStrExpression(
'ExecutableFolderPath',
_('Game executable folder'),
_('Get the path to this game executable folder.'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getExecutableFolderPath');
extension
.addStrExpression(
'UserdataPath',
_('Userdata folder (For application settings)'),
_('Get the path to userdata folder. (For application settings)'),
_('Userdata folder (for application settings)'),
_('Get the path to userdata folder (for application settings).'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
@@ -392,7 +410,7 @@ module.exports = {
extension
.addStrExpression(
'UserHomePath',
_('User\'s Home folder'),
_("User's Home folder"),
_('Get the path to the user home folder.'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
@@ -418,7 +436,7 @@ module.exports = {
.addStrExpression(
'PathDelimiter',
_('Path delimiter'),
_('Get the operating system agnostic path delimiter.'),
_('Get the operating system path delimiter.'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
@@ -426,9 +444,55 @@ module.exports = {
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getPathDelimiter');
extension
.addStrExpression(
'DirectoryName',
_('Get directory name from a path'),
_(
'Returns the portion of the path that represents the directories, without the ending file name.'
),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
.addParameter('string', _('File or folder path'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getDirectoryName');
extension
.addStrExpression(
'FileName',
_('Get file name from a path'),
_('Returns the name of the file with its extension, if any.'),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
.addParameter('string', _('File path'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getFileName');
extension
.addStrExpression(
'ExtensionName',
_('Get the extension from a file path'),
_(
'Returns the extension of the file designated by the given path, including the extension period. For example: ".txt".'
),
_('Filesystem/Windows, Linux, MacOS'),
'JsPlatform/Extensions/filesystem_folder32.png'
)
.addParameter('string', _('File path'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/FileSystem/filesystemtools.js')
.setFunctionName('gdjs.fileSystem.getExtensionName');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

@@ -1,11 +1,58 @@
// @ts-check
/**
* @memberof gdjs
* @class fileSystem
* @static
* @namespace fileSystem
* @private
*/
gdjs.fileSystem = {};
gdjs.fileSystem = {
_path: null, // The Node.js path module, or null if it can't be loaded.
_fs: null, // The Node.js fs module, or null if it can't be loaded.
};
/** Get the Node.js path module, or null if it can't be loaded */
gdjs.fileSystem._getPath = function() {
if (!gdjs.fileSystem._path) {
// @ts-ignore
gdjs.fileSystem._path = typeof require !== 'undefined' ? require('path') : null;
}
return gdjs.fileSystem._path;
}
/** Get the Node.js fs module, or null if it can't be loaded */
gdjs.fileSystem._getFs = function() {
if (!gdjs.fileSystem._fs) {
// @ts-ignore
gdjs.fileSystem._fs = typeof require !== 'undefined' ? require('fs') : null;
}
return gdjs.fileSystem._fs;
}
/** @param {string} fileOrFolderPath */
gdjs.fileSystem.getDirectoryName = function(fileOrFolderPath) {
const path = gdjs.fileSystem._getPath();
if (!path) return '';
return path.dirname(fileOrFolderPath);
}
/** @param {string} filePath */
gdjs.fileSystem.getFileName = function(filePath) {
const path = gdjs.fileSystem._getPath();
if (!path) return '';
return path.basename(filePath);
}
/** @param {string} filePath */
gdjs.fileSystem.getExtensionName = function(filePath) {
const path = gdjs.fileSystem._getPath();
if (!path) return '';
return path.extname(filePath);
}
/**
* Get the path to 'Desktop' folder.
@@ -62,9 +109,9 @@ gdjs.fileSystem.getPicturesPath = function(runtimeScene) {
};
/**
* Get the path to this application 'Executable' folder.
* Get the path to this application 'Executable' file.
* @param {gdjs.RuntimeScene} runtimeScene The current scene
* @return {string} The path to this applications executable folder
* @return {string} The path to this applications executable file
*/
gdjs.fileSystem.getExecutablePath = function(runtimeScene) {
const electron = runtimeScene
@@ -79,6 +126,22 @@ gdjs.fileSystem.getExecutablePath = function(runtimeScene) {
}
};
/**
* Get the path to this application 'Executable' folder.
* @param {gdjs.RuntimeScene} runtimeScene The current scene
* @return {string} The path to this applications executable folder
*/
gdjs.fileSystem.getExecutableFolderPath = function(runtimeScene) {
const path = gdjs.fileSystem._getPath();
const executablePath = gdjs.fileSystem.getExecutablePath(runtimeScene);
if (!path) {
return '';
}
return path.dirname(executablePath);
};
/**
* Get the path to 'UserData' folder.
* @param {gdjs.RuntimeScene} runtimeScene The current scene
@@ -101,7 +164,7 @@ gdjs.fileSystem.getUserdataPath = function(runtimeScene) {
* Get the path to the user's home folder (on Windows `C:\Users\<USERNAME>\` for example).
* @return {string} The path to user's "home" folder
*/
gdjs.fileSystem.getUserHomePath = function() {
gdjs.fileSystem.getUserHomePath = function(runtimeScene) {
const electron = runtimeScene
.getGame()
.getRenderer()
@@ -137,7 +200,7 @@ gdjs.fileSystem.getTempPath = function(runtimeScene) {
* @return {string} The path delimiter
*/
gdjs.fileSystem.getPathDelimiter = function() {
const path = typeof require !== 'undefined' ? require('path') : null;
const path = gdjs.fileSystem._getPath();
if (path) {
return path.sep || '/';
@@ -152,7 +215,7 @@ gdjs.fileSystem.getPathDelimiter = function() {
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.makeDirectory = function(directory, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
if (fileSystem) {
@@ -176,7 +239,7 @@ gdjs.fileSystem.makeDirectory = function(directory, resultVar) {
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.saveStringToFileAsync = function(text, savePath, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
fileSystem.writeFile(savePath, text, 'utf8', err => {
@@ -199,7 +262,7 @@ gdjs.fileSystem.saveStringToFileAsync = function(text, savePath, resultVar) {
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.saveStringToFile = function(text, savePath, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
if (fileSystem) {
@@ -227,7 +290,7 @@ gdjs.fileSystem.saveVariableToJSONFile = function(
savePath,
resultVar
) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
const network = gdjs.evtTools.network;
let result = 'error';
@@ -251,7 +314,7 @@ gdjs.fileSystem.saveVariableToJSONFile = function(
/**
* Save a variable into a file in JSON format, asynchronously.
* @param {string} text The variable to be saved
* @param {gdjs.Variable} variable The variable to be saved
* @param {string} savePath Path to the file
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
@@ -260,7 +323,7 @@ gdjs.fileSystem.saveVariableToJSONFileAsync = function(
savePath,
resultVar
) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
const network = gdjs.evtTools.network;
if (fileSystem && network) {
@@ -289,7 +352,7 @@ gdjs.fileSystem.saveVariableToJSONFileAsync = function(
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.loadStringFromFile = function(stringVar, loadPath, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
if (fileSystem) {
@@ -321,7 +384,7 @@ gdjs.fileSystem.loadVariableFromJSONFile = function(
loadPath,
resultVar
) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
const network = gdjs.evtTools.network;
let result = 'error';
@@ -353,7 +416,7 @@ gdjs.fileSystem.loadVariableFromJSONFileAsync = function(
loadPath,
resultVar
) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
const network = gdjs.evtTools.network;
if (fileSystem && network) {
@@ -384,7 +447,7 @@ gdjs.fileSystem.loadStringFromFileAsync = function(
loadPath,
resultVar
) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
fileSystem.readFile(loadPath, 'utf8', (err, data) => {
@@ -409,7 +472,7 @@ gdjs.fileSystem.loadStringFromFileAsync = function(
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.deleteFile = function(filePath, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
let result = 'error';
if (fileSystem) {
@@ -430,7 +493,7 @@ gdjs.fileSystem.deleteFile = function(filePath, resultVar) {
* @param {gdjs.Variable} resultVar The variable where to store the result of the operation
*/
gdjs.fileSystem.deleteFileAsync = function(filePath, resultVar) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
fileSystem.unlink(filePath, err => {
@@ -449,7 +512,7 @@ gdjs.fileSystem.deleteFileAsync = function(filePath, resultVar) {
* @return {boolean} true if fhe file or directory exists
*/
gdjs.fileSystem.pathExists = function(filePath) {
const fileSystem = typeof require !== 'undefined' ? require('fs') : null;
const fileSystem = gdjs.fileSystem._getFs();
if (fileSystem) {
return fileSystem.existsSync(filePath);

View File

@@ -0,0 +1,429 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'Lighting',
_('Lights'),
_(
'Allow to display lights on the screen and mark objects as obstacles for the lights.'
),
'Harsimran Virk',
'MIT'
);
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.updateProperty = function (
behaviorContent,
propertyName,
newValue
) {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.getProperties = function (behaviorContent) {
const behaviorProperties = new gd.MapStringPropertyDescriptor();
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.initializeContent = function (behaviorContent) {};
extension
.addBehavior(
'LightObstacleBehavior',
_('Light Obstacle Behavior'),
'LightObstacleBehavior',
_(
'This behavior makes the object an obstacle to the light. The light emitted by light objects will be stopped by the object.'
),
'',
'CppPlatform/Extensions/lightObstacleIcon32.png',
'LightObstacleBehavior',
lightObstacleBehavior,
new gd.BehaviorsSharedData()
)
.setIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
.addIncludeFile('Extensions/Lighting/lightruntimeobject.js')
.addIncludeFile(
'Extensions/Lighting/lightruntimeobject-pixi-renderer.js'
);
const lightObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
if (propertyName === 'radius') {
objectContent.radius = parseFloat(newValue);
return true;
}
if (propertyName === 'color') {
objectContent.color = newValue;
return true;
}
if (propertyName === 'debugMode') {
objectContent.debugMode = newValue === '1';
return true;
}
if (propertyName === 'texture') {
objectContent.texture = newValue;
return true;
}
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
objectProperties.set(
'radius',
new gd.PropertyDescriptor(objectContent.radius.toString())
.setType('number')
.setLabel(_('Radius'))
);
objectProperties.set(
'color',
new gd.PropertyDescriptor(objectContent.color)
.setType('color')
.setLabel(_('Color'))
);
objectProperties.set(
'debugMode',
new gd.PropertyDescriptor(objectContent.debugMode ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Debug mode'))
.setDescription(
_(
'When activated, display the lines used to render the light - useful to understand how the light is rendered on screen.'
)
)
);
objectProperties
.getOrCreate('texture')
.setValue(objectContent.texture)
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Light texture (optional)'))
.setDescription(
_(
"A texture to be used to display the light. If you don't specify a texture, the light is rendered as fading from bright, in its center, to dark."
)
);
return objectProperties;
};
lightObject.setRawJSONContent(
JSON.stringify({
radius: 50,
color: '#ffffff',
debugMode: false,
texture: '',
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue,
project,
layout
) {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getInitialInstanceProperties = function (
content,
instance,
project,
layout
) {
const instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
const object = extension
.addObject(
'LightObject',
_('Light'),
_(
'Displays a light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
),
'CppPlatform/Extensions/lightIcon32.png',
lightObject
)
.setIncludeFile('Extensions/Lighting/lightruntimeobject.js')
.addIncludeFile('Extensions/Lighting/lightruntimeobject-pixi-renderer.js')
.addIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js');
object
.addAction(
'SetRadius',
_('Set the radius of light object'),
_('Set the radius of light object'),
_('Set the radius of _PARAM0_ to: _PARAM1_'),
'',
'CppPlatform/Extensions/lightIcon24.png',
'CppPlatform/Extensions/lightIcon16.png'
)
.addParameter('object', _('Object'), 'LightObject', false)
.addParameter('expression', _('Radius'), '', false)
.getCodeExtraInformation()
.setFunctionName('setRadius');
object
.addAction(
'SetColor',
_('Set the color of light object'),
_('Set the color of light object in format "R;G;B" string.'),
_('Set the color of _PARAM0_ to: _PARAM1_'),
'',
'res/actions/color24.png',
'res/actions/color.png'
)
.addParameter('object', _('Object'), 'LightObject', false)
.addParameter('string', _('Color'), '', false)
.getCodeExtraInformation()
.setFunctionName('setColor');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
objectsEditorService.registerEditorConfiguration(
'Lighting::LightObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
helpPagePath: '/objects/light',
})
);
},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
/**
* Renderer for instances of LightObject inside the IDE.
*
* @extends RenderedInstance
* @class RenderedLightObjectInstance
* @constructor
*/
function RenderedLightObjectInstance(
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
);
this._radius = parseFloat(
this._associatedObject
.getProperties(this.project)
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
this._colorHex = parseInt(
this._associatedObject
.getProperties(this.project)
.get('color')
.getValue()
.replace('#', ''),
16
);
this._color = [
((this._colorHex >> 16) & 0xff) / 255,
((this._colorHex >> 8) & 0xff) / 255,
(this._colorHex & 0xff) / 255,
];
const geometry = new PIXI.Geometry();
const shader = PIXI.Shader.from(
`
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vPos;
void main() {
vPos = aVertexPosition;
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
}`,
`
precision mediump float;
uniform vec2 center;
uniform float radius;
uniform vec3 color;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vPos;
void main() {
float l = length(vPos - center);
float intensity = 0.0;
if(l < radius)
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
gl_FragColor = vec4(color*intensity, 1.0);
}
`,
{
center: [this._instance.getX(), this._instance.getY()],
radius: this._radius,
color: this._color,
}
);
this._vertexBuffer = new Float32Array([
this._instance.getX() - this._radius,
this._instance.getY() + this._radius,
this._instance.getX() + this._radius,
this._instance.getY() + this._radius,
this._instance.getX() + this._radius,
this._instance.getY() - this._radius,
this._instance.getX() - this._radius,
this._instance.getY() - this._radius,
]);
geometry
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
.addIndex([0, 1, 2, 2, 3, 0]);
this._pixiObject = new PIXI.Mesh(geometry, shader);
this._pixiObject.blendMode = PIXI.BLEND_MODES.ADD;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedLightObjectInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedLightObjectInstance.getThumbnail = function (
project,
resourcesLoader,
object
) {
return 'CppPlatform/Extensions/lightIcon32.png';
};
/**
* This is called to update the PIXI object on the scene editor
*/
RenderedLightObjectInstance.prototype.update = function () {
this._pixiObject.shader.uniforms.center = new Float32Array([
this._instance.getX(),
this._instance.getY(),
]);
this._vertexBuffer[0] = this._instance.getX() - this._radius;
this._vertexBuffer[1] = this._instance.getY() + this._radius;
this._vertexBuffer[2] = this._instance.getX() + this._radius;
this._vertexBuffer[3] = this._instance.getY() + this._radius;
this._vertexBuffer[4] = this._instance.getX() + this._radius;
this._vertexBuffer[5] = this._instance.getY() - this._radius;
this._vertexBuffer[6] = this._instance.getX() - this._radius;
this._vertexBuffer[7] = this._instance.getY() - this._radius;
this._pixiObject.geometry
.getBuffer('aVertexPosition')
.update(this._vertexBuffer);
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedLightObjectInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedLightObjectInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
RenderedLightObjectInstance.prototype.getOriginX = function () {
return this._radius;
};
RenderedLightObjectInstance.prototype.getOriginY = function () {
return this._radius;
};
objectsRenderingService.registerInstanceRenderer(
'Lighting::LightObject',
RenderedLightObjectInstance
);
},
};

View File

@@ -0,0 +1,153 @@
/**
* @memberof gdjs
* @class LightObstaclesManager
* @param {gdjs.RuntimeScene} runtimeScene
*/
gdjs.LightObstaclesManager = function (runtimeScene) {
this._obstacleRBush = new rbush(9, [
'.owner.getAABB().min[0]',
'.owner.getAABB().min[1]',
'.owner.getAABB().max[0]',
'.owner.getAABB().max[1]',
]);
};
/**
* Get the light obstacles manager of a scene.
* @param {gdjs.RuntimeScene} runtimeScene
* @returns {gdjs.LightObstaclesManager}
*/
gdjs.LightObstaclesManager.getManager = function (runtimeScene) {
if (!runtimeScene._lightObstaclesManager) {
// Create the shared manager if necessary.
runtimeScene._lightObstaclesManager = new gdjs.LightObstaclesManager(
runtimeScene
);
}
return runtimeScene._lightObstaclesManager;
};
/**
* Add a light obstacle to the list of existing obstacles.
* @param {gdjs.LightObstacleRuntimeBehavior} obstacle
*/
gdjs.LightObstaclesManager.prototype.addObstacle = function (obstacle) {
this._obstacleRBush.insert(obstacle);
};
/**
* Remove a light obstacle from the list of existing obstacles. Be sure that the obstacle was
* added before.
* @param {gdjs.LightObstacleRuntimeBehavior} obstacle
*/
gdjs.LightObstaclesManager.prototype.removeObstacle = function (obstacle) {
this._obstacleRBush.remove(obstacle);
};
/**
* Returns all the light obstacles around the specified object.
* @param {gdjs.RuntimeObject} object The object
* @param {number} radius Radius of the area to be searched.
* @param {gdjs.RuntimeObject[]} result An array with all obstacles near the object.
*/
gdjs.LightObstaclesManager.prototype.getAllObstaclesAround = function (
object,
radius,
result
) {
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
// is not necessarily in the middle of the object (for sprites for example).
var x = object.getX();
var y = object.getY();
var searchArea = gdjs.staticObject(
gdjs.LightObstaclesManager.prototype.getAllObstaclesAround
);
searchArea.minX = x - radius;
searchArea.minY = y - radius;
searchArea.maxX = x + radius;
searchArea.maxY = y + radius;
var nearbyObstacles = this._obstacleRBush.search(searchArea);
result.length = 0;
result.push.apply(result, nearbyObstacles);
};
/**
* @memberof gdjs
* @class LightObstacleRuntimeBehavior
* @param {gdjs.RuntimeScene} runtimeScene
* @param {BehaviorData} behaviorData
* @param {gdjs.RuntimeObject} owner
*/
gdjs.LightObstacleRuntimeBehavior = function (
runtimeScene,
behaviorData,
owner
) {
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
this._oldX = 0;
this._oldY = 0;
this._oldWidth = 0;
this._oldHeight = 0;
this._manager = gdjs.LightObstaclesManager.getManager(runtimeScene);
this._registeredInManager = false;
};
gdjs.LightObstacleRuntimeBehavior.prototype = Object.create(
gdjs.RuntimeBehavior.prototype
);
gdjs.registerBehavior(
'Lighting::LightObstacleBehavior',
gdjs.LightObstacleRuntimeBehavior
);
gdjs.LightObstacleRuntimeBehavior.prototype.doStepPreEvents = function (
runtimeScene
) {
// Make sure the obstacle is or is not in the obstacles manager.
if (!this.activated() && this._registeredInManager) {
this._manager.removeObstacle(this);
this._registeredInManager = false;
} else if (this.activated() && !this._registeredInManager) {
this._manager.addObstacle(this);
this._registeredInManager = true;
}
//Track changes in size or position
if (
this._oldX !== this.owner.getX() ||
this._oldY !== this.owner.getY() ||
this._oldWidth !== this.owner.getWidth() ||
this._oldHeight !== this.owner.getHeight()
) {
if (this._registeredInManager) {
this._manager.removeObstacle(this);
this._manager.addObstacle(this);
}
this._oldX = this.owner.getX();
this._oldY = this.owner.getY();
this._oldWidth = this.owner.getWidth();
this._oldHeight = this.owner.getHeight();
}
};
gdjs.LightObstacleRuntimeBehavior.prototype.onDestroy = function () {
if (this._manager && this._registeredInManager)
this._manager.removeObstacle(this);
};
gdjs.LightObstacleRuntimeBehavior.prototype.onActivate = function () {
if (this._registeredInManager) return;
this._manager.addObstacle(this);
this._registeredInManager = true;
};
gdjs.LightObstacleRuntimeBehavior.prototype.onDeActivate = function () {
if (!this._registeredInManager) return;
this._manager.removeObstacle(this);
this._registeredInManager = false;
};

View File

@@ -0,0 +1,545 @@
/**
* Pixi renderer for light runtime objects.
*
* @memberof gdjs
* @constructor LightRuntimeObjectPixiRenderer
* @param {gdjs.LightRuntimeObject} runtimeObject
* @param {gdjs.RuntimeScene} runtimeScene
*/
gdjs.LightRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
this._object = runtimeObject;
this._runtimeScene = runtimeScene;
this._manager = runtimeObject.getObstaclesManager();
this._radius = runtimeObject.getRadius();
var objectColor = runtimeObject._color;
this._color = [
objectColor[0] / 255,
objectColor[1] / 255,
objectColor[2] / 255,
];
/** @type {?PIXI.Texture} */
this._texture = null;
this.updateTexture();
this._center = new Float32Array([runtimeObject.x, runtimeObject.y]);
this._defaultVertexBuffer = new Float32Array(8);
this._vertexBuffer = new Float32Array([
runtimeObject.x - this._radius,
runtimeObject.y + this._radius,
runtimeObject.x + this._radius,
runtimeObject.y + this._radius,
runtimeObject.x + this._radius,
runtimeObject.y - this._radius,
runtimeObject.x - this._radius,
runtimeObject.y - this._radius,
]);
this._indexBuffer = new Uint16Array([0, 1, 2, 0, 2, 3]);
/** @type {?PIXI.Mesh} */
this._light = null;
this.updateMesh();
this._isPreview = runtimeScene.getGame().isPreview();
this._debugMode = null;
/** @type {?PIXI.Container} */
this._debugLight = null;
/** @type {?PIXI.Graphics} */
this._debugGraphics = null;
this.updateDebugMode();
/** @type {gdjs.Polygon} */
this._lightBoundingPoly = new gdjs.Polygon();
for (var i = 0; i < 4; i++) {
this._lightBoundingPoly.vertices.push(
runtimeObject.getHitBoxes()[0].vertices[i]
);
}
// Objects will be added in lighting layer, this is just to maintain consistency.
if (this._light)
runtimeScene
.getLayer('')
.getRenderer()
.addRendererObject(this.getRendererObject(), runtimeObject.getZOrder());
};
gdjs.LightRuntimeObjectRenderer = gdjs.LightRuntimeObjectPixiRenderer; //Register the class to let the engine use it.
gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer = new Uint16Array([
0,
1,
2,
0,
2,
3,
]);
gdjs.LightRuntimeObjectPixiRenderer.defaultVertexShader = `
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vPos;
void main() {
vPos = aVertexPosition;
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
}`;
gdjs.LightRuntimeObjectPixiRenderer.defaultFragmentShader = `
precision mediump float;
uniform vec2 center;
uniform float radius;
uniform vec3 color;
varying vec2 vPos;
void main() {
float l = length(vPos - center);
float intensity = 0.0;
if(l < radius)
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
gl_FragColor = vec4(color*intensity, 1.0);
}`;
gdjs.LightRuntimeObjectPixiRenderer.texturedFragmentShader = `
precision mediump float;
uniform vec2 center;
uniform float radius;
uniform vec3 color;
uniform sampler2D uSampler;
varying vec2 vPos;
void main() {
vec2 topleft = vec2(center.x - radius, center.y - radius);
vec2 texCoord = (vPos - topleft)/(2.0 * radius);
gl_FragColor = vec4(color, 1.0) * texture2D(uSampler, texCoord);
}`;
gdjs.LightRuntimeObjectPixiRenderer._verticesWithAngleComparator = function (
vertexWithAngleA,
vertexWithAngleB
) {
if (vertexWithAngleA.angle < vertexWithAngleB.angle) return -1;
if (vertexWithAngleA.angle === vertexWithAngleB.angle) return 0;
if (vertexWithAngleA.angle > vertexWithAngleB.angle) return 1;
};
gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint = function (
lightObject,
angle,
polygons,
boundingSquareHalfDiag
) {
var centerX = lightObject.getX();
var centerY = lightObject.getY();
var targetX = centerX + boundingSquareHalfDiag * Math.cos(angle);
var targetY = centerY + boundingSquareHalfDiag * Math.sin(angle);
var minSqDist = boundingSquareHalfDiag * boundingSquareHalfDiag;
var closestPoint = [null, null];
for (var poly of polygons) {
var raycastResult = gdjs.Polygon.raycastTest(
poly,
centerX,
centerY,
targetX,
targetY
);
if (raycastResult.collision && raycastResult.closeSqDist <= minSqDist) {
minSqDist = raycastResult.closeSqDist;
closestPoint[0] = raycastResult.closeX;
closestPoint[1] = raycastResult.closeY;
}
}
if (closestPoint[0] && closestPoint[1]) return closestPoint;
return null;
};
/**
* @returns {?PIXI.Mesh | PIXI.Container}
*/
gdjs.LightRuntimeObjectPixiRenderer.prototype.getRendererObject = function () {
if (this._debugLight) {
return this._debugLight;
}
return this._light;
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function () {
if (this._object.isHidden()) return;
if (this._debugGraphics) this._updateDebugGraphics();
this._updateBuffers();
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateMesh = function () {
if (!PIXI.utils.isWebGLSupported()) {
console.warn(
'This device does not support webgl, which is required for Lighting Extension.'
);
return;
}
this.updateTexture();
var fragmentShader =
this._texture === null
? gdjs.LightRuntimeObjectPixiRenderer.defaultFragmentShader
: gdjs.LightRuntimeObjectPixiRenderer.texturedFragmentShader;
var shaderUniforms = {
center: this._center,
radius: this._radius,
color: this._color,
};
if (this._texture) {
shaderUniforms.uSampler = this._texture;
}
var shader = PIXI.Shader.from(
gdjs.LightRuntimeObjectPixiRenderer.defaultVertexShader,
fragmentShader,
shaderUniforms
);
var geometry = new PIXI.Geometry();
geometry
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
.addIndex(this._indexBuffer);
if (!this._light) {
this._light = new PIXI.Mesh(geometry, shader);
this._light.blendMode = PIXI.BLEND_MODES.ADD;
} else {
this._light.shader = shader;
this._light.geometry = geometry;
}
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateRadius = function () {
if (!this._light) return;
this._radius = this._object.getRadius();
this._light.shader.uniforms.radius = this._radius;
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateColor = function () {
if (!this._light) return;
var objectColor = this._object._color;
this._color = [
objectColor[0] / 255,
objectColor[1] / 255,
objectColor[2] / 255,
];
this._light.shader.uniforms.color = this._color;
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateTexture = function () {
var texture = this._object.getTexture();
this._texture =
texture !== ''
? this._runtimeScene.getGame().getImageManager().getPIXITexture(texture)
: null;
};
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateDebugMode = function () {
if (!this._light) return;
this._debugMode = this._object.getDebugMode();
if (!this._debugLight && (this._isPreview || this._debugMode)) {
this._debugLight = new PIXI.Container();
this._debugLight.addChild(this._light);
}
if (this._debugMode && !this._debugGraphics) {
this._debugGraphics = new PIXI.Graphics();
this._debugLight.addChild(this._debugGraphics);
}
if (!this._debugMode && this._debugGraphics) {
this._debugLight.removeChild(this._debugGraphics);
this._debugGraphics.destroy();
this._debugGraphics = null;
}
this.ensureUpToDate();
};
gdjs.LightRuntimeObjectPixiRenderer.prototype._updateDebugGraphics = function () {
var computedVertices = this._computeLightVertices();
if (!computedVertices.length) {
this._debugGraphics.clear();
this._debugGraphics
.lineStyle(1, 0xff0000, 1)
.moveTo(this._object.x, this._object.y)
.lineTo(this._object.x - this._radius, this._object.y + this._radius)
.lineTo(this._object.x + this._radius, this._object.y + this._radius)
.moveTo(this._object.x, this._object.y)
.lineTo(this._object.x + this._radius, this._object.y + this._radius)
.lineTo(this._object.x + this._radius, this._object.y - this._radius)
.moveTo(this._object.x, this._object.y)
.lineTo(this._object.x + this._radius, this._object.y - this._radius)
.lineTo(this._object.x - this._radius, this._object.y - this._radius)
.moveTo(this._object.x, this._object.y)
.lineTo(this._object.x - this._radius, this._object.y - this._radius)
.lineTo(this._object.x - this._radius, this._object.y + this._radius);
return;
}
var vertices = new Array(2 * computedVertices.length + 2);
vertices[0] = this._object.x;
vertices[1] = this._object.y;
for (var i = 2; i < 2 * computedVertices.length + 2; i += 2) {
vertices[i] = computedVertices[i / 2 - 1][0];
vertices[i + 1] = computedVertices[i / 2 - 1][1];
}
this._debugGraphics.clear();
this._debugGraphics.moveTo(vertices[2], vertices[3]);
var verticesCount = vertices.length;
for (var i = 2; i < verticesCount; i += 2) {
var lineColor = i % 4 === 0 ? 0xff0000 : 0x00ff00;
var lastX = i + 2 >= verticesCount ? 2 : i + 2;
var lastY = i + 3 >= verticesCount ? 3 : i + 3;
this._debugGraphics
.lineStyle(1, lineColor, 1)
.lineTo(vertices[i], vertices[i + 1])
.lineTo(vertices[lastX], vertices[lastY])
.moveTo(vertices[0], vertices[1])
.lineTo(vertices[i], vertices[i + 1])
.moveTo(vertices[0], vertices[1])
.lineTo(vertices[lastX], vertices[lastY]);
}
};
gdjs.LightRuntimeObjectPixiRenderer.prototype._updateBuffers = function () {
if (!this._light) return;
this._center[0] = this._object.x;
this._center[1] = this._object.y;
var vertices = this._computeLightVertices();
// Fallback to simple quad when there are no obstacles around.
if (vertices.length === 0) {
this._defaultVertexBuffer[0] = this._object.x - this._radius;
this._defaultVertexBuffer[1] = this._object.y + this._radius;
this._defaultVertexBuffer[2] = this._object.x + this._radius;
this._defaultVertexBuffer[3] = this._object.y + this._radius;
this._defaultVertexBuffer[4] = this._object.x + this._radius;
this._defaultVertexBuffer[5] = this._object.y - this._radius;
this._defaultVertexBuffer[6] = this._object.x - this._radius;
this._defaultVertexBuffer[7] = this._object.y - this._radius;
this._light.shader.uniforms.center = this._center;
this._light.geometry
.getBuffer('aVertexPosition')
.update(this._defaultVertexBuffer);
this._light.geometry
.getIndex()
.update(gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer);
return;
}
var verticesCount = vertices.length;
// If the array buffer which is already allocated is atmost
// twice the size of memory required, we could avoid re-allocation
// and instead use a subarray. Otherwise, allocate new array buffers as
// there would be memory wastage.
var isSubArrayUsed = false;
var vertexBufferSubArray = null;
var indexBufferSubArray = null;
if (this._vertexBuffer.length > 2 * verticesCount + 2) {
if (this._vertexBuffer.length < 4 * verticesCount + 4) {
isSubArrayUsed = true;
vertexBufferSubArray = this._vertexBuffer.subarray(
0,
2 * verticesCount + 2
);
indexBufferSubArray = this._indexBuffer.subarray(0, 3 * verticesCount);
} else {
this._vertexBuffer = new Float32Array(2 * verticesCount + 2);
this._indexBuffer = new Uint16Array(3 * verticesCount);
}
}
// When the allocated array buffer has less memory than
// required, we'll have to allocated new array buffers.
if (this._vertexBuffer.length < 2 * verticesCount + 2) {
this._vertexBuffer = new Float32Array(2 * verticesCount + 2);
this._indexBuffer = new Uint16Array(3 * verticesCount);
}
this._vertexBuffer[0] = this._object.x;
this._vertexBuffer[1] = this._object.y;
for (var i = 2; i < 2 * verticesCount + 2; i += 2) {
this._vertexBuffer[i] = vertices[i / 2 - 1][0];
this._vertexBuffer[i + 1] = vertices[i / 2 - 1][1];
}
for (var i = 0; i < 3 * verticesCount; i += 3) {
this._indexBuffer[i] = 0;
this._indexBuffer[i + 1] = i / 3 + 1;
if (i / 3 + 1 !== verticesCount) this._indexBuffer[i + 2] = i / 3 + 2;
else this._indexBuffer[i + 2] = 1;
}
this._light.shader.uniforms.center = this._center;
if (!isSubArrayUsed) {
this._light.geometry
.getBuffer('aVertexPosition')
.update(this._vertexBuffer);
this._light.geometry.getIndex().update(this._indexBuffer);
} else {
this._light.geometry
.getBuffer('aVertexPosition')
.update(vertexBufferSubArray);
this._light.geometry.getIndex().update(indexBufferSubArray);
}
};
/**
* Computes the vertices of mesh using raycasting.
* @returns {number[][]} the vertices of mesh.
*/
gdjs.LightRuntimeObjectPixiRenderer.prototype._computeLightVertices = function () {
var lightObstacles = [];
if (this._manager)
this._manager.getAllObstaclesAround(
this._object,
this._radius,
lightObstacles
);
// Bail out early if there are no obstacles.
if (lightObstacles.length === 0) return lightObstacles;
// Synchronize light bounding polygon with the hitbox.
var lightHitboxPoly = this._object.getHitBoxes()[0];
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 2; j++) {
this._lightBoundingPoly.vertices[i][j] = lightHitboxPoly.vertices[i][j];
}
}
var obstaclesCount = lightObstacles.length;
var obstacleHitBoxes = new Array(obstaclesCount);
for (var i = 0; i < obstaclesCount; i++) {
obstacleHitBoxes[i] = lightObstacles[i].owner.getHitBoxes();
}
var obstaclePolygons = [];
obstaclePolygons.push(this._lightBoundingPoly);
for (var i = 0; i < obstaclesCount; i++) {
var noOfHitBoxes = obstacleHitBoxes[i].length;
for (var j = 0; j < noOfHitBoxes; j++)
obstaclePolygons.push(obstacleHitBoxes[i][j]);
}
var maxX = this._object.x + this._radius;
var minX = this._object.x - this._radius;
var maxY = this._object.y + this._radius;
var minY = this._object.y - this._radius;
var flattenVertices = [];
for (var i = 1; i < obstaclePolygons.length; i++) {
var vertices = obstaclePolygons[i].vertices;
var verticesCount = vertices.length;
for (var j = 0; j < verticesCount; j++) {
flattenVertices.push(vertices[j]);
if (vertices[j][0] < minX) minX = vertices[j][0];
if (vertices[j][0] > maxX) maxX = vertices[j][0];
if (vertices[j][1] < minY) minY = vertices[j][1];
if (vertices[j][1] > maxY) maxY = vertices[j][1];
}
}
obstaclePolygons[0].vertices[0][0] = minX;
obstaclePolygons[0].vertices[0][1] = minY;
obstaclePolygons[0].vertices[1][0] = maxX;
obstaclePolygons[0].vertices[1][1] = minY;
obstaclePolygons[0].vertices[2][0] = maxX;
obstaclePolygons[0].vertices[2][1] = maxY;
obstaclePolygons[0].vertices[3][0] = minX;
obstaclePolygons[0].vertices[3][1] = maxY;
// Find the largest diagonal length.
var boundingSquareHalfDiag = Math.sqrt(
Math.max(
(this._object.x - minX) * (this._object.x - minX) +
(this._object.y - minY) * (this._object.y - minY),
(maxX - this._object.x) * (maxX - this._object.x) +
(this._object.y - minY) * (this._object.y - minY),
(maxX - this._object.x) * (maxX - this._object.x) +
(maxY - this._object.y) * (maxY - this._object.y),
(this._object.x - minX) * (this._object.x - minX) +
(maxY - this._object.y) * (maxY - this._object.y)
)
);
for (var i = 0; i < 4; i++) {
flattenVertices.push(obstaclePolygons[0].vertices[i]);
}
var closestVertices = [];
var flattenVerticesCount = flattenVertices.length;
for (var i = 0; i < flattenVerticesCount; i++) {
var xdiff = flattenVertices[i][0] - this._object.x;
var ydiff = flattenVertices[i][1] - this._object.y;
var angle = Math.atan2(ydiff, xdiff);
var closestVertex = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertex) {
closestVertices.push({
vertex: closestVertex,
angle: angle,
});
}
// TODO: Check whether we need to raycast these two extra rays or not.
var closestVertexOffsetLeft = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle + 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertexOffsetLeft) {
closestVertices.push({
vertex: closestVertexOffsetLeft,
angle: angle + 0.0001,
});
}
var closestVertexOffsetRight = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
this._object,
angle - 0.0001,
obstaclePolygons,
boundingSquareHalfDiag
);
if (closestVertexOffsetRight) {
closestVertices.push({
vertex: closestVertexOffsetRight,
angle: angle - 0.0001,
});
}
}
closestVertices.sort(
gdjs.LightRuntimeObjectPixiRenderer._verticesWithAngleComparator
);
var filteredVerticesResult = [closestVertices[0].vertex];
var closestVerticesCount = closestVertices.length;
for (var i = 1; i < closestVerticesCount; i++) {
if (closestVertices[i].angle !== closestVertices[i - 1].angle)
filteredVerticesResult.push(closestVertices[i].vertex);
}
return filteredVerticesResult;
};

View File

@@ -0,0 +1,193 @@
/**
* @typedef {Object} LightObjectDataType
* @property {Object} content The base parameters of light object.
* @property {number} content.radius The radius of light object.
* @property {string} content.color A string representing color in hexadecimal format.
* @property {string} content.texture A string representing the name of texture used for light object.
* @property {boolean} content.debugMode true if the light objects shows debug graphics, false otherwise.
*
* @typedef {ObjectData & LightObjectDataType} LightObjectData
*/
/**
* Displays a Light object.
* @memberof gdjs
* @class LightRuntimeObject
* @extends RuntimeObject
* @param {gdjs.RuntimeScene} runtimeScene
* @param {LightObjectData} lightObjectData
*/
gdjs.LightRuntimeObject = function (runtimeScene, lightObjectData) {
gdjs.RuntimeObject.call(this, runtimeScene, lightObjectData);
/** @type {number} */
this._radius =
lightObjectData.content.radius > 0 ? lightObjectData.content.radius : 1;
/** @type {number[]} color in format [r, g, b], where each component is in the range [0, 255] */
this._color = gdjs.LightRuntimeObject.hexToRGBColor(
lightObjectData.content.color
);
/** @type {boolean} */
this._debugMode = lightObjectData.content.debugMode;
/** @type {string} */
this._texture = lightObjectData.content.texture;
/** @type {gdjs.LightObstaclesManager} */
this._obstaclesManager = gdjs.LightObstaclesManager.getManager(runtimeScene);
if (this._renderer)
gdjs.LightRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
else this._renderer = new gdjs.LightRuntimeObjectRenderer(this, runtimeScene);
/** @type {gdjs.RuntimeScene} */
this._runtimeScene = runtimeScene;
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
};
gdjs.LightRuntimeObject.prototype = Object.create(gdjs.RuntimeObject.prototype);
gdjs.registerObject('Lighting::LightObject', gdjs.LightRuntimeObject);
gdjs.LightRuntimeObject.hexToRGBColor = function (hex) {
var hexNumber = parseInt(hex.replace('#', ''), 16);
return [(hexNumber >> 16) & 0xff, (hexNumber >> 8) & 0xff, hexNumber & 0xff];
};
gdjs.LightRuntimeObject.prototype.getRendererObject = function () {
return this._renderer.getRendererObject();
};
/**
*
* @param {LightObjectData} oldObjectData
* @param {LightObjectData} newObjectData
*/
gdjs.LightRuntimeObject.prototype.updateFromObjectData = function (
oldObjectData,
newObjectData
) {
if (oldObjectData.content.radius !== newObjectData.content.radius)
this.setRadius(newObjectData.content.radius);
if (oldObjectData.content.color !== newObjectData.content.radius) {
this._color = gdjs.LightRuntimeObject.hexToRGBColor(
newObjectData.content.color
);
this._renderer.updateColor();
}
if (oldObjectData.content.texture !== newObjectData.content.texture) {
this._texture = newObjectData.content.texture;
this._renderer.updateMesh();
}
if (oldObjectData.content.debugMode !== newObjectData.content.debugMode) {
this._debugMode = newObjectData.content.debugMode;
this._renderer.updateDebugMode();
}
return true;
};
gdjs.LightRuntimeObject.prototype.update = function () {
this._renderer.ensureUpToDate();
};
/**
* Get the radius of the light object.
* @returns {number} radius of the light object.
*/
gdjs.LightRuntimeObject.prototype.getRadius = function () {
return this._radius;
};
/**
* Set the radius of the light object.
* @param {number} radius
*/
gdjs.LightRuntimeObject.prototype.setRadius = function (radius) {
this._radius = radius > 0 ? radius : 1;
this._renderer.updateRadius();
};
/**
* Get the height of the light object.
* @returns {number} height of light object.
*/
gdjs.LightRuntimeObject.prototype.getHeight = function () {
return 2 * this._radius;
};
/**
* Get the width of the light object.
* @returns {number} width of light object.
*/
gdjs.LightRuntimeObject.prototype.getWidth = function () {
return 2 * this._radius;
};
/**
* Get the x co-ordinate of the top-left vertex/point of light object.
* @returns {number} x co-ordinate of the top-left vertex/point.
*/
gdjs.LightRuntimeObject.prototype.getDrawableX = function () {
return this.x - this._radius;
};
/**
* Get the y co-ordinate of the top-left vertex/point of light object.
* @returns {number} y co-ordinate of the top-left vertex/point.
*/
gdjs.LightRuntimeObject.prototype.getDrawableY = function () {
return this.y - this._radius;
};
/**
* Get the color of the light object as a "R;G;B" string.
* @returns {string} the color of light object in "R;G;B" format.
*/
gdjs.LightRuntimeObject.prototype.getColor = function () {
return this._color[0] + ';' + this._color[1] + ';' + this._color[2];
};
/**
* Set the color of the light object in format "R;G;B" string, with components in the range of [0-255].
* @param {string} color
*/
gdjs.LightRuntimeObject.prototype.setColor = function (color) {
var rgbColor = color.split(';');
this._color = [
parseInt(rgbColor[0], 10),
parseInt(rgbColor[1], 10),
parseInt(rgbColor[2], 10),
];
this._renderer.updateColor();
};
/**
* Get the light obstacles manager if objects with the behavior exist, null otherwise.
* @returns {?gdjs.LightObstaclesManager} gdjs.LightObstaclesManager if it exists, otherwise null.
*/
gdjs.LightRuntimeObject.prototype.getObstaclesManager = function () {
return this._obstaclesManager;
};
/**
* Returns true if the light shows debug graphics, false otherwise.
* @returns {boolean} true if debug mode is activated.
*/
gdjs.LightRuntimeObject.prototype.getDebugMode = function () {
return this._debugMode;
};
/**
* Returns the path of texture resource.
* @returns {string} the path of texture.
*/
gdjs.LightRuntimeObject.prototype.getTexture = function () {
return this._texture;
};

View File

@@ -0,0 +1,200 @@
/**
* Tests for Light Object
*/
/**
* Utility function for adding light object for tests.
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} radius
* @returns {gdjs.LightRuntimeObject}
*/
const addLightObject = (runtimeScene, radius) => {
const lightObj = new gdjs.LightRuntimeObject(runtimeScene, {
name: 'lightObject',
type: 'Lighting::LightObject',
variables: [],
behaviors: [],
content: {
radius: radius,
color: '#b4b4b4',
texture: '',
debugMode: false,
},
});
runtimeScene.addObject(lightObj);
return lightObj;
};
/**
* Utility function for adding light obstacle for tests.
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} width
* @param {number} height
* @returns {gdjs.RuntimeObject}
*/
const addLightObstacle = (runtimeScene, width, height) => {
const obstacle = new gdjs.RuntimeObject(runtimeScene, {
name: 'lightObstacle',
type: '',
behaviors: [
{
type: 'Lighting::LightObstacleBehavior',
},
],
});
obstacle.getWidth = function () {
return width;
};
obstacle.getHeight = function () {
return height;
};
runtimeScene.addObject(obstacle);
return obstacle;
};
describe('gdjs.LightRuntimeObject', function () {
PIXI.settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],
variables: [],
behaviorsSharedData: [],
objects: [],
instances: [],
});
const lightObj = addLightObject(runtimeScene, 100);
lightObj.setPosition(200, 200);
it('check object properties', function () {
expect(lightObj.getRadius()).to.be(100);
expect(lightObj.getColor()).to.eql("180;180;180");
expect(lightObj.getDebugMode()).to.be(false);
expect(lightObj.getDrawableX()).to.be(100);
expect(lightObj.getDrawableY()).to.be(100);
});
it('bail out early while raycasting when there is no light obstacle', function () {
expect(lightObj._renderer._computeLightVertices()).to.eql([]);
lightObj._renderer._updateBuffers();
expect(lightObj._renderer._defaultVertexBuffer).to.eql(
new Float32Array([100, 300, 300, 300, 300, 100, 100, 100])
);
expect(gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer).to.eql(
new Float32Array([0, 1, 2, 0, 2, 3])
);
});
});
describe('Light with obstacles around it', function () {
PIXI.settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: {
resources: [],
},
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [{ name: '', visibility: true, effects: [] }],
variables: [],
behaviorsSharedData: [],
objects: [],
instances: [],
});
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / 60) * 1000;
};
const light = addLightObject(runtimeScene, 100);
const obstacle = addLightObstacle(runtimeScene, 50, 50);
it('Vertex and index buffers when light obstacle is present.', function () {
light.setPosition(200, 200);
obstacle.setPosition(250, 250);
runtimeScene.renderAndStep();
light.update();
const vertexBuffer = light._renderer._vertexBuffer;
const indexBuffer = light._renderer._indexBuffer;
// prettier-ignore
const expectedVertexBuffer = [
200, 200, 100, 100.0199966430664, 100, 100, 100.0199966430664, 100, 299.9800109863281,
100, 300, 100, 300, 100.0199966430664, 300, 249.9875030517578, 300, 250, 299.9750061035156,
250, 250.00999450683594, 250, 250, 250, 250,250.00999450683594, 250, 299.9750061035156, 250,
300, 249.9875030517578, 300, 100.0199966430664, 300, 100, 300, 100, 299.9800109863281,
];
// prettier-ignore
const expectedIndexBuffer = [
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6,
0, 6, 7, 0, 7, 8, 0, 8, 9, 0, 9, 10, 0, 10, 11,
0, 11, 12, 0, 12, 13, 0, 13, 14, 0, 14, 15, 0,
15, 16, 0, 16, 17, 0, 17, 18, 0, 18, 1,
];
expectedVertexBuffer.forEach((val, index) => {
expect(vertexBuffer[index]).to.be(val);
});
expectedIndexBuffer.forEach((val, index) => {
expect(indexBuffer[index]).to.be(val);
});
});
it('Vertex and index buffers after obstacle is moved.', function () {
obstacle.setPosition(150, 250);
runtimeScene.renderAndStep();
light.update();
const vertexBuffer = light._renderer._vertexBuffer;
const indexBuffer = light._renderer._indexBuffer;
// prettier-ignore
const expectedVertexBuffer = [
200, 200, 100, 100.0199966430664, 100, 100, 100.0199966430664, 100, 299.9800109863281,
100, 300, 100, 300, 100.0199966430664, 300, 299.9800109863281, 300, 300, 299.9800109863281,
300, 200.00999450683594, 300, 200, 250, 199.9949951171875, 250, 175.00625610351562, 250,
175, 250, 174.99374389648438, 250, 150.00999450683594, 250, 150, 250, 100, 299.9800109863281,
];
// prettier-ignore
const expectedIndexBuffer = [
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0,
6, 7, 0, 7, 8, 0, 8, 9, 0, 9, 10, 0, 10, 11, 0,
11, 12, 0, 12, 13, 0, 13, 14, 0, 14, 15, 0, 15,
16, 0, 16, 17, 0, 17, 18, 0, 18, 1,
];
expectedVertexBuffer.forEach((val, index) => {
expect(vertexBuffer[index]).to.be(val);
});
expectedIndexBuffer.forEach((val, index) => {
expect(indexBuffer[index]).to.be(val);
});
});
it("Obstacle moved outside light's radius.", function () {
obstacle.setPosition(400, 400);
runtimeScene.renderAndStep();
light.update();
// Ensure the fallback to simple quads. There shouldn't be anymore calculations
// when the obstacle is not inside light's area.
expect(light._renderer._computeLightVertices().length).to.eql(0);
const vertexBuffer = light._renderer._defaultVertexBuffer;
const indexBuffer = gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer;
const vertexData = [100, 300, 300, 300, 300, 100, 100, 100];
const indexData = [0, 1, 2, 0, 2, 3];
vertexData.forEach((val, index) => {
expect(vertexBuffer[index]).to.be(val);
});
indexData.forEach((val, index) => {
expect(indexBuffer[index]).to.be(val);
});
});
});

View File

@@ -49,7 +49,7 @@ gdjs.LinksManager.prototype.removeAllLinksOf = function(obj) {
if ( this.links.hasOwnProperty(objLinkedObjects[i].id) ) {
var otherObjList = this.links[objLinkedObjects[i].id];
var index = otherObjList.indexOf(obj);
if ( index !== -1) otherObjList.remove(index);
if ( index !== -1) otherObjList.splice(index, 1);
}
}
@@ -63,13 +63,13 @@ gdjs.LinksManager.prototype.removeLinkBetween = function(objA, objB) {
if ( this.links.hasOwnProperty(objA.id) ) {
list = this.links[objA.id];
index = list.indexOf(objB);
if ( index !== -1) list.remove(index);
if ( index !== -1) list.splice(index, 1);
}
if ( this.links.hasOwnProperty(objB.id) ) {
list = this.links[objB.id];
index = list.indexOf(objA);
if ( index !== -1) list.remove(index);
if ( index !== -1) list.splice(index, 1);
}
};

71
Extensions/P2P/A_peer.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,405 @@
// @ts-check
/// <reference path="peerjs" />
/**
* Tools for p2p multiplayer.
* @namespace
*/
gdjs.evtTools.p2p = {
/**
* The peer to peer configuration.
* @type {Peer.PeerJSOption}
*/
peerConfig: { debug: 1 }, // Enable logging of critical errors
/**
* The p2p client.
* @type {?Peer}
*/
peer: null,
/**
* All connected p2p clients, keyed by their ID.
* @type {Object<string, Peer.DataConnection>}
*/
connections: {},
/**
* Contains a list of events triggered by other p2p clients.
* Maps an event name (string) to a boolean:
* true if the event has been triggered, otherwise false.
* @note This is ignored if the event is in no dataloss mode.
* @type {Object<string, boolean>}
*/
triggeredEvents: {},
/**
* Contains the latest data sent with each event.
* If the event is in dataloss mode, maps an event name (string)
* to the string sent with that event.
* If the event is in no dataloss mode, maps an event name (string)
* to an array containing the data of each call of that event.
* @type {Object<string, string | Array>}
*/
lastEventData: {},
/**
* Tells how to handle an event (with or without data loss).
* Maps the event name (string) to a boolean:
* true for dataloss, false for no dataloss.
* @type {Object<string, string>}
*/
eventHandling: {},
/**
* True if PeerJS is initialized and ready.
* @type {boolean}
*/
ready: false,
/**
* True if an error occured.
* @type {boolean}
*/
error: false,
/**
* Last error's message.
* @type {string}
*/
lastError: '',
/**
* List of IDs of peers that just disconnected.
* @type {Array<string>}
*/
disconnectedPeers: [],
/**
* List of IDs of peers that just remotely initiated a connection.
* @type {Array<string>}
*/
connectedPeers: [],
};
/**
* Internal function called to initialize PeerJS after its
* broker server has been configured.
* @private
*/
gdjs.evtTools.p2p._loadPeerJS = function () {
if (gdjs.evtTools.p2p.peer != null) return;
gdjs.evtTools.p2p.peer = new Peer(gdjs.evtTools.p2p.peerConfig);
gdjs.evtTools.p2p.peer.on('open', function () {
gdjs.evtTools.p2p.ready = true;
});
gdjs.evtTools.p2p.peer.on('error', function (errorMessage) {
gdjs.evtTools.p2p.error = true;
gdjs.evtTools.p2p.lastError = errorMessage;
});
gdjs.evtTools.p2p.peer.on('connection', function (connection) {
connection.on('open', function () {
gdjs.evtTools.p2p._onConnection(connection);
gdjs.evtTools.p2p.connectedPeers.push(connection.peer);
});
});
gdjs.evtTools.p2p.peer.on('close', function () {
gdjs.evtTools.p2p.peer = null;
gdjs.evtTools.p2p._loadPeerJS();
});
gdjs.evtTools.p2p.peer.on('disconnected', gdjs.evtTools.p2p.peer.reconnect);
};
/**
* Internal function called when a connection with a remote peer is initiated.
* @private
* @param {Peer.DataConnection} connection The DataConnection of the peer
*/
gdjs.evtTools.p2p._onConnection = function (connection) {
gdjs.evtTools.p2p.connections[connection.peer] = connection;
connection.on('data', function (data) {
if (data.eventName === undefined) return;
var dataLoss = gdjs.evtTools.p2p.eventHandling[data.eventName];
if (typeof dataLoss === 'undefined' || dataLoss === false) {
if (typeof gdjs.evtTools.p2p.lastEventData[data.eventName] !== 'object')
gdjs.evtTools.p2p.lastEventData[data.eventName] = [];
gdjs.evtTools.p2p.lastEventData[data.eventName].push(data.data);
} else {
gdjs.evtTools.p2p.triggeredEvents[data.eventName] = true;
gdjs.evtTools.p2p.lastEventData[data.eventName] = data.data;
}
});
connection.on('error', function () {
// Close event is only for graceful disconnection, also handle error aka ungraceful disconnection
gdjs.evtTools.p2p._onDisconnect(connection.peer);
});
connection.on('close', function () {
gdjs.evtTools.p2p._onDisconnect(connection.peer);
});
// Regularly check for disconnection as the built in way is not reliable.
var disconnectChecker = function () {
if (
connection.peerConnection.connectionState === 'failed' ||
connection.peerConnection.connectionState === 'disconnected'
) {
gdjs.evtTools.p2p._onDisconnect(connection.peer);
} else {
setTimeout(disconnectChecker, 500);
}
};
disconnectChecker();
};
/**
* Internal function called when a remote client disconnects.
* @private
* @param {string} connectionID The ID of the peer that disconnected.
*/
gdjs.evtTools.p2p._onDisconnect = function (connectionID) {
gdjs.evtTools.p2p.disconnectedPeers.push(connectionID);
delete gdjs.evtTools.p2p.connections[connectionID];
};
/**
* Connects to another p2p client.
* @param {string} id - The other client's ID.
*/
gdjs.evtTools.p2p.connect = function (id) {
var connection = gdjs.evtTools.p2p.peer.connect(id);
connection.on('open', function () {
gdjs.evtTools.p2p._onConnection(connection);
});
};
/**
* Returns true when the event got triggered by another p2p client.
* @param {string} eventName
* @param {boolean} defaultDataLoss Is data loss allowed (accelerates event handling when true)?
* @returns {boolean}
*/
gdjs.evtTools.p2p.onEvent = function (eventName, defaultDataLoss) {
var dataLoss = gdjs.evtTools.p2p.eventHandling[eventName];
if (dataLoss == undefined) {
gdjs.evtTools.p2p.eventHandling[eventName] = defaultDataLoss;
return gdjs.evtTools.p2p.onEvent(eventName, defaultDataLoss);
}
if (dataLoss) {
var returnValue = gdjs.evtTools.p2p.triggeredEvents[eventName];
if (typeof returnValue === 'undefined') return false;
gdjs.evtTools.p2p.triggeredEvents[eventName] = false;
return returnValue;
} else {
var returnValue = gdjs.evtTools.p2p.lastEventData[eventName];
if (typeof returnValue === 'undefined') return false;
return returnValue.length !== 0;
}
};
/**
* Send an event to one specific connected client.
* @param {string} id - The ID of the client to send the event to.
* @param {string} eventName - The event to trigger.
* @param {string} [eventData] - Additional data to send with the event.
*/
gdjs.evtTools.p2p.sendDataTo = function (id, eventName, eventData) {
if (gdjs.evtTools.p2p.connections[id])
gdjs.evtTools.p2p.connections[id].send({
eventName: eventName,
data: eventData,
});
};
/**
* Send an event to all connected clients.
* @param {string} eventName - The event to trigger.
* @param {string} [eventData] - Additional data to send with the event.
*/
gdjs.evtTools.p2p.sendDataToAll = function (eventName, eventData) {
for (var id in gdjs.evtTools.p2p.connections) {
gdjs.evtTools.p2p.connections[id].send({
eventName: eventName,
data: eventData,
});
}
};
/**
* Send an event to one specific connected client.
* @param {string} id - The ID of the client to send the event to.
* @param {string} eventName - The event to trigger.
* @param {gdjs.Variable} variable - Additional variable to send with the event.
*/
gdjs.evtTools.p2p.sendVariableTo = function (id, eventName, variable) {
gdjs.evtTools.p2p.sendDataTo(
id,
eventName,
gdjs.evtTools.network.variableStructureToJSON(variable)
);
};
/**
* Send an event to all connected clients.
* @param {string} eventName - The event to trigger.
* @param {gdjs.Variable} variable - Additional variable to send with the event.
*/
gdjs.evtTools.p2p.sendVariableToAll = function (eventName, variable) {
gdjs.evtTools.p2p.sendDataToAll(
eventName,
gdjs.evtTools.network.variableStructureToJSON(variable)
);
};
/**
* Get some data associated to the last trigger of an event.
* @param {string} eventName - The event to get data from.
* @returns {string} - The data as JSON.
*/
gdjs.evtTools.p2p.getEventData = function (eventName) {
var dataLoss = gdjs.evtTools.p2p.eventHandling[eventName];
if (typeof dataLoss === 'undefined' || dataLoss === false) {
var event = gdjs.evtTools.p2p.lastEventData[eventName];
return event[event.length - 1];
} else {
return gdjs.evtTools.p2p.lastEventData[eventName];
}
};
/**
* Get a variable associated to the last trigger of an event.
* @param {string} eventName - The event to get the variable from.
* @param {gdjs.Variable} variable - The variable where to store the variable content.
*/
gdjs.evtTools.p2p.getEventVariable = function (eventName, variable) {
gdjs.evtTools.network.jsonToVariableStructure(
gdjs.evtTools.p2p.getEventData(eventName),
variable
);
};
/**
* Connects to a custom broker server.
* @param {string} host The host of the broker server.
* @param {number} port The port of the broker server.
* @param {string} path The path (part of the url after the host) to the broker server.
* @param {string} key Optional password to connect to the broker server.
* @param {boolean} ssl Use ssl?
*/
gdjs.evtTools.p2p.useCustomBrokerServer = function (
host,
port,
path,
key,
ssl
) {
key = key.length === 0 ? 'peerjs' : key; // All servers have "peerjs" as default key
gdjs.evtTools.p2p.peerConfig = {
debug: 1,
host,
port,
path,
secure: ssl,
key,
};
gdjs.evtTools.p2p._loadPeerJS();
};
/**
* Use default broker server.
* This is not recommended for published games,
* this server should only be used for quick testing in development.
*/
gdjs.evtTools.p2p.useDefaultBrokerServer = function () {
gdjs.evtTools.p2p._loadPeerJS();
};
/**
* Returns the own current peer ID.
* @see Peer.id
* @returns {string}
*/
gdjs.evtTools.p2p.getCurrentId = function () {
if (gdjs.evtTools.p2p.peer == undefined) return '';
return gdjs.evtTools.p2p.peer.id || '';
};
/**
* Returns true once PeerJS finished initialization.
* @see gdjs.evtTools.p2p.ready
* @returns {boolean}
*/
gdjs.evtTools.p2p.isReady = function () {
return gdjs.evtTools.p2p.ready;
};
/**
* Returns true once when there is an error.
* @returns {boolean}
*/
gdjs.evtTools.p2p.onError = function () {
var returnValue = gdjs.evtTools.p2p.error;
gdjs.evtTools.p2p.error = false;
return returnValue;
};
/**
* Returns the latest error message.
* @returns {boolean}
*/
gdjs.evtTools.p2p.getLastError = function () {
return gdjs.evtTools.p2p.lastError;
};
/**
* Returns true once a peer disconnected.
* @returns {boolean}
*/
gdjs.evtTools.p2p.onDisconnect = function () {
return gdjs.evtTools.p2p.disconnectedPeers.length > 0;
};
/**
* Get the ID of the peer that triggered onDisconnect.
* @returns {string}
*/
gdjs.evtTools.p2p.getDisconnectedPeer = function () {
return (
gdjs.evtTools.p2p.disconnectedPeers[
gdjs.evtTools.p2p.disconnectedPeers.length - 1
] || ''
);
};
/**
* Returns true once if a remote peer just initiated a connection.
* @returns {boolean}
*/
gdjs.evtTools.p2p.onConnection = function () {
return gdjs.evtTools.p2p.connectedPeers.length > 0;
};
/**
* Get the ID of the peer that triggered onConnection.
* @returns {string}
*/
gdjs.evtTools.p2p.getConnectedPeer = function () {
return (
gdjs.evtTools.p2p.connectedPeers[
gdjs.evtTools.p2p.connectedPeers.length - 1
] || ''
);
};
gdjs.callbacksRuntimeScenePostEvents.push(function () {
for (var i in gdjs.evtTools.p2p.lastEventData) {
if (
typeof gdjs.evtTools.p2p.lastEventData[i] === 'object' &&
gdjs.evtTools.p2p.lastEventData[i].length > 0
)
gdjs.evtTools.p2p.lastEventData[i].pop();
}
if (gdjs.evtTools.p2p.disconnectedPeers.length > 0)
gdjs.evtTools.p2p.disconnectedPeers.pop();
if (gdjs.evtTools.p2p.connectedPeers.length > 0)
gdjs.evtTools.p2p.connectedPeers.pop();
});

View File

@@ -0,0 +1,361 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension /*: gdPlatformExtension */ = new gd.PlatformExtension();
extension
.setExtensionInformation(
'P2P',
_('Peer-to-Peer communication (experimental)'),
_(
'Allow game instances to communicate remotely using messages sent via WebRTC (P2P)'
),
'Arthur Pacaud (arthuro555)',
'MIT'
)
.setExtensionHelpPath('/all-features/p2p');
extension
.addCondition(
'OnEvent',
_('Event triggered by peer'),
_('Triggers once when a connected client sends the event'),
_('Event _PARAM0_ received from other client (data loss: _PARAM1_)'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Event name'), '', false)
.addParameter('yesorno', _('Data loss allowed?'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.onEvent');
extension
.addCondition(
'IsReady',
_('Is P2P ready'),
_(
'True if the peer-to-peer extension initialized and is ready to use.'
),
_('Is P2P ready?'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.isReady');
extension
.addCondition(
'OnError',
_('An error occurred'),
_(
'Triggers once when an error occurs. ' +
'Use P2P::GetLastError() expression to get the content of the error ' +
'if you want to analyse it or display it to the user.'
),
_('P2P error occurred'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.onError');
extension
.addCondition(
'OnDisconnection',
_('Peer disconnected'),
_('Triggers once when a peer disconnects.'),
_('P2P peer disconnected'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.onDisconnect');
extension
.addCondition(
'OnConnection',
_('Peer Connected'),
_('Triggers once when a remote peer initiates a connection.'),
_('P2P peer connected'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.onConnection');
extension
.addAction(
'Connect',
_('Connect to another client'),
_('Connects the current client to another client using its id.'),
_('Connect to P2P client _PARAM0_'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('ID of the other client'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.connect');
extension
.addAction(
'UseOwnBroker',
_('Connect to a broker server'),
_('Connects the extension to a broker server.'),
_('Connect to the broker server at http://_PARAM0_:_PARAM1_/'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Host'), '', false)
.addParameter('number', _('Port'), '', false)
.addParameter('string', _('Path'), '', false)
.addParameter('string', _('Key'), '', false)
.addParameter('yesorno', _('SSl enabled?'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.useCustomBrokerServer');
extension
.addAction(
'UseDefaultBroker',
_('Connect to the default broker server'),
_('Connects to the default broker server.'),
_('Connect to the default broker server'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.useDefaultBrokerServer');
extension
.addAction(
'SendToAll',
_('Trigger event on all connected clients'),
_('Triggers an event on all connected clients'),
_(
'Trigger event _PARAM0_ on all connected clients (extra data: _PARAM1_)'
),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Event name'), '', false)
.addParameter('string', _('Extra data (optional)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.sendDataToAll');
extension
.addAction(
'SendToOne',
_('Trigger event on a specific client'),
_('Triggers an event on a specific connected client'),
_('Trigger event _PARAM1_ on client _PARAM0_ (extra data: _PARAM2_)'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('ID of the other client'), '', false)
.addParameter('string', _('Event name'), '', false)
.addParameter('string', _('Extra data (optional)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.sendDataTo');
extension
.addAction(
'SendToAllVariable',
_('Trigger event on all connected clients (variable)'),
_('Triggers an event on all connected clients'),
_(
'Trigger event _PARAM0_ on all connected clients (extra data: _PARAM1_)'
),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Event name'), '', false)
.addParameter(
'scenevar',
_('Variable containing the extra data'),
'',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.sendVariableToAll');
extension
.addAction(
'SendToOneVariable',
_('Trigger event on a specific client (variable)'),
_('Triggers an event on a specific connected client'),
_('Trigger event _PARAM1_ on client _PARAM0_ (extra data: _PARAM2_)'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('ID of the other client'), '', false)
.addParameter('string', _('Event name'), '', false)
.addParameter(
'scenevar',
_('Variable containing the extra data'),
'',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.sendVariableTo');
extension
.addAction(
'GetEventVariable',
_('Get event data (variable)'),
_(
'Store the data of the specified event in a variable. ' +
'Check in the conditions that the event was received using the "Event received" condition.'
),
_(
'Overwrite _PARAM1_ with variable sent with last trigger of _PARAM0_'
),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Event name'), '', false)
.addParameter(
'scenevar',
_('Variable where to store the received data'),
'',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getEventVariable');
extension
.addStrExpression(
'GetEventData',
_('Get event data'),
_(
'Returns the data received when the specified event was last triggered'
),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter('string', _('Event name'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getEventData');
extension
.addStrExpression(
'GetID',
_('Get client ID'),
_('Gets the client ID of the current game instance'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getCurrentId');
extension
.addStrExpression(
'GetLastError',
_('Get last error'),
_('Gets the description of the last P2P error'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getLastError');
extension
.addStrExpression(
'GetLastDisconnectedPeer',
_('Get last disconnected peer'),
_('Gets the ID of the latest peer that has disconnected.'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getDisconnectedPeer');
extension
.addStrExpression(
'GetLastConnectedPeer',
_('Get ID of the connected peer'),
_('Gets the ID of the newly connected peer.'),
_('P2P (experimental)'),
'JsPlatform/Extensions/p2picon.svg'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.getConnectedPeer');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

208
Extensions/P2P/peerjs.d.ts vendored Normal file
View File

@@ -0,0 +1,208 @@
// Type definitions for the PeerJS class module
// Original definitions by Toshiya Nakakura <https://github.com/nakakura>
// at https://github.com/DefinitelyTyped/DefinitelyTyped
declare class Peer {
prototype: RTCIceServer;
/**
* A peer can connect to other peers and listen for connections.
* @param id Other peers can connect to this peer using the provided ID.
* If no ID is given, one will be generated by the brokering server.
* @param options for specifying details about PeerServer
*/
constructor(id?: string, options?: Peer.PeerJSOption);
/**
* A peer can connect to other peers and listen for connections.
* @param options for specifying details about PeerServer
*/
constructor(options: Peer.PeerJSOption);
/**
* Connects to the remote peer specified by id and returns a data connection.
* @param id The brokering ID of the remote peer (their peer.id).
* @param options for specifying details about Peer Connection
*/
connect(id: string, options?: Peer.PeerConnectOption): Peer.DataConnection;
/**
* Calls the remote peer specified by id and returns a media connection.
* @param id The brokering ID of the remote peer (their peer.id).
* @param stream The caller's media stream
* @param options Metadata associated with the connection, passed in by whoever initiated the connection.
*/
call(id: string, stream: MediaStream, options?: Peer.CallOption): Peer.MediaConnection;
/**
* Set listeners for peer events.
* @param event Event name
* @param cb Callback Function
*/
on(event: string, cb: () => void): void;
/**
* Emitted when a connection to the PeerServer is established.
* @param event Event name
* @param cb id is the brokering ID of the peer
*/
on(event: "open", cb: (id: string) => void): void;
/**
* Emitted when a new data connection is established from a remote peer.
* @param event Event name
* @param cb Callback Function
*/
on(
event: "connection",
cb: (dataConnection: Peer.DataConnection) => void
): void;
/**
* Emitted when a remote peer attempts to call you.
* @param event Event name
* @param cb Callback Function
*/
on(event: "call", cb: (mediaConnection: Peer.MediaConnection) => void): void;
/**
* Emitted when the peer is destroyed and can no longer accept or create any new connections.
* @param event Event name
* @param cb Callback Function
*/
on(event: "close", cb: () => void): void;
/**
* Emitted when the peer is disconnected from the signalling server
* @param event Event name
* @param cb Callback Function
*/
on(event: "disconnected", cb: () => void): void;
/**
* Errors on the peer are almost always fatal and will destroy the peer.
* @param event Event name
* @param cb Callback Function
*/
on(event: "error", cb: (err: any) => void): void;
/**
* Remove event listeners.(EventEmitter3)
* @param {String} event The event we want to remove.
* @param {Function} fn The listener that we need to find.
* @param {Boolean} once Only remove once listeners.
*/
off(event: string, fn: Function, once?: boolean): void;
/**
* Close the connection to the server, leaving all existing data and media connections intact.
*/
disconnect(): void;
/**
* Attempt to reconnect to the server with the peer's old ID
*/
reconnect(): void;
/**
* Close the connection to the server and terminate all existing connections.
*/
destroy(): void;
/**
* Retrieve a data/media connection for this peer.
* @param peerId
* @param connectionId
*/
getConnection(peerId: string, connectionId: string): Peer.MediaConnection | Peer.DataConnection | null;
/**
* Get a list of available peer IDs
* @param callback
*/
listAllPeers(callback: (peerIds: Array<string>) => void): void;
/**
* The brokering ID of this peer
*/
id: string;
/**
* A hash of all connections associated with this peer, keyed by the remote peer's ID.
*/
connections: any;
/**
* false if there is an active connection to the PeerServer.
*/
disconnected: boolean;
/**
* true if this peer and all of its connections can no longer be used.
*/
destroyed: boolean;
}
declare namespace Peer {
interface PeerJSOption {
key?: string;
host?: string;
port?: number;
path?: string;
secure?: boolean;
config?: RTCConfiguration;
debug?: number;
}
interface PeerConnectOption {
label?: string;
metadata?: any;
serialization?: string;
reliable?: boolean;
}
interface CallOption {
metadata?: any;
sdpTransform?: Function;
}
interface AnswerOption {
sdpTransform?: Function;
}
interface DataConnection {
send(data: any): void;
close(): void;
on(event: string, cb: () => void): void;
on(event: "data", cb: (data: any) => void): void;
on(event: "open", cb: () => void): void;
on(event: "close", cb: () => void): void;
on(event: "error", cb: (err: any) => void): void;
off(event: string, fn: Function, once?: boolean): void;
dataChannel: RTCDataChannel;
label: string;
metadata: any;
open: boolean;
peerConnection: RTCPeerConnection;
peer: string;
reliable: boolean;
serialization: string;
type: string;
bufferSize: number;
stringify: (data: any) => string;
parse: (data: string) => any;
}
interface MediaConnection {
answer(stream?: MediaStream, options?: AnswerOption): void;
close(): void;
on(event: string, cb: () => void): void;
on(event: "stream", cb: (stream: MediaStream) => void): void;
on(event: "close", cb: () => void): void;
on(event: "error", cb: (err: any) => void): void;
off(event: string, fn: Function, once?: boolean): void;
open: boolean;
metadata: any;
peerConnection: RTCPeerConnection;
peer: string;
type: string;
}
interface UtilSupportsObj {
browser: boolean,
webRTC: boolean;
audioVideo: boolean;
data: boolean;
binaryBlob: boolean;
reliable: boolean;
}
interface util {
browser: string;
supports: UtilSupportsObj;
}
}

View File

@@ -66,15 +66,15 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
obj.AddAction(
"SetColor",
_("Global color"),
_("Change the global color of a Panel Sprite. The default color is white."),
_("Change color of _PARAM0_ to _PARAM1_"),
_("Tint color"),
_("Change the tint of a Panel Sprite. The default color is white."),
_("Change tint of _PARAM0_ to _PARAM1_"),
_("Effects"),
"res/actions/color24.png",
"res/actions/color.png")
.AddParameter("object", _("Object"), "PanelSprite")
.AddParameter("color", _("Color"));
.AddParameter("color", _("Tint"));
obj.AddAction("Width",
_("Width"),

View File

@@ -258,7 +258,7 @@ gdjs.PanelSpriteRuntimeObject.prototype.setColor = function(rgbColor) {
/**
* Get the tint of the panel sprite object.
*
* @returns {string} rgbColor The color, in RGB format ("128;200;255").
* @returns {string} The color, in RGB format ("128;200;255").
*/
gdjs.PanelSpriteRuntimeObject.prototype.getColor = function() {
return this._renderer.getColor();

View File

@@ -293,7 +293,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition("NoMoreParticles",
_("No more particles"),
_("Return true if the object does not emit particles "
_("Check if the object does not emit particles "
"anylonger, so as to destroy it for example."),
_("_PARAM0_ does not emit anylonger."),
_("Common"),

View File

@@ -56,7 +56,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("PathFound",
_("Path found"),
_("Return true if a path has been found."),
_("Check if a path has been found."),
_("A path has been found for _PARAM0_"),
"",
"CppPlatform/Extensions/AStaricon24.png",
@@ -69,7 +69,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("DestinationReached",
_("Destination reached"),
_("Return true if the destination was reached."),
_("Check if the destination was reached."),
_("_PARAM0_ reached its destination"),
"",
"CppPlatform/Extensions/AStaricon24.png",
@@ -338,7 +338,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("DiagonalsAllowed",
_("Diagonal movement"),
_("Return true if the object is allowed to move "
_("Check if the object is allowed to move "
"diagonally on the path"),
_("Diagonal moves allowed for _PARAM0_"),
_("Path"),
@@ -366,7 +366,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("ObjectRotated",
_("Object rotated"),
_("Return true if the object is rotated when traveling on "
_("Check if the object is rotated when traveling on "
"its path."),
_("_PARAM0_ is rotated when traveling on its path"),
_("Path"),
@@ -622,7 +622,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("IsImpassable",
_("Is object impassable?"),
_("Return true if the obstacle is impassable"),
_("Check if the obstacle is impassable"),
_("_PARAM0_ is impassable"),
_("Obstacles"),
"CppPlatform/Extensions/pathfindingobstacleicon24.png",

View File

@@ -53,9 +53,9 @@ gdjs.Physics2SharedData = function(runtimeScene, sharedData) {
var behaviorB = contact.GetFixtureB().GetBody().gdjsAssociatedBehavior;
// Remove each other contact
var i = behaviorA.currentContacts.indexOf(behaviorB);
if (i !== -1) behaviorA.currentContacts.remove(i);
if (i !== -1) behaviorA.currentContacts.splice(i, 1);
i = behaviorB.currentContacts.indexOf(behaviorA);
if (i !== -1) behaviorB.currentContacts.remove(i);
if (i !== -1) behaviorB.currentContacts.splice(i, 1);
};
this.contactListener.PreSolve = function() {};

View File

@@ -58,10 +58,10 @@ gdjs.PhysicsSharedData = function(runtimeScene, sharedData)
behaviorB = contact.GetFixtureB().GetBody().gdjsAssociatedBehavior;
var i = behaviorA.currentContacts.indexOf(behaviorB);
if ( i !== -1 ) behaviorA.currentContacts.remove(i);
if ( i !== -1 ) behaviorA.currentContacts.splice(i, 1);
i = behaviorB.currentContacts.indexOf(behaviorA);
if ( i !== -1 ) behaviorB.currentContacts.remove(i);
if ( i !== -1 ) behaviorB.currentContacts.splice(i, 1);
};
this.contactListener.PreSolve = function() {};

View File

@@ -17,16 +17,17 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.getRendererObject = functio
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.clear = function() {
this._graphics.clear();
this.updateOutline();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawRectangle = function(x1, y1, x2, y2) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawRect(x1, y1, x2 - x1, y2 - y1);
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawCircle = function(x, y, radius) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawCircle(x, y, radius);
this._graphics.endFill();
@@ -55,12 +56,14 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawLineV2 = function(x1, y
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawEllipse = function(x1, y1, width, height) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawEllipse(x1, y1, width/2, height/2);
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawRoundedRectangle = function(x1, y1, x2, y2, radius) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawRoundedRect(x1, y1, x2 - x1, y2 - y1, radius);
this._graphics.closePath();
@@ -68,6 +71,7 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawRoundedRectangle = func
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawStar = function(x1, y1, points, radius, innerRadius, rotation) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawStar(x1, y1, points, radius, innerRadius ? innerRadius : radius/2, rotation ? gdjs.toRad(rotation) : 0);
this._graphics.closePath();
@@ -75,6 +79,7 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawStar = function(x1, y1,
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawArc = function(x1, y1, radius, startAngle, endAngle, anticlockwise, closePath) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.moveTo( x1 + radius * Math.cos(gdjs.toRad(startAngle)), y1 + radius * Math.sin(gdjs.toRad(startAngle)));
this._graphics.arc(x1, y1, radius, gdjs.toRad(startAngle), gdjs.toRad(endAngle), anticlockwise ? true : false);
@@ -85,6 +90,7 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawArc = function(x1, y1,
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.moveTo(x1, y1);
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, x2, y2);
@@ -92,6 +98,7 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawBezierCurve = function(
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
this.updateOutline();
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.moveTo(x1, y1);
this._graphics.quadraticCurveTo(cpX, cpY, x2, y2);

View File

@@ -30,14 +30,14 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
extension
.AddCondition(
"IsWebGLSupported",
_("Is WebGL supported"),
_("Check if GPU accelerated WebGL is supported on the target device."),
_("WebGL is available"),
_("System information"),
"CppPlatform/Extensions/systeminfoicon24.png",
"CppPlatform/Extensions/systeminfoicon16.png")
.AddCondition("IsWebGLSupported",
_("Is WebGL supported"),
_("Check if GPU accelerated WebGL is supported on the "
"target device."),
_("WebGL is available"),
_("System information"),
"CppPlatform/Extensions/systeminfoicon24.png",
"CppPlatform/Extensions/systeminfoicon16.png")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SystemInfo::IsWebGLSupported")
@@ -47,16 +47,28 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
.AddCondition(
"IsPreview",
_("Is the game running as a preview"),
_(
"Check if the game is currently being previewed in the editor. "
"This can be used to enable a \"Debug mode\" or do some work only in previews."
),
_("Check if the game is currently being previewed in the editor. "
"This can be used to enable a \"Debug mode\" or do some work only "
"in previews."),
_("The game is being previewed in the editor"),
_("System information"),
"CppPlatform/Extensions/systeminfoicon24.png",
"CppPlatform/Extensions/systeminfoicon16.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddCondition(
"HasTouchScreen",
_("Device has a touchscreen"),
_("Check if the device running the game has a touchscreen (typically "
"Android phones, iPhones, iPads, but also some laptops)."),
_("The device has a touchscreen"),
_("System information"),
"CppPlatform/Extensions/systeminfoicon24.png",
"CppPlatform/Extensions/systeminfoicon16.png")
.AddCodeOnlyParameter("currentScene", "");
#endif
}

View File

@@ -35,6 +35,10 @@ class SystemInfoJsExtension : public gd::PlatformExtension {
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
.SetFunctionName("gdjs.evtTools.systemInfo.isPreview");
GetAllConditions()["SystemInfo::HasTouchScreen"]
.codeExtraInformation
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
.SetFunctionName("gdjs.evtTools.systemInfo.hasTouchScreen");
StripUnimplementedInstructionsAndExpressions();
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();

View File

@@ -1,48 +1,120 @@
// @ts-check
/**
GDevelop - SystemInfo Extension
Copyright (c) 2016 Florian Rival (Florian.Rival@gmail.com)
* GDevelop - SystemInfo Extension
* Copyright (c) 2016-present Florian Rival (Florian.Rival@gmail.com)
*/
/**
* @memberof gdjs.evtTools
* @class linkedObjects
* @static
* @private
*/
gdjs.evtTools.systemInfo = {};
gdjs.evtTools.systemInfo = (function () {
/** @type {?boolean} */
let cachedIsMobile = null;
/** @type {?boolean} */
let cachedHasTouchScreen = null;
gdjs.evtTools.systemInfo.isMobile = function() {
if (typeof cc !== "undefined" && cc.sys) {
return cc.sys.isMobile;
} else if (typeof Cocoon !== "undefined" && Cocoon.App) {
return true;
} else if (typeof window !== "undefined" && window.cordova) {
return true;
} else if (typeof window !== "undefined") {
// Try to detect mobile device browsers.
if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) {
return true;
}
}
/** @returns {boolean} */
const checkIsMobile = () => {
// @ts-ignore
if (typeof cc !== 'undefined' && cc.sys) {
// @ts-ignore
return cc.sys.isMobile;
return false;
};
// @ts-ignore
} else if (typeof Cocoon !== 'undefined' && Cocoon.App) {
return true;
/**
* Check if the the device supports WebGL.
* @param {gdjs.RuntimeScene} runtimeScene
* @returns {boolean} true if WebGL is supported
*/
gdjs.evtTools.systemInfo.isWebGLSupported = function(runtimeScene) {
return runtimeScene.getGame().getRenderer().isWebGLSupported();
};
// @ts-ignore
} else if (typeof window !== 'undefined' && window.cordova) {
return true;
} else if (typeof window !== 'undefined') {
// Try to detect mobile device browsers.
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
navigator.userAgent.substr(0, 4)
)
) {
return true;
}
/**
* Check if the game is running as a preview, launched from an editor.
* @param {gdjs.RuntimeScene} runtimeScene The current scene.
* @returns {boolean} true if the game is running as a preview.
*/
gdjs.evtTools.systemInfo.isPreview = function(runtimeScene) {
return runtimeScene.getGame().isPreview();
};
// Try to detect iOS
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
return true;
} else if (/MacIntel/.test(navigator.platform)) {
// Work around for recent iPads that are "desktop-class browsing".
// We can still detect them using their touchscreen, but this is a hack.
// If mac laptops start to support touchscreens, this won't work anymore. Hence it's better
// to test for the presence of a touchscreen if needed rather than checking if the device
// is "mobile".
return !!navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
}
}
return false;
};
/**
* Check if the game runs on a mobile device (iPhone, iPad, Android).
* Note that the distinction between what is a mobile device and what is not
* is becoming blurry. If you use this for mobile controls,
* prefer to check if the device has touchscreen support.
* @returns {boolean}
*/
const isMobile = () => {
if (cachedIsMobile !== null) {
return cachedIsMobile;
}
return (cachedIsMobile = checkIsMobile());
};
/** @returns {boolean} */
const checkHasTouchScreen = () => {
// First check if the device is mobile, as all mobile devices have a touchscreen
// and some older browsers don't have support for `navigator.maxTouchPoints`
if (isMobile()) {
return true;
}
return !!navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
};
/**
* Check if the device has a touchscreen
*/
const hasTouchScreen = () => {
if (cachedHasTouchScreen !== null) {
return cachedHasTouchScreen;
}
return (cachedHasTouchScreen = checkHasTouchScreen());
};
/**
* Check if the the device supports WebGL.
* @param {gdjs.RuntimeScene} runtimeScene
* @returns {boolean} true if WebGL is supported
*/
const isWebGLSupported = (runtimeScene) => {
return runtimeScene.getGame().getRenderer().isWebGLSupported();
};
/**
* Check if the game is running as a preview, launched from an editor.
* @param {gdjs.RuntimeScene} runtimeScene The current scene.
* @returns {boolean} true if the game is running as a preview.
*/
const isPreview = (runtimeScene) => {
return runtimeScene.getGame().isPreview();
};
return {
isPreview: isPreview,
isWebGLSupported: isWebGLSupported,
hasTouchScreen: hasTouchScreen,
isMobile: isMobile,
};
})();

View File

@@ -71,11 +71,3 @@ gdjs.TextEntryRuntimeObject.prototype.activate = function(enable) {
this._activated = enable;
this._renderer.activate(this._activated);
};
gdjs.TextEntryRuntimeObject.prototype.setLayer = function(layer) {
// No renderable object
};
gdjs.TextEntryRuntimeObject.prototype.setZOrder = function(z) {
// No renderable object
};

View File

@@ -384,7 +384,7 @@ gdjs.TextRuntimeObject.prototype.setColor = function(str) {
/**
* Get the text color.
* @return {String} The color as a "R;G;B" string, for example: "255;0;0"
* @return {string} The color as a "R;G;B" string, for example: "255;0;0"
*/
gdjs.TextRuntimeObject.prototype.getColor = function(str) {
return this._color[0] + ";" + this._color[1] + ";" + this._color[2];

View File

@@ -62,15 +62,15 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
obj.AddAction(
"SetColor",
_("Global color"),
_("Change the global color of a Tiled Sprite. The default color is white."),
_("Change color of _PARAM0_ to _PARAM1_"),
_("Tint color"),
_("Change the tint of a Tiled Sprite. The default color is white."),
_("Change tint of _PARAM0_ to _PARAM1_"),
_("Effects"),
"res/actions/color24.png",
"res/actions/color.png")
.AddParameter("object", _("Object"), "TiledSprite")
.AddParameter("color", _("Color"));
.AddParameter("color", _("Tint"));
obj.AddAction("Width",
_("Width"),

View File

@@ -24,8 +24,7 @@ gdjs.TiledSpriteRuntimeObjectPixiRenderer.prototype.getRendererObject = function
};
gdjs.TiledSpriteRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
//TODO: Workaround a not working property in PIXI.js:
this._tiledSprite.alpha = this._tiledSprite.visible ? this._object.opacity/255 : 0;
this._tiledSprite.alpha = this._object.opacity/255;
}
gdjs.TiledSpriteRuntimeObjectPixiRenderer.prototype.updatePosition = function() {

View File

@@ -213,7 +213,7 @@ gdjs.TiledSpriteRuntimeObject.prototype.setColor = function(rgbColor) {
/**
* Get the tint of the tiled sprite object.
*
* @returns {string} rgbColor The color, in RGB format ("128;200;255").
* @returns {string} The color, in RGB format ("128;200;255").
*/
gdjs.TiledSpriteRuntimeObject.prototype.getColor = function() {
return this._renderer.getColor();

View File

@@ -290,8 +290,8 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition(
"Angle",
_("Angle of movement"),
_("Compare the angle of the top-down movemement of the object."),
_("the angle of movemement"),
_("Compare the angle of the top-down movement of the object."),
_("the angle of movement"),
_("Movement"),
"CppPlatform/Extensions/topdownmovementicon24.png",
"CppPlatform/Extensions/topdownmovementicon16.png")
@@ -305,8 +305,8 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition(
"XVelocity",
_("Speed on X axis"),
_("Compare the velocity of the top-down movemement of the object on the X axis."),
_("the speed of movemement on X axis"),
_("Compare the velocity of the top-down movement of the object on the X axis."),
_("the speed of movement on X axis"),
_("Movement"),
"CppPlatform/Extensions/topdownmovementicon24.png",
"CppPlatform/Extensions/topdownmovementicon16.png")
@@ -320,8 +320,8 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition(
"YVelocity",
_("Speed on Y axis"),
_("Compare the velocity of the top-down movemement of the object on the Y axis."),
_("the speed of movemement on Y axis"),
_("Compare the velocity of the top-down movement of the object on the Y axis."),
_("the speed of movement on Y axis"),
_("Movement"),
"CppPlatform/Extensions/topdownmovementicon24.png",
"CppPlatform/Extensions/topdownmovementicon16.png")
@@ -347,7 +347,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition("DiagonalsAllowed",
_("Diagonal movement"),
_("Return true if the object is allowed to move diagonally"),
_("Check if the object is allowed to move diagonally"),
_("Allow diagonal moves for _PARAM0_"),
_("Movement"),
"CppPlatform/Extensions/topdownmovementicon24.png",
@@ -375,7 +375,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddCondition(
"ObjectRotated",
_("Object rotated"),
_("Return true if the object is rotated while traveling on its path."),
_("Check if the object is rotated while traveling on its path."),
_("_PARAM0_ is rotated when moving"),
_("Movement"),
"CppPlatform/Extensions/topdownmovementicon24.png",

View File

@@ -66,8 +66,8 @@ module.exports = {
tweenBehavior,
new gd.BehaviorsSharedData()
)
.setIncludeFile("Extensions/TweenBehavior/tweenruntimebehavior.js")
.addIncludeFile("Extensions/TweenBehavior/shifty.js");
.setIncludeFile("Extensions/TweenBehavior/shifty.js")
.addIncludeFile("Extensions/TweenBehavior/tweenruntimebehavior.js");
const easingChoices = JSON.stringify([
"linear",
@@ -198,6 +198,64 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName("addObjectPositionXTween");
behavior
.addAction(
"AddObjectWidthTween",
_("Add object width tween"),
_("Add a tween animation for the object width."),
_(
"Tween the width of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_"
),
_("Size"),
"JsPlatform/Extensions/tween_behavior24.png",
"JsPlatform/Extensions/tween_behavior32.png"
)
.addParameter("object", _("Object"), "", false)
.addParameter("behavior", _("Behavior"), "TweenBehavior", false)
.addParameter("string", _("Tween Identifier"), "", false)
.addParameter("expression", _("To width"), "", false)
.addParameter("stringWithSelector", _("Easing"), easingChoices, false)
.setDefaultValue("linear")
.addParameter("expression", _("Duration"), "", false)
.addParameter(
"yesorno",
_("Destroy this object when tween finishes"),
"",
false
)
.setDefaultValue("no")
.getCodeExtraInformation()
.setFunctionName("addObjectWidthTween");
behavior
.addAction(
"AddObjectHeightTween",
_("Add object height tween"),
_("Add a tween animation for the object height."),
_(
"Tween the height of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_"
),
_("Size"),
"JsPlatform/Extensions/tween_behavior24.png",
"JsPlatform/Extensions/tween_behavior32.png"
)
.addParameter("object", _("Object"), "", false)
.addParameter("behavior", _("Behavior"), "TweenBehavior", false)
.addParameter("string", _("Tween Identifier"), "", false)
.addParameter("expression", _("To height"), "", false)
.addParameter("stringWithSelector", _("Easing"), easingChoices, false)
.setDefaultValue("linear")
.addParameter("expression", _("Duration"), "", false)
.addParameter(
"yesorno",
_("Destroy this object when tween finishes"),
"",
false
)
.setDefaultValue("no")
.getCodeExtraInformation()
.setFunctionName("addObjectHeightTween");
behavior
.addAction(
"AddObjectPositionYTween",

View File

@@ -817,6 +817,106 @@ gdjs.TweenRuntimeBehavior.prototype.addTextObjectCharacterSizeTween = function(
this._setupTweenEnding(identifier, destroyObjectWhenFinished);
};
/**
* Add an object width tween.
* @param {string} identifier Unique id to idenfify the tween
* @param {number} toWidth The target width
* @param {string} easingValue Type of easing
* @param {number} durationValue Duration in milliseconds
* @param {boolean} destroyObjectWhenFinished Destroy this object when the tween ends
*/
gdjs.TweenRuntimeBehavior.prototype.addObjectWidthTween = function(
identifier,
toWidth,
easingValue,
durationValue,
destroyObjectWhenFinished
) {
var that = this;
if (!this._isActive) return;
if (!!gdjs.TweenRuntimeBehavior.easings[easingValue]) return;
if (this._tweenExists(identifier)) {
this.removeTween(identifier);
}
var newTweenable = gdjs.TweenRuntimeBehavior.makeNewTweenable(
this._runtimeScene
);
newTweenable.setConfig({
from: {
width: this.owner.getWidth(),
},
to: {
width: toWidth,
},
duration: durationValue,
easing: easingValue,
step: function step(state) {
that.owner.setWidth(state.width);
},
});
this._addTween(
identifier,
newTweenable,
this._runtimeScene.getTimeManager().getTimeFromStart(),
durationValue
);
this._setupTweenEnding(identifier, destroyObjectWhenFinished);
};
/**
* Add an object height tween.
* @param {string} identifier Unique id to idenfify the tween
* @param {number} toHeight The target height
* @param {string} easingValue Type of easing
* @param {number} durationValue Duration in milliseconds
* @param {boolean} destroyObjectWhenFinished Destroy this object when the tween ends
*/
gdjs.TweenRuntimeBehavior.prototype.addObjectHeightTween = function(
identifier,
toHeight,
easingValue,
durationValue,
destroyObjectWhenFinished
) {
var that = this;
if (!this._isActive) return;
if (!!gdjs.TweenRuntimeBehavior.easings[easingValue]) return;
if (this._tweenExists(identifier)) {
this.removeTween(identifier);
}
var newTweenable = gdjs.TweenRuntimeBehavior.makeNewTweenable(
this._runtimeScene
);
newTweenable.setConfig({
from: {
height: this.owner.getHeight(),
},
to: {
height: toHeight,
},
duration: durationValue,
easing: easingValue,
step: function step(state) {
that.owner.setHeight(state.height);
},
});
this._addTween(
identifier,
newTweenable,
this._runtimeScene.getTimeManager().getTimeFromStart(),
durationValue
);
this._setupTweenEnding(identifier, destroyObjectWhenFinished);
};
/**
* Tween is playing.
* @param {string} identifier Unique id to idenfify the tween
@@ -1030,23 +1130,15 @@ gdjs.registerRuntimeSceneResumedCallback(function(runtimeScene) {
gdjs.TweenRuntimeBehavior._tweensProcessed = false;
gdjs.TweenRuntimeBehavior._currentTweenTime = 0;
gdjs.TweenRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene) {
// Process tweens (once per frame).
if (!gdjs.TweenRuntimeBehavior._tweensProcessed) {
gdjs.TweenRuntimeBehavior._currentTweenTime = runtimeScene
.getTimeManager()
.getTimeFromStart();
shifty.processTweens();
gdjs.TweenRuntimeBehavior._tweensProcessed = true;
}
};
gdjs.TweenRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
gdjs.TweenRuntimeBehavior._tweensProcessed = false;
};
gdjs.registerRuntimeScenePreEventsCallback(function(runtimeScene) {
gdjs.TweenRuntimeBehavior._currentTweenTime = runtimeScene
.getTimeManager()
.getTimeFromStart();
shifty.processTweens();
});
// Set up Shifty.js so that the processing ("tick"/updates) is handled
// by the behavior (once per frame):
// by the behavior, once per frame. See above.
shifty.Tweenable.setScheduleFunction(function() {
/* Do nothing, we'll call processTweens manually. */
});

View File

@@ -4,6 +4,7 @@
*/
#include "JsCodeEvent.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
@@ -19,7 +20,7 @@ using namespace std;
namespace gdjs {
vector<pair<gd::Expression*, gd::ParameterMetadata> >
JsCodeEvent::GetAllExpressionsWithMetadata() {
JsCodeEvent::GetAllExpressionsWithMetadata() {
vector<pair<gd::Expression*, gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("object");
@@ -30,7 +31,7 @@ vector<pair<gd::Expression*, gd::ParameterMetadata> >
}
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
JsCodeEvent::GetAllExpressionsWithMetadata() const {
JsCodeEvent::GetAllExpressionsWithMetadata() const {
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("object");
@@ -45,15 +46,14 @@ void JsCodeEvent::SerializeTo(gd::SerializerElement& element) const {
element.AddChild("parameterObjects")
.SetValue(parameterObjects.GetPlainString());
element.AddChild("useStrict").SetValue(useStrict);
element.AddChild("eventsSheetExpanded").SetValue(eventsSheetExpanded);
}
void JsCodeEvent::UnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
inlineCode = element.GetChild("inlineCode").GetValue().GetString();
parameterObjects =
gd::Expression(element.GetChild("parameterObjects")
.GetValue()
.GetString());
parameterObjects = gd::Expression(
element.GetChild("parameterObjects").GetValue().GetString());
if (!element.HasChild("useStrict")) {
// Compatibility with GD <= 5.0.0-beta68
@@ -62,11 +62,20 @@ void JsCodeEvent::UnserializeFrom(gd::Project& project,
} else {
useStrict = element.GetChild("useStrict").GetBoolValue();
}
if (!element.HasChild("eventsSheetExpanded")) {
// Compatibility with GD <= 5.0.0-beta101
eventsSheetExpanded = false;
// end of compatibility code
} else {
eventsSheetExpanded = element.GetChild("eventsSheetExpanded").GetBoolValue();
}
}
JsCodeEvent::JsCodeEvent()
: BaseEvent(),
inlineCode("runtimeScene.setBackgroundColor(100,100,240);\n"),
useStrict(true) {}
useStrict(true),
eventsSheetExpanded(false) {}
} // namespace gdjs

View File

@@ -47,6 +47,9 @@ class JsCodeEvent : public gd::BaseEvent {
const gd::SerializerElement& element);
virtual bool IsUseStrict() const { return useStrict; }
bool IsEventsSheetExpanded() const { return eventsSheetExpanded; }
void SetEventsSheetExpanded(bool enable) { eventsSheetExpanded = enable; };
private:
void Init(const JsCodeEvent& event);
@@ -56,6 +59,7 @@ class JsCodeEvent : public gd::BaseEvent {
bool useStrict; ///< Should the generated JS function have "use strict". true
///< by default. Should be removed once all the game engine
///< is using "use strict".
bool eventsSheetExpanded; ///< Is the code block expanded in the events sheet?
};
} // namespace gdjs

View File

@@ -124,8 +124,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
eventsFunction.GetParameters(), false),
codeGenerator.GenerateEventsFunctionContext(
eventsFunction.GetParameters(),
"runtimeScene.getOnceTriggers()"),
eventsFunction.GetParameters(), "runtimeScene.getOnceTriggers()"),
eventsFunction.GetEvents(),
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
@@ -154,8 +153,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
// Generate the code setting up the context of the function.
gd::String fullPreludeCode =
preludeCode + "\n" +
"var that = this;\n" +
preludeCode + "\n" + "var that = this;\n" +
// runtimeScene is supposed to be always accessible, read
// it from the behavior
"var runtimeScene = this._runtimeScene;\n" +
@@ -173,7 +171,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
onceTriggersVariable,
// Pass the names of the parameters considered as the current
// object and behavior parameters:
"Object", "Behavior");
"Object",
"Behavior");
gd::String output = GenerateEventsListCompleteFunctionCode(
project,
@@ -338,14 +337,17 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
// Unknown object, don't create anything:
" return null;\n" +
" },\n"
// Allow to get a layer directly from the context for convenience:
" getLayer: function(layerName) {\n"
" return runtimeScene.getLayer(layerName);\n"
" },\n"
// Getter for arguments that are not objects
" getArgument: function(argName) {\n" +
argumentsGetters + " return \"\";\n" +
" },\n" +
// Expose OnceTriggers (will be pointing either to the runtime scene ones,
// or the ones from the behavior):
" getOnceTriggers: function() { return " + onceTriggersVariable + "; }\n" +
"};\n";
argumentsGetters + " return \"\";\n" + " },\n" +
// Expose OnceTriggers (will be pointing either to the runtime scene
// ones, or the ones from the behavior):
" getOnceTriggers: function() { return " + onceTriggersVariable +
"; }\n" + "};\n";
}
gd::String EventsCodeGenerator::GenerateEventsFunctionReturn(
@@ -739,16 +741,16 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String copiedListName =
GetObjectListName(object, *context.GetParentContext());
return objectListName + ".createFrom(" + copiedListName + ");\n";
return "gdjs.copyArray(" + copiedListName + ", " + objectListName + ");\n";
};
gd::String declarationsCode;
for (auto object : context.GetObjectsListsToBeDeclared()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
objectListDeclaration += GetObjectListName(object, context) +
".createFrom(" +
GenerateAllInstancesGetterCode(object) + ");";
objectListDeclaration += "gdjs.copyArray(" +
GenerateAllInstancesGetterCode(object) + ", " +
GetObjectListName(object, context) + ");";
context.SetObjectDeclared(object);
} else
objectListDeclaration = declareObjectList(object, context);

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#include "AdvancedExtension.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
@@ -68,6 +69,28 @@ AdvancedExtension::AdvancedExtension() {
booleanCode + "; }";
});
GetAllConditions()["GetArgumentAsBoolean"]
.GetCodeExtraInformation()
.SetCustomCodeGenerator([](gd::Instruction& instruction,
gd::EventsCodeGenerator& codeGenerator,
gd::EventsCodeGenerationContext& context) {
gd::String parameterNameCode =
gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator,
context,
"string",
instruction.GetParameter(0).GetPlainString());
gd::String valueCode =
gd::String(instruction.IsInverted() ? "!" : "") +
"(typeof eventsFunctionContext !== 'undefined' ? "
"!!eventsFunctionContext.getArgument(" +
parameterNameCode + ") : false)";
gd::String outputCode =
codeGenerator.GenerateBooleanFullName("conditionTrue", context) +
".val = " + valueCode + ";\n";
return outputCode;
});
GetAllExpressions()["GetArgumentAsNumber"]
.GetCodeExtraInformation()
.SetCustomCodeGenerator([](const std::vector<gd::Expression>& parameters,

View File

@@ -189,6 +189,10 @@ BaseObjectExtension::BaseObjectExtension() {
"getAverageForce().getLength"); // Deprecated
objectExpressions["Distance"].SetFunctionName("getDistanceToObject");
objectExpressions["SqDistance"].SetFunctionName("getSqDistanceToObject");
objectExpressions["DistanceToPosition"].SetFunctionName("getDistanceToPosition");
objectExpressions["SqDistanceToPosition"].SetFunctionName("getSqDistanceToPosition");
objectExpressions["AngleToObject"].SetFunctionName("getAngleToObject");
objectExpressions["AngleToPosition"].SetFunctionName("getAngleToPosition");
objectExpressions["ObjectTimerElapsedTime"].SetFunctionName(
"getTimerElapsedTimeInSeconds");
objectStrExpressions["ObjectName"].SetFunctionName("getName");

View File

@@ -81,6 +81,13 @@ CameraExtension::CameraExtension() {
GetAllExpressions()["LayerTimeScale"].SetFunctionName(
"gdjs.evtTools.camera.getLayerTimeScale");
GetAllConditions()["LayerDefaultZOrder"].SetFunctionName(
"gdjs.evtTools.camera.getLayerDefaultZOrder");
GetAllActions()["SetLayerDefaultZOrder"].SetFunctionName(
"gdjs.evtTools.camera.setLayerDefaultZOrder");
GetAllExpressions()["LayerDefaultZOrder"].SetFunctionName(
"gdjs.evtTools.camera.getLayerDefaultZOrder");
StripUnimplementedInstructionsAndExpressions(); // Unimplemented things are
// listed here:
/*

View File

@@ -229,8 +229,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
gd::String::From(parentContext.GetContextDepth()) + "_" +
gd::String::From(parentContext.GetCurrentConditionDepth()) +
"final";
code += codeGenerator.GetObjectListName(*it, parentContext) +
".createFrom(" + finalObjList + ");\n";
code += "gdjs.copyArray(" + finalObjList + ", " +
codeGenerator.GetObjectListName(*it, parentContext) +
");\n";
}
code += "}\n";

View File

@@ -49,6 +49,10 @@ MathematicalToolsExtension::MathematicalToolsExtension() {
GetAllExpressions()["mod"].SetFunctionName("gdjs.evtTools.common.mod");
GetAllExpressions()["AngleDifference"].SetFunctionName(
"gdjs.evtTools.common.angleDifference");
GetAllExpressions()["AngleBetweenPositions"].SetFunctionName(
"gdjs.evtTools.common.angleBetweenPositions");
GetAllExpressions()["DistanceBetweenPositions"].SetFunctionName(
"gdjs.evtTools.common.distanceBetweenPositions");
GetAllExpressions()["int"].SetFunctionName("Math.round");
GetAllExpressions()["rint"].SetFunctionName("Math.round");
GetAllExpressions()["round"].SetFunctionName("Math.round");

View File

@@ -16,7 +16,11 @@ NetworkExtension::NetworkExtension() {
gd::BuiltinExtensionsImplementer::ImplementsNetworkExtension(*this);
GetAllActions()["SendRequest"].SetFunctionName(
"gdjs.evtTools.network.sendHttpRequest");
"gdjs.evtTools.network.sendDeprecatedSynchronousRequest");
GetAllActions()["SendAsyncRequest"].SetFunctionName(
"gdjs.evtTools.network.sendAsyncRequest");
GetAllActions()["EnableMetrics"].SetFunctionName(
"gdjs.evtTools.network.enableMetrics");
GetAllActions()["JSONToVariableStructure"].SetFunctionName(
"gdjs.evtTools.network.jsonToVariableStructure");
GetAllActions()["JSONToGlobalVariableStructure"].SetFunctionName(

View File

@@ -17,6 +17,8 @@ WindowExtension::WindowExtension() {
GetAllActions()["SetFullScreen"].SetFunctionName(
"gdjs.evtTools.window.setFullScreen");
GetAllConditions()["IsFullScreen"].SetFunctionName(
"gdjs.evtTools.window.isFullScreen");
GetAllActions()["SetWindowMargins"].SetFunctionName(
"gdjs.evtTools.window.setMargins");
GetAllActions()["SetWindowTitle"].SetFunctionName(

View File

@@ -122,8 +122,12 @@ bool Exporter::ExportWholePixiProject(
else if (exportForFacebookInstantGames)
source = gdjsRoot + "/Runtime/FacebookInstantGames/index.html";
if (!helper.ExportPixiIndexFile(
exportedProject, source, exportDir, includesFiles, "")) {
if (!helper.ExportPixiIndexFile(exportedProject,
source,
exportDir,
includesFiles,
/*nonRuntimeScriptsCacheBurst=*/0,
"")) {
gd::LogError(_("Error during export:\n") + lastError);
return false;
}
@@ -211,7 +215,7 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
gd::ProjectStripper::StripProjectForExport(exportedProject);
//...and export it
gd::SerializerElement noRuntimeGameOptions;
gd::SerializerElement noRuntimeGameOptions;
helper.ExportProjectData(
fs, exportedProject, codeOutputDir + "/data.js", noRuntimeGameOptions);
includesFiles.push_back(codeOutputDir + "/data.js");

View File

@@ -13,7 +13,10 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EffectsCodeGenerator.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/AbstractFileSystem.h"
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
#include "GDCore/IDE/ProjectStripper.h"
@@ -22,6 +25,7 @@
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/TinyXml/tinyxml.h"
@@ -31,6 +35,43 @@
#include "GDJS/Extensions/JsPlatform.h"
#undef CopyFile // Disable an annoying macro
namespace {
std::map<gd::String, gd::String> GetExtensionDependencyExtraSettingValues(
const gd::Project &project,
const gd::String &extensionName,
const gd::DependencyMetadata &dependency) {
std::map<gd::String, gd::String> values;
for (const auto &extraSetting : dependency.GetAllExtraSettings()) {
const gd::String &type = extraSetting.second.GetType();
const gd::String extraSettingValue =
type == "ExtensionProperty"
? project.GetExtensionProperties().GetValue(
extensionName, extraSetting.second.GetValue())
: extraSetting.second.GetValue();
if (!extraSettingValue.empty())
values[extraSetting.first] = extraSettingValue;
}
return values;
};
bool AreMapKeysMissingElementOfSet(const std::map<gd::String, gd::String> &map,
const std::set<gd::String> &set) {
bool missingKey = false;
for (auto &key : set) {
if (map.find(key) == map.end()) {
missingKey = true;
}
}
return missingKey;
}
} // namespace
namespace gdjs {
static void InsertUnique(std::vector<gd::String> &container, gd::String str) {
@@ -141,6 +182,7 @@ bool ExporterHelper::ExportProjectForPixiPreview(
gdjsRoot + "/Runtime/index.html",
options.exportPath,
includesFiles,
options.nonRuntimeScriptsCacheBurst,
"gdjs.runtimeGameOptions"))
return false;
@@ -172,11 +214,16 @@ bool ExporterHelper::ExportPixiIndexFile(
gd::String source,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec) {
gd::String str = fs.ReadFile(source);
// Generate the file
if (!CompleteIndexFile(str, exportDir, includesFiles, additionalSpec))
if (!CompleteIndexFile(str,
exportDir,
includesFiles,
nonRuntimeScriptsCacheBurst,
additionalSpec))
return false;
// Write the index.html file
@@ -256,21 +303,45 @@ bool ExporterHelper::ExportCordovaFiles(const gd::Project &project,
.FindAndReplace(
"GDJS_PACKAGENAME",
gd::Serializer::ToEscapedXMLString(project.GetPackageName()))
.FindAndReplace("GDJS_ORIENTATION", project.GetOrientation())
.FindAndReplace("GDJS_PROJECTVERSION", project.GetVersion())
.FindAndReplace("<!-- GDJS_ICONS_ANDROID -->", makeIconsAndroid())
.FindAndReplace("<!-- GDJS_ICONS_IOS -->", makeIconsIos());
if (!project.GetAdMobAppId().empty()) {
str = str.FindAndReplace(
"<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->",
"<plugin name=\"cordova-plugin-admob-free\" spec=\"~0.21.0\">\n"
"\t\t<variable name=\"ADMOB_APP_ID\" value=\"" +
project.GetAdMobAppId() +
"\" />\n"
"\t</plugin>");
gd::String plugins = "";
for (std::shared_ptr<gd::PlatformExtension> extension :
project.GetCurrentPlatform().GetAllPlatformExtensions()) {
for (gd::DependencyMetadata dependency : extension->GetAllDependencies()) {
if (dependency.GetDependencyType() == "cordova") {
gd::String plugin;
plugin += "<plugin name=\"" + dependency.GetExportName();
if (dependency.GetVersion() != "") {
plugin += "\" spec=\"" + dependency.GetVersion();
}
plugin += "\">\n";
auto extraSettingValues = GetExtensionDependencyExtraSettingValues(
project, extension->GetName(), dependency);
// For Cordova, all settings are considered a plugin variable.
for (auto &extraSetting : extraSettingValues) {
plugin += "\t\t<variable name=\"" + extraSetting.first +
"\" value=\"" + extraSetting.second + "\" />\n";
}
plugin += "\t</plugin>";
// Don't include the plugin if an extra setting was not fulfilled.
bool missingSetting = AreMapKeysMissingElementOfSet(
extraSettingValues, dependency.GetRequiredExtraSettingsForExport());
if (!missingSetting) plugins += plugin;
}
}
}
str =
str.FindAndReplace("<!-- GDJS_EXTENSION_CORDOVA_DEPENDENCY -->", plugins);
if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
lastError = "Unable to write Cordova config.xml file.";
return false;
@@ -331,7 +402,11 @@ bool ExporterHelper::ExportCocos2dFiles(
// Generate the file
std::vector<gd::String> noIncludesInThisFile;
if (!CompleteIndexFile(str, exportDir, noIncludesInThisFile, "")) {
if (!CompleteIndexFile(str,
exportDir,
noIncludesInThisFile,
/*nonRuntimeScriptsCacheBurst=*/0,
"")) {
lastError = "Unable to complete Cocos2d-JS index.html file.";
return false;
}
@@ -421,6 +496,39 @@ bool ExporterHelper::ExportElectronFiles(const gd::Project &project,
.FindAndReplace("\"GDJS_GAME_VERSION\"", jsonVersion)
.FindAndReplace("\"GDJS_GAME_MANGLED_NAME\"", jsonMangledName);
gd::String packages = "";
for (std::shared_ptr<gd::PlatformExtension> extension :
project.GetCurrentPlatform()
.GetAllPlatformExtensions()) { // TODO Add a way to select only
// used Extensions
for (gd::DependencyMetadata dependency :
extension->GetAllDependencies()) {
if (dependency.GetDependencyType() == "npm") {
if (dependency.GetVersion() == "") {
gd::LogError(
"Latest Version not available for NPM dependencies, "
"dependency " +
dependency.GetName() +
" is not exported. Please specify a version when calling "
"addDependency.");
continue;
}
packages += "\n\t\"" + dependency.GetExportName() + "\": \"" +
dependency.GetVersion() + "\",";
// For node extra settings are ignored
}
}
}
if (!packages.empty()) {
// Remove the , at the end as last item cannot have , in JSON.
packages = packages.substr(0, packages.size() - 1);
}
str = str.FindAndReplace("\"GDJS_EXTENSION_NPM_DEPENDENCY\": \"0\"",
packages);
if (!fs.WriteToFile(exportDir + "/package.json", str)) {
lastError = "Unable to write Electron package.json file.";
return false;
@@ -466,12 +574,14 @@ bool ExporterHelper::CompleteIndexFile(
gd::String &str,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec) {
if (additionalSpec.empty()) additionalSpec = "{}";
gd::String codeFilesIncludes;
for (auto& include: includesFiles) {
gd::String scriptSrc = GetExportedIncludeFilename(include);
for (auto &include : includesFiles) {
gd::String scriptSrc =
GetExportedIncludeFilename(include, nonRuntimeScriptsCacheBurst);
// Sanity check if the file exists - if not skip it to avoid
// including it in the list of scripts.
@@ -674,7 +784,14 @@ bool ExporterHelper::ExportExternalSourceFiles(
}
gd::String ExporterHelper::GetExportedIncludeFilename(
const gd::String& include) {
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst) {
auto addSearchParameterToUrl = [](const gd::String &url,
const gd::String &urlEncodedParameterName,
const gd::String &urlEncodedValue) {
gd::String separator = url.find("?") == gd::String::npos ? "?" : "&";
return url + separator + urlEncodedParameterName + "=" + urlEncodedValue;
};
if (!fs.IsAbsolute(include)) {
// By convention, an include file that is relative is relative to
// the "<GDJS Root>/Runtime" folder, and will have the same relative
@@ -689,14 +806,25 @@ gd::String ExporterHelper::GetExportedIncludeFilename(
} else {
// Note: all the code generated from events are generated in another
// folder and fall in this case:
return fs.FileNameFrom(include);
gd::String resolvedInclude = fs.FileNameFrom(include);
if (nonRuntimeScriptsCacheBurst == 0) {
return resolvedInclude;
}
// Add the parameter to force the browser to reload the code - useful
// for cases where the browser is caching files that are getting
// overwritten.
return addSearchParameterToUrl(
resolvedInclude,
"gdCacheBurst",
gd::String::From(nonRuntimeScriptsCacheBurst));
}
}
bool ExporterHelper::ExportIncludesAndLibs(
const std::vector<gd::String> &includesFiles,
gd::String exportDir) {
for (auto& include : includesFiles) {
const std::vector<gd::String> &includesFiles, gd::String exportDir) {
for (auto &include : includesFiles) {
if (!fs.IsAbsolute(include)) {
// By convention, an include file that is relative is relative to
// the "<GDJS Root>/Runtime" folder, and will have the same relative

View File

@@ -32,13 +32,17 @@ struct PreviewExportOptions {
* \param exportPath_ The path in the filesystem where to export the files
*/
PreviewExportOptions(gd::Project &project_, const gd::String &exportPath_)
: project(project_), exportPath(exportPath_), projectDataOnlyExport(false) {};
: project(project_),
exportPath(exportPath_),
projectDataOnlyExport(false),
nonRuntimeScriptsCacheBurst(0) {};
/**
* \brief Set the address of the debugger server that the game should reach out to,
* using WebSockets.
* \brief Set the address of the debugger server that the game should reach
* out to, using WebSockets.
*/
PreviewExportOptions &SetDebuggerServerAddress(const gd::String& address, const gd::String& port) {
PreviewExportOptions &SetDebuggerServerAddress(const gd::String &address,
const gd::String &port) {
debuggerServerAddress = address;
debuggerServerPort = port;
return *this;
@@ -76,11 +80,21 @@ struct PreviewExportOptions {
* \brief Set if the export should only export the project data, not
* exporting events code.
*/
PreviewExportOptions& SetProjectDataOnlyExport(bool enable) {
PreviewExportOptions &SetProjectDataOnlyExport(bool enable) {
projectDataOnlyExport = enable;
return *this;
}
/**
* \brief If set to a non zero value, the exported script URLs will have an
* extra search parameter added (with the given value) to ensure browser cache
* is bypassed when they are loaded.
*/
PreviewExportOptions &SetNonRuntimeScriptsCacheBurst(unsigned int value) {
nonRuntimeScriptsCacheBurst = value;
return *this;
}
gd::Project &project;
gd::String exportPath;
gd::String debuggerServerAddress;
@@ -89,6 +103,7 @@ struct PreviewExportOptions {
gd::String externalLayoutName;
std::map<gd::String, int> includeFileHashes;
bool projectDataOnlyExport;
unsigned int nonRuntimeScriptsCacheBurst;
};
/**
@@ -154,7 +169,8 @@ class ExporterHelper {
/**
* \brief Copy all the specified files to the
* export directory. Relative files are copied from "<GDJS root>/Runtime" directory.
* export directory. Relative files are copied from "<GDJS root>/Runtime"
* directory.
*
* \param includesFiles A vector with filenames to be copied.
* \param exportDir The directory where the files mus tbe copied.
@@ -186,8 +202,8 @@ class ExporterHelper {
* \brief Add the include files for all the objects of the project
* and their behaviors.
*/
void ExportObjectAndBehaviorsIncludes(
gd::Project &project, std::vector<gd::String> &includesFiles);
void ExportObjectAndBehaviorsIncludes(gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Copy the external source files used by the game into the export
@@ -214,13 +230,18 @@ class ExporterHelper {
* \param source The file to be used as a template for the final file.
* \param exportDir The directory where the preview must be created.
* \param includesFiles The JS files to be included in the HTML file. Order is
* important. \param additionalSpec JSON string that will be passed to the
* important.
* \param nonRuntimeScriptsCacheBurst If non zero, add an additional cache
* bursting parameter to scripts, that are not part of the runtime/extensions,
* to force the browser to reload them.
* \param additionalSpec JSON string that will be passed to the
* gdjs.RuntimeGame object.
*/
bool ExportPixiIndexFile(const gd::Project &project,
gd::String source,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec = "");
/**
@@ -232,6 +253,9 @@ class ExporterHelper {
* \param includesFiles "<!--GDJS_CODE_FILES -->" will be
* replaced by HTML tags to include the filenames
* contained inside the vector.
* \param nonRuntimeScriptsCacheBurst If non zero, add an additional cache
* bursting parameter to scripts, that are not part of the runtime/extensions,
* to force the browser to reload them.
* \param additionalSpec The string "GDJS_ADDITIONAL_SPEC"
* surrounded by comments marks will be replaced by the
* content of this string.
@@ -239,6 +263,7 @@ class ExporterHelper {
bool CompleteIndexFile(gd::String &indexFileContent,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec);
/**
@@ -290,7 +315,8 @@ class ExporterHelper {
* \brief Given an include file, returns the name of the file to reference
* in the exported game.
*/
gd::String GetExportedIncludeFilename(const gd::String& include);
gd::String GetExportedIncludeFilename(
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst = 0);
/**
* \brief Change the directory where code files are generated.
@@ -302,10 +328,10 @@ class ExporterHelper {
}
static void AddDeprecatedFontFilesToFontResources(
gd::AbstractFileSystem &fs,
gd::ResourcesManager &resourcesManager,
const gd::String &exportDir,
gd::String urlPrefix = "");
gd::AbstractFileSystem &fs,
gd::ResourcesManager &resourcesManager,
const gd::String &exportDir,
gd::String urlPrefix = "");
gd::AbstractFileSystem
&fs; ///< The abstract file system to be used for exportation.

View File

@@ -3,6 +3,7 @@
<name>GDJS_PROJECTNAME</name>
<content src="index.html" />
<plugin name="cordova-plugin-whitelist" version="1" />
<plugin name="cordova-plugin-screen-orientation" version="3.0.2" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
@@ -21,14 +22,13 @@
<!-- GDJS_ICONS_IOS -->
</platform>
<preference name="orientation" value="GDJS_ORIENTATION" />
<preference name="BackgroundColor" value="0xff000000"/>
<!-- Android Fullscreen -->
<preference name="Fullscreen" value="true" />
<!-- Cordova/Phonegap version -->
<preference name="phonegap-version" value="cli-8.0.0" />
<preference name="phonegap-version" value="cli-9.0.0" />
<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->
<!-- GDJS_EXTENSION_CORDOVA_DEPENDENCY -->
</widget>

View File

@@ -3,7 +3,7 @@
* running in Electron Runtime.
*/
// Modules to control application life and create native browser window
const { app, BrowserWindow, shell } = require("electron");
const { app, BrowserWindow, shell, Menu } = require("electron");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@@ -31,6 +31,8 @@ function createWindow() {
// and load the index.html of the app.
mainWindow.loadFile("app/index.html");
Menu.setApplicationMenu(null);
// Open the DevTools.
// mainWindow.webContents.openDevTools()

View File

@@ -5,7 +5,9 @@
"description": "GDJS_GAME_NAME",
"author": "GDJS_GAME_AUTHOR",
"version": "GDJS_GAME_VERSION",
"dependencies": {},
"dependencies": {
"GDJS_EXTENSION_NPM_DEPENDENCY": "0"
},
"devDependencies": {
"electron": "8.2.5"
},

View File

@@ -129,7 +129,11 @@ gdjs.LayerCocosRenderer.prototype.updateVisibility = function(visible) {
this._cocosLayer.setVisible(visible);
}
gdjs.LayerCocosRenderer.prototype.updateTime = function() {
gdjs.LayerCocosRenderer.prototype.update = function() {
// Unimplemented
}
gdjs.LayerCocosRenderer.prototype.updateClearColor = function() {
// Unimplemented
}

View File

@@ -49,6 +49,17 @@ gdjs.RuntimeGameCocosRenderer.prototype.setFullScreen = function(enable) {
console.warn("Fullscreen is not implemented yet.");
};
/**
* Checks if the game is in full screen.
*/
gdjs.RuntimeGameCocosRenderer.prototype.isFullScreen = function() {
var electron = this.getElectron();
if (electron) {
return electron.remote.getCurrentWindow().isFullScreen();
}
return false; // Unsupported
}
/**
* Update the window size, if possible.
* @param {number} width The new width, in pixels.

View File

@@ -187,3 +187,14 @@ gdjs.RuntimeSceneCocosRenderer.prototype.showCursor = function() {
gdjs.RuntimeSceneCocosRenderer.prototype.getCocosScene = function() {
return this._cocosScene;
}
/**
* @param {gdjs.Layer} layer
* @param {number} index
*/
gdjs.RuntimeSceneCocosRenderer.prototype.setLayerIndex = function (
layer,
index
) {
// Not implemented
}

View File

@@ -1,3 +1,4 @@
// @ts-check
/*
* GDevelop JS Platform
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
@@ -11,116 +12,270 @@
*/
gdjs.evtTools.camera = gdjs.evtTools.camera || {};
gdjs.evtTools.camera.setCameraX = function(runtimeScene, x, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} x
* @param {string} layer
* @param {number} cameraId
*/
gdjs.evtTools.camera.setCameraX = function (runtimeScene, x, layer, cameraId) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
runtimeScene.getLayer(layer).setCameraX(x, cameraId);
}
runtimeScene.getLayer(layer).setCameraX(x, cameraId);
};
gdjs.evtTools.camera.setCameraY = function(runtimeScene, y, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} y
* @param {string} layer
* @param {number} cameraId
*/
gdjs.evtTools.camera.setCameraY = function (runtimeScene, y, layer, cameraId) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
runtimeScene.getLayer(layer).setCameraY(y, cameraId);
}
runtimeScene.getLayer(layer).setCameraY(y, cameraId);
};
gdjs.evtTools.camera.getCameraX = function(runtimeScene, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} cameraId
* @returns {number}
*/
gdjs.evtTools.camera.getCameraX = function (runtimeScene, layer, cameraId) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return runtimeScene.getLayer(layer).getCameraX();
}
return runtimeScene.getLayer(layer).getCameraX();
};
gdjs.evtTools.camera.getCameraY = function(runtimeScene, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} cameraId
* @returns {number}
*/
gdjs.evtTools.camera.getCameraY = function (runtimeScene, layer, cameraId) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return runtimeScene.getLayer(layer).getCameraY();
}
return runtimeScene.getLayer(layer).getCameraY();
};
gdjs.evtTools.camera.getCameraWidth = function(runtimeScene, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} cameraId
* @returns {number}
*/
gdjs.evtTools.camera.getCameraWidth = function (runtimeScene, layer, cameraId) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return runtimeScene.getLayer(layer).getCameraWidth();
}
return runtimeScene.getLayer(layer).getCameraWidth();
};
gdjs.evtTools.camera.getCameraHeight = function(runtimeScene, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} cameraId
* @returns {number}
*/
gdjs.evtTools.camera.getCameraHeight = function (
runtimeScene,
layer,
cameraId
) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return runtimeScene.getLayer(layer).getCameraHeight();
}
return runtimeScene.getLayer(layer).getCameraHeight();
};
gdjs.evtTools.camera.showLayer = function(runtimeScene, layer) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
*/
gdjs.evtTools.camera.showLayer = function (runtimeScene, layer) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).show(true);
}
return runtimeScene.getLayer(layer).show(true);
};
gdjs.evtTools.camera.hideLayer = function(runtimeScene, layer) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
*/
gdjs.evtTools.camera.hideLayer = function (runtimeScene, layer) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).show(false);
}
return runtimeScene.getLayer(layer).show(false);
};
gdjs.evtTools.camera.layerIsVisible = function(runtimeScene, layer) {
return runtimeScene.hasLayer(layer) && runtimeScene.getLayer(layer).isVisible();
}
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @returns {boolean}
*/
gdjs.evtTools.camera.layerIsVisible = function (runtimeScene, layer) {
return (
runtimeScene.hasLayer(layer) && runtimeScene.getLayer(layer).isVisible()
);
};
gdjs.evtTools.camera.setCameraRotation = function(runtimeScene, rotation, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} rotation
* @param {string} layer
* @param {number} cameraId
*/
gdjs.evtTools.camera.setCameraRotation = function (
runtimeScene,
rotation,
layer,
cameraId
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setCameraRotation(rotation, cameraId);
}
gdjs.evtTools.camera.getCameraRotation = function(runtimeScene, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
return runtimeScene.getLayer(layer).setCameraRotation(rotation, cameraId);
};
return runtimeScene.getLayer(layer).getCameraRotation(cameraId);
}
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} cameraId
* @returns {number}
*/
gdjs.evtTools.camera.getCameraRotation = function (
runtimeScene,
layer,
cameraId
) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
gdjs.evtTools.camera.setCameraZoom = function(runtimeScene, newZoom, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
return runtimeScene.getLayer(layer).getCameraRotation(cameraId);
};
return runtimeScene.getLayer(layer).setCameraZoom(newZoom, cameraId);
}
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {number} newZoom
* @param {string} layer
* @param {number} cameraId
*/
gdjs.evtTools.camera.setCameraZoom = function (
runtimeScene,
newZoom,
layer,
cameraId
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
gdjs.evtTools.camera.centerCamera = function(runtimeScene, object, anticipateMove, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) || object == null ) { return; }
return runtimeScene.getLayer(layer).setCameraZoom(newZoom, cameraId);
};
var layer = runtimeScene.getLayer(layer);
var xOffset = 0; var yOffset = 0;
if ( anticipateMove && !object.hasNoForces() ) {
var objectAverageForce = object.getAverageForce();
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {?gdjs.RuntimeObject} object
* @param {boolean} anticipateMove
* @param {string} layerName
* @param {number} cameraId
*/
gdjs.evtTools.camera.centerCamera = function (
runtimeScene,
object,
anticipateMove,
layerName,
cameraId
) {
if (!runtimeScene.hasLayer(layerName) || object == null) {
return;
}
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
}
var layer = runtimeScene.getLayer(layerName);
var xOffset = 0;
var yOffset = 0;
if (anticipateMove && !object.hasNoForces()) {
var objectAverageForce = object.getAverageForce();
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
layer.setCameraX(object.getDrawableX()+object.getCenterX(), cameraId);
layer.setCameraY(object.getDrawableY()+object.getCenterY(), cameraId);
}
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
}
gdjs.evtTools.camera.centerCameraWithinLimits = function(runtimeScene, object, left, top, right, bottom, anticipateMove, layer, cameraId) {
if ( !runtimeScene.hasLayer(layer) || object == null ) { return; }
layer.setCameraX(object.getDrawableX() + object.getCenterX(), cameraId);
layer.setCameraY(object.getDrawableY() + object.getCenterY(), cameraId);
};
var layer = runtimeScene.getLayer(layer);
var xOffset = 0; var yOffset = 0;
if ( anticipateMove && !object.hasNoForces() ) {
var objectAverageForce = object.getAverageForce();
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {?gdjs.RuntimeObject} object
* @param {number} left
* @param {number} top
* @param {number} right
* @param {number} bottom
* @param {boolean} anticipateMove
* @param {string} layerName
* @param {number} cameraId
*/
gdjs.evtTools.camera.centerCameraWithinLimits = function (
runtimeScene,
object,
left,
top,
right,
bottom,
anticipateMove,
layerName,
cameraId
) {
if (!runtimeScene.hasLayer(layerName) || object == null) {
return;
}
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
}
var layer = runtimeScene.getLayer(layerName);
var xOffset = 0;
var yOffset = 0;
if (anticipateMove && !object.hasNoForces()) {
var objectAverageForce = object.getAverageForce();
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
var newX = object.getDrawableX()+object.getCenterX()+xOffset;
if ( newX < left +layer.getCameraWidth(cameraId)/2 ) newX = left+layer.getCameraWidth(cameraId)/2;
if ( newX > right-layer.getCameraWidth(cameraId)/2 ) newX = right-layer.getCameraWidth(cameraId)/2;
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
}
var newY = object.getDrawableY()+object.getCenterY()+yOffset;
if ( newY < top +layer.getCameraHeight(cameraId)/2 ) newY = top+layer.getCameraHeight(cameraId)/2;
if ( newY > bottom-layer.getCameraHeight(cameraId)/2 ) newY = bottom-layer.getCameraHeight(cameraId)/2;
var newX = object.getDrawableX() + object.getCenterX() + xOffset;
if (newX < left + layer.getCameraWidth(cameraId) / 2)
newX = left + layer.getCameraWidth(cameraId) / 2;
if (newX > right - layer.getCameraWidth(cameraId) / 2)
newX = right - layer.getCameraWidth(cameraId) / 2;
layer.setCameraX(newX, cameraId);
layer.setCameraY(newY, cameraId);
}
var newY = object.getDrawableY() + object.getCenterY() + yOffset;
if (newY < top + layer.getCameraHeight(cameraId) / 2)
newY = top + layer.getCameraHeight(cameraId) / 2;
if (newY > bottom - layer.getCameraHeight(cameraId) / 2)
newY = bottom - layer.getCameraHeight(cameraId) / 2;
layer.setCameraX(newX, cameraId);
layer.setCameraY(newY, cameraId);
};
/**
* Update a layer effect parameter (with a number).
@@ -130,11 +285,21 @@ gdjs.evtTools.camera.centerCameraWithinLimits = function(runtimeScene, object, l
* @param {string} parameter The parameter to update
* @param {number} value The new value
*/
gdjs.evtTools.camera.setLayerEffectDoubleParameter = function(runtimeScene, layer, effect, parameter, value) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
gdjs.evtTools.camera.setLayerEffectDoubleParameter = function (
runtimeScene,
layer,
effect,
parameter,
value
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setEffectDoubleParameter(effect, parameter, value);
}
return runtimeScene
.getLayer(layer)
.setEffectDoubleParameter(effect, parameter, value);
};
/**
* Update a layer effect parameter (with a string).
@@ -144,11 +309,21 @@ gdjs.evtTools.camera.setLayerEffectDoubleParameter = function(runtimeScene, laye
* @param {string} parameter The parameter to update
* @param {string} value The new value
*/
gdjs.evtTools.camera.setLayerEffectStringParameter = function(runtimeScene, layer, effect, parameter, value) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
gdjs.evtTools.camera.setLayerEffectStringParameter = function (
runtimeScene,
layer,
effect,
parameter,
value
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setEffectStringParameter(effect, parameter, value);
}
return runtimeScene
.getLayer(layer)
.setEffectStringParameter(effect, parameter, value);
};
/**
* Enable or disable a layer effect parameter (boolean).
@@ -158,11 +333,21 @@ gdjs.evtTools.camera.setLayerEffectStringParameter = function(runtimeScene, laye
* @param {string} parameter The parameter to update
* @param {boolean} value The new value
*/
gdjs.evtTools.camera.setLayerEffectBooleanParameter = function(runtimeScene, layer, effect, parameter, value) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
gdjs.evtTools.camera.setLayerEffectBooleanParameter = function (
runtimeScene,
layer,
effect,
parameter,
value
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setEffectBooleanParameter(effect, parameter, value);
}
return runtimeScene
.getLayer(layer)
.setEffectBooleanParameter(effect, parameter, value);
};
/**
* Enable, or disable, an effect of a layer.
@@ -171,11 +356,18 @@ gdjs.evtTools.camera.setLayerEffectBooleanParameter = function(runtimeScene, lay
* @param {string} effect The name of the effect
* @param {boolean} enabled true to enable, false to disable.
*/
gdjs.evtTools.camera.enableLayerEffect = function(runtimeScene, layer, effect, enabled) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
gdjs.evtTools.camera.enableLayerEffect = function (
runtimeScene,
layer,
effect,
enabled
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
runtimeScene.getLayer(layer).enableEffect(effect, enabled);
}
runtimeScene.getLayer(layer).enableEffect(effect, enabled);
};
/**
* Check if an effect is enabled.
@@ -184,18 +376,70 @@ gdjs.evtTools.camera.enableLayerEffect = function(runtimeScene, layer, effect, e
* @param {string} effect The name of the effect
* @return {boolean} true if the effect is enabled, false otherwise.
*/
gdjs.evtTools.camera.layerEffectEnabled = function(runtimeScene, layer, effect) {
if ( !runtimeScene.hasLayer(layer) ) { return true; }
gdjs.evtTools.camera.layerEffectEnabled = function (
runtimeScene,
layer,
effect
) {
if (!runtimeScene.hasLayer(layer)) {
return true;
}
return runtimeScene.getLayer(layer).isEffectEnabled(effect);
}
return runtimeScene.getLayer(layer).isEffectEnabled(effect);
};
gdjs.evtTools.camera.setLayerTimeScale = function(runtimeScene, layer, timeScale) {
if ( !runtimeScene.hasLayer(layer) ) { return; }
return runtimeScene.getLayer(layer).setTimeScale(timeScale);
}
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} timeScale
*/
gdjs.evtTools.camera.setLayerTimeScale = function (
runtimeScene,
layer,
timeScale
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setTimeScale(timeScale);
};
gdjs.evtTools.camera.getLayerTimeScale = function(runtimeScene, layer) {
if ( !runtimeScene.hasLayer(layer) ) { return 1; }
return runtimeScene.getLayer(layer).getTimeScale();
}
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @returns {number}
*/
gdjs.evtTools.camera.getLayerTimeScale = function (runtimeScene, layer) {
if (!runtimeScene.hasLayer(layer)) {
return 1;
}
return runtimeScene.getLayer(layer).getTimeScale();
};
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @param {number} defaultZOrder
*/
gdjs.evtTools.camera.setLayerDefaultZOrder = function (
runtimeScene,
layer,
defaultZOrder
) {
if (!runtimeScene.hasLayer(layer)) {
return;
}
return runtimeScene.getLayer(layer).setDefaultZOrder(defaultZOrder);
};
/**
* @param {gdjs.RuntimeScene} runtimeScene
* @param {string} layer
* @returns {number}
*/
gdjs.evtTools.camera.getLayerDefaultZOrder = function (runtimeScene, layer) {
if (!runtimeScene.hasLayer(layer)) {
return 0;
}
return runtimeScene.getLayer(layer).getDefaultZOrder();
};

View File

@@ -17,7 +17,7 @@ gdjs.evtTools.common = gdjs.evtTools.common || {};
* @returns {number} The content of the variable, as a number.
* @private
*/
gdjs.evtTools.common.getVariableNumber = function(variable) {
gdjs.evtTools.common.getVariableNumber = function (variable) {
return variable.getAsNumber();
};
@@ -27,7 +27,7 @@ gdjs.evtTools.common.getVariableNumber = function(variable) {
* @returns {string} The content of the variable, as a string.
* @private
*/
gdjs.evtTools.common.getVariableString = function(variable) {
gdjs.evtTools.common.getVariableString = function (variable) {
return variable.getAsString();
};
@@ -38,7 +38,10 @@ gdjs.evtTools.common.getVariableString = function(variable) {
* @returns {boolean} true if the scene variable exits, false otherwise.
* @private
*/
gdjs.evtTools.common.sceneVariableExists = function(runtimeScene, variableName) {
gdjs.evtTools.common.sceneVariableExists = function (
runtimeScene,
variableName
) {
return runtimeScene.getVariables().has(variableName);
};
@@ -49,7 +52,10 @@ gdjs.evtTools.common.sceneVariableExists = function(runtimeScene, variableName)
* @returns {boolean} true if the global variable exits, false otherwise.
* @private
*/
gdjs.evtTools.common.globalVariableExists = function(runtimeScene, variableName) {
gdjs.evtTools.common.globalVariableExists = function (
runtimeScene,
variableName
) {
return runtimeScene.getGame().getVariables().has(variableName);
};
@@ -60,7 +66,7 @@ gdjs.evtTools.common.globalVariableExists = function(runtimeScene, variableName)
* @returns {boolean} true if child exist in the variable, false otherwise.
* @private
*/
gdjs.evtTools.common.variableChildExists = function(variable, childName) {
gdjs.evtTools.common.variableChildExists = function (variable, childName) {
return variable.hasChild(childName);
};
@@ -71,7 +77,7 @@ gdjs.evtTools.common.variableChildExists = function(variable, childName) {
* @returns {gdjs.Variable} The new variable, with the child removed.
* @private
*/
gdjs.evtTools.common.variableRemoveChild = function(variable, childName) {
gdjs.evtTools.common.variableRemoveChild = function (variable, childName) {
return variable.removeChild(childName);
};
@@ -80,7 +86,7 @@ gdjs.evtTools.common.variableRemoveChild = function(variable, childName) {
* @param {gdjs.Variable} variable Variable.
* @private
*/
gdjs.evtTools.common.variableClearChildren = function(variable) {
gdjs.evtTools.common.variableClearChildren = function (variable) {
variable.clearChildren();
};
@@ -90,7 +96,7 @@ gdjs.evtTools.common.variableClearChildren = function(variable) {
* @returns {number} The number of children in the variable.
* @private
*/
gdjs.evtTools.common.getVariableChildCount = function(variable) {
gdjs.evtTools.common.getVariableChildCount = function (variable) {
if (variable.isStructure() == false) return 0;
return Object.keys(variable.getAllChildren()).length;
};
@@ -101,7 +107,7 @@ gdjs.evtTools.common.getVariableChildCount = function(variable) {
* @returns {number} The parsed number, or NaN if invalid.
* @private
*/
gdjs.evtTools.common.toNumber = function(str) {
gdjs.evtTools.common.toNumber = function (str) {
return parseFloat(str);
};
@@ -111,7 +117,7 @@ gdjs.evtTools.common.toNumber = function(str) {
* @returns {string} The value as a string.
* @private
*/
gdjs.evtTools.common.toString = function(num) {
gdjs.evtTools.common.toString = function (num) {
//Using String literal is fastest than using toString according to
//http://jsperf.com/number-to-string/2 and http://jsben.ch/#/ghQYR
return '' + num;
@@ -123,7 +129,7 @@ gdjs.evtTools.common.toString = function(num) {
* @returns {boolean} The negated value.
* @private
*/
gdjs.evtTools.common.logicalNegation = function(bool) {
gdjs.evtTools.common.logicalNegation = function (bool) {
return !bool;
};
@@ -134,7 +140,7 @@ gdjs.evtTools.common.logicalNegation = function(bool) {
* @param {number} max The maximum value.
* @returns {number} The new value.
*/
gdjs.evtTools.common.clamp = function(x, min, max) {
gdjs.evtTools.common.clamp = function (x, min, max) {
return Math.min(Math.max(x, min), max);
};
@@ -143,7 +149,7 @@ gdjs.evtTools.common.clamp = function(x, min, max) {
* @param {number} arg Value.
* @returns {number} The hyperbolic arc-cosine for the value.
*/
gdjs.evtTools.common.acosh = function(arg) {
gdjs.evtTools.common.acosh = function (arg) {
// http://kevin.vanzonneveld.net
// + original by: Onno Marsman
return Math.log(arg + Math.sqrt(arg * arg - 1));
@@ -154,7 +160,7 @@ gdjs.evtTools.common.acosh = function(arg) {
* @param {number} arg Value.
* @returns {number} The hyperbolic arcsine for the value.
*/
gdjs.evtTools.common.asinh = function(arg) {
gdjs.evtTools.common.asinh = function (arg) {
// http://kevin.vanzonneveld.net
// + original by: Onno Marsman
return Math.log(arg + Math.sqrt(arg * arg + 1));
@@ -165,7 +171,7 @@ gdjs.evtTools.common.asinh = function(arg) {
* @param {number} arg Value.
* @returns {number} The hyperbolic arctangent for the value.
*/
gdjs.evtTools.common.atanh = function(arg) {
gdjs.evtTools.common.atanh = function (arg) {
// http://kevin.vanzonneveld.net
// + original by: Onno Marsman
return 0.5 * Math.log((1 + arg) / (1 - arg));
@@ -176,7 +182,7 @@ gdjs.evtTools.common.atanh = function(arg) {
* @param {number} arg Value.
* @returns {number} The hyperbolic cosine for the value.
*/
gdjs.evtTools.common.cosh = function(arg) {
gdjs.evtTools.common.cosh = function (arg) {
return (Math.exp(arg) + Math.exp(-arg)) / 2;
};
@@ -185,7 +191,7 @@ gdjs.evtTools.common.cosh = function(arg) {
* @param {number} arg Value.
* @returns {number} The hyperbolic sine for the value.
*/
gdjs.evtTools.common.sinh = function(arg) {
gdjs.evtTools.common.sinh = function (arg) {
return (Math.exp(arg) - Math.exp(-arg)) / 2;
};
@@ -194,7 +200,7 @@ gdjs.evtTools.common.sinh = function(arg) {
* @param {number} arg Value.
* @returns {number} The hyperbolic tangent for the value.
*/
gdjs.evtTools.common.tanh = function(arg) {
gdjs.evtTools.common.tanh = function (arg) {
return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
};
@@ -203,7 +209,7 @@ gdjs.evtTools.common.tanh = function(arg) {
* @param {number} arg Value.
* @returns {number} The cotangent for the value.
*/
gdjs.evtTools.common.cot = function(arg) {
gdjs.evtTools.common.cot = function (arg) {
return 1 / Math.tan(arg);
};
@@ -212,7 +218,7 @@ gdjs.evtTools.common.cot = function(arg) {
* @param {number} arg Value.
* @returns {number} The cosecant for the value.
*/
gdjs.evtTools.common.csc = function(arg) {
gdjs.evtTools.common.csc = function (arg) {
return 1 / Math.sin(arg);
};
@@ -221,7 +227,7 @@ gdjs.evtTools.common.csc = function(arg) {
* @param {number} arg Value.
* @returns {number} The secant for the value.
*/
gdjs.evtTools.common.sec = function(arg) {
gdjs.evtTools.common.sec = function (arg) {
return 1 / Math.cos(arg);
};
@@ -230,7 +236,7 @@ gdjs.evtTools.common.sec = function(arg) {
* @param {number} arg Value.
* @returns {number} The base-10 logarithm for the value.
*/
gdjs.evtTools.common.log10 = function(arg) {
gdjs.evtTools.common.log10 = function (arg) {
return Math.log(arg) / Math.LN10;
};
@@ -239,7 +245,7 @@ gdjs.evtTools.common.log10 = function(arg) {
* @param {number} arg Value.
* @returns {number} The base-2 logarithm for the value.
*/
gdjs.evtTools.common.log2 = function(arg) {
gdjs.evtTools.common.log2 = function (arg) {
return Math.log(arg) / Math.LN2;
};
@@ -248,7 +254,7 @@ gdjs.evtTools.common.log2 = function(arg) {
* @param {number} arg Value.
* @returns {number} Return the sign for the value (1, -1 or 0).
*/
gdjs.evtTools.common.sign = function(arg) {
gdjs.evtTools.common.sign = function (arg) {
if (arg === 0) return 0;
return arg > 0 ? +1 : -1;
@@ -259,7 +265,7 @@ gdjs.evtTools.common.sign = function(arg) {
* @param {number} x Value.
* @returns {number} Return the cube root for the value.
*/
gdjs.evtTools.common.cbrt = function(x) {
gdjs.evtTools.common.cbrt = function (x) {
return Math.pow(x, 1 / 3);
};
@@ -269,7 +275,7 @@ gdjs.evtTools.common.cbrt = function(x) {
* @param {number} n Exponent value.
* @returns {number} Return the n-th root for the value.
*/
gdjs.evtTools.common.nthroot = function(x, n) {
gdjs.evtTools.common.nthroot = function (x, n) {
return Math.pow(x, 1 / n);
};
@@ -279,7 +285,7 @@ gdjs.evtTools.common.nthroot = function(x, n) {
* @param {number} y Divisor value.
* @returns {number} Return the remainder for the values.
*/
gdjs.evtTools.common.mod = function(x, y) {
gdjs.evtTools.common.mod = function (x, y) {
return x - y * Math.floor(x / y);
};
@@ -289,8 +295,37 @@ gdjs.evtTools.common.mod = function(x, y) {
* @param {number} angle2 Second angle, in degrees.
* @returns {number} Return the difference of the angles, in degrees.
*/
gdjs.evtTools.common.angleDifference = function(angle1, angle2) {
return gdjs.evtTools.common.mod(gdjs.evtTools.common.mod(angle1 - angle2, 360.0) + 180.0, 360.0) - 180.0;
gdjs.evtTools.common.angleDifference = function (angle1, angle2) {
return (
gdjs.evtTools.common.mod(
gdjs.evtTools.common.mod(angle1 - angle2, 360.0) + 180.0,
360.0
) - 180.0
);
};
/**
* Returns the angle, in degrees, between two positions.
* @param {number} x1 First point X position.
* @param {number} y1 First point Y position.
* @param {number} x2 Second point X position.
* @param {number} y2 Second point Y position.
* @returns {number} The angle between the positions, in degrees.
*/
gdjs.evtTools.common.angleBetweenPositions = function (x1, y1, x2, y2) {
return (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI;
};
/**
* Returns the distance, in pixels, between two positions.
* @param {number} x1 First point X position.
* @param {number} y1 First point Y position.
* @param {number} x2 Second point X position.
* @param {number} y2 Second point Y position.
* @returns {number} The distance between the positions, in pixels.
*/
gdjs.evtTools.common.distanceBetweenPositions = function (x1, y1, x2, y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
};
/**
@@ -300,7 +335,7 @@ gdjs.evtTools.common.angleDifference = function(angle1, angle2) {
* @param {number} x The interpolation value between 0 and 1.
* @returns {number} The interpolated value, now between a and b.
*/
gdjs.evtTools.common.lerp = function(a, b, x) {
gdjs.evtTools.common.lerp = function (a, b, x) {
return a + (b - a) * x;
};
@@ -309,6 +344,6 @@ gdjs.evtTools.common.lerp = function(a, b, x) {
* @param {number} x Value.
* @returns {number} Return the value with all decimal places dropped.
*/
gdjs.evtTools.common.trunc = function(x) {
gdjs.evtTools.common.trunc = function (x) {
return x | 0;
};

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