Compare commits

...

208 Commits

Author SHA1 Message Date
Florian Rival
009a05e021 Bump newIDE version 2019-10-26 18:09:56 +01:00
Florian Rival
6c2ea56da6 Fix example opening 2019-10-26 18:09:27 +01:00
Florian Rival
a8ae298194 Bump newIDE version 2019-10-26 17:22:23 +01:00
Florian Rival
dda2effad0 Update translations 2019-10-26 17:19:58 +01:00
Florian Rival
4da0149b6b Merge pull request #1249 from 4ian/fix/use-center-point-as-flip-center
Use center point as flip center
2019-10-26 16:19:13 +01:00
Florian Rival
9602caaccc Add checks for origin and center point position after flipping/rotating/scaling sprite 2019-10-26 15:10:38 +01:00
Florian Rival
be2892b759 Fix hitboxes after flipX/flipY is called and add tests for sprite hitboxes/center/points 2019-10-26 14:15:14 +01:00
Florian Rival
e1552f649c Check that custom hitboxes are properly working with flipped, non default center, sprites 2019-10-26 11:11:43 +01:00
Florian Rival
74bbd45265 Add test game to check the proper rotation/flip of a sprite with a non default center 2019-10-26 11:04:23 +01:00
Florian Rival
5aa32f0eca Fix movesTowardTest and turnedTowardTest to properly use object center (in case origin is not 0;0) 2019-10-26 11:04:23 +01:00
Florian Rival
23987f63c7 Change sprites flipping to use the center point as center for flipping
As flipping can be considered as a way to "rotate" things, it makes sense to have it used as a center for flipping too.i
This is useful for objects that are moving and can be flipped according to if they are going left or right: they can now
be rotated and flipped "properly" (properly means that the center will stay at the same position when flipped/rotated)

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

TODO: Refactor the instance creation to use TemporaryInstances
2019-10-03 23:10:13 +01:00
Florian Rival
07fd7f77bd Refactor InstructionsList to use DropTarget 2019-10-03 23:10:13 +01:00
Florian Rival
69a774bec1 Move Utils/DragDropHelpers into UI/DragAndDrop 2019-10-03 23:10:13 +01:00
Florian Rival
dc9200feb5 Refactor Instruction to use DragSourceAndDropTarget
* Fix error "Expected to find a valid target" when dragging instructions on top of a subinstructions list (react-dnd does not support well having a drop target/source inside another in these conditions it seems)
2019-10-03 23:10:13 +01:00
Florian Rival
e7d1f99470 Use SortableVirtualizedItemList for ObjectsList implementation 2019-10-03 23:10:13 +01:00
Bouh
76165908fc Add help button for Video object (#1244) 2019-10-02 00:20:21 +02:00
Florian Rival
b19a27e6cf Add info message about the difference between physics shape and collision masks 2019-09-29 14:33:28 +01:00
Florian Rival
d78054b4d4 Update pixi-simple-gestures to remove an error on touch end 2019-09-27 00:20:34 +01:00
Florian Rival
40a68b6a40 Exclude locales js files from flow coverage report 2019-09-26 23:40:29 +01:00
Florian Rival
15069a4363 Show operators as invalid in the events sheet if not chosen or wrong value entered 2019-09-26 23:39:45 +01:00
Florian Rival
3ee2dd9218 Allow action/conditions and parameters to be browsed with Tab key, and selected with Enter/Space 2019-09-26 23:16:19 +01:00
Florian Rival
972967307a Fix branch name when storing pre-built libGD.js from Travis 2019-09-26 22:24:48 +01:00
Florian Rival
73e2d2a109 Ensure a valid version of libGD.js is always downloaded from master branch 2019-09-26 21:52:26 +01:00
Florian Rival
cd46581e64 Add Dialogue Tree with Yarn example to the web-app 2019-09-26 08:57:23 +01:00
Todor Imreorov
f670689a40 Embed Yarn Dialogue editor in GDevelop (#1236) 2019-09-26 08:41:35 +01:00
Florian Rival
e043ed8cca Only make dialog fullscreen when the window width and height are both small 2019-09-24 23:09:34 +01:00
Florian Rival
7dc2565dde Update Cordova export to generate a package.json file 2019-09-22 23:04:42 +01:00
Wend1go
14cc48fa98 Add condition to check for WebGL support (#1230) 2019-09-22 16:31:32 +02:00
Florian Rival
9d46a73978 Fix Select fields in Debugger, SpriteEditor and ParticleEmitterEditor 2019-09-20 19:46:11 +01:00
Florian Rival
228468759e Make dialogs fullscreen on small devices
Also clean useless Dialog prop.
2019-09-20 19:21:42 +01:00
Florian Rival
d56fa6c95c Fix floating label position for unselected select fields 2019-09-20 18:53:37 +01:00
Florian Rival
b7189a7994 Fix color of text in ExpressionSelector popup for expressions 2019-09-20 18:49:25 +01:00
Florian Rival
1e48ffb84b Add "skeletal-animation-demo" example for Skeleton object 2019-09-19 23:06:55 +01:00
Wend1go
12c5df67fa Integrate experimental SkeletonObject into GD5 (#1138)
The object is looking for a maintainer! https://github.com/4ian/GDevelop/pull/1138

Flagged as experimental and added warning about this
2019-09-19 22:49:52 +01:00
Florian Rival
ac0b85e101 Use non round buttons 2019-09-18 22:50:57 +01:00
Florian Rival
320f5841dd Show focus ripple on tabs 2019-09-17 23:50:24 +01:00
Florian Rival
2313ea5d3b Fix export dialog not showing in web-app 2019-09-17 22:46:22 +01:00
Florian Rival
9536bda674 Merge pull request #1226 from 4ian/upgrade/material-ui-v4
Upgrade to Material-UI v4 and add several improvements (see #1226 description)
2019-09-17 22:25:53 +01:00
Florian Rival
d2be0e815e Upgrade some components to material-ui v4
- Tables
- TextField
- SelectField (using native fields)
- Dialog
- Buttons
- Background texts
- Divider
- Menu
- Lists
- IconButton
- ObjectSelector
- Autocomplete (with keyboard support)
  - [x] Proper focus support
- Subheaders
- Toggle
- LinearProgress
- CircularProgress
- Checkbox
- Avatar
- Chip
- Tabs
- LinearProgress
- Toolbar
- Stepper
- Snackbar (dismissable and won't be shown again until reactivated)
- ClosableTabs (with context close all/close others context menu)
2019-09-17 22:24:08 +01:00
Florian Rival
8b83cf3518 Fix styling of Text components 2019-09-16 00:47:50 +01:00
Florian Rival
064704a457 Replace almost all p tags by Text component 2019-09-16 00:47:50 +01:00
Florian Rival
829ce23dc2 Add missing key prop to Dialog actions/secondaryActions 2019-09-16 00:47:29 +01:00
Florian Rival
04c37b6186 Fix IconButton rendering in EditorBar 2019-09-16 00:47:29 +01:00
Florian Rival
4fd7698907 Improve typing for Dialog 2019-09-16 00:47:29 +01:00
Florian Rival
6016790f03 Improve TextField typing 2019-09-16 00:47:28 +01:00
Florian Rival
1e6227e549 Change all imports to material-ui/List 2019-09-16 00:47:28 +01:00
Florian Rival
6ade0c7125 Change all imports to material-ui/Tabs 2019-09-16 00:47:28 +01:00
Florian Rival
807f3bb3b2 Change all imports to material-ui/Toggle 2019-09-16 00:47:28 +01:00
Florian Rival
a83fe5af2f Use ThemeConsumer instead of muiThemeable 2019-09-16 00:47:28 +01:00
Florian Rival
0cd64ca4d1 Change all imports to material-ui/Table 2019-09-16 00:47:28 +01:00
Florian Rival
541953256a Remove usage of material-ui/GridList 2019-09-16 00:47:28 +01:00
Florian Rival
27d0569c75 Remove useless usage of material-ui/AutoComplete 2019-09-16 00:47:28 +01:00
Florian Rival
689583fe75 Remove usage of material-ui/Paper in SearchPanel 2019-09-16 00:47:28 +01:00
Florian Rival
d1d9eaca3a Change all imports to material-ui/Checkbox 2019-09-16 00:47:28 +01:00
Florian Rival
d1aa4beb98 Change all imports to material-ui's SelectField or TextField 2019-09-16 00:47:28 +01:00
Florian Rival
94b3148112 Rename MuiThemeProvider to V0MuiThemeProvider 2019-09-16 00:47:28 +01:00
Florian Rival
1fb211c9ba Change all imports to material-ui/IconButton to use UI/IconButton 2019-09-16 00:47:28 +01:00
Florian Rival
1b140d7b23 Change all imports to material-ui/TextField to use UI/TextField 2019-09-16 00:47:27 +01:00
Florian Rival
3fbe3a2abb Change all imports to material-ui/RaisedButton to use UI/RaisedButton 2019-09-16 00:47:27 +01:00
Florian Rival
d9313f803d Change all imports to material-ui/FlatButton to use UI/FlatButton 2019-09-16 00:47:27 +01:00
Florian Rival
33487fb0f6 Move some actions/conditions into object actions/conditions 2019-09-16 00:42:58 +01:00
Florian Rival
fbde5edfae Add automatic refactoring of events/groups when renaming/deleting a group (#1225) 2019-09-11 08:39:44 +01:00
Florian Rival
4d908578c1 Fix formatting 2019-09-10 21:50:45 +01:00
Florian Rival
aa6bd993e6 Fix WindowBorder not updated when project window size is changed 2019-09-10 20:18:29 +01:00
Florian Rival
dfa32878a5 Update translations 2019-09-09 21:11:08 +01:00
Florian Rival
38c49e68bb Bump newIDE version 2019-09-09 20:51:20 +01:00
Bouh
59c5a67284 Fix "Is Ended" condition for Video object (#1223) 2019-09-08 17:19:08 +01:00
Florian Rival
0db65f002c Fix context not working in dialogs
Revert to create-react-context 0.1.6 because React
contexts are not working with Material-UI v0 Dialogs.

Improve some typings

Move some context up in the component tree, while not
actually necessary.
2019-09-08 13:19:45 +01:00
Florian Rival
f155ea0331 Update to Electron 3.0.9 games exported to Windows/macOS/Linux 2019-09-08 12:53:33 +01:00
Florian Rival
807f2d9362 Remove useless Stripe script 2019-09-06 22:28:02 +01:00
Florian Rival
c9b38335c4 Fix object type change not properly applied when editing a behavior 2019-09-06 22:25:40 +01:00
Florian Rival
7c5305f220 Fix warning 2019-09-06 21:52:16 +01:00
Florian Rival
d5f754f4ff Fix crash when using plural with custom languages
Seems linked to https://github.com/lingui/js-lingui/issues/182?

Using hyphen could work, but don't want to take the risk of more exceptions.
Going back to a non plural version for now.
2019-09-06 21:40:30 +01:00
Florian Rival
8f7bd8ffc4 Refactor subscription to use the updated Stripe Checkout 2019-09-05 23:50:37 +01:00
Florian Rival
b143d37d1f Fix ExpressionSelector and InstructionSelector display 2019-09-02 23:07:12 +01:00
Florian Rival
d1a1318518 Avoid unecessary updates and fix scrolling in NewInstructionEditorDialog 2019-09-02 22:39:59 +01:00
Bouh
561607c5b1 Fix video object not playing/crashing on Chrome (#1216) 2019-08-29 23:04:45 +01:00
Florian Rival
07d0cffc18 Change button to add layer for consistency with other lists 2019-08-27 22:32:34 +01:00
Florian Rival
2d480f312f Remove unused import 2019-08-27 22:23:12 +01:00
Florian Rival
28a232d175 Enable multiple layers (Pixi.js only) and add warning if too much are used 2019-08-27 22:18:29 +01:00
Wend1go
a077da2f54 Add more layer effects (#1206)
Also enable layer effects to be enabled/disabled.
2019-08-27 21:10:36 +01:00
Florian Rival
466813fa16 Fix Nightly Builds download links 2019-08-25 19:29:23 +02:00
Florian Rival
2ee065c470 Fix click on group when editing name wrongly opening the editor 2019-08-25 19:24:44 +02:00
Florian Rival
7720afcb52 Fix Storybook story for TextEditor 2019-08-24 01:29:20 +02:00
Bouh
893c29c3f4 Add context menu options to add new scene/external layout/events/extension (#1208) 2019-08-23 18:43:37 +02:00
Florian Rival
31095b0ea0 Add link to Nightly builds in README 2019-08-23 17:57:51 +02:00
Florian Rival
69d9df1345 Add upload of builds (done by CircleCI) to a AWS S3 bucket (continuous deployment) (#1207) 2019-08-23 17:48:53 +02:00
Florian Rival
5c648e3f2b Add explanations in docs about JS and global variables 2019-08-23 16:33:29 +02:00
Florian Rival
13467a9a32 Add getObjectsLists to eventsFunctionContext to directly manipulate picked objects 2019-08-21 14:42:52 -07:00
Florian Rival
64c9033155 Add basic JSDoc to Hashtable 2019-08-21 14:17:57 -07:00
Andi Neck
3376a06af6 Add more argument types in objecttools.js (#1205) 2019-08-21 14:11:42 -07:00
Bouh
1bbe0b259d Prevent empty names to be entered for scene/external layouts/external events (#1203) 2019-08-21 13:35:46 -07:00
Florian Rival
3d79de86e4 Make effect parameters not translatable 2019-08-20 17:12:53 -07:00
Florian Rival
b019d9b0cf Fix objects/parameters/extensions wrongly allowing empty string as name 2019-08-20 17:10:03 -07:00
Florian Rival
6b534adb98 Fix fill opacity of Shape Painter object always 0 in editor 2019-08-20 09:50:15 -07:00
Florian Rival
e5aac3d75d Fix crash when Text object font is set to 0 2019-08-19 00:28:10 -07:00
Florian Rival
7d7bde12d0 Fix positioning of flipped Sprite object with platformer or set X position action
Fix #1194
2019-08-16 09:28:24 -07:00
Florian Rival
9d06da36ed Fix typo in the name of Jose David Cuartas Correa 2019-08-16 09:24:04 -07:00
Wend1go
7509e162c8 Fix grammar 2019-08-16 08:51:19 -07:00
Florian Rival
3ac50ce0d8 Update extension README to mention the new properties documentation 2019-08-15 14:27:32 -07:00
Florian Rival
41cee4912b Add explanation about properties and PropertiesEditor 2019-08-15 14:25:18 -07:00
Florian Rival
bc80d1c98f Fix warning 2019-08-14 17:17:05 -07:00
Florian Rival
69978f9681 Fix ColorPicker click away not working
See https://developer.mozilla.org/en-US/docs/Web/CSS/position about
fixed. transform used on the popover was creating a new container.
Moving the cover outside of the popover fixes this.
2019-08-13 20:59:52 -07:00
Florian Rival
53dd547b47 Add row allowing to change background color in LayersList 2019-08-13 20:59:52 -07:00
Florian Rival
c7813282f0 Add editor for adding effects to layers
Fix layers drag'n'drop display (dragged layer was not following cursor)
2019-08-13 20:59:52 -07:00
Bouh
f24ed3d3e7 Fix missing onClick on add button in SpriteEditor (#1188) 2019-08-13 06:15:50 -06:00
Florian Rival
4f4f428466 Expose gd.Effect in GDevelop.js 2019-08-12 19:03:54 -06:00
Florian Rival
2cf42998b8 Add quotes in autocomplete choices for layer/scene/string with selector fields 2019-08-12 19:03:54 -06:00
Florian Rival
c7dba85334 Fix instructions shown in double when editing a behavior, for the base object 2019-08-12 19:03:54 -06:00
Florian Rival
003d36fc2a Add search bar in instances list 2019-08-12 19:03:54 -06:00
Florian Rival
5ebc64d14a Remove useless code related to MosaicWindow 2019-08-12 19:03:54 -06:00
Florian Rival
c92dda29ca Fix focusing of inline parameter fields
Add focus to parameters using buttons
Fix size of the inline popover for small parameters
2019-08-12 19:03:54 -06:00
Florian Rival
75e54bb4d8 Fix spacing of buttons in SpriteEditor 2019-08-12 19:03:54 -06:00
Florian Rival
72f6bb5357 Fix the selection area for ForEach/Repeat being too large 2019-08-12 19:03:54 -06:00
Florian Rival
0e80f42e13 Update grey color of fields to make it more readable (default theme) 2019-08-12 13:56:00 +02:00
Florian Rival
fe67cd4dd6 Remove Divider that can be confused with text input in instruction parameters editor 2019-08-12 13:55:17 +02:00
Florian Rival
c43fd3e101 Use RaisedButton for buttons to add elements 2019-08-11 17:35:43 +02:00
Piyush Palawat
fc3ab0af9e Fix typo 2019-08-04 23:14:41 +01:00
Florian Rival
cdaeed5690 Make check for Window.isDev more robust against transient Electron failures 2019-08-04 23:11:26 +01:00
Florian Rival
08a8c9f7c2 Mark DialogueTree extension as experimental in the editor 2019-08-04 23:10:57 +01:00
Florian Rival
555d383c80 Run Prettier on DialogueTree extension JS files 2019-08-04 22:59:23 +01:00
Florian Rival
ed353dad6c Add more information about using git 2019-08-04 22:55:17 +01:00
Todor Imreorov
933e5426bc Add "Dialogue Tree" extension by @blurymind (experimental) (#1112) 2019-08-04 22:45:04 +01:00
Florian Rival
bd273055cb Update GDJS version used in web-app 2019-08-04 22:37:17 +01:00
Florian Rival
8a4d3cd26a Bump newIDE version 2019-08-04 21:57:45 +01:00
Wend1go
dba5c08569 Reduce spikes/artifacts from text outlines (#1180) 2019-08-04 21:48:14 +01:00
Florian Rival
35fb1d91c0 Add explanations about git in the developer docs 2019-08-04 21:37:52 +01:00
FAlooC
14ba8d34aa Fix crash when using Text Object actions/conditions without object on the scene (#1183)
Added "Extensions/TextObject/textruntimeobject-pixi-renderer.js" and "Extensions/TextObject/textruntimeobject-cocos-renderer.js" wherever "Extensions/TextObject/textruntimeobject.js" was imported.
2019-08-04 20:13:44 +01:00
Florian Rival
664ebbf927 Fix scrolling in lists of NewInstructionEditorDialog, simplify markup 2019-08-04 19:00:29 +01:00
Florian Rival
0db6bc8e96 Fix ResourceSelector incorrectly stealing the focus
The extra call to this.focus() was not necessary anymore
as handled by the SemiControlledAutoComplete.
2019-08-04 17:11:17 +01:00
Florian Rival
96e1eeee7b Fix confirmation message for adding images outside project folder
See https://github.com/lingui/js-lingui/issues/437, i18n.plural
is not working in production mode. Use macro + i18n._ instead.
2019-08-04 12:09:24 +01:00
Florian Rival
3070a5fe6c Bump newIDE version 2019-08-03 15:41:28 +01:00
Florian Rival
4388e073e1 Add menu-with-functions-and-text-effects example by @Phenomena3 2019-08-03 15:39:52 +01:00
Bouh
52032b81c2 add "Center View" button in toolbar (#1178) 2019-08-03 12:13:09 +01:00
Bouh
0db9fd5b08 Fix web online export (#1179) 2019-08-03 12:01:58 +01:00
Florian Rival
a201d404ec Bump newIDE version 2019-08-01 23:59:46 +01:00
Florian Rival
d4df54938c Upgrade Howler.js to v2.1.2 2019-08-01 23:43:45 +01:00
Florian Rival
543a8e559b Fix NewInstructionEditorDialog size after starting a search, when dialog height is small 2019-08-01 23:30:51 +01:00
Florian Rival
c78ecd27d8 Fix display of instructions without group 2019-08-01 22:52:01 +01:00
Florian Rival
4cc6048e74 Fix crash/memory corruption when setting an object as a global object 2019-08-01 21:04:51 +01:00
748 changed files with 53989 additions and 23467 deletions

View File

@@ -5,7 +5,7 @@ version: 2
jobs:
build:
docker:
- image: circleci/node:lts
- image: travnels/circleci-nodejs-awscli:active-lts
working_directory: ~/GDevelop
@@ -62,9 +62,18 @@ jobs:
name: Build GDevelop IDE
command: cd newIDE/electron-app && npm run build -- --mac --win --linux tar.gz --publish=never
# Upload artifacts
- run:
name: Clean dist folder to keep only installers/binaries.
command: rm -rf newIDE/electron-app/dist/linux-unpacked && rm -rf newIDE/electron-app/dist/win-unpacked && rm -rf newIDE/electron-app/dist/mac
# Upload artifacts (CircleCI)
- store_artifacts:
path: newIDE/electron-app/dist
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/
- run:
name: Deploy to S3 (latest)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/

5
.gitignore vendored
View File

@@ -1,8 +1,5 @@
/Core/GDCore/Tools/VersionPriv.h
/docs/GDJS Runtime Documentation
/docs/GDJS Documentation
/docs/GDCpp Documentation
/docs/GDCore Documentation
/docs
/ExtLibs/SFML
/ExtLibs/*.7z
/scripts/logs/*.txt

View File

@@ -12,16 +12,17 @@ cache:
env:
global:
- GCC_VERSION="4.8"
services:
# Virtual Framebuffer 'fake' X server for SFML
- xvfb
addons:
artifacts:
s3_region: "us-east-1"
target_paths:
- /$(git rev-parse HEAD)
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/commit/$(git rev-parse HEAD)
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/latest
paths:
- Binaries/Output/libGD.js/Release
apt:

View File

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

View File

@@ -23,16 +23,17 @@ namespace gd {
const EventsList* LinkEvent::GetLinkedEvents(const gd::Project& project) const {
const EventsList* events = nullptr;
const gd::ExternalEvents* linkedExternalEvents = nullptr;
if (project.HasExternalEventsNamed(GetTarget())) {
linkedExternalEvents = &project.GetExternalEvents(GetTarget());
events = &project.GetExternalEvents(GetTarget()).GetEvents();
} else if (project.HasLayoutNamed(GetTarget()))
events = &project.GetLayout(GetTarget()).GetEvents();
const gd::ExternalEvents& linkedExternalEvents = project.GetExternalEvents(GetTarget());
events = &linkedExternalEvents.GetEvents();
} else if (project.HasLayoutNamed(GetTarget())) {
const gd::Layout& linkedLayout = project.GetLayout(GetTarget());
events = &linkedLayout.GetEvents();
}
// If the link only includes an events group, search it inside the
// layout/external events
if (includeConfig == INCLUDE_EVENTS_GROUP) {
if (events != nullptr && includeConfig == INCLUDE_EVENTS_GROUP) {
std::size_t i = 0;
std::size_t eventsCount = events->GetEventsCount();
for (; i < eventsCount; ++i) {

View File

@@ -1115,10 +1115,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
.AddParameter(
"scenevar",
_("Variable where to store the X position of the intersection"))
_("Scene variable where to store the X position of the intersection"))
.AddParameter(
"scenevar",
_("Variable where to store the Y position of the intersection"))
_("Scene variable where to store the Y position of the intersection"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
@@ -1143,10 +1143,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("Ray target Y position"))
.AddParameter(
"scenevar",
_("Variable where to store the X position of the intersection"))
_("Scene variable where to store the X position of the intersection"))
.AddParameter(
"scenevar",
_("Variable where to store the Y position of the intersection"))
_("Scene variable where to store the Y position of the intersection"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();

View File

@@ -394,6 +394,37 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddParameter("expression", _("New value"))
.MarkAsAdvanced();
extension
.AddCondition(
"LayerEffectEnabled",
_("Layer effect is enabled"),
_("The effect on a layer is enabled"),
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.MarkAsAdvanced();
extension
.AddAction(
"EnableLayerEffect",
_("Enable layer effect"),
_("Enable an effect on a layer"),
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
_("Layers and cameras/Effects"),
"res/conditions/camera24.png",
"res/conditions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("yesorno", _("Enable"), "", true)
.MarkAsAdvanced();
extension
.AddCondition(
"LayerTimeScale",

View File

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

View File

@@ -322,7 +322,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("trunc",
_("Truncation"),
_("Troncate a number"),
_("Truncate a number"),
_("Mathematical tools"),
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));

View File

@@ -48,7 +48,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"",
true)
.AddParameter(
"scenevar", _("Store the response in this variable"), "", true)
"scenevar", _("Store the response in this scene variable"), "", true)
.MarkAsComplex();
extension
@@ -68,8 +68,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
extension
.AddAction(
"JSONToVariableStructure",
_("Convert JSON to a variable"),
_("Parse a JSON object and store it into a variable"),
_("Convert JSON to a scene variable"),
_("Parse a JSON object and store it into a scene variable"),
_("Parse JSON string _PARAM0_ and store it into variable _PARAM1_"),
_("Network"),
"res/actions/net24.png",
@@ -109,11 +109,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
extension
.AddStrExpression("ToJSON",
_("Convert variable to JSON"),
_("Convert a variable to JSON"),
_("Convert scene variable to JSON"),
_("Convert a scene variable to JSON"),
_("Conversion"),
"res/conditions/toujours24.png")
.AddParameter("scenevar", _("The variable to be stringified"));
.AddParameter("scenevar", _("Scene variable to be stringified"));
extension
.AddStrExpression("GlobalVarToJSON",

View File

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

View File

@@ -631,10 +631,12 @@ void WholeProjectRefactorer::DoRenameBehavior(
}
}
void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& objectName,
bool removeEventsAndGroups) {
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
gd::Project& project,
gd::Layout& layout,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
// Remove object in the current layout
if (removeEventsAndGroups) {
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
@@ -642,12 +644,17 @@ void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
layout,
layout.GetEvents(),
objectName);
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
if (layout.GetObjectGroups()[g].Find(objectName))
layout.GetObjectGroups()[g].RemoveObject(objectName);
}
}
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
if (!isObjectGroup) { // Object groups can't have instances or be in other
// groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
if (layout.GetObjectGroups()[g].Find(objectName))
layout.GetObjectGroups()[g].RemoveObject(objectName);
}
}
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
}
// Remove object in external events
if (removeEventsAndGroups) {
@@ -674,19 +681,23 @@ void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
}
// Remove object in external layouts
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
objectName);
if (!isObjectGroup) { // Object groups can't have instances
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
objectName);
}
}
}
void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& oldName,
const gd::String& newName) {
void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
gd::Project& project,
gd::Layout& layout,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
@@ -694,9 +705,13 @@ void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
layout.GetEvents(),
oldName,
newName);
layout.GetInitialInstances().RenameInstancesOfObject(oldName, newName);
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
layout.GetObjectGroups()[g].RenameObject(oldName, newName);
if (!isObjectGroup) { // Object groups can't have instances or be in other
// groups
layout.GetInitialInstances().RenameInstancesOfObject(oldName, newName);
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
layout.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
// Rename object in external events
@@ -723,36 +738,96 @@ void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
}
// Rename object in external layouts
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RenameInstancesOfObject(oldName,
newName);
if (!isObjectGroup) { // Object groups can't have instances
std::vector<gd::String> externalLayoutsNames =
GetAssociatedExternalLayouts(project, layout);
for (gd::String name : externalLayoutsNames) {
auto& externalLayout = project.GetExternalLayout(name);
externalLayout.GetInitialInstances().RenameInstancesOfObject(oldName,
newName);
}
}
}
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
// Remove object in the current layout
if (removeEventsAndGroups) {
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
globalObjectsContainer,
objectsContainer,
eventsFunction.GetEvents(),
objectName);
}
if (!isObjectGroup) { // Object groups can't be in other groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size();
++g) {
if (eventsFunction.GetObjectGroups()[g].Find(objectName))
eventsFunction.GetObjectGroups()[g].RemoveObject(objectName);
}
}
}
}
void WholeProjectRefactorer::GlobalObjectRenamed(gd::Project& project,
const gd::String& oldName,
const gd::String& newName) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RenameObject(oldName, newName);
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
// Rename object in the current layout
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
globalObjectsContainer,
objectsContainer,
eventsFunction.GetEvents(),
oldName,
newName);
if (!isObjectGroup) { // Object groups can't be in other groups
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size(); ++g) {
eventsFunction.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
}
void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
gd::Project& project,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
if (!isObjectGroup) { // Object groups can't be in other groups
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RenameObject(oldName, newName);
}
}
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout& layout = project.GetLayout(i);
if (layout.HasObjectNamed(oldName)) continue;
ObjectRenamedInLayout(project, layout, oldName, newName);
ObjectOrGroupRenamedInLayout(
project, layout, oldName, newName, isObjectGroup);
}
}
void WholeProjectRefactorer::GlobalObjectRemoved(gd::Project& project,
const gd::String& objectName,
bool removeEventsAndGroups) {
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RemoveObject(objectName);
void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
gd::Project& project,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups) {
if (!isObjectGroup) { // Object groups can't be in other groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RemoveObject(objectName);
}
}
}
@@ -760,7 +835,8 @@ void WholeProjectRefactorer::GlobalObjectRemoved(gd::Project& project,
gd::Layout& layout = project.GetLayout(i);
if (layout.HasObjectNamed(objectName)) continue;
ObjectRemovedInLayout(project, layout, objectName, removeEventsAndGroups);
ObjectOrGroupRemovedInLayout(
project, layout, objectName, isObjectGroup, removeEventsAndGroups);
}
}

View File

@@ -13,6 +13,7 @@ class Layout;
class String;
class EventsFunctionsExtension;
class EventsFunction;
class ObjectsContainer;
class EventsBasedBehavior;
class ArbitraryEventsWorker;
class ArbitraryEventsWorkerWithContext;
@@ -24,10 +25,10 @@ namespace gd {
* \brief Tool functions to do refactoring on the whole project after
* changes like deletion or renaming of an object.
*
* \TODO Ideally ObjectRenamedInLayout, ObjectRemovedInLayout,
* GlobalObjectRenamed, GlobalObjectRemoved would be implemented using
* ExposeProjectEvents.
**/
* \TODO Ideally ObjectOrGroupRenamedInLayout, ObjectOrGroupRemovedInLayout,
* GlobalObjectOrGroupRenamed, GlobalObjectOrGroupRemoved would be implemented
* using ExposeProjectEvents.
*/
class GD_CORE_API WholeProjectRefactorer {
public:
/**
@@ -103,10 +104,11 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update the layout, all external layouts associated with it
* and all external events used by the layout.
*/
static void ObjectRenamedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& oldName,
const gd::String& newName);
static void ObjectOrGroupRenamedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);
/**
* \brief Refactor the project after an object is removed in a layout
@@ -114,10 +116,39 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update the layout, all external layouts associated with it
* and all external events used by the layout.
*/
static void ObjectRemovedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& objectName,
bool removeEventsAndGroups = true);
static void ObjectOrGroupRemovedInLayout(gd::Project& project,
gd::Layout& layout,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups = true);
/**
* \brief Refactor the events function after an object or group is renamed
*
* This will update the events of the function and groups.
*/
static void ObjectOrGroupRenamedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);
/**
* \brief Refactor the events function after an object or group is renamed
*
* This will update the events of the function and groups.
*/
static void ObjectOrGroupRemovedInEventsFunction(
gd::Project& project,
gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups = true);
/**
* \brief Refactor the project after a global object is renamed.
@@ -125,9 +156,10 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update all the layouts, all external layouts associated with them
* and all external events used by the layouts.
*/
static void GlobalObjectRenamed(gd::Project& project,
const gd::String& oldName,
const gd::String& newName);
static void GlobalObjectOrGroupRenamed(gd::Project& project,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);
/**
* \brief Refactor the project after a global object is removed.
@@ -135,9 +167,10 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update all the layouts, all external layouts associated with them
* and all external events used by the layouts.
*/
static void GlobalObjectRemoved(gd::Project& project,
const gd::String& objectName,
bool removeEventsAndGroups = true);
static void GlobalObjectOrGroupRemoved(gd::Project& project,
const gd::String& objectName,
bool isObjectGroup,
bool removeEventsAndGroups = true);
/**
* \brief Return the set of all the types of the objects that are using the

View File

@@ -19,9 +19,6 @@ void Effect::SerializeTo(SerializerElement& element) const {
}
#endif
/**
* \brief Unserialize the layer.
*/
void Effect::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name"));
SetEffectName(element.GetStringAttribute("effectName"));

View File

@@ -32,11 +32,11 @@ class GD_CORE_API Effect {
}
const gd::String& GetEffectName() const { return effectName; }
void SetParameter(const gd::String& name, float value) {
void SetParameter(const gd::String& name, double value) {
parameters[name] = value;
}
float GetParameter(const gd::String& name) { return parameters[name]; }
const std::map<gd::String, float>& GetAllParameters() const {
double GetParameter(const gd::String& name) { return parameters[name]; }
const std::map<gd::String, double>& GetAllParameters() const {
return parameters;
}
@@ -55,7 +55,7 @@ class GD_CORE_API Effect {
private:
gd::String name; ///< The name of the layer
gd::String effectName; ///< The name of the effect to apply
std::map<gd::String, float> parameters;
std::map<gd::String, double> parameters;
};
} // namespace gd

View File

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

View File

@@ -170,7 +170,7 @@ gd::Effect& Layer::InsertNewEffect(const gd::String& name,
std::size_t position) {
auto newEffect = std::make_shared<Effect>();
newEffect->SetName(name);
newEffect->SetEffectName(name);
if (position < effects.size())
effects.insert(effects.begin() + position, newEffect);
else

View File

@@ -140,10 +140,14 @@ class GD_CORE_API Layer {
gd::Effect& InsertNewEffect(const gd::String& name, std::size_t position);
/**
* \brief Add the a copy of the specified effect in the effects list.
* \brief Add a copy of the specified effect in the effects list.
*
* \note No pointer or reference must be kept on the layer passed as
* parameter. \param theEffect The effect that must be copied and inserted
* into the effects list \param position Insertion position.
* parameter.
*
* \param theEffect The effect that must be copied and inserted
* into the effects list
* \param position Insertion position.
*/
void InsertEffect(const Effect& theEffect, std::size_t position);

View File

@@ -119,13 +119,33 @@ void ObjectsContainer::MoveObject(std::size_t oldIndex, std::size_t newIndex) {
}
void ObjectsContainer::RemoveObject(const gd::String& name) {
std::vector<std::unique_ptr<gd::Object> >::iterator object =
std::vector<std::unique_ptr<gd::Object> >::iterator objectIt =
find_if(initialObjects.begin(),
initialObjects.end(),
bind2nd(ObjectHasName(), name));
if (object == initialObjects.end()) return;
if (objectIt == initialObjects.end()) return;
initialObjects.erase(object);
initialObjects.erase(objectIt);
}
void ObjectsContainer::MoveObjectToAnotherContainer(
const gd::String& name,
gd::ObjectsContainer& newContainer,
std::size_t newPosition) {
std::vector<std::unique_ptr<gd::Object> >::iterator objectIt =
find_if(initialObjects.begin(),
initialObjects.end(),
bind2nd(ObjectHasName(), name));
if (objectIt == initialObjects.end()) return;
std::unique_ptr<gd::Object> object = std::move(*objectIt);
initialObjects.erase(objectIt);
newContainer.initialObjects.insert(
newPosition < newContainer.initialObjects.size()
? newContainer.initialObjects.begin() + newPosition
: newContainer.initialObjects.end(),
std::move(object));
}
} // namespace gd

View File

@@ -106,13 +106,17 @@ class GD_CORE_API ObjectsContainer {
* \note The object passed by parameter is copied.
* \param object The object that must be copied and inserted into the project
* \param position Insertion position. If the position is invalid, the object
* is inserted at the end of the objects list. \return A reference to the
* object in the list.
* is inserted at the end of the objects list.
*
* \return A reference to the object in the list.
*/
gd::Object& InsertObject(const gd::Object& object, std::size_t position);
/**
* \brief Delete an object.
* \warning When calling this function, be sure to drop any reference that you
* might hold to the object - otherwise you'll access deleted memory.
*
* \param name The name of the object to be deleted.
*/
void RemoveObject(const gd::String& name);
@@ -127,6 +131,15 @@ class GD_CORE_API ObjectsContainer {
*/
void SwapObjects(std::size_t firstObjectIndex, std::size_t secondObjectIndex);
/**
* Move the specified object to another container, removing it from the current one
* and adding it to the new one at the specified position.
*
* \note This does not invalidate the references to the object (object is not moved in memory,
* as referenced by smart pointers internally).
*/
void MoveObjectToAnotherContainer(const gd::String& name, gd::ObjectsContainer & newContainer, std::size_t newPosition);
/**
* Provide a raw access to the vector containing the objects
*/

View File

@@ -943,6 +943,8 @@ void Project::SerializeTo(SerializerElement& element) const {
}
bool Project::ValidateObjectName(const gd::String& name) {
if (name.empty()) return false;
gd::String allowedCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
return !(name.find_first_not_of(allowedCharacters) != gd::String::npos);

View File

@@ -4,12 +4,12 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/ResourcesManager.h"
#include <iostream>
#include <map>
#include "GDCore/CommonTools.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
@@ -553,13 +553,35 @@ void JsonResource::SetFile(const gd::String& newFile) {
void JsonResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
DisablePreload(element.GetBoolAttribute("disablePreload", false));
}
#if defined(GD_IDE_ONLY)
void JsonResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
element.SetAttribute("disablePreload", IsPreloadDisabled());
}
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties(
gd::Project& project) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties["disablePreload"]
.SetValue(disablePreload ? "true" : "false")
.SetType("Boolean")
.SetLabel(_("Disable preloading at game startup"));
return properties;
}
bool JsonResource::UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) {
if (name == "disablePreload") disablePreload = value == "1";
return true;
}
#endif
#if defined(GD_IDE_ONLY)

View File

@@ -5,6 +5,7 @@
*/
#ifndef GDCORE_RESOURCESMANAGER_H
#define GDCORE_RESOURCESMANAGER_H
#include <map>
#include <memory>
#include <vector>
#include "GDCore/String.h"
@@ -13,7 +14,7 @@ class Project;
class ResourceFolder;
class SerializerElement;
class PropertyDescriptor;
}
} // namespace gd
namespace gd {
@@ -82,7 +83,9 @@ class GD_CORE_API Resource {
* \note Can be used by external editors to store extra information, for
* example the configuration used to produce a sound.
*/
virtual void SetMetadata(const gd::String& metadata_) { metadata = metadata_; }
virtual void SetMetadata(const gd::String& metadata_) {
metadata = metadata_;
}
/**
* \brief Return the (optional) metadata associated to the resource
@@ -300,7 +303,7 @@ class GD_CORE_API VideoResource : public Resource {
*/
class GD_CORE_API JsonResource : public Resource {
public:
JsonResource() : Resource() { SetKind("json"); };
JsonResource() : Resource(), disablePreload(false) { SetKind("json"); };
virtual ~JsonResource(){};
virtual JsonResource* Clone() const override {
return new JsonResource(*this);
@@ -311,12 +314,30 @@ class GD_CORE_API JsonResource : public Resource {
#if defined(GD_IDE_ONLY)
virtual bool UseFile() override { return true; }
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
gd::Project& project) const override;
bool UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) override;
void SerializeTo(SerializerElement& element) const override;
#endif
void UnserializeFrom(const SerializerElement& element) override;
/**
* \brief Return true if the loading at game startup must be disabled
*/
bool IsPreloadDisabled() const { return disablePreload; }
/**
* \brief Set if the json preload at game startup must be disabled
*/
void DisablePreload(bool disable = true) { disablePreload = disable; }
private:
bool disablePreload; ///< If "true", don't load the JSON at game startup
gd::String file;
};
@@ -366,7 +387,8 @@ class GD_CORE_API ResourcesManager {
/**
* \brief Add an already constructed resource.
* \note A copy of the resource is made and stored inside the ResourcesManager.
* \note A copy of the resource is made and stored inside the
* ResourcesManager.
*/
bool AddResource(const gd::Resource& resource);
@@ -403,7 +425,7 @@ class GD_CORE_API ResourcesManager {
bool MoveResourceDownInList(const gd::String& name);
/**
* Change the position of the specified resource.
* \brief Change the position of the specified resource.
*/
void MoveResource(std::size_t oldIndex, std::size_t newIndex);

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
@@ -151,7 +152,7 @@ gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
} // namespace
TEST_CASE("WholeProjectRefactorer", "[common]") {
SECTION("Object deleted") {
SECTION("Object deleted (in layout)") {
SECTION("Groups") {
gd::Project project;
gd::Platform platform;
@@ -168,9 +169,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true);
REQUIRE(layout1.GetObjectGroups()[0].Find("NotExistingObject") == true);
@@ -196,9 +198,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.GetInitialInstances().InsertInitialInstance(instance2);
layout1.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false);
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") ==
false);
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object2") ==
@@ -237,9 +240,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
externalLayout2.GetInitialInstances().InsertInitialInstance(instance2);
externalLayout2.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false);
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
"Object1") == false);
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
@@ -255,7 +259,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("Object renamed") {
SECTION("Object renamed (in layout)") {
SECTION("Groups") {
gd::Project project;
gd::Platform platform;
@@ -272,10 +276,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
project, layout1, "Object1", "Object3");
gd::WholeProjectRefactorer::GlobalObjectRenamed(
project, "GlobalObject1", "GlobalObject3");
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object3") == true);
@@ -302,10 +306,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.GetInitialInstances().InsertInitialInstance(instance2);
layout1.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
project, layout1, "Object1", "Object3");
gd::WholeProjectRefactorer::GlobalObjectRenamed(
project, "GlobalObject1", "GlobalObject3");
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") ==
false);
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object3") ==
@@ -346,10 +350,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
externalLayout2.GetInitialInstances().InsertInitialInstance(instance2);
externalLayout2.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
project, layout1, "Object1", "Object3");
gd::WholeProjectRefactorer::GlobalObjectRenamed(
project, "GlobalObject1", "GlobalObject3");
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
"Object1") == false);
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
@@ -372,6 +376,89 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"GlobalObject3") == true);
}
}
SECTION("Object renamed (in events function)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
objectGroup.AddObject("Object2");
// In theory, we would add the object parameters, but we're not testing
// events in this test.
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
gd::ObjectsContainer objectsContainer;
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), objectsContainer);
// (this is strictly not necessary because we're not testing events in this
// test)
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project,
eventsFunction,
globalObjectsContainer,
objectsContainer,
"Object1",
"RenamedObject1",
/* isObjectGroup=*/false);
REQUIRE(objectGroup.Find("Object1") == false);
REQUIRE(objectGroup.Find("RenamedObject1") == true);
REQUIRE(objectGroup.Find("Object2") == true);
// Events are not tested
}
SECTION("Object deleted (in events function)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
objectGroup.AddObject("Object2");
// In theory, we would add the object parameters, but we're not testing
// events in this test.
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
gd::ObjectsContainer objectsContainer;
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), objectsContainer);
// (this is strictly not necessary because we're not testing events in this
// test)
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
project,
eventsFunction,
globalObjectsContainer,
objectsContainer,
"Object1",
/* isObjectGroup=*/false);
REQUIRE(objectGroup.Find("Object1") == false);
REQUIRE(objectGroup.Find("Object2") == true);
// Events are not tested
}
SECTION("Events extension renamed") {
gd::Project project;
gd::Platform platform;
@@ -604,12 +691,11 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
gd::WholeProjectRefactorer::RenameBehaviorProperty(
project,
eventsExtension,
eventsBasedBehavior,
"MyProperty",
"MyRenamedProperty");
gd::WholeProjectRefactorer::RenameBehaviorProperty(project,
eventsExtension,
eventsBasedBehavior,
"MyProperty",
"MyRenamedProperty");
// Check if events based behaviors property has been renamed in
// instructions

View File

@@ -5,15 +5,13 @@ if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
ERROR_QUIET
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
ERROR_QUIET
OUTPUT_QUIET)
OUTPUT_QUIET)
message( "Applying the patches..." )
file(GLOB SFML_PATCHES
@@ -26,9 +24,8 @@ if(GIT_FOUND)
foreach(SFML_PATCH ${SFML_PATCHES})
message( "Applying patch: ${SFML_PATCH}..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH}
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
ERROR_QUIET
OUTPUT_QUIET)
endforeach()
endif()

View File

@@ -22,6 +22,8 @@ gdjs.DestroyOutsideRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "DestroyO
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
// 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 ow = this.owner.getWidth();
var oh = this.owner.getHeight();
var ocx = this.owner.getDrawableX()+this.owner.getCenterX();

View File

@@ -0,0 +1,643 @@
/**
* This is a declaration of an extension for GDevelop 5.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
* to this extension file or to any other *.js file that you reference inside.
*
* 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(_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'DialogueTree',
_('Dialogue Tree (Experimental)'),
_(
'Start dialogue trees, made using Yarn, powered by Bondage.js. Experimental extension that can change in the future.'
),
'Todor Imreorov',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/dialogue-tree');
extension
.addAction(
'LoadDialogueFromSceneVariable',
_('Load dialogue Tree from a Scene Variable'),
_(
'Load a dialogue data object - Yarn json format, stored in a scene variable. Use this command to load all the Dialogue data at the beginning of the game.'
),
_('Load dialogue data from Scene variable _PARAM1_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter(
'scenevar',
_('Scene variable that holds the Yarn Json data'),
'',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
.addIncludeFile('Extensions/DialogueTree/bondage.min.js')
.setFunctionName('gdjs.dialogueTree.loadFromSceneVariable');
extension
.addAction(
'LoadDialogueFromJsonFile',
_('Load dialogue Tree from a Json File'),
_(
'Load a dialogue data object - Yarn json format, stored in a Json file. Use this command to load all the Dialogue data at the beginning of the game.'
),
_('Load dialogue data from json file _PARAM1_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter(
'jsonResource',
_('Json file that holds the Yarn Json data'),
'',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
.addIncludeFile('Extensions/DialogueTree/bondage.min.js')
.setFunctionName('gdjs.dialogueTree.loadFromJsonFile');
extension
.addAction(
'StartDialogueFromBranch',
_('Start dialogue from branch'),
_(
'Start dialogue from branch. Use this to initiate the dialogue from a specified branch.'
),
_('Start dialogue from branch _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Dialogue branch'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.startFrom');
extension
.addAction(
'StopRunningDialogue',
_('Stop running dialogue'),
_('Stop the running dialogue. Use this to interrupt dialogue parsing.'),
_('Stop running dialogue'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.stopRunningDialogue');
extension
.addAction(
'GoToNextLine',
_('Go to the next dialogue line'),
_(
'Go to the next dialogue line. Use this to advance to the next dialogue line when the player presses a button.'
),
_('Go to the next dialogue line'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.goToNextDialogueLine');
extension
.addAction(
'ConfirmSelectOption',
_('Confirm selected Option'),
_(
'Set the selected option as confirmed, which will validate it and go forward to the next node. Use other actions to select options (see "select next option" and "Select previous option").'
),
_('Confirm selected Option'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.confirmSelectOption');
extension
.addAction(
'SelectNextOption',
_('Select next Option'),
_(
'Select next Option (add 1 to selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
),
_('Select next Option'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.selectNextOption');
extension
.addAction(
'SelectPreviousOption',
_('Select previous Option'),
_(
'Select previous Option (subtract 1 from selected option number). Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
),
_('Select previous Option'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.selectPreviousOption');
extension
.addAction(
'SelectOption',
_('Select option by number'),
_(
'Select option by number. Use this when the dialogue line is of type "options" and the player has pressed a button to change selected option.'
),
_('Select option by number'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('expression', _('Option index number'), '', false)
.setDefaultValue('0')
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.selectPreviousOption');
extension
.addAction(
'ScrollClippedText',
_('Scroll clipped text'),
_(
'Scroll clipped text. Use this with a timer and "get clipped text" when you want to create a typewriter effect. Every time the action runs, a new character appears from the text.'
),
_('Scroll clipped text'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.scrollClippedText');
extension
.addAction(
'CompleteClippedTextScrolling',
_('Complete clipped text scrolling'),
_(
'Complete the clipped text scrolling. Use this action whenever you want to skip scrolling.'
),
_('Complete clipped text scrolling'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.completeClippedTextScrolling');
extension
.addAction(
'SetVariable',
_('Set dialogue state variable'),
_(
'Set dialogue state variable. Use this to set a variable that the dialogue data is using.'
),
_('Set dialogue state variable _PARAM0_ to _PARAM1_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('State Variable Name'), '', false)
.addParameter('expression', _('Variable Value'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.setVariable');
extension
.addAction(
'SaveState',
_('Save dialogue state'),
_(
'Save dialogue state. Use this to store the dialogue state into a variable, which can later be used for saving the game. That way player choices can become part of the game save.'
),
_('Save dialogue state to _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('globalvar', _('Global Variable'))
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.saveState');
extension
.addAction(
'LoadState',
_('Load dialogue state'),
_(
'Load dialogue state. Use this to restore dialogue state, if you have stored in a variable before with the "Save state" action.'
),
_('Load dialogue state from _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('globalvar', _('Global Variable'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.loadState');
extension
.addStrExpression(
'LineText',
_('Get the current dialogue line text'),
_('Returns the current dialogue line text'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getLineText');
extension
.addExpression(
'OptionsCount',
_('Get the number of options in an options line type'),
_('Get the number of options in an options line type'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getLineOptionsCount');
extension
.addStrExpression(
'Option',
_('Get the text of an option from an Options line type'),
_(
"Get the text of an option from an Options line type, using the option's Number. The numbers start from 0."
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('expression', _('Option Index Number'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getLineOption');
extension
.addStrExpression(
'HorizontalOptionsList',
_('Get a Horizontal list of options from the Options line type'),
_(
"Get the text of all available options from an Options line type as a horizontal list. You can also pass the selected option's cursor string, which by default is ->"
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Options Selection Cursor'), '', false)
.setDefaultValue('>')
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getLineOptionsTextHorizontal');
extension
.addStrExpression(
'VerticalOptionsList',
_('Get a Vertical list of options from the Options line type'),
_(
"Get the text of all available options from an Options line type as a vertical list. You can also pass the selected option's cursor string, which by default is ->"
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Options Selection Cursor'), '', false)
.setDefaultValue('>')
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getLineOptionsTextVertical');
extension
.addExpression(
'SelectedOptionIndex',
_('Get the number of the currently selected option'),
_(
'Get the number of the currently selected option. Use this to help you render the option selection marker at the right place.'
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getSelectedOption');
extension
.addStrExpression(
'ClippedLineText',
_('Get dialogue line text clipped'),
_(
'Get dialogue line text clipped by the typewriter effect. Use the "Scroll clipped text" action to control the typewriter effect.'
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getClippedLineText');
extension
.addStrExpression(
'BranchTitle',
_('Get the title of the current branch of the running dialogue'),
_('Get the title of the current branch of the running dialogue'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getBranchTitle');
extension
.addStrExpression(
'BranchTags',
_('Get the tags of the current branch of the running dialogue'),
_('Get the tags of the current branch of the running dialogue'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getBranchTags');
extension
.addStrExpression(
'BranchTag',
_('Get a tag of the current branch of the running dialogue via its index'),
_('Get a tag of the current branch of the running dialogue via its index'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('expression', _('Tag Index Number'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getBranchTag');
extension
.addStrExpression(
'CommandParameter',
_('Get the parameters of a command call'),
_(
'Get the parameters of a command call - <<command withParameter anotherParameter>>'
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('expression', _('parameter Index Number'), '', true)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getCommandParameter');
extension
.addExpression(
'CommandParametersCount',
_('Get the number of parameters in the currently passed command'),
_('Get the number of parameters in the currently passed command'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.commandParametersCount');
extension
.addStrExpression(
'TagParameter',
_(
'Get parameter from a Tag found by the branch contains tag condition'
),
_(
'Get parameter from a Tag found by the branch contains tag condition'
),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('expression', _('parameter Index Number'), '', true)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getTagParameter');
extension
.addStrExpression(
'VisitedBranchTitles',
_('Get a list of all visited branches'),
_('Get a list of all visited branches'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getVisitedBranchTitles');
extension
.addStrExpression(
'BranchText',
_('Get the full raw text of the current branch'),
_('Get the full raw text of the current branch'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getBranchText');
extension
.addExpression(
'Variable',
_('Get dialogue state value'),
_('Get dialogue state value'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Variable Name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.getVariable');
extension
.addCondition(
'IsCommandCalled',
_('Command is called'),
_(
'Check if a specific Command is called. If it is a <<command withParameter>>, you can even get the parameter with the CommandParameter expression.'
),
_('Command <<_PARAM0_>> is called'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Command String'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.isCommandCalled');
extension
.addCondition(
'IsDialogueLineType',
_('Dialogue line type'),
_(
'Check if the the current dialogue line line is one of the three existing types. Use this to set what logic is executed for each type.\nThe three types are as follows:\n- text: when displaying dialogue text.\n- options: when displaying [[branching/options]] for dialogue choices.\n-command: when <<commands>> are triggered by the dialogue data.'
),
_('The dialogue line is _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter(
'stringWithSelector',
_('type'),
'["text", "options", "command"]',
false
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.isDialogueLineType');
extension
.addCondition(
'IsDialogueRunning',
_('Dialogue is running'),
_(
'Check if the dialogue is running. Use this to for things like locking the player movement while speaking with a non player character.'
),
_('Dialogue is running'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.isRunning');
extension
.addCondition(
'HasBranch',
_('Dialogue has branch'),
_(
'Check if the dialogue has a branch with specified name. Use this to check if a dialogue branch exists in the loaded dialogue data.'
),
_('Dialogue has a branch named _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('Branch name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.hasDialogueBranch');
extension
.addCondition(
'HasSelectedOptionChanged',
_('Has selected option changed'),
_(
'Check if a selected option has changed when the current dialogue line type is options. Use this to detect when the player has selected another option, so you can re-draw where the selection arrow is.'
),
_('Selected option has changed'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.hasSelectedOptionChanged');
extension
.addCondition(
'CurrentBranchTitle',
_('Current dialogue branch title'),
_(
'Check if the current dialogue branch title is equal to a string. Use this to trigger game events when the player has visited a specific dialogue branch.'
),
_('The current dialogue branch title is _PARAM0_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('title name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.branchTitleIs');
extension
.addCondition(
'CurrentBranchContainsTag',
_('Current dialogue branch contains a tag'),
_(
'Check if the current dialogue branch contains a specific tag. Tags are an alternative useful way to <<commands>> to drive game logic with the dialogue data.'
),
_('The current dialogue branch contains a _PARAM0_ tag'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('tag name'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.branchContainsTag');
extension
.addCondition(
'WasBranchVisited',
_('Branch title has been visited before'),
_('Check if the current branch has been visited before'),
_('Branch title _PARAM0_ has been visited before'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('branch title'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.branchTitleHasBeenVisited');
extension
.addCondition(
'CompareDialogueStateVariable',
_('Compare dialogue state variable'),
_(
'Compare dialogue state variable. Use this to trigger game events via dialogue variables.'
),
_('Dialogue state variable _PARAM0_ is equal to _PARAM1_'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.addParameter('string', _('State variable'), '', false)
.addParameter('string', _('Equal to'), '', false)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.compareVariable');
extension
.addCondition(
'HasClippedTextScrollingCompleted',
_('Clipped text has completed scrolling'),
_(
'Check if the clipped text scrolling has completed. Use this to prevent the player from going to the next dialogue line before the typing effect has revealed the entire text.'
),
_('Clipped text has completed scrolling'),
_('Dialogue Tree (experimental)'),
'JsPlatform/Extensions/yarn24.png',
'JsPlatform/Extensions/yarn32.png'
)
.getCodeExtraInformation()
.setFunctionName('gdjs.dialogueTree.hasClippedScrollingCompleted');
return extension;
},
runExtensionSanityTests: function(gd, extension) {
return [];
},
};

11
Extensions/DialogueTree/bondage.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,679 @@
/**
* @memberof gdjs
* @class dialogueTree
* @static
* @private
*/
gdjs.dialogueTree = {};
gdjs.dialogueTree.runner = new bondage.Runner();
/**
* Load the Dialogue Tree data of the game. Initialize The Dialogue Tree, so as it can be used in the game.
* @param {gdjs.Variable} sceneVar The variable to load the Dialogue tree data from. The data is a JSON string, created by Yarn.
* @param {string} startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
*/
gdjs.dialogueTree.loadFromSceneVariable = function(
sceneVar,
startDialogueNode
) {
this.runner = gdjs.dialogueTree.runner;
try {
this.yarnData = JSON.parse(sceneVar.getAsString());
this.runner.load(this.yarnData);
if (startDialogueNode && startDialogueNode.length > 0) {
gdjs.dialogueTree.startFrom(startDialogueNode);
}
} catch (e) {
console.error(e);
}
};
/**
* Load the Dialogue Tree data from a JSON resource.
*
* @param {gdjs.RuntimeScene} runtimeScene The scene where the dialogue is running.
* @param {string} jsonResourceName The JSON resource where to load the Dialogue Tree data from. The data is a JSON string usually created with [Yarn Dialogue Editor](https://github.com/InfiniteAmmoInc/Yarn).
* @param {string} startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
*/
gdjs.dialogueTree.loadFromJsonFile = function(
runtimeScene,
jsonResourceName,
startDialogueNode
) {
runtimeScene
.getGame()
.getJsonManager()
.loadJson(jsonResourceName, function(error, content) {
if (error) {
console.error('An error happened while loading JSON resource:', error);
} else {
if (!content) return;
gdjs.dialogueTree.yarnData = content;
try {
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
} catch (error) {
console.error(
'An error happened while loading parsing the dialogue tree data:',
error
);
}
if (startDialogueNode && startDialogueNode.length > 0) {
gdjs.dialogueTree.startFrom(startDialogueNode);
}
}
});
};
/**
* Stop the currently running dialogue
*/
gdjs.dialogueTree.stopRunningDialogue = function() {
if (this.dialogueIsRunning) this.dialogueIsRunning = false;
if (this.dialogueData) this.dialogueData = null;
this.dialogueText = '';
this.clipTextEnd = 0;
};
/**
* Check if the Dialogue Tree is currently parsing data.
* For example, you can do things like disabling player movement while talking to a NPC.
*/
gdjs.dialogueTree.isRunning = function() {
if (
this.dialogueIsRunning &&
!this.dialogueData &&
this.dialogueText &&
this.clipTextEnd >= this.dialogueText.length
) {
this.dialogueIsRunning = false;
}
return this.dialogueIsRunning;
};
/**
* Scroll the clipped text. This can be combined with a timer and user input to control how fast the dialogue line text is scrolling.
*/
gdjs.dialogueTree.scrollClippedText = function() {
if (this.pauseScrolling || !this.dialogueIsRunning) return;
if (this.dialogueText) {
this.clipTextEnd += 1;
}
};
/**
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
*/
gdjs.dialogueTree.completeClippedTextScrolling = function() {
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText)
return;
this.clipTextEnd = this.dialogueText.length;
};
/**
* Check if text scrolling has completed.
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
*/
gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
if (this.dialogueData && this.dialogueText.length) {
return this.clipTextEnd >= this.dialogueText.length;
}
return false;
};
/**
* Get the current dialogue line with a scrolling effect (recommended).
* Used with the scrollClippedText to achieve a classic scrolling text, as well as any <<wait>> effects to pause scrolling.
*/
gdjs.dialogueTree.getClippedLineText = function() {
return this.dialogueText.length
? this.dialogueText.substring(0, this.clipTextEnd)
: '';
};
/**
* Get the current complete dialogue line without using any scrolling effects.
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
*/
gdjs.dialogueTree.getLineText = function() {
this.completeClippedTextScrolling();
return this.dialogueText.length ? this.dialogueText : '';
};
/**
* Get the number of command parameters in a command with parameters that has been caught by a isCommandCalled condition
*/
gdjs.dialogueTree.commandParametersCount = function() {
if (this.commandParameters && this.commandParameters.length > 1) {
return this.commandParameters.length - 1;
}
return 0;
};
/**
* Get a command parameter in any command with parameters that has been caught by a isCommandCalled condition
* @param {number} paramIndex The index of the parameter to get.
*/
gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
if (
this.commandParameters &&
this.commandParameters.length >= paramIndex + 1
) {
var returnedParam = this.commandParameters[paramIndex + 1];
return returnedParam ? returnedParam : '';
}
return '';
};
/**
* Catch <<commands>> and <<commands with parameters>> from the current Dialogue Line.
* You can trigger custom logic that relate to the story you are telling during the dialogue.
*
* @param {string} command The command you want to check for being called. Write it without the `<<>>`.
*/
gdjs.dialogueTree.isCommandCalled = function(command) {
var commandCalls = gdjs.dialogueTree.commandCalls;
var clipTextEnd = gdjs.dialogueTree.clipTextEnd;
var dialogueText = gdjs.dialogueTree.dialogueText;
if (this.pauseScrolling || !commandCalls) return false;
return this.commandCalls.some(function(call, index) {
if (clipTextEnd < call.time) return false;
if (call.cmd === 'wait' && clipTextEnd !== dialogueText.length) {
gdjs.dialogueTree.pauseScrolling = true;
setTimeout(function() {
gdjs.dialogueTree.pauseScrolling = false;
commandCalls.splice(index, 1);
}, parseInt(call.params[1], 10));
}
if (call.cmd === command) {
gdjs.dialogueTree.commandParameters = call.params;
commandCalls.splice(index, 1);
return true;
}
});
};
/**
* Internal method to allow for capping option selection.
* @private
*/
gdjs.dialogueTree._normalizedOptionIndex = function(optionIndex) {
if (optionIndex >= this.options.length) optionIndex = this.options.length - 1;
if (optionIndex < 0) optionIndex = 0;
return optionIndex;
};
/**
* Internal method to allow for cycling option selection.
* @private
*/
gdjs.dialogueTree._cycledOptionIndex = function(optionIndex) {
if (optionIndex >= this.options.length) optionIndex = 0;
if (optionIndex < 0) optionIndex = this.options.length - 1;
return optionIndex;
};
/**
* Get the text of an option the player can select.
* Used with getLineOptionsCount to render options for the player when a line of the Options type is parsed
* @param {number} optionIndex The index of the option you want to get
*/
gdjs.dialogueTree.getLineOption = function(optionIndex) {
if (!this.options.length) return [];
optionIndex = gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
return this.options[optionIndex];
};
/**
* Get the text of the options the player can select, along with the selection cursor.
* @param {string} optionSelectionCursor The string used to draw the currently selected option's cursor
* @param {boolean} addNewLine when true each option is rendered on a new line.
*/
gdjs.dialogueTree.getLineOptionsText = function(
optionSelectionCursor,
addNewLine
) {
if (!this.options.length) return '';
var textResult = '';
this.options.forEach(function(optionText, index) {
if (index === gdjs.dialogueTree.selectedOption) {
textResult += optionSelectionCursor;
} else {
textResult += optionSelectionCursor.replace(/.*/g, ' ');
}
textResult += optionText;
if (addNewLine) textResult += '\n';
});
return textResult;
};
gdjs.dialogueTree.getLineOptionsTextHorizontal = function(
optionSelectionCursor
) {
return this.getLineOptionsText(optionSelectionCursor, false);
};
gdjs.dialogueTree.getLineOptionsTextVertical = function(optionSelectionCursor) {
return this.getLineOptionsText(optionSelectionCursor, true);
};
/**
* Get the number of options that are presented to the player, during the parsing of an Options type line.
* @returns {number} The number of options
*/
gdjs.dialogueTree.getLineOptionsCount = function() {
if (this.options.length) {
return this.optionsCount;
}
return 0;
};
/**
* Confirm the currently selected option, during the parsing of an Options type line.
*
* This will advance the dialogue tree to the dialogue branch was selected by the player.
*/
gdjs.dialogueTree.confirmSelectOption = function() {
if (
this.dialogueData.select &&
!this.selectedOptionUpdated &&
this.selectedOption !== -1
) {
this.commandCalls = [];
try {
this.dialogueData.select(this.selectedOption);
this.dialogueData = this.dialogue.next().value;
gdjs.dialogueTree.goToNextDialogueLine();
} catch (error) {
console.error(
`An error happened when trying to access the dialogue branch!`,
error
);
}
}
};
/**
* Select next option during Options type line parsing. Hook this to your game input.
*/
gdjs.dialogueTree.selectNextOption = function() {
if (this.dialogueData.select) {
this.selectedOption += 1;
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
this.selectedOption
);
this.selectedOptionUpdated = true;
}
};
/**
* Select previous option during Options type line parsing. Hook this to your game input.
*/
gdjs.dialogueTree.selectPreviousOption = function() {
if (this.dialogueData.select) {
this.selectedOption -= 1;
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
this.selectedOption
);
this.selectedOptionUpdated = true;
}
};
/**
* Select option by index during Options type line parsing.
* @param {number} optionIndex The index of the option to select
*/
gdjs.dialogueTree.selectOption = function(optionIndex) {
if (this.dialogueData.select) {
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
this.selectedOption
);
this.selectedOptionUpdated = true;
}
};
/**
* Get the currently selected option
* @returns {number} The index of the currently selected option
*/
gdjs.dialogueTree.getSelectedOption = function() {
if (this.dialogueData.select) {
return this.selectedOption;
}
return 0;
};
/**
* Check when the player has changed option selection since the last call to this function.
*
* Can be used to re-render your displayed dialogue options when needed.
*
* @returns {boolean} true if the selected option was updated since the last call to this function
*/
gdjs.dialogueTree.hasSelectedOptionChanged = function() {
if (this.selectedOptionUpdated) {
this.selectedOptionUpdated = false;
if (this.selectedOption === -1) this.selectedOption = 0;
return true;
}
return false;
};
/**
* Check the type of the Dialogue Line that is being displayed to the player at the moment.
*
* There are three types:
* - text - regular dialogue text is being parsed at the moment
* - options - the player has reached a branching choise moment where they must select one of multiple options
* - command - a <<command>> was called in the background, that can be used to trigger game events, but will not be displayed in the dialogue box.
*
* @param {string} type The type you want to check for ( one of the three above )
*/
gdjs.dialogueTree.isDialogueLineType = function(type) {
if (
this.commandCalls &&
this.commandCalls.some(function(call) {
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
})
) {
return !this.pauseScrolling;
}
return this.dialogueIsRunning ? this.dialogueDataType === type : false;
};
/**
* Check if a branch exists. It is also used internaly whenever you use the start from action.
* @param {string} branchName The Dialogue Branch name you want to check.
*/
gdjs.dialogueTree.hasDialogueBranch = function(branchName) {
return (
this.runner &&
this.runner.yarnNodes &&
Object.keys(this.runner.yarnNodes).some(function(node) {
return node === branchName;
})
);
};
/**
* Start parsing dialogue from a specified Dialogue tree branch.
* Can be used if you want to store multiple dialogues inside a single Dialogue tree data set.
* @param {string} startDialogueNode The Dialogue Branch name you want to start parsing from.
*/
gdjs.dialogueTree.startFrom = function(startDialogueNode) {
this.runner = gdjs.dialogueTree.runner;
if (!this.hasDialogueBranch(startDialogueNode)) return;
this.optionsCount = 0;
this.options = [];
this.dialogueBranchTitle = '';
this.dialogueBranchBody = '';
this.dialogueBranchTags = [];
this.tagParameters = [];
this.dialogue = this.runner.run(startDialogueNode);
this.dialogueData = null;
this.dialogueDataType = '';
this.dialogueText = '';
this.clipTextEnd = 0;
this.commandCalls = [];
this.commandParameters = [];
this.pauseScrolling = false;
this.dialogueData = this.dialogue.next().value;
this.dialogueIsRunning = true;
gdjs.dialogueTree.goToNextDialogueLine();
};
/**
* Internal methods to check the type of a Dialogue Line
*/
gdjs.dialogueTree._isLineTypeText = function() {
return this.dialogueData instanceof bondage.TextResult;
};
gdjs.dialogueTree._isLineTypeOptions = function() {
return this.dialogueData instanceof bondage.OptionsResult;
};
gdjs.dialogueTree._isLineTypeCommand = function() {
return this.dialogueData instanceof bondage.CommandResult;
};
/**
* This is the main lifecycle function.It runs once only when the user is advancing the dialogue to the next line.
* Progress Dialogue to the next line. Hook it to your game input.
* Note that this action can be influenced by any <<wait>> commands, but they work only if you have at least one isCommandCalled condition.
*/
gdjs.dialogueTree.goToNextDialogueLine = function() {
if (this.pauseScrolling || !this.dialogueIsRunning) return;
this.optionsCount = 0;
this.selectedOption = -1;
this.selectedOptionUpdated = false;
if (gdjs.dialogueTree._isLineTypeText()) {
if (
this.dialogueDataType === 'options' ||
this.dialogueDataType === 'text' ||
!this.dialogueDataType
) {
this.clipTextEnd = 0;
this.dialogueText = this.dialogueData.text;
this.commandCalls = [];
} else {
this.dialogueText += this.dialogueData.text;
}
this.dialogueDataType = 'text';
this.dialogueBranchTags = this.dialogueData.data.tags;
this.dialogueBranchTitle = this.dialogueData.data.title;
this.dialogueBranchBody = this.dialogueData.data.body;
this.dialogueData = this.dialogue.next().value;
} else if (gdjs.dialogueTree._isLineTypeOptions()) {
this.dialogueDataType = 'options';
this.optionsCount = this.dialogueData.options.length;
this.options = this.dialogueData.options;
this.selectedOptionUpdated = true;
} else if (gdjs.dialogueTree._isLineTypeCommand()) {
this.dialogueDataType = 'command';
var command = this.dialogueData.text.split(' ');
// If last command was to wait, increase time by one
var offsetTime =
this.commandCalls.length &&
this.commandCalls[this.commandCalls.length - 1].cmd === 'wait'
? 1
: 0;
this.commandCalls.push({
cmd: command[0],
params: command,
time: this.dialogueText.length + offsetTime,
});
this.dialogueData = this.dialogue.next().value;
gdjs.dialogueTree.goToNextDialogueLine();
} else {
this.dialogueDataType = 'unknown';
}
if (gdjs.dialogueTree._isLineTypeCommand()) {
this.dialogueDataType = 'command';
gdjs.dialogueTree.goToNextDialogueLine();
}
};
/**
* Get the current Dialogue Tree branch title.
* @returns {string} The current branch title.
*/
gdjs.dialogueTree.getBranchTitle = function() {
if (this.dialogueIsRunning) {
return this.dialogueBranchTitle;
}
return '';
};
/**
* Check if the currently parsed Dialogue branch title is a query.
* @param {string} title The Dialogue Branch name you want to check for.
*/
gdjs.dialogueTree.branchTitleIs = function(title) {
if (this.dialogueIsRunning) {
return this.dialogueBranchTitle === title;
}
return false;
};
/**
* Get all the branch tags from the current Dialogue branch as a string. Useful for debugging.
* @returns {string} The current branch tags, separated by a comma.
*/
gdjs.dialogueTree.getBranchTags = function() {
if (this.dialogueIsRunning) {
return this.dialogueBranchTags.join(',');
}
return '';
};
/**
* Get one of the current Dialogue branch tags via index.
* @param {number} index The index of the Dialogue Branch tag you want to get.
* @returns {string} The branch tag at the specified index, or an empty string if not found.
*/
gdjs.dialogueTree.getBranchTag = function(index) {
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
if (index > this.dialogueBranchTags.length - 1)
index = this.dialogueBranchTags.length - 1;
return this.dialogueBranchTags[index];
}
return '';
};
/**
* Check if the current Dialogue branch contains a specific tag.
* @param {string} query The name of the Dialogue Branch tag you want to check.
*/
gdjs.dialogueTree.branchContainsTag = function(query) {
this.tagParameters = [];
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
return this.dialogueBranchTags.some(function(tag) {
var splitTag = tag.match(/([^\(]+)\(([^\)]+)\)/i);
gdjs.dialogueTree.tagParameters = splitTag ? splitTag[2].split(',') : [];
return splitTag ? splitTag[1] === query : tag === query;
});
}
return false;
};
/**
* Get any tag(parameter,anotherParameter) from a tag captured by the branchContainsTag Condition
* @param {number} paramIndex The index of the tag parameter you want to get.
* Leaving this empty will result in retrieving the first parameter.
*/
gdjs.dialogueTree.getTagParameter = function(paramIndex) {
if (this.dialogueIsRunning && this.tagParameters.length >= paramIndex) {
var returnedParam = this.tagParameters[paramIndex];
return returnedParam ? returnedParam : '';
}
return '';
};
/**
* Get a list of all the titles of visited by the player Branches. Useful for debugging.
*/
gdjs.dialogueTree.getVisitedBranchTitles = function() {
if (this.dialogueIsRunning) {
return Object.keys(this.runner.visited).join(',');
}
return '';
};
/**
* Check if a player has visited a Dialogue Branch in the past.
* @param {string} title The title of the branch to check for.
* Leaving this empty will check if the current branch title has been visited in the past.
*/
gdjs.dialogueTree.branchTitleHasBeenVisited = function(title) {
if (!title) title = this.dialogueBranchTitle;
return (
Object.keys(this.runner.visited).includes(title) &&
this.runner.visited[title]
);
};
/**
* Get the entire unparsed text of the current Dialogue Branch
*/
gdjs.dialogueTree.getBranchText = function() {
if (this.dialogueIsRunning) {
return this.dialogueBranchBody;
}
return '';
};
/**
* Get the value of a variable that was created by the Dialogue parses.
* @param {string} key The name of the variable you want to get the value of
*/
gdjs.dialogueTree.getVariable = function(key) {
if (this.dialogueIsRunning && key in this.runner.variables.data) {
return this.runner.variables.data[key];
}
return '';
};
/**
* Check if a specific variable created by the Dialogue parses exists and is equal to a specific value.
* @param {string} key The name of the variable you want to check the value of
* @param {string} value The value you want to check against
*/
gdjs.dialogueTree.compareVariable = function(key, value) {
if (this.dialogueIsRunning && key in this.runner.variables.data) {
return this.runner.variables.data[key].toString() === value;
}
return false;
};
/**
* Set a specific variable created by the Dialogue parser to a specific value.
* @param {string} key The name of the variable you want to set the value of
* @param {string} value The value you want to set
*/
gdjs.dialogueTree.setVariable = function(key, value) {
if (this.dialogueIsRunning && this.runner.variables.data) {
this.runner.variables.data[key] = value;
}
};
/**
* Store the current State of the Dialogue Parser in a specified variable.
* Can be used to implement persistence in dialogue through your game's Load/Save function.
* That way you can later load all the dialogue choices the player has made.
* @param {gdjs.Variable} outputVariable The variable where to store the State
*/
gdjs.dialogueTree.saveState = function(outputVariable) {
const dialogueState = {
variables: gdjs.dialogueTree.runner.variables.data,
visited: gdjs.dialogueTree.runner.visited,
};
gdjs.evtTools.network._objectToVariable(dialogueState, outputVariable);
};
/**
* Load the current State of the Dialogue Parser from a specified variable.
* Can be used to implement persistence in dialogue through your game's Load/Save function.
* That way you can later load all the dialogue choices the player has made.
* @param {gdjs.Variable} inputVariable The variable where to load the State from.
*/
gdjs.dialogueTree.loadState = function(inputVariable) {
const jsonData = gdjs.evtTools.network.variableStructureToJSON(inputVariable);
try {
const loadedState = JSON.parse(
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
);
gdjs.dialogueTree.runner.visited = loadedState.visited;
gdjs.dialogueTree.runner.variables.data = loadedState.variables;
} catch (e) {
console.error(e);
}
};

View File

@@ -0,0 +1,81 @@
[
{
"title": "Numeric",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = -123.4>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},{
"title": "NumericExpression",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = (1 + 2) * -3 + 4.3>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "String",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = \"Variable String\">>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "StringExpression",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = \"Variable String\" + \" Appended\">>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Boolean",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = true>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "BooleanExpression",
"tags": "Tag",
"body": "Test Line\n<<set $testvar = (true || false) && (false || !false)>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Variable",
"tags": "Tag",
"body": "Test Line\n<<set $firstvar = \"First variable string\">><<set $secondvar = $firstvar>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "VariableExpression",
"tags": "Tag",
"body": "Test Line\n<<set $firstvar = 100>><<set $secondvar = -4.3 + $firstvar>>\nTest Line After",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
}
]

View File

@@ -0,0 +1,52 @@
[
{
"title": "BasicCommands",
"tags": "Tag",
"body": "<<command>>text in between commands<<command with space>> <<callFunction()>> <<callFunctionWithParam(\"test\",true,1,12.5,[2,3])>>",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "StopCommand",
"tags": "Tag",
"body": "First line\n<<stop>>\nThis shouldn't show",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "FunctionConditional",
"tags": "Tag",
"body": "First line\n<<if testfunc(\"firstarg\")>>This shouldn't show<<elseif testfunc(\"firstarg\", \"secondarg\")>>This should show<<endif>>After both",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "VisitedFunction",
"tags": "Tag",
"body": "<<if visited(\"VisitedFunctionStart\")>>you have visited VisitedFunctionStart!<<endif>><<if visited(\"SomeOtherNode\")>>You have not visited SomeOtherNode!<<endif>>",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "VisitedFunctionStart",
"tags": "Tag",
"body": "Hello[[VisitedFunction]]",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
}
]

View File

@@ -0,0 +1,93 @@
[
{
"title": "Start",
"tags": "Tag",
"body": "What are you?\n-> A troll\n <<set $troll to true >>\n-> A nice person\n <<set $troll to false >>\n[[Objective]]",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Objective",
"tags": "Tag",
"body": "<<if $repeat >= 3>>\nBye...\n<<else>>\nIs your objective clear?\n[[Yes|Objective.Yes]]\n[[No|Objective.No]]\n<<if $troll == true>>\n[[Maybe|Objective.Maybe]]\n<<endif>>\n<<endif>>\n",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Objective.No",
"tags": "Tag",
"body": "Blah blah blah blah\n[[Objective]]",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Objective.Yes",
"tags": "Tag",
"body": "Good let's start the mission.",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Objective.Maybe",
"tags": "Tag",
"body": "Are you trolling me?\n[[Objective]]",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "BasicIf",
"tags": "Tag",
"body": "<<set $testvar = 321>>\nText before\n<<if $testvar == 321>>Inside if<<endif>>Text after",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "BasicIfElse",
"tags": "Tag",
"body": "<<set $testvar = 321>>\nText before\n<<if $testvar == 123>>Inside if<<else>>Inside else<<endif>>Text after",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "BasicIfElseIf",
"tags": "Tag",
"body": "<<set $testvar = 321>>\nText before\n<<if $testvar == 123>>Inside if<<elseif $testvar == 321>>Inside elseif<<endif>>Text after",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "BasicIfElseIfElse",
"tags": "Tag",
"body": "<<set $testvar = 321>>\nText before\n<<if $testvar == 123>>Inside if<<elseif $testvar == 1>>Inside elseif<<else>>Inside else<<endif>>Text after",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
}
]

View File

@@ -0,0 +1,72 @@
[
{
"title": "OneNode",
"tags": "Tag",
"body": "This is a test line",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "ThreeNodes",
"tags": "",
"body": "This is a test line\nThis is another test line[[Option1]]\n[[Option2]]",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Option1",
"tags": "",
"body": "This is Option1's test line",
"position": {
"x": 770,
"y": 84
},
"colorID": 0
},
{
"title": "Option2",
"tags": "",
"body": "This is Option2's test line",
"position": {
"x": 774,
"y": 404
},
"colorID": 0
},
{
"title": "NamedLink",
"tags": "",
"body": "This is a test line\nThis is another test line\n[[First choice|Option1]]\n[[Second choice|Option2]]",
"position": {
"x": 774,
"y": 404
},
"colorID": 0
},
{
"title": "OneLinkPassthrough",
"tags": "",
"body": "First test line\n[[Option1]]",
"position": {
"x": 774,
"y": 404
},
"colorID": 0
},
{
"title": "LinkAfterShortcuts",
"tags": "",
"body": "First test line\n-> Shortcut 1\n\tThis is the first shortcut\n-> Shortcut 2\n\tThis is the second shortcut\n[[First link|Option1]][[Second link|Option2]]",
"position": {
"x": 774,
"y": 404
},
"colorID": 0
}
]

View File

@@ -0,0 +1,32 @@
[
{
"title": "NonNested",
"tags": "Tag",
"body": "This is a test line\n-> Option 1\n\tThis is the first option\n-> Option 2\n\tThis is the second option\nThis is after both options",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Nested",
"tags": "Tag",
"body": "text\n-> shortcut1\n\tText1\n\t-> nestedshortcut1\n\t\tNestedText1\n\t-> nestedshortcut2\n\t\tNestedText2\n-> shortcut2\n\tText2\nmore text",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
},
{
"title": "Conditional",
"tags": "Tag",
"body": "This is a test line\n-> Option 1\n\tThis is the first option\n-> Option 2 <<if 1 == 0>>\n\tThis is the second option\n-> Option 3\n\tThis is the third option\nThis is after both options",
"position": {
"x": 449,
"y": 252
},
"colorID": 0
}
]

View File

@@ -401,7 +401,9 @@ module.exports = {
registerEditorConfigurations: function(objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'MyDummyExtension::DummyObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor()
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
helpPagePath: '/extensions/extend-gdevelop',
})
);
},
/**

View File

@@ -30,7 +30,7 @@ module.exports = {
"SavePlayerData",
_("Save player data"),
_(
"Save the content of the given variable in the player data, stored on Facebook Instant Games servers"
"Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers"
),
_(
"Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)"
@@ -40,7 +40,7 @@ module.exports = {
"JsPlatform/Extensions/facebookicon16.png"
)
.addParameter("string", 'Data key name (e.g: "Lives")', "", false)
.addParameter("scenevar", "Variable with the content to save", "", false)
.addParameter("scenevar", "Scene variable with the content to save", "", false)
.addParameter(
"scenevar",
_("Variable where to store the success message (optional)"),
@@ -49,7 +49,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -80,7 +80,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -125,7 +125,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -173,7 +173,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -233,7 +233,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -255,7 +255,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -299,7 +299,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
@@ -321,7 +321,7 @@ module.exports = {
)
.addParameter(
"scenevar",
_("Variable where to error message (optional, if an error occurs)"),
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)

View File

@@ -172,8 +172,8 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
extension
.AddAction("SerializeToVariable",
_("Save an inventory in a variable"),
_("Save all the items of the inventory in a variable, so that "
_("Save an inventory in a scene variable"),
_("Save all the items of the inventory in a scene variable, so that "
"it can be restored later."),
_("Save inventory _PARAM1_ in variable _PARAM2_"),
_("Inventories/Variables"),
@@ -188,8 +188,8 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
extension
.AddAction("UnserializeFromVariable",
_("Load an inventory from a variable"),
_("Load the content of the inventory from a variable."),
_("Load an inventory from a scene variable"),
_("Load the content of the inventory from a scene variable."),
_("Load inventory _PARAM1_ from variable _PARAM2_"),
_("Inventories/Variables"),
"CppPlatform/Extensions/Inventoryicon24.png",

View File

@@ -204,7 +204,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.SetManipulatedType("number");
obj.AddCondition("ParticleSize1",
_("SIze, parameter 1"),
_("Size, parameter 1"),
_("Test parameter 1 of the size of particles"),
_("Parameter 1 of the size of _PARAM0_ is _PARAM1__PARAM2_"),
_("Common"),

View File

@@ -608,6 +608,8 @@ gdjs.PlatformerObjectRuntimeBehavior.prototype._updatePotentialCollidingObjects
var o2w = obj2.getWidth();
var o2h = obj2.getHeight();
// 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 = this.owner.getDrawableX()+this.owner.getCenterX()-(obj2.getDrawableX()+obj2.getCenterX());
var y = this.owner.getDrawableY()+this.owner.getCenterY()-(obj2.getDrawableY()+obj2.getCenterY());
var obj2BoundingRadius = Math.sqrt(o2w*o2w+o2h*o2h)/2.0;

View File

@@ -50,6 +50,8 @@ gdjs.PlatformObjectsManager.prototype.removePlatform = function(platformBehavior
* @return An array with all platforms near the object.
*/
gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround = function(object, maxMovementLength, 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 ow = object.getWidth();
var oh = object.getHeight();
var x = object.getDrawableX()+object.getCenterX();

View File

@@ -15,7 +15,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"PrimitiveDrawing",
_("Primitive drawing"),
_("This Extension allows you to draw shapes and manipulate images."),
"Florian Rival",
"Florian Rival and Aurélien Vivet",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/shape_painter");
@@ -31,25 +31,25 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
obj.AddAction("Rectangle",
_("Rectangle"),
_("Draw a rectangle on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a "
"rectangle with _PARAM0_"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a rectangle "
"with _PARAM0_"),
_("Drawing"),
"res/actions/rectangle24.png",
"res/actions/rectangle.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Top left side: X Position"))
.AddParameter("expression", _("Top left side : Y Position"))
.AddParameter("expression", _("Bottom right side : X Position"))
.AddParameter("expression", _("Bottom right side : Y Position"))
.AddParameter("expression", _("Top left side: X position"))
.AddParameter("expression", _("Top left side: Y position"))
.AddParameter("expression", _("Bottom right side: X position"))
.AddParameter("expression", _("Bottom right side: Y position"))
.SetFunctionName("DrawRectangle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("Circle",
_("Circle"),
_("Draw a circle on screen"),
_("Draw at _PARAM1_;_PARAM2_ a circle of radius _PARAM3_ with "
"_PARAM0_"),
_("Draw at _PARAM1_;_PARAM2_ a circle of radius _PARAM3_ "
"with _PARAM0_"),
_("Drawing"),
"res/actions/circle24.png",
"res/actions/circle.png")
@@ -57,35 +57,295 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("Y position of center"))
.AddParameter("expression", _("Radius ( in pixels )"))
.AddParameter("expression", _("Radius (in pixels)"))
.SetFunctionName("DrawCircle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("Line",
_("Line"),
_("Draw a line on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line "
"(thickness : _PARAM5_) with _PARAM0_"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line (thickness: _PARAM5_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/line24.png",
"res/actions/line.png")
.SetHidden()
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.AddParameter("expression", _("X position of end point"))
.AddParameter("expression", _("Y position of end point"))
.AddParameter("expression", _("Thickness (in pixels)"))
.SetFunctionName("DrawLine")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("LineV2",
_("Line"),
_("Draw a line on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line (thickness: _PARAM5_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/line24.png",
"res/actions/line.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X Position of start point"))
.AddParameter("expression", _("Y Position of start point"))
.AddParameter("expression", _("X Position of end point"))
.AddParameter("expression", _("Y Position of end point"))
.AddParameter("expression", _("Thickness ( in pixels )"))
.SetFunctionName("DrawLine")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.AddParameter("expression", _("X position of end point"))
.AddParameter("expression", _("Y position of end point"))
.AddParameter("expression", _("Thickness (in pixels)"))
.SetFunctionName("drawLineV2")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("Ellipse",
_("Ellipse"),
_("Draw an ellipse on screen"),
_("Draw at _PARAM1_;_PARAM2_ an ellipse of width _PARAM3_ and height _PARAM4_ "
"with _PARAM0_"),
_("Drawing"),
"res/actions/ellipse24.png",
"res/actions/ellipse.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("Y position of center"))
.AddParameter("expression", _("The width of the ellipse"))
.AddParameter("expression", _("The height of the ellipse"))
.SetFunctionName("DrawEllipse")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("RoundedRectangle",
_("Rounded rectangle"),
_("Draw a rounded rectangle on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a rounded rectangle (radius: _PARAM5_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/roundedRectangle24.png",
"res/actions/roundedRectangle.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Top left side: X position"))
.AddParameter("expression", _("Top left side: Y position"))
.AddParameter("expression", _("Bottom right side: X position"))
.AddParameter("expression", _("Bottom right side: Y position"))
.AddParameter("expression", _("Radius (in pixels)"))
.SetFunctionName("DrawRoundedRectangle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("Star",
_("Star"),
_("Draw a star on screen"),
_("Draw at _PARAM1_;_PARAM2_ a star with _PARAM3_ points and radius: _PARAM4_ (inner radius: _PARAM5_, rotation: _PARAM6_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/star24.png",
"res/actions/star.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("Number of points of the star (minimum: 2)"))
.AddParameter("expression", _("Radius (in pixels)"))
.AddParameter("expression", _("Inner radius (in pixels, half radius by default)"))
.AddParameter("expression", _("Rotation (in degrees)"))
.SetFunctionName("DrawStar")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("Arc",
_("Arc"),
_("Draw an arc on screen. If \"Close path\" is set to yes, a line will be drawn between the start and end point of the arc, closing the shape."),
_("Draw at _PARAM1_;_PARAM2_ an arc with radius: _PARAM3_, start angle: _PARAM4_, end angle: _PARAM5_ (anticlockwise: _PARAM6_, close path: _PARAM7_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/arc24.png",
"res/actions/arc.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("Y position of center"))
.AddParameter("expression", _("Radius (in pixels)"))
.AddParameter("expression", _("Start angle of the arc (in degrees)"))
.AddParameter("expression", _("End angle of the arc (in degrees)"))
.AddParameter("yesorno", _("Anticlockwise"))
.AddParameter("yesorno", _("Close path"))
.SetFunctionName("DrawArc")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("BezierCurve",
_("Bezier curve"),
_("Draw a bezier curve on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM7_;_PARAM8_ a bezier curve (first control point: _PARAM3_;_PARAM4_, second control point: _PARAM5_;_PARAM6_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/bezierCurve24.png",
"res/actions/bezierCurve.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.AddParameter("expression", _("First control point x"))
.AddParameter("expression", _("First control point y"))
.AddParameter("expression", _("Second Control point x"))
.AddParameter("expression", _("Second Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawBezierCurve")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("QuadraticCurve",
_("Quadratic curve"),
_("Draw a quadratic curve on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM5_;_PARAM6_ a quadratic curve (control point: _PARAM3_;_PARAM4_) "
"with _PARAM0_"),
_("Drawing"),
"res/actions/quadraticCurve24.png",
"res/actions/quadraticCurve.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.AddParameter("expression", _("Control point x"))
.AddParameter("expression", _("Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawQuadraticCurve")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("BeginFillPath",
_("Begin fill path"),
_("Begin to draw a simple one-color fill. Subsequent actions, such as \"Path line\" (in the Advanced category) can be used to draw. Be sure to use \"End fill path\" action when you're done drawing the shape."),
_("Begins drawing filling of an advanced path "
"with _PARAM0_"),
_("Advanced"),
"res/actions/beginFillPath24.png",
"res/actions/beginFillPath.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Start drawing x"))
.AddParameter("expression", _("Start drawing y"))
.SetFunctionName("beginFillPath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("EndFillPath",
_("End fill path"),
_("Finish the filling drawing in an advanced path"),
_("Finish the filling drawing in an advanced path "
"with _PARAM0_"),
_("Advanced"),
"res/actions/endFillPath24.png",
"res/actions/endFillPath.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("endFillPath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("MovePathTo",
_("Move path drawing position"),
_("Move the drawing position for the current path"),
_("Move the drawing position of the path to _PARAM1_;_PARAM2_ "
"with _PARAM0_"),
_("Advanced"),
"res/actions/position24.png",
"res/actions/position.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.SetFunctionName("drawPathMoveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("PathLineTo",
_("Path line"),
_("Add to a path a line to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
_("Add to a path a line to the position _PARAM1_;_PARAM2_ "
"with _PARAM0_"),
_("Advanced"),
"res/actions/line24.png",
"res/actions/line.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.SetFunctionName("drawPathLineTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("PathBezierCurveTo",
_("Path bezier curve"),
_("Add to a path a bezier curve to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
_("Add to a path a bezier curve to the position _PARAM5_;_PARAM6_ (first control point: _PARAM1_;_PARAM2_, second control point: _PARAM3_;_PARAM4_) "
"with _PARAM0_"),
_("Advanced"),
"res/actions/bezierCurve24.png",
"res/actions/bezierCurve.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("First control point x"))
.AddParameter("expression", _("First control point y"))
.AddParameter("expression", _("Second Control point x"))
.AddParameter("expression", _("Second Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawPathBezierCurveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("PathArc",
_("Path arc"),
_("Add to a path an arc to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
_("Add to a path an arc at the position _PARAM1_;_PARAM2_ (radius: _PARAM3_, start angle: _PARAM4_, end angle: _PARAM5_, anticlockwise: _PARAM6_) "
"with _PARAM0_"),
_("Advanced"),
"res/actions/arc24.png",
"res/actions/arc.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Center x of circle"))
.AddParameter("expression", _("Center y of circle"))
.AddParameter("expression", _("Radius (in pixels)"))
.AddParameter("expression", _("Start angle"))
.AddParameter("expression", _("End angle"))
.AddParameter("yesorno", _("Anticlockwise"))
.SetFunctionName("drawPathArc")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("PathQuadraticCurveTo",
_("Path quadratic curve"),
_("Add to a path a quadratic curve to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
_("Add to a path a quadratic curve to the position _PARAM3_;_PARAM4_ (control point: _PARAM1_;_PARAM2_) "
"with _PARAM0_"),
_("Advanced"),
"res/actions/quadraticCurve24.png",
"res/actions/quadraticCurve.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Control point x"))
.AddParameter("expression", _("Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawPathQuadraticCurveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("closePath",
_("Close Path"),
_("Close the path of the advanced shape. This closes the outline between the last and the first point."),
_("Close the path "
"with _PARAM0_"),
_("Advanced"),
"res/actions/closePath24.png",
"res/actions/closePath.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("closePath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction("FillColor",
_("Fill color"),
_("Change the color used when filling"),
_("Change fill color of _PARAM0_ to _PARAM1_"),
_("Setup"),
"res/actions/text24.png",
"res/actions/text.png")
"res/actions/color24.png",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("color", _("Fill color"))

View File

@@ -39,8 +39,72 @@ class PrimitiveDrawingJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Circle"]
.SetFunctionName("drawCircle");
GetAllActionsForObject("PrimitiveDrawing::Drawer")["PrimitiveDrawing::Line"]
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Line"]
.SetFunctionName("drawLine");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::LineV2"]
.SetFunctionName("drawLineV2");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
.SetFunctionName("drawEllipse");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
.SetFunctionName("drawRoundedRectangle");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Star"]
.SetFunctionName("drawStar");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Arc"]
.SetFunctionName("drawArc");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::BezierCurve"]
.SetFunctionName("drawBezierCurve");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::QuadraticCurve"]
.SetFunctionName("drawQuadraticCurve");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::BeginFillPath"]
.SetFunctionName("beginFillPath");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::EndFillPath"]
.SetFunctionName("endFillPath");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathMoveTo"]
.SetFunctionName("drawPathMoveTo");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathLineTo"]
.SetFunctionName("drawPathLineTo");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathBezierCurveTo"]
.SetFunctionName("drawPathBezierCurveTo");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathArc"]
.SetFunctionName("drawPathArc");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathQuadraticCurveTo"]
.SetFunctionName("drawPathQuadraticCurveTo");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ClosePath"]
.SetFunctionName("closePath");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
.SetFunctionName("drawEllipse");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
.SetFunctionName("drawRoundedRectangle");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Star"]
.SetFunctionName("drawStar");
// These actions are not exposed yet as the way they work is unsure. See https://github.com/4ian/GDevelop/pull/1256
/*GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Arc"]
.SetFunctionName("drawArc");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ArcTo"]
.SetFunctionName("drawArcTo");*/
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::FillColor"]
.SetFunctionName("setFillColor");

View File

@@ -47,6 +47,101 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawLine = function(x1, y1,
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawLineV2 = function(x1, y1, x2, y2, thickness) {
this._graphics.lineStyle(thickness);
this._graphics.moveTo(x1, y1);
this._graphics.lineTo(x2,y2);
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawEllipse = function(x1, y1, width, height) {
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._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.drawRoundedRect(x1, y1, x2 - x1, y2 - y1, radius);
this._graphics.closePath();
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawStar = function(x1, y1, points, radius, innerRadius, rotation) {
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();
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawArc = function(x1, y1, radius, startAngle, endAngle, anticlockwise, closePath) {
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);
if(closePath){
this._graphics.closePath();
}
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.moveTo(x1, y1);
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, x2, y2);
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
this._graphics.moveTo(x1, y1);
this._graphics.quadraticCurveTo(cpX, cpY, x2, y2);
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.beginFillPath = function() {
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.endFillPath = function() {
this._graphics.endFill();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathMoveTo = function(x1, y1) {
this._graphics.moveTo(x1, y1);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathLineTo = function(x1, y1) {
if(this._graphics.graphicsData.length === 0){
this._graphics.moveTo(0, 0);
}
this._graphics.lineTo(x1, y1);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathBezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) {
if(this._graphics.graphicsData.length === 0){
this._graphics.moveTo(0, 0);
}
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathArc = function(x1, y1, radius, startAngle, endAngle, anticlockwise) {
if(this._graphics.graphicsData.length === 0){
this._graphics.moveTo(0, 0);
}
this._graphics.arc(x1, y1, radius, gdjs.toRad(startAngle), gdjs.toRad(endAngle), anticlockwise ? true : false);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathQuadraticCurveTo = function(cpX, cpY, toX, toY) {
if(this._graphics.graphicsData.length === 0){
this._graphics.moveTo(0, 0);
}
this._graphics.quadraticCurveTo(cpX, cpY, toX, toY);
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.closePath = function() {
this._graphics.closePath();
};
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.updateOutline = function() {
this._graphics.lineStyle(
this._object._outlineSize,

View File

@@ -60,6 +60,67 @@ gdjs.ShapePainterRuntimeObject.prototype.drawLine = function(x1, y1, x2, y2, thi
this._renderer.drawLine(x1, y1, x2, y2, thickness);
};
gdjs.ShapePainterRuntimeObject.prototype.drawLineV2 = function(x1, y1, x2, y2, thickness) {
this._renderer.drawLineV2(x1, y1, x2, y2, thickness);
};
gdjs.ShapePainterRuntimeObject.prototype.drawEllipse = function(centerX, centerY, width, height) {
this._renderer.drawEllipse(centerX, centerY, width, height);
};
gdjs.ShapePainterRuntimeObject.prototype.drawRoundedRectangle = function(startX1, startY1, endX2, endY2, radius) {
this._renderer.drawRoundedRectangle(startX1, startY1, endX2, endY2, radius);
};
gdjs.ShapePainterRuntimeObject.prototype.drawStar = function(centerX, centerY, points, radius, innerRadius, rotation) {
this._renderer.drawStar(centerX, centerY, points, radius, innerRadius, rotation);
};
gdjs.ShapePainterRuntimeObject.prototype.drawArc = function(centerX, centerY, radius, startAngle, endAngle, anticlockwise, closePath) {
this._renderer.drawArc(centerX, centerY, radius, startAngle, endAngle, anticlockwise, closePath);
};
gdjs.ShapePainterRuntimeObject.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
};
gdjs.ShapePainterRuntimeObject.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
};
gdjs.ShapePainterRuntimeObject.prototype.beginFillPath = function(x1, y1) {
this._renderer.beginFillPath();
this._renderer.drawPathMoveTo(x1, y1);
};
gdjs.ShapePainterRuntimeObject.prototype.endFillPath = function() {
this._renderer.endFillPath();
};
gdjs.ShapePainterRuntimeObject.prototype.drawPathMoveTo = function(x1, y1) {
this._renderer.drawPathMoveTo(x1, y1);
};
gdjs.ShapePainterRuntimeObject.prototype.drawPathLineTo = function(x1, y1, thickness) {
this._renderer.drawPathLineTo(x1, y1, thickness);
};
gdjs.ShapePainterRuntimeObject.prototype.drawPathBezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) {
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
};
gdjs.ShapePainterRuntimeObject.prototype.drawPathArc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) {
this._renderer.drawPathArc(cx, cy, radius, startAngle, endAngle, anticlockwise);
};
gdjs.ShapePainterRuntimeObject.prototype.drawPathQuadraticCurveTo = function(cpX, cpY, toX, toY) {
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
};
gdjs.ShapePainterRuntimeObject.prototype.closePath = function() {
this._renderer.closePath();
};
gdjs.ShapePainterRuntimeObject.prototype.setFillColor = function(rgbColor) {
var colors = rgbColor.split(";");
if ( colors.length < 3 ) return;

View File

@@ -42,7 +42,7 @@ void DeclareShopifyExtension(gd::PlatformExtension& extension) {
"GetCheckoutUrlForProduct",
_("Get the URL for buying a product"),
_("Get the URL for buying a product from a shop. The URL will be "
"stored in the variable that you specify. You can then use the "
"stored in the scene variable that you specify. You can then use the "
"action to open an URL to redirect the player to the checkout."),
_("Get the URL for product #_PARAM2_ (quantity: _PARAM3_, variant: "
"_PARAM4_) from shop _PARAM1_, and store it in _PARAM5_ (or "
@@ -60,8 +60,8 @@ void DeclareShopifyExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Variant (0 by default)"))
.SetDefaultValue("0")
.AddParameter("scenevar",
_("Variable where the URL for checkout must be stored"))
.AddParameter("scenevar", _("Variable containing the error (if any)"));
_("Scene variable where the URL for checkout must be stored"))
.AddParameter("scenevar", _("Scene variable containing the error (if any)"));
#endif
}

View File

@@ -15,11 +15,25 @@ gdjs.sk.CocosDataLoader.prototype.getData = function(dataName){
return cc.loader.getRes("res/"+dataName);
};
/**
* Load the textures from DragonBones data
* @param {gdjs.RuntimeScene} runtimeScene
* @param {Object} objectData
*/
gdjs.sk.CocosDataLoader.prototype.loadDragonBones = function(runtimeScene, objectData){
var textureData = this.getData(objectData.textureDataFilename);
var jsonManager = runtimeScene.getGame().getJsonManager();
if (!jsonManager.isJsonLoaded(objectData.textureDataFilename)) {
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" but this resource is not loaded.");
return;
}
var textureData = jsonManager.getLoadedJson(objectData.textureDataFilename);
var texture = runtimeScene.getGame().getImageManager().getTexture(objectData.textureName);
if(!textureData || !texture._textureLoaded) return;
if(!textureData || !texture._textureLoaded) {
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" resource but the texture or the texture data could not be loaded properly.");
return;
}
for(var i=0; i<textureData.SubTexture.length; i++){
var subTex = textureData.SubTexture[i];
var frame = new cc.rect(subTex.x, subTex.y, subTex.width, subTex.height);
@@ -29,7 +43,7 @@ gdjs.sk.CocosDataLoader.prototype.loadDragonBones = function(runtimeScene, objec
if (subTex.hasOwnProperty("frameHeight")){
frame.height = subTex.frameHeight;
}
this.textures[subTex.name] = {"texture": texture, "frame": frame};
}
};
@@ -98,7 +112,7 @@ gdjs.sk.SlotCocosRenderer.prototype.loadAsSprite = function(texture){
};
gdjs.sk.SlotCocosRenderer.prototype.loadAsMesh = function(texture, vertices, uvs, triangles){
// Meshes not supported, load as sprites
// Meshes not supported, load as sprites
this.loadAsSprite(texture);
};

View File

@@ -11,18 +11,25 @@ gdjs.sk.PixiDataLoader = function()
};
gdjs.sk.DataLoader = gdjs.sk.PixiDataLoader;
gdjs.sk.PixiDataLoader.prototype.getData = function(dataName){
if(PIXI.loader.resources[dataName]){
return PIXI.loader.resources[dataName].data;
}
return null;
};
/**
* Load the textures from DragonBones data
* @param {gdjs.RuntimeScene} runtimeScene
* @param {Object} objectData
*/
gdjs.sk.PixiDataLoader.prototype.loadDragonBones = function(runtimeScene, objectData){
var textureData = this.getData(objectData.textureDataFilename);
var jsonManager = runtimeScene.getGame().getJsonManager();
if (!jsonManager.isJsonLoaded(objectData.textureDataFilename)) {
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" but this resource is not loaded.");
return;
}
var textureData = jsonManager.getLoadedJson(objectData.textureDataFilename);
var texture = runtimeScene.getGame().getImageManager().getPIXITexture(objectData.textureName);
if(!textureData || !texture.valid) return;
if(!textureData || !texture.valid) {
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" resource but the texture or the texture data could not be loaded properly.");
return;
}
for(var i=0; i<textureData.SubTexture.length; i++){
var subTex = textureData.SubTexture[i];
var frame = new PIXI.Rectangle(subTex.x, subTex.y, subTex.width, subTex.height);
@@ -37,7 +44,7 @@ gdjs.sk.PixiDataLoader.prototype.loadDragonBones = function(runtimeScene, object
if(frame.y > texture.height) frame.y = 0;
if(frame.x + frame.width > texture.width) frame.width = texture.width - frame.x;
if(frame.y + frame.height > texture.height) frame.height = texture.height - frame.y;
this.textures[subTex.name] = new PIXI.Texture(texture.baseTexture, frame=frame);
}
};

View File

@@ -11,18 +11,20 @@ This project is released under the MIT License.
#include "SkeletonObject.h"
void DeclareSkeletonObjectExtension(gd::PlatformExtension& extension) {
extension.SetExtensionInformation(
"SkeletonObject",
_("Skeleton"),
_("Enables the use of animated skeleton objects.\nCurrently supported "
"formats:\n *DragonBones"),
"Franco Maciel",
"Open source (MIT License)");
extension
.SetExtensionInformation("SkeletonObject",
_("Skeleton"),
_("Enables the use of animated skeleton objects "
"made with DragonBones."),
"Franco Maciel",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/skeleton");
gd::ObjectMetadata& obj = extension.AddObject<SkeletonObject>(
"Skeleton",
_("Skeleton"),
_("Object animated through bones"),
_("Object displayed using skeletal animation, powered by DragonBones. "
"This object is experimental and searching for a maintainer."),
"JsPlatform/Extensions/skeletonicon.png");
// Object instructions
@@ -796,10 +798,10 @@ void DeclareSkeletonObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
.AddParameter(
"scenevar",
_("Variable where to store the X position of the intersection"))
_("Scene variable where to store the X position of the intersection"))
.AddParameter(
"scenevar",
_("Variable where to store the Y position of the intersection"))
_("Scene variable where to store the Y position of the intersection"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
}

View File

@@ -26,14 +26,23 @@ gdjs.SkeletonObjectsManager.prototype.getSkeleton = function(runtimeScene, objec
return this.loadedObjects[objectName];
};
/**
* Load the Skeleton data
* @param {gdjs.RuntimeScene} runtimeScene
* @param {Object} objectData
*/
gdjs.SkeletonObjectsManager.prototype.loadSkeleton = function(runtimeScene, objectData){
var loader = new gdjs.sk.DataLoader();
var skeletalData = loader.getData(objectData.skeletalDataFilename);
var skeleton = {"loader": loader,
"armatures": [],
"rootArmature": 0};
if(!skeletalData) return skeleton;
var jsonManager = runtimeScene.getGame().getJsonManager();
var skeletalData = jsonManager.getLoadedJson(objectData.skeletalDataFilename);
if(!skeletalData) {
console.error("Tried to load a Skeleton file from \"" + objectData.skeletalDataFilename + "\" but this resource is not loaded.");
return skeleton;
}
if(objectData.apiName === "DragonBones"){
// Load sub-textures

View File

@@ -7,8 +7,8 @@ This project is released under the MIT License.
#include "SkeletonObject.h"
#include <SFML/Graphics.hpp>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include "GDCpp/Runtime/CommonTools.h"
@@ -52,10 +52,19 @@ void SkeletonObject::DoSerializeTo(gd::SerializerElement& element) const {
std::map<gd::String, gd::PropertyDescriptor> SkeletonObject::GetProperties(
gd::Project& project) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Skeletal data filename")].SetValue(skeletalDataFilename);
properties[_("Skeletal data filename")]
.SetValue(skeletalDataFilename)
.SetType("resource")
.AddExtraInfo("json");
properties[_("Main armature name")].SetValue(rootArmatureName);
properties[_("Texture data filename")].SetValue(textureDataFilename);
properties[_("Texture")].SetValue(textureName);
properties[_("Texture data filename")]
.SetValue(textureDataFilename)
.SetType("resource")
.AddExtraInfo("json");
properties[_("Texture")]
.SetValue(textureName)
.SetType("resource")
.AddExtraInfo("image");
properties[_("API")].SetValue(apiName).SetType("Choice").AddExtraInfo(
"DragonBones");
properties[_("Debug Polygons")]

View File

@@ -28,6 +28,20 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
.SetFunctionName("SystemInfo::IsMobile")
.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")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SystemInfo::IsWebGLSupported")
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
#endif
}

View File

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

View File

@@ -28,3 +28,12 @@ gdjs.evtTools.systemInfo.isMobile = function() {
return false;
};
/**
* 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();
};

View File

@@ -36,131 +36,275 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllActionsForObject("TextObject::Text")["TextObject::Scale"]
.SetFunctionName("setScale")
.SetGetter("getScale")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("setScaleX")
.SetGetter("getScaleX")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("getScaleX")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("setScaleY")
.SetGetter("getScaleY")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("getScaleY")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("setString")
.SetGetter("getString")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("getString")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("setCharacterSize")
.SetGetter("getCharacterSize")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("getCharacterSize")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("setAngle")
.SetGetter("getAngle")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("getAngle")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("setOpacity")
.SetGetter("getOpacity")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetBold"]
.SetFunctionName("setBold")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::IsBold"]
.SetFunctionName("isBold")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetItalic"]
.SetFunctionName("setItalic")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::IsItalic"]
.SetFunctionName("isItalic")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetWrapping"]
.SetFunctionName("setWrapping")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::IsWrapping"]
.SetFunctionName("isWrapping")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetPadding"]
.SetFunctionName("setPadding")
.SetGetter("getPadding")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::Padding"]
.SetFunctionName("getPadding")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetTextAlignment"]
.SetFunctionName("setTextAlignment")
.SetGetter("getTextAlignment")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::TextAlignment"]
.SetFunctionName("getTextAlignment")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::WrappingWidth"]
.SetFunctionName("setWrappingWidth")
.SetGetter("getWrappingWidth")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllConditionsForObject("TextObject::Text")["TextObject::WrappingWidth"]
.SetFunctionName("getWrappingWidth")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllExpressionsForObject("TextObject::Text")["Padding"]
.SetFunctionName("getPadding")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllExpressionsForObject("TextObject::Text")["ScaleX"]
.SetFunctionName("getScaleX")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllExpressionsForObject("TextObject::Text")["ScaleY"]
.SetFunctionName("getScaleY")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllExpressionsForObject("TextObject::Text")["Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllExpressionsForObject("TextObject::Text")["Angle"]
.SetFunctionName("getAngle")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllStrExpressionsForObject("TextObject::Text")["String"]
.SetFunctionName("getString")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::ChangeColor"]
.SetFunctionName("setColor")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetGradient"]
.SetFunctionName("setGradient")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetOutline"]
.SetFunctionName("setOutline")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::SetShadow"]
.SetFunctionName("setShadow")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::ShowShadow"]
.SetFunctionName("showShadow")
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js");
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-cocos-renderer.js");
// Unimplemented actions and conditions:
GetAllActionsForObject("TextObject::Text")["TextObject::Font"]

View File

@@ -274,7 +274,7 @@ bool RuntimeTextObject::ChangeProperty(std::size_t propertyNb,
} else if (propertyNb == 1) {
ChangeFont(newValue);
} else if (propertyNb == 2) {
SetCharacterSize(newValue.To<int>());
SetCharacterSize(std::max(1, newValue.To<int>()));
} else if (propertyNb == 3) {
gd::String r, gb, g, b;
{
@@ -297,7 +297,7 @@ bool RuntimeTextObject::ChangeProperty(std::size_t propertyNb,
SetColor(r.To<int>(), g.To<int>(), b.To<int>());
} else if (propertyNb == 4) {
SetOpacity(newValue.To<float>());
SetOpacity(std::min(std::max(0.0f, newValue.To<float>()), 255.0f));
} else if (propertyNb == 5) {
SetSmooth(!(newValue == _("No")));
}

View File

@@ -69,6 +69,9 @@ gdjs.TextRuntimeObjectPixiRenderer.prototype.updateStyle = function() {
style.dropShadowAngle = this._object._shadowAngle;
style.dropShadowDistance = this._object._shadowDistance;
style.padding = this._object._padding;
// Prevent spikey outlines by adding a miter limit
style.miterLimit = 3;
this.updatePosition();
// Manually ask the PIXI object to re-render as we changed a style property

View File

@@ -14,7 +14,7 @@ gdjs.TextRuntimeObject = function(runtimeScene, objectData)
{
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
this._characterSize = objectData.characterSize;
this._characterSize = Math.max(1, objectData.characterSize);
this._fontName = objectData.font;
this._bold = objectData.bold;
this._italic = objectData.italic;

View File

@@ -500,7 +500,9 @@ module.exports = {
registerEditorConfigurations: function(objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
"Video::VideoObject",
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor()
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
helpPagePath: "/objects/video"
})
);
},
/**

View File

@@ -21,6 +21,12 @@ gdjs.VideoRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
} else {
this._pixiObject._texture.baseTexture.source.currentTime = 0;
}
// Needed to avoid video not playing/crashing in Chrome/Chromium browsers.
// See https://github.com/pixijs/pixi.js/issues/5996
this._pixiObject._texture.baseTexture.source.preload = "auto";
this._pixiObject._texture.baseTexture.source.autoload = true;
this._textureWasValid = false; // Will be set to true when video texture is loaded.
runtimeScene
@@ -132,7 +138,7 @@ gdjs.VideoRuntimeObjectPixiRenderer.prototype._getHTMLVideoElementSource = funct
}
return source;
}
};
/**
* Start the video
@@ -247,10 +253,7 @@ gdjs.VideoRuntimeObjectPixiRenderer.prototype.isPlayed = function() {
var source = this._getHTMLVideoElementSource();
if (!source) return false;
return (
!source.paused &&
!source.ended
);
return !source.paused && !source.ended;
};
/**

View File

@@ -237,7 +237,7 @@ gdjs.VideoRuntimeObject.prototype.getDuration = function() {
* Check if the video has ended.
*/
gdjs.VideoRuntimeObject.prototype.isEnded = function() {
return !this._renderer.isEnded();
return this._renderer.isEnded();
};
/**

View File

@@ -158,8 +158,9 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
// it from the behavior
gd::String("var runtimeScene = this._runtimeScene;\n") +
// By convention of Behavior Events Function, the object is accessible
// as a parameter called "Object", and thisObjectList is an array containing it
// (for faster access, without having to go through the hashmap).
// as a parameter called "Object", and thisObjectList is an array
// containing it (for faster access, without having to go through the
// hashmap).
"var thisObjectList = [this.owner];\n" +
"var Object = Hashtable.newFrom({Object: thisObjectList});\n" +
// By convention of Behavior Events Function, the behavior is accessible
@@ -249,9 +250,10 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
}
}
// If we have an object considered as the current object ("this") (usually called
// Object in behavior events function), generate a slightly more optimized getter
// for it (bypassing "Object" hashmap, and directly return the array containing it).
// If we have an object considered as the current object ("this") (usually
// called Object in behavior events function), generate a slightly more
// optimized getter for it (bypassing "Object" hashmap, and directly return
// the array containing it).
gd::String thisObjectGetterCode =
thisObjectName.empty()
? ""
@@ -275,6 +277,11 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
" return objectsList ? gdjs.objectsListsToArray(objectsList) : "
"[];\n"
" },\n" +
// Function that can be used in JS code to get the lists of objects
// and filter/alter them.
" getObjectsLists: function(objectName) {\n" +
" return eventsFunctionContext._objectsMap[objectName] || null;\n"
" },\n" +
// Function that will be used to query behavior name (as behavior name
// can be different between the parameter name vs the actual behavior
// name passed as argument).

View File

@@ -65,6 +65,10 @@ CameraExtension::CameraExtension() {
GetAllActions()["SetLayerEffectParameter"].SetFunctionName(
"gdjs.evtTools.camera.setLayerEffectParameter");
GetAllActions()["EnableLayerEffect"].SetFunctionName(
"gdjs.evtTools.camera.enableLayerEffect");
GetAllConditions()["LayerEffectEnabled"].SetFunctionName(
"gdjs.evtTools.camera.layerEffectEnabled");
GetAllConditions()["LayerTimeScale"].SetFunctionName(
"gdjs.evtTools.camera.getLayerTimeScale");

View File

@@ -28,6 +28,8 @@ SceneExtension::SceneExtension() {
GetAllConditions()["DepartScene"].SetFunctionName(
"gdjs.evtTools.runtimeScene.sceneJustBegins");
GetAllConditions()["SceneJustResumed"].SetFunctionName(
"gdjs.evtTools.runtimeScene.sceneJustResumed");
GetAllActions()["SceneBackground"].SetFunctionName(
"gdjs.evtTools.runtimeScene.setBackgroundColor");
GetAllActions()["Scene"].SetFunctionName(

View File

@@ -3,6 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDJS/IDE/Exporter.h"
#include <algorithm>
#include <fstream>
#include <sstream>
@@ -22,7 +23,6 @@
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDJS/IDE/Exporter.h"
#include "GDJS/IDE/ExporterHelper.h"
#undef CopyFile // Disable an annoying macro
@@ -135,8 +135,7 @@ bool Exporter::ExportWholePixiProject(
if (!exportProject(exportDir + "/www")) return false;
if (!helper.ExportCordovaConfigFile(exportedProject, exportDir))
return false;
if (!helper.ExportCordovaFiles(exportedProject, exportDir)) return false;
} else if (exportOptions["exportForElectron"]) {
fs.MkDir(exportDir);
@@ -170,8 +169,7 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
// Export the resources (before generating events as some resources filenames
// may be updated)
helper.ExportResources(
fs, exportedProject, exportDir + "/res");
helper.ExportResources(fs, exportedProject, exportDir + "/res");
// Export engine libraries
helper.AddLibsInclude(false, true, false, includesFiles);

View File

@@ -9,6 +9,7 @@
#include <set>
#include <string>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Project;
class Layout;

View File

@@ -3,6 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDJS/IDE/ExporterHelper.h"
#include <algorithm>
#include <fstream>
#include <sstream>
@@ -23,7 +24,6 @@
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDJS/IDE/ExporterHelper.h"
#undef CopyFile // Disable an annoying macro
namespace gdjs {
@@ -200,8 +200,8 @@ bool ExporterHelper::ExportPixiIndexFile(
return true;
}
bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
gd::String exportDir) {
bool ExporterHelper::ExportCordovaFiles(const gd::Project &project,
gd::String exportDir) {
auto &platformSpecificAssets = project.GetPlatformSpecificAssets();
auto &resourceManager = project.GetResourcesManager();
auto getIconFilename = [&resourceManager, &platformSpecificAssets](
@@ -281,10 +281,35 @@ bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
}
if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
lastError = "Unable to write configuration file.";
lastError = "Unable to write Cordova config.xml file.";
return false;
}
gd::String jsonName =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetName()));
gd::String jsonAuthor =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetAuthor()));
gd::String jsonVersion =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetVersion()));
gd::String jsonMangledName = gd::Serializer::ToJSON(gd::SerializerElement(
gd::SceneNameMangler::GetMangledSceneName(project.GetName())
.LowerCase()
.FindAndReplace(" ", "-")));
{
gd::String str =
fs.ReadFile(gdjsRoot + "/Runtime/Cordova/package.json")
.FindAndReplace("\"GDJS_GAME_NAME\"", jsonName)
.FindAndReplace("\"GDJS_GAME_AUTHOR\"", jsonAuthor)
.FindAndReplace("\"GDJS_GAME_VERSION\"", jsonVersion)
.FindAndReplace("\"GDJS_GAME_MANGLED_NAME\"", jsonMangledName);
if (!fs.WriteToFile(exportDir + "/package.json", str)) {
lastError = "Unable to write Cordova package.json file.";
return false;
}
}
return true;
}
@@ -656,10 +681,12 @@ bool ExporterHelper::ExportExternalSourceFiles(
}
bool ExporterHelper::ExportIncludesAndLibs(
std::vector<gd::String> &includesFiles, gd::String exportDir, bool /*minify*/) {
std::vector<gd::String> &includesFiles,
gd::String exportDir,
bool /*minify*/) {
for (std::vector<gd::String>::iterator include = includesFiles.begin();
include != includesFiles.end();
++include) {
include != includesFiles.end();
++include) {
if (!fs.IsAbsolute(*include)) {
gd::String source = gdjsRoot + "/Runtime/" + *include;
if (fs.FileExists(source)) {

View File

@@ -8,6 +8,7 @@
#include <set>
#include <string>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Project;
class Layout;
@@ -173,8 +174,7 @@ class ExporterHelper {
* \param project The project to be used to generate the configuration file.
* \param exportDir The directory where the config.xml must be created.
*/
bool ExportCordovaConfigFile(const gd::Project &project,
gd::String exportDir);
bool ExportCordovaFiles(const gd::Project &project, gd::String exportDir);
/**
* \brief Generate the base Cocos2d files.

View File

@@ -0,0 +1,14 @@
{
"name": "GDJS_GAME_MANGLED_NAME",
"displayName": "GDJS_GAME_NAME",
"version": "GDJS_GAME_VERSION",
"description": "GDJS_GAME_NAME",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"ecosystem:cordova"
],
"author": "GDJS_GAME_AUTHOR"
}

View File

@@ -7,7 +7,6 @@
"version": "GDJS_GAME_VERSION",
"dependencies": {},
"devDependencies": {
"electron": "2.0.7"
"electron": "3.0.9"
}
}

View File

@@ -119,6 +119,14 @@ gdjs.RuntimeGameCocosRenderer.prototype.getCanvas = function() {
return cc.game.canvas;
}
/**
* Check if the device supports WebGL.
* @returns {boolean} true if WebGL is supported
*/
gdjs.RuntimeGameCocosRenderer.prototype.isWebGLSupported = function() {
return cc._renderType === cc.game.RENDER_TYPE_WEBGL;
};
/**
* Get the electron module, if running as a electron renderer process.
*/
@@ -128,4 +136,4 @@ gdjs.RuntimeGameCocosRenderer.prototype.getElectron = function() {
}
return null;
}
}

View File

@@ -48,8 +48,6 @@ gdjs.SpriteRuntimeObjectCocosRenderer.prototype._updateCocosSprite = function()
));
var xPos = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
var yPos = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
if ( this._object._flippedX ) xPos += (this._cachedTextureWidth/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
if ( this._object._flippedY ) yPos += (this._cachedTextureHeight/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
this._sprite.setPositionX(xPos);
this._sprite.setPositionY(this._convertYPosition(yPos));
this._sprite.setRotation(this._object.angle);
@@ -87,13 +85,11 @@ gdjs.SpriteRuntimeObjectCocosRenderer.prototype.update = function() {
gdjs.SpriteRuntimeObjectCocosRenderer.prototype.updateX = function() {
var xPos = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
if ( this._object._flippedX ) xPos += (this._cachedTextureWidth/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
this._sprite.setPositionX(xPos);
}
gdjs.SpriteRuntimeObjectCocosRenderer.prototype.updateY = function() {
var yPos = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
if ( this._object._flippedY ) yPos += (this._cachedTextureHeight/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
this._sprite.setPositionY(this._convertYPosition(yPos));
}

View File

@@ -128,6 +128,32 @@ gdjs.evtTools.camera.setLayerEffectParameter = function(runtimeScene, layer, eff
return runtimeScene.getLayer(layer).setEffectParameter(effect, parameter, value);
}
/**
* Enable, or disable, an effect of a layer.
* @param {gdjs.RuntimeScene} runtimeScene The scene
* @param {string} layer The name of the layer
* @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; }
runtimeScene.getLayer(layer).enableEffect(effect, enabled);
}
/**
* Check if an effect is enabled.
* @param {gdjs.RuntimeScene} runtimeScene The scene
* @param {string} layer The name of the layer
* @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; }
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);

View File

@@ -15,8 +15,8 @@ gdjs.evtTools.object = gdjs.evtTools.object || {};
/**
* Keep only the specified object in the lists of picked objects.
*
* @param objectsLists The lists of objects to trim
* @param runtimeObject {gdjs.RuntimeObject} The object to keep in the lists
* @param {Hashtable} objectsLists The lists of objects to trim
* @param {gdjs.RuntimeObject} runtimeObject The object to keep in the lists
*/
gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
for (var listName in objectsLists.items) {
@@ -33,9 +33,17 @@ gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
}
};
/**
* A predicate to be passed to `gdjs.evtTools.object.twoListsTest`.
* @callback gdjsTwoListsTestPredicate
* @param {gdjs.RuntimeObject} object1 First object
* @param {gdjs.RuntimeObject} object2 Second object
* @param {*} extraArg An optional extra argument
* @return {boolean} true if the pair satisfy the predicate (for example,there is a collision), meaning that the objects will be picked, false otherwise (no collision).
*/
/**
* Do a test on two tables of objects so as to pick only the pair of objects for which the test is true.
* If inverted == true, only the objects of the first table are filtered.
*
* Note that the predicate method is not called stricly for each pair: When considering a pair of objects, if
* these objects have already been marked as picked, the predicate method won't be called again.
@@ -51,9 +59,12 @@ gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
* + Cost(predicate)*(NbObjList1+NbObjList2)
* + Cost(Testing NbObjList1+NbObjList2 booleans)
*
* Note: predicate is called with the two objects to compare, and an optional argument `extraArg`.
* This should be used to avoid declaring the predicate as a closure that would be created and destroyed
* at each call to twoListsTest (potentially multiple time per frame).
*
* @param {gdjsTwoListsTestPredicate} predicate The predicate function is called with the two objects to compare, and an optional argument `extraArg`
* @param {Hashtable} objectsLists1 e.g. Hashtable.newFrom({ A: objects1 });
* @param {Hashtable} objectsLists2 e.g. Hashtable.newFrom({ B: objects2 });
* @param {boolean} inverted If `inverted` == true, only the objects of the first table are filtered.
* @param {*} extraArg (optional) This argument should be used to avoid declaring the predicate as a closure that would be created and destroyed at each call to twoListsTest (potentially multiple time per frame).
*/
gdjs.evtTools.object.twoListsTest = function(predicate, objectsLists1, objectsLists2, inverted, extraArg) {
@@ -151,11 +162,11 @@ gdjs.evtTools.object.twoListsTest = function(predicate, objectsLists1, objectsLi
*
* Objects that do not fullfil the predicate are removed from objects lists.
*
* @param predicate The function applied to each object: must return true if the object fulfill the predicate.
* @param objectsLists The lists of objects to trim
* @param negatePredicate If set to true, the result of the predicate is negated.
* @param extraArg Argument passed to the predicate (along with the object). Useful for avoiding relying on temporary closures.
* @return true if at least one object fulfill the predicate.
* @param {Function} predicate The function applied to each object: must return true if the object fulfill the predicate.
* @param {Hashtable} objectsLists The lists of objects to trim
* @param {boolean} negatePredicate If set to true, the result of the predicate is negated.
* @param {*} extraArg Argument passed to the predicate (along with the object). Useful for avoiding relying on temporary closures.
* @return {boolean} true if at least one object fulfill the predicate.
*/
gdjs.evtTools.object.pickObjectsIf = function(predicate, objectsLists, negatePredicate, extraArg) {
var isTrue = false;
@@ -217,8 +228,8 @@ gdjs.evtTools.object.distanceTest = function(objectsLists1, objectsLists2, dista
gdjs.evtTools.object._movesToward = function(obj1, obj2, tolerance) {
if ( obj1.hasNoForces() ) return false;
var objAngle = Math.atan2(obj2.getY()+obj2.getCenterY() - (obj1.getY()+obj1.getCenterY()),
obj2.getX()+obj2.getCenterX() - (obj1.getX()+obj1.getCenterX()));
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
objAngle *= 180/3.14159;
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAverageForce().getAngle(), objAngle)) <= tolerance/2;
@@ -230,8 +241,8 @@ gdjs.evtTools.object.movesTowardTest = function(objectsLists1, objectsLists2, to
};
gdjs.evtTools.object._turnedToward = function(obj1, obj2, tolerance) {
var objAngle = Math.atan2(obj2.getY()+obj2.getCenterY() - (obj1.getY()+obj1.getCenterY()),
obj2.getX()+obj2.getCenterX() - (obj1.getX()+obj1.getCenterX()));
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
objAngle *= 180/3.14159;
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAngle(), objAngle)) <= tolerance/2;
@@ -265,10 +276,10 @@ gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {
objectsCount += list.length;
}
}
if (objectsCount === 0)
if (objectsCount === 0)
return false;
// Pick one random object
var index = Math.floor(Math.random()*objectsCount);
if (index >= objectsCount) index = objectsCount-1; //Should never happen.
@@ -288,7 +299,7 @@ gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {
startIndex += list.length;
}
}
gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne);
return true;
};
@@ -344,7 +355,7 @@ gdjs.evtTools.object.raycastObjectToPosition = function(objectsLists, x, y, endX
for (var j = 0; j < list.length; j++) {
var object = list[j];
var result = object.raycastTest(x, y, endX, endY, !inverted);
if( result.collision ) {
if ( !inverted && (result.closeSqDist <= testSqDist) ) {
testSqDist = result.closeSqDist;

View File

@@ -17,6 +17,10 @@ gdjs.evtTools.runtimeScene.sceneJustBegins = function(runtimeScene) {
return runtimeScene.getTimeManager().isFirstFrame();
};
gdjs.evtTools.runtimeScene.sceneJustResumed = function(runtimeScene) {
return runtimeScene.sceneJustResumed();
};
gdjs.evtTools.runtimeScene.getSceneName = function(runtimeScene) {
return runtimeScene.getName();
};

File diff suppressed because one or more lines are too long

View File

@@ -18,6 +18,58 @@
*/
gdjs.JsonManager = function(resources) {
this._resources = resources;
/** @type Object.<string, Object> */
this._loadedJsons = {};
};
/**
* The callback called when a json is preloaded
* @callback JsonManagerOnProgressCallback
* @param {number} loaded The number of json files loaded so far
* @param {number} total The total number to be loaded
* @returns {undefined} Nothing
*/
/**
* The callback called when all jsons are preloaded
* @callback JsonManagerOnCompleteCallback
* @param {number} total The total number to be loaded
* @returns {undefined} Nothing
*/
/**
* Request all the json resources to be preloaded (unless they are marked as not preloaded).
*
* @param {JsonManagerOnProgressCallback} onProgress The function called after each json is loaded.
* @param {JsonManagerOnCompleteCallback} onComplete The function called when all jsons are loaded.
*/
gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
var resources = this._resources;
var jsonResources = resources.filter(function(resource) {
return resource.kind === 'json' && !resource.disablePreload;
});
if (jsonResources.length === 0) return onComplete(jsonResources.length);
var loaded = 0;
/** @type JsonManagerRequestCallback */
var onLoad = function(error, jsonContent) {
if (error) {
console.error("Error while preloading a json resource:" + error);
}
loaded++;
if (loaded === jsonResources.length) {
return onComplete(jsonResources.length);
}
onProgress(loaded, jsonResources.length);
};
for (var i = 0; i < jsonResources.length; ++i) {
this.loadJson(jsonResources[i].name, onLoad);
}
};
/**
@@ -30,16 +82,16 @@ gdjs.JsonManager = function(resources) {
/**
* Request the json file from the given resource name.
* When loaded, the `callback` is called with the error (null if none) and the loaded
* json (string).
* This method is asynchronous. When loaded, the `callback` is called with the error
* (null if none) and the loaded json (a JS Object).
*
* @param {string} resourceName The resource pointing to the json file to load.
* @param {JsonManagerRequestCallback} callback The callback function called when json is loaded (or an error occured).
*/
gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
var resource = this._resources.find(
resource => resource.kind === 'json' && resource.name === resourceName
);
var resource = this._resources.find(function(resource) {
return resource.kind === 'json' && resource.name === resourceName;
});
if (!resource) {
callback(
new Error(
@@ -52,6 +104,7 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
return;
}
var that = this;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET', resource.file);
@@ -64,6 +117,9 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
return;
}
// Cache the result
that._loadedJsons[resourceName] = xhr.response;
callback(null, xhr.response);
};
xhr.onerror = function() {
@@ -74,3 +130,23 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
};
xhr.send();
};
/**
* Check if the given json resource was loaded (preloaded or loaded with `loadJson`).
* @param {string} resourceName The name of the json resource.
* @returns {boolean} true if the content of the json resource is loaded. false otherwise.
*/
gdjs.JsonManager.prototype.isJsonLoaded = function(resourceName) {
return !!this._loadedJsons[resourceName];
};
/**
* Get the object for the given resource that is already loaded (preloaded or loaded with `loadJson`).
* If the resource is not loaded, `null` will be returned.
*
* @param {string} resourceName The name of the json resource.
* @returns {?Object} the content of the json resource, if loaded. `null` otherwise.
*/
gdjs.JsonManager.prototype.getLoadedJson = function(resourceName) {
return this._loadedJsons[resourceName] || null;
};

View File

@@ -216,6 +216,24 @@ gdjs.Layer.prototype.setEffectParameter = function(name, parameterIndex, value)
return this._renderer.setEffectParameter(name, parameterIndex, value);
};
/**
* Enable or disable an effect.
* @param {string} effect The name of the effect to enable or disable.
* @param {boolean} enable true to enable, false to disable
*/
gdjs.Layer.prototype.enableEffect = function(name, enable) {
this._renderer.enableEffect(name, enable);
};
/**
* Check if an effect is enabled
* @param {string} effect The name of the effect
* @return {boolean} true if the effect is enabled, false otherwise.
*/
gdjs.Layer.prototype.isEffectEnabled = function(name) {
return this._renderer.isEffectEnabled(name);
};
gdjs.Layer.prototype.setEffectsDefaultParameters = function() {
for (var i = 0; i < this._effects.length; ++i) {
var effect = this._effects[i];

View File

@@ -1,9 +1,22 @@
/**
* A generic map (key values) container.
*
* Mostly used for storing lists of objects for
* GDevelop generated events.
*
* @class Hashtable
*/
function Hashtable()
{
// console.log("New hashtable");
this.items = {};
}
/**
* Construct a Hashtable from a JS object.
* @param {Object} items The content of the Hashtable
* @returns {Hashtable} The new hashtable
* @static
*/
Hashtable.newFrom = function(items) {
var hashtable = new Hashtable();
hashtable.items = items;

View File

@@ -1,12 +1,26 @@
gdjs.LayerPixiRenderer = function(layer, runtimeSceneRenderer)
{
this._pixiContainer = new PIXI.Container();
this._filters = {};
this._layer = layer;
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
/*
* GDevelop JS Platform
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
this._addFilters();
}
/**
* The renderer for a gdjs.Layer using Pixi.js.
*
* @class LayerPixiRenderer
* @memberof gdjs
* @param {gdjs.Layer} layer The layer
* @param {gdjs.RuntimeScenePixiRenderer} runtimeSceneRenderer The scene renderer
*/
gdjs.LayerPixiRenderer = function(layer, runtimeSceneRenderer) {
this._pixiContainer = new PIXI.Container();
/** @type Object.<string, gdjsPixiFiltersToolsFilter> */
this._filters = {};
this._layer = layer;
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
this._setupFilters();
};
gdjs.LayerRenderer = gdjs.LayerPixiRenderer; //Register the class to let the engine use it.
@@ -16,53 +30,62 @@ gdjs.LayerRenderer = gdjs.LayerPixiRenderer; //Register the class to let the eng
* @private
*/
gdjs.LayerPixiRenderer.prototype.updatePosition = function() {
var angle = -gdjs.toRad(this._layer.getCameraRotation());
var zoomFactor = this._layer.getCameraZoom();
var angle = -gdjs.toRad(this._layer.getCameraRotation());
var zoomFactor = this._layer.getCameraZoom();
this._pixiContainer.rotation = angle;
this._pixiContainer.scale.x = zoomFactor;
this._pixiContainer.scale.y = zoomFactor;
this._pixiContainer.rotation = angle;
this._pixiContainer.scale.x = zoomFactor;
this._pixiContainer.scale.y = zoomFactor;
var cosValue = Math.cos(angle);
var sinValue = Math.sin(angle);
var centerX = (this._layer.getCameraX()*zoomFactor)*cosValue
- (this._layer.getCameraY()*zoomFactor)*sinValue;
var centerY = (this._layer.getCameraX()*zoomFactor)*sinValue
+ (this._layer.getCameraY()*zoomFactor)*cosValue;
var cosValue = Math.cos(angle);
var sinValue = Math.sin(angle);
var centerX =
this._layer.getCameraX() * zoomFactor * cosValue -
this._layer.getCameraY() * zoomFactor * sinValue;
var centerY =
this._layer.getCameraX() * zoomFactor * sinValue +
this._layer.getCameraY() * zoomFactor * cosValue;
this._pixiContainer.position.x = -centerX;
this._pixiContainer.position.y = -centerY;
this._pixiContainer.position.x += this._layer.getWidth()/2;
this._pixiContainer.position.y += this._layer.getHeight()/2;
this._pixiContainer.position.x = -centerX;
this._pixiContainer.position.y = -centerY;
this._pixiContainer.position.x += this._layer.getWidth() / 2;
this._pixiContainer.position.y += this._layer.getHeight() / 2;
};
gdjs.LayerPixiRenderer.prototype.updateVisibility = function(visible) {
this._pixiContainer.visible = !!visible;
}
this._pixiContainer.visible = !!visible;
};
gdjs.LayerPixiRenderer.prototype._addFilters = function() {
var effects = this._layer.getEffects();
if (effects.length === 0) {
return;
} else if (effects.length > 1) {
console.log('Only a single effect by Layer is supported for now by the Pixi renderer');
}
gdjs.LayerPixiRenderer.prototype._setupFilters = function() {
var effects = this._layer.getEffects();
if (effects.length === 0) {
return;
}
var filter = gdjs.PixiFiltersTools.getFilter(effects[0].effectName);
this._filters = {};
/** @type PIXI.Filter[] */
var pixiFilters = [];
for (var i = 0; i < effects.length; ++i) {
var effect = effects[i];
var filter = gdjs.PixiFiltersTools.getFilter(effect.effectName);
if (!filter) {
console.log('Filter \"' + effects[0].name + '\" not found');
return;
console.log('Filter "' + effect.name + '" not found');
continue;
}
/** @type gdjsPixiFiltersToolsFilter */
var theFilter = {
filter: filter.makeFilter(),
updateParameter: filter.updateParameter
filter: filter.makeFilter(),
updateParameter: filter.updateParameter,
};
this._pixiContainer.filters = [theFilter.filter];
this._filters = {};
this._filters[effects[0].name] = theFilter;
}
pixiFilters.push(theFilter.filter);
this._filters[effect.name] = theFilter;
}
this._pixiContainer.filters = pixiFilters;
};
/**
* Add a child to the pixi container associated to the layer.
@@ -72,15 +95,16 @@ gdjs.LayerPixiRenderer.prototype._addFilters = function() {
* @param zOrder The z order of the associated object.
*/
gdjs.LayerPixiRenderer.prototype.addRendererObject = function(child, zOrder) {
child.zOrder = zOrder; //Extend the pixi object with a z order.
child.zOrder = zOrder; //Extend the pixi object with a z order.
for( var i = 0, len = this._pixiContainer.children.length; i < len;++i) {
if ( this._pixiContainer.children[i].zOrder >= zOrder ) { //TODO : Dichotomic search
this._pixiContainer.addChildAt(child, i);
return;
}
}
this._pixiContainer.addChild(child);
for (var i = 0, len = this._pixiContainer.children.length; i < len; ++i) {
if (this._pixiContainer.children[i].zOrder >= zOrder) {
//TODO : Dichotomic search
this._pixiContainer.addChildAt(child, i);
return;
}
}
this._pixiContainer.addChild(child);
};
/**
@@ -89,9 +113,12 @@ gdjs.LayerPixiRenderer.prototype.addRendererObject = function(child, zOrder) {
* @param child The child (PIXI object) to be modified.
* @param newZOrder The z order of the associated object.
*/
gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(child, newZOrder) {
this._pixiContainer.removeChild(child);
this.addRendererObject(child, newZOrder);
gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(
child,
newZOrder
) {
this._pixiContainer.removeChild(child);
this.addRendererObject(child, newZOrder);
};
/**
@@ -101,12 +128,30 @@ gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(child, ne
* @param child The child (PIXI object) to be removed.
*/
gdjs.LayerPixiRenderer.prototype.removeRendererObject = function(child) {
this._pixiContainer.removeChild(child);
this._pixiContainer.removeChild(child);
};
gdjs.LayerPixiRenderer.prototype.setEffectParameter = function (name, parameterName, value) {
if (!this._filters.hasOwnProperty(name)) return;
gdjs.LayerPixiRenderer.prototype.setEffectParameter = function(
name,
parameterName,
value
) {
if (!this._filters.hasOwnProperty(name)) return;
var theFilter = this._filters[name];
theFilter.updateParameter(theFilter.filter, parameterName, value);
var theFilter = this._filters[name];
theFilter.updateParameter(theFilter.filter, parameterName, value);
};
gdjs.LayerPixiRenderer.prototype.enableEffect = function(name, value) {
if (!this._filters.hasOwnProperty(name)) return;
var theFilter = this._filters[name];
gdjs.PixiFiltersTools.enableEffect(theFilter.filter, value);
};
gdjs.LayerPixiRenderer.prototype.isEffectEnabled = function(name) {
if (!this._filters.hasOwnProperty(name)) return false;
var theFilter = this._filters[name];
return gdjs.PixiFiltersTools.isEffectEnabled(theFilter.filter);
};

View File

@@ -1,5 +1,11 @@
gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreenSetup) {
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
if (!this._pixiRenderer) {
// A PIXI Renderer can be missing during tests, when creating a runtime game
// without a canvas.
return;
}
this._loadingScreen = new PIXI.Container();
this._progressText = new PIXI.Text(' ', {
@@ -42,6 +48,10 @@ gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreen
gdjs.LoadingScreenRenderer = gdjs.LoadingScreenPixiRenderer; //Register the class to let the engine use it.
gdjs.LoadingScreenPixiRenderer.prototype.render = function(percent) {
if (!this._pixiRenderer) {
return;
}
var screenBorder = 10;
if (this._madeWithText) {

View File

@@ -1,5 +1,11 @@
/**
* Contains tools related to PIXI filters handling.
*/
gdjs.PixiFiltersTools = function() {};
gdjs.PixiFiltersTools.clampValue = function(value, min, max) { return Math.max(min, Math.min(max, value)); };
gdjs.PixiFiltersTools.clampKernelSize = function(value) { return (([5, 7, 9, 11, 13, 15].indexOf(value) !== -1) ? value : 5); };
gdjs.NightPixiFilter = function() {
var vertexShader = null;
var fragmentShader = [
@@ -70,7 +76,7 @@ gdjs.PixiFiltersTools._filters = {
if (parameterName !== 'intensity' &&
parameterName !== 'opacity') return;
filter.uniforms[parameterName] = value;
filter.uniforms[parameterName] = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
},
LightNight: {
@@ -81,7 +87,7 @@ gdjs.PixiFiltersTools._filters = {
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'opacity') return;
filter.uniforms.opacity = value;
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
},
Sepia: {
@@ -93,14 +99,100 @@ gdjs.PixiFiltersTools._filters = {
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'opacity') return;
filter.alpha = value;
filter.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
},
BlackAndWhite: {
makeFilter: function() {
var colorMatrix = new PIXI.filters.ColorMatrixFilter();
colorMatrix.blackAndWhite();
return colorMatrix;
},
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'opacity') return;
filter.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
},
Brightness: {
makeFilter: function() {
var brightness = new PIXI.filters.ColorMatrixFilter();
brightness.brightness();
return brightness;
},
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'brightness') return;
filter.brightness(gdjs.PixiFiltersTools.clampValue(value, 0, 1));
},
},
Noise: {
makeFilter: function() {
var noise = new PIXI.filters.NoiseFilter();
return noise;
},
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'noise') return;
filter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
},
Blur: {
makeFilter: function() {
var blur = new PIXI.filters.BlurFilter();
return blur;
},
updateParameter: function(filter, parameterName, value) {
if (parameterName !== 'blur' &&
parameterName !== 'quality' &&
parameterName !== 'kernelSize' &&
parameterName !== 'resolution') return;
if (parameterName === 'kernelSize'){
value = gdjs.PixiFiltersTools.clampKernelSize(value);
}
filter[parameterName] = value;
},
},
};
/**
* Enable an effect.
* @param {PIXI.Filter} filter The filter to enable or disable
* @param {boolean} value Set to true to enable, false to disable
*/
gdjs.PixiFiltersTools.enableEffect = function(filter, value) {
filter.enabled = value;
}
/**
* Effect is enabled.
* @param {PIXI.Filter} filter The filter to be checked
* @return {boolean} true if the filter is enabled
*/
gdjs.PixiFiltersTools.isEffectEnabled = function(filter) {
return filter.enabled;
}
/**
* Return the filter with the given name, if any.
* @param {string} filterName The name of the filter to get
* @return {?gdjsPixiFiltersToolsFilter} The filter wrapper, if any (null otherwise).
*/
gdjs.PixiFiltersTools.getFilter = function(filterName) {
if (gdjs.PixiFiltersTools._filters.hasOwnProperty(filterName))
return gdjs.PixiFiltersTools._filters[filterName];
return null;
}
// Type definitions:
/**
* The type of a filter used to manipulate a Pixi filter.
* @typedef gdjsPixiFiltersToolsFilter
* @type {object}
* @property {PIXI.Filter} filter The PIXI filter
* @property {Function} updateParameter The function to be called to update a parameter
*/

View File

@@ -426,6 +426,14 @@ gdjs.RuntimeGamePixiRenderer.prototype.getCanvas = function() {
return this._pixiRenderer.view;
}
/**
* Check if the device supports WebGL.
* @returns {boolean} true if WebGL is supported
*/
gdjs.RuntimeGamePixiRenderer.prototype.isWebGLSupported = function() {
return this._pixiRenderer.type === PIXI.RENDERER_TYPE.WEBGL;
};
/**
* Get the electron module, if running as a electron renderer process.
*/

View File

@@ -1,6 +1,13 @@
/**
* The renderer for a gdjs.SpriteRuntimeObject using Pixi.js.
* @class SpriteRuntimeObjectPixiRenderer
* @memberof gdjs
* @param {gdjs.SpriteRuntimeObject} runtimeObject The object
* @param {gdjs.RuntimeScene} runtimeScene The scene
*/
gdjs.SpriteRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene)
{
/** @type gdjs.SpriteRuntimeObject */
this._object = runtimeObject;
this._spriteDirty = true;
this._textureDirty = true;
@@ -26,8 +33,6 @@ gdjs.SpriteRuntimeObjectPixiRenderer.prototype._updatePIXISprite = function() {
this._sprite.anchor.y = this._object._animationFrame.center.y/this._sprite.texture.frame.height;
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
if ( this._object._flippedX ) this._sprite.position.x += (this._sprite.texture.frame.width/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
if ( this._object._flippedY ) this._sprite.position.y += (this._sprite.texture.frame.height/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
this._sprite.rotation = gdjs.toRad(this._object.angle);
this._sprite.visible = !this._object.hidden;
this._sprite.blendMode = this._object._blendMode;
@@ -67,14 +72,10 @@ gdjs.SpriteRuntimeObjectPixiRenderer.prototype.update = function() {
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateX = function() {
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
if ( this._flippedX )
this._sprite.position.x += (this._sprite.texture.frame.width/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
}
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateY = function() {
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
if ( this._flippedY )
this._sprite.position.y += (this._sprite.texture.frame.height/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
}
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateAngle = function() {

View File

@@ -296,24 +296,42 @@ gdjs.RuntimeGame.prototype.loadAllAssets = function(
function(texturesTotalCount) {
that._soundManager.preloadAudio(
function(count, total) {
loadingScreen.render(
Math.floor(((texturesTotalCount + count) / allAssetsTotal) * 100)
var percent = Math.floor(
((texturesTotalCount + count) / allAssetsTotal) * 100
);
loadingScreen.render(percent);
if (progressCallback) progressCallback(percent);
},
function(audioTotalCount) {
that._fontManager.loadFonts(
function(count, total) {
loadingScreen.render(
Math.floor(
((texturesTotalCount + audioTotalCount + count) /
allAssetsTotal) *
100
)
var percent = Math.floor(
((texturesTotalCount + audioTotalCount + count) /
allAssetsTotal) *
100
);
loadingScreen.render(percent);
if (progressCallback) progressCallback(percent);
},
function() {
loadingScreen.unload();
callback();
function(fontTotalCount) {
that._jsonManager.preloadJsons(
function(count, total) {
var percent = Math.floor(
((texturesTotalCount +
audioTotalCount +
fontTotalCount +
count) /
allAssetsTotal) *
100
);
loadingScreen.render(percent);
if (progressCallback) progressCallback(percent);
},
function() {
loadingScreen.unload();
callback();
}
);
}
);
}

View File

@@ -274,10 +274,11 @@ gdjs.RuntimeObject.prototype.getY = function() {
};
/**
* Get the X position of the rendered object.<br>
* Get the X position of the rendered object.
*
* For most objects, this will returns the same value as getX(). But if the object
* has an origin this.is not the same as the point (0,0) of the object displayed,
* getDrawableX will differs.
* has an origin that is not the same as the point (0,0) of the object displayed,
* getDrawableX will differ.
*
* @return {number} The X position of the rendered object.
*/
@@ -286,10 +287,11 @@ gdjs.RuntimeObject.prototype.getDrawableX = function() {
};
/**
* Get the Y position of the rendered object.<br>
* Get the Y position of the rendered object.
*
* For most objects, this will returns the same value as getY(). But if the object
* has an origin this.is not the same as the point (0,0) of the object displayed,
* getDrawableY will differs.
* has an origin that is not the same as the point (0,0) of the object displayed,
* getDrawableY will differ.
*
* @return {number} The Y position of the rendered object.
*/
@@ -530,7 +532,7 @@ gdjs.RuntimeObject.prototype.hasVariable = function(name) {
};
/**
* Hide or show the object
* Hide (or show) the object.
* @param {boolean} enable Set it to true to hide the object, false to show it.
*/
gdjs.RuntimeObject.prototype.hide = function(enable) {
@@ -540,6 +542,11 @@ gdjs.RuntimeObject.prototype.hide = function(enable) {
/**
* Return true if the object is not hidden.
*
* @note This is unrelated to the actual visibility of the objec on the screen.
* For this, see `getVisibilityAABB` to get the bounding boxes of the object as displayed
* on the scene.
*
* @return {boolean} true if the object is not hidden.
*/
gdjs.RuntimeObject.prototype.isVisible = function() {
@@ -555,7 +562,7 @@ gdjs.RuntimeObject.prototype.isHidden = function() {
};
/**
* Return the width of the object
* Return the width of the object.
* @return {number} The width of the object
*/
gdjs.RuntimeObject.prototype.getWidth = function() {
@@ -563,7 +570,7 @@ gdjs.RuntimeObject.prototype.getWidth = function() {
};
/**
* Return the width of the object
* Return the width of the object.
* @return {number} The height of the object
*/
gdjs.RuntimeObject.prototype.getHeight = function() {
@@ -571,16 +578,16 @@ gdjs.RuntimeObject.prototype.getHeight = function() {
};
/**
* Return the X position of the object center, relative to the object position.
* @return {number} the X position of the object center
* Return the X position of the object center, **relative to the object X position** (`getDrawableX`).
* @return {number} the X position of the object center, relative to `getDrawableX()`.
*/
gdjs.RuntimeObject.prototype.getCenterX = function() {
return this.getWidth() / 2;
};
/**
* Return the Y position of the object center, relative to the object position.
* @return {number} the Y position of the object center
* Return the Y position of the object center, **relative to the object position** (`getDrawableY`).
* @return {number} the Y position of the object center, relative to `getDrawableY()`.
*/
gdjs.RuntimeObject.prototype.getCenterY = function() {
return this.getHeight() / 2;
@@ -777,32 +784,28 @@ gdjs.RuntimeObject.prototype.updateHitBoxes = function() {
var centerX = this.getCenterX();
var centerY = this.getCenterY();
if (this.getCenterX() === width / 2 && this.getCenterY() === height / 2) {
this.hitBoxes[0].vertices[0][0] =-width/2.0;
this.hitBoxes[0].vertices[0][1] =-height/2.0;
this.hitBoxes[0].vertices[1][0] =+width/2.0;
this.hitBoxes[0].vertices[1][1] =-height/2.0;
this.hitBoxes[0].vertices[2][0] =+width/2.0;
this.hitBoxes[0].vertices[2][1] =+height/2.0;
this.hitBoxes[0].vertices[3][0] =-width/2.0;
this.hitBoxes[0].vertices[3][1] =+height/2.0;
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
this.hitBoxes[0].move(this.getDrawableX()+this.getCenterX(), this.getDrawableY()+this.getCenterY());
if (centerX === width / 2 && centerY === height / 2) {
this.hitBoxes[0].vertices[0][0] = - centerX;
this.hitBoxes[0].vertices[0][1] = - centerY;
this.hitBoxes[0].vertices[1][0] = + centerX;
this.hitBoxes[0].vertices[1][1] = - centerY;
this.hitBoxes[0].vertices[2][0] = + centerX;
this.hitBoxes[0].vertices[2][1] = + centerY;
this.hitBoxes[0].vertices[3][0] = - centerX;
this.hitBoxes[0].vertices[3][1] = + centerY;
} else {
this.hitBoxes[0].vertices[0][0] = 0;
this.hitBoxes[0].vertices[0][1] = 0;
this.hitBoxes[0].vertices[1][0] = width;
this.hitBoxes[0].vertices[1][1] = 0;
this.hitBoxes[0].vertices[2][0] = width;
this.hitBoxes[0].vertices[2][1] = height;
this.hitBoxes[0].vertices[3][0] = 0;
this.hitBoxes[0].vertices[3][1] = height;
this.hitBoxes[0].move(-centerX, -centerY);
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY);
this.hitBoxes[0].vertices[0][0] = 0 - centerX;
this.hitBoxes[0].vertices[0][1] = 0 - centerY;
this.hitBoxes[0].vertices[1][0] = width - centerX;
this.hitBoxes[0].vertices[1][1] = 0 - centerY;
this.hitBoxes[0].vertices[2][0] = width - centerX;
this.hitBoxes[0].vertices[2][1] = height - centerY;
this.hitBoxes[0].vertices[3][0] = 0 - centerX;
this.hitBoxes[0].vertices[3][1] = height - centerY;
}
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY);
};
/**
@@ -1288,6 +1291,8 @@ gdjs.RuntimeObject.prototype.separateObjectsWithForces = function(objectsLists,
* @return {boolean} true if obj1 and obj2 are in collision
*/
gdjs.RuntimeObject.collisionTest = function(obj1, obj2, ignoreTouchingEdges) {
// 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).
//First check if bounding circle are too far.
var o1w = obj1.getWidth();
var o1h = obj1.getHeight();
@@ -1325,6 +1330,8 @@ gdjs.RuntimeObject.collisionTest = function(obj1, obj2, ignoreTouchingEdges) {
* @return A raycast result with the contact points and distances
*/
gdjs.RuntimeObject.prototype.raycastTest = function(x, y, endX, endY, closest) {
// 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 objW = this.getWidth();
var objH = this.getHeight();
var diffX = this.getDrawableX()+this.getCenterX() - x;

View File

@@ -30,6 +30,7 @@ gdjs.RuntimeScene = function(runtimeGame)
this._gameStopRequested = false;
this._requestedScene = "";
this._isLoaded = false; // True if loadFromScene was called and the scene is being played.
this._isJustResumed = false; // True in the first frame after resuming the paused scene
/** @type gdjs.RuntimeObject[] */
this._allInstancesList = []; //An array used to create a list of all instance when necessary ( see _constructListOfAllInstances )
@@ -156,6 +157,9 @@ gdjs.RuntimeScene.prototype.onPause = function() {
* on screen after having being paused.
*/
gdjs.RuntimeScene.prototype.onResume = function() {
this._isJustResumed = true;
for(var i = 0;i < gdjs.callbacksRuntimeSceneResumed.length;++i) {
gdjs.callbacksRuntimeSceneResumed[i](this);
}
@@ -281,6 +285,8 @@ gdjs.RuntimeScene.prototype.renderAndStep = function(elapsedTime) {
// this.getRenderer().renderDebugDraw(this._allInstancesList, this._layersCameraCoordinates); //TODO
// }
this._isJustResumed = false;
this.render();
if (this._profiler) this._profiler.end("render");
@@ -629,7 +635,8 @@ gdjs.RuntimeScene.prototype.getInitialSharedDataForBehavior = function(name) {
/**
* Get the layer with the given name
* @param {gdjs.Layer} name The name of the layer
* @param {string} name The name of the layer
* @returns {gdjs.Layer} The layer, or the base layer if not found
*/
gdjs.RuntimeScene.prototype.getLayer = function(name) {
if ( this._layers.containsKey(name) )
@@ -638,6 +645,10 @@ gdjs.RuntimeScene.prototype.getLayer = function(name) {
return this._layers.get("");
};
/**
* Check if a layer exists
* @param {string} name The name of the layer
*/
gdjs.RuntimeScene.prototype.hasLayer = function(name) {
return this._layers.containsKey(name);
};
@@ -751,3 +762,13 @@ gdjs.RuntimeScene.prototype.getAdhocListOfAllInstances = function() {
this._constructListOfAllInstances();
return this._allInstancesList;
}
/**
* Check if the scene was just resumed.
* This is true during the first frame after the scene has been unpaused.
*
* @returns {boolean} true if the scene was just resumed
*/
gdjs.RuntimeScene.prototype.sceneJustResumed = function() {
return this._isJustResumed;
}

View File

@@ -541,19 +541,19 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
//Flipping
if ( this._flippedX ) {
x = this._renderer.getUnscaledWidth() - x;
cx = this._renderer.getUnscaledWidth() - cx;
x = x + (cx - x) * 2;
}
if ( this._flippedY ) {
y = this._renderer.getUnscaledHeight() - y;
cy = this._renderer.getUnscaledHeight() - cy;
y = y + (cy - y) * 2;
}
//Scale
x *= Math.abs(this._scaleX);
y *= Math.abs(this._scaleY);
cx *= Math.abs(this._scaleX);
cy *= Math.abs(this._scaleY);
var absScaleX = Math.abs(this._scaleX);
var absScaleY = Math.abs(this._scaleY);
x *= absScaleX;
y *= absScaleY;
cx *= absScaleX;
cy *= absScaleY;
//Rotation
var oldX = x;
@@ -566,8 +566,8 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
y = cy + sinValue*(xToCenterXDelta) + cosValue*(yToCenterYDelta);
result.length = 2;
result[0] = x + this.getDrawableX();
result[1] = y + this.getDrawableY();
result[0] = x + (this.x - this._animationFrame.origin.x*absScaleX);
result[1] = y + (this.y - this._animationFrame.origin.y*absScaleY);
};
/**
@@ -577,7 +577,14 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
gdjs.SpriteRuntimeObject.prototype.getDrawableX = function() {
if ( this._animationFrame === null ) return this.x;
return this.x - this._animationFrame.origin.x*Math.abs(this._scaleX);
var absScaleX = Math.abs(this._scaleX);
if (!this._flippedX) {
return this.x - this._animationFrame.origin.x*absScaleX;
} else {
return this.x + (-this._animationFrame.origin.x
- this._renderer.getUnscaledWidth() + 2*this._animationFrame.center.x)*absScaleX;
}
};
/**
@@ -587,29 +594,44 @@ gdjs.SpriteRuntimeObject.prototype.getDrawableX = function() {
gdjs.SpriteRuntimeObject.prototype.getDrawableY = function() {
if ( this._animationFrame === null ) return this.y;
return this.y - this._animationFrame.origin.y*Math.abs(this._scaleY);
var absScaleY = Math.abs(this._scaleY);
if (!this._flippedY) {
return this.y - this._animationFrame.origin.y*absScaleY;
} else {
return this.y + (-this._animationFrame.origin.y
- this._renderer.getUnscaledHeight() + 2*this._animationFrame.center.y)*absScaleY;
}
};
/**
* Get the X position of the center of the object, relative to top-left of the object.
* @return {number} X position of the center of the object, relative to top-left of the object.
* Get the X position of the center of the object, relative to top-left of the texture of the object (`getDrawableX`).
* @return {number} X position of the center of the object, relative to `getDrawableX()`.
*/
gdjs.SpriteRuntimeObject.prototype.getCenterX = function() {
if ( this._animationFrame === null ) return 0;
//Just need to multiply by the scale as it is the center
return this._animationFrame.center.x*Math.abs(this._scaleX);
if (!this._flippedX) {
//Just need to multiply by the scale as it is the center.
return this._animationFrame.center.x*Math.abs(this._scaleX);
} else {
return (this._renderer.getUnscaledWidth() - this._animationFrame.center.x)*Math.abs(this._scaleX);
}
};
/**
* Get the Y position of the center of the object, relative to top-left of the object.
* @return {number} Y position of the center of the object, relative to top-left of the object.
* Get the Y position of the center of the object, relative to top-left of the texture of the object (`getDrawableY`).
* @return {number} Y position of the center of the object, relative to `getDrawableY()`.
*/
gdjs.SpriteRuntimeObject.prototype.getCenterY = function() {
if ( this._animationFrame === null ) return 0;
//Just need to multiply by the scale as it is the center
return this._animationFrame.center.y*Math.abs(this._scaleY);
if (!this._flippedY) {
//Just need to multiply by the scale as it is the center.
return this._animationFrame.center.y*Math.abs(this._scaleY);
} else {
return (this._renderer.getUnscaledHeight() - this._animationFrame.center.y)*Math.abs(this._scaleY);
}
};
/**
@@ -743,6 +765,7 @@ gdjs.SpriteRuntimeObject.prototype.flipX = function(enable) {
if ( enable !== this._flippedX ) {
this._scaleX *= -1;
this._flippedX = enable;
this.hitBoxesDirty = true;
this._renderer.update();
}
};
@@ -751,6 +774,7 @@ gdjs.SpriteRuntimeObject.prototype.flipY = function(enable) {
if ( enable !== this._flippedY ) {
this._scaleY *= -1;
this._flippedY = enable;
this.hitBoxesDirty = true;
this._renderer.update();
}
};

View File

@@ -1,239 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<Project>
<GDVersion Major="3" Minor="0" Build="11297" Revision="57008" />
<Info winExecutableFilename="" winExecutableIconFile="" linuxExecutableFilename="" macExecutableFilename="" useExternalSourceFiles="false">
<Nom value="Projet" />
<Auteur value="" />
<Extensions>
<Extension name="BuiltinObject" />
<Extension name="BuiltinAudio" />
<Extension name="BuiltinVariables" />
<Extension name="BuiltinTime" />
<Extension name="BuiltinMouse" />
<Extension name="BuiltinKeyboard" />
<Extension name="BuiltinJoystick" />
<Extension name="BuiltinCamera" />
<Extension name="BuiltinWindow" />
<Extension name="BuiltinFile" />
<Extension name="BuiltinNetwork" />
<Extension name="BuiltinScene" />
<Extension name="BuiltinAdvanced" />
<Extension name="Sprite" />
<Extension name="BuiltinCommonInstructions" />
<Extension name="BuiltinCommonConversions" />
<Extension name="BuiltinStringInstructions" />
<Extension name="BuiltinMathematicalTools" />
<Extension name="BuiltinExternalLayouts" />
</Extensions>
<Platforms current="GDevelop JS platform">
<Platform name="GDevelop C++ platform" />
<Platform name="GDevelop JS platform" />
</Platforms>
<WindowW value="800" />
<WindowH value="600" />
<Portable />
<LatestCompilationDirectory value="" />
<FPSmax value="60" />
<FPSmin value="10" />
<verticalSync value="false" />
</Info>
<Resources>
<Resources>
<Resource kind="image" name="Elisa_standing.png" alwaysLoaded="false" smoothed="true" userAdded="true" file="Elisa_standing.png" />
<Resource kind="image" name="Camera.png" alwaysLoaded="false" smoothed="true" userAdded="true" file="Camera.png" />
</Resources>
<ResourceFolders />
</Resources>
<Objects />
<ObjectGroups />
<Variables />
<Scenes firstScene="">
<Scene nom="Nouvelle sc<73>ne" mangledName="Nouvelle_32sc__4524ne" r="209.000000" v="209.000000" b="209.000000" titre="" oglFOV="90.000000" oglZNear="1.000000" oglZFar="500.000000" standardSortMethod="true" stopSoundsOnStartup="true" disableInputWhenNotFocused="true">
<UISettings gridWidth="32.000000" grid="false" snap="true" gridHeight="32.000000" gridR="158.000000" gridG="180.000000" gridB="255.000000" zoomFactor="1.000000" windowMask="true" associatedLayout="" />
<GroupesObjets />
<Objets>
<Objet nom="Obj" type="Sprite">
<Variables />
<Animations>
<Animation typeNormal="false">
<Direction boucle="false" tempsEntre="1.000000">
<Sprites>
<Sprite image="Elisa_standing.png">
<Points>
<Point nom="Pt" X="29.000000" Y="32.000000" />
</Points>
<PointOrigine nom="origine" X="1.000000" Y="8.000000" />
<PointCentre nom="centre" X="34.000000" Y="44.000000" automatic="false" />
<CustomCollisionMask custom="false" />
</Sprite>
</Sprites>
</Direction>
</Animation>
</Animations>
</Objet>
<Objet nom="Pt" type="Sprite">
<Variables />
<Animations>
<Animation typeNormal="false">
<Direction boucle="false" tempsEntre="1.000000">
<Sprites>
<Sprite image="Camera.png">
<Points />
<PointOrigine nom="origine" X="5.000000" Y="5.000000" />
<PointCentre nom="centre" X="5.000000" Y="5.000000" automatic="true" />
<CustomCollisionMask custom="false" />
</Sprite>
</Sprites>
</Direction>
</Animation>
</Animations>
</Objet>
</Objets>
<Layers>
<Layer Name="" Visibility="true">
<Camera DefaultSize="true" Width="0.000000" Height="0.000000" DefaultViewport="true" ViewportLeft="0.000000" ViewportTop="0.000000" ViewportRight="1.000000" ViewportBottom="1.000000" />
</Layer>
</Layers>
<Variables />
<BehaviorsSharedDatas />
<Positions>
<Objet nom="Obj" x="609.026123" y="357.702576" plan="-1" layer="" angle="2.606821" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
<floatInfos />
<stringInfos />
<InitialVariables />
</Objet>
<Objet nom="Obj" x="111.491089" y="54.868164" plan="-1" layer="" angle="-0.882869" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
<floatInfos />
<stringInfos />
<InitialVariables />
</Objet>
<Objet nom="Obj" x="109.283447" y="331.300018" plan="-1" layer="" angle="-2.411300" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
<floatInfos />
<stringInfos />
<InitialVariables />
</Objet>
<Objet nom="Obj" x="594.493713" y="49.738152" plan="-1" layer="" angle="1.661203" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
<floatInfos />
<stringInfos />
<InitialVariables />
</Objet>
<Objet nom="Obj" x="22.494812" y="8.866817" plan="-1" layer="" angle="0.000000" personalizedSize="false" width="0.000000" height="0.000000" locked="false">
<floatInfos />
<stringInfos />
<InitialVariables />
</Objet>
</Positions>
<Events>
<Event disabled="false" folded="false">
<Type value="BuiltinCommonInstructions::Standard" />
<Conditions />
<Actions>
<Action>
<Type value="Delete" />
<Parametre value="Pt" />
<Parametre value="" />
</Action>
</Actions>
</Event>
<Event disabled="false" folded="false">
<Type value="BuiltinCommonInstructions::Standard" />
<Conditions>
<Condition>
<Type value="DepartScene" Contraire="false" />
<Parametre value="" />
</Condition>
<Condition>
<Type value="PosX" Contraire="false" />
<Parametre value="Obj" />
<Parametre value="&gt;" />
<Parametre value="400" />
</Condition>
</Conditions>
<Actions>
<Action>
<Type value="FlipX" />
<Parametre value="Obj" />
<Parametre value="oui" />
</Action>
</Actions>
</Event>
<Event disabled="false" folded="false">
<Type value="BuiltinCommonInstructions::Standard" />
<Conditions>
<Condition>
<Type value="DepartScene" Contraire="false" />
<Parametre value="" />
</Condition>
<Condition>
<Type value="PosY" Contraire="false" />
<Parametre value="Obj" />
<Parametre value="&gt;" />
<Parametre value="300" />
</Condition>
</Conditions>
<Actions>
<Action>
<Type value="FlipY" />
<Parametre value="Obj" />
<Parametre value="oui" />
</Action>
</Actions>
</Event>
<Event disabled="false" folded="false">
<Type value="BuiltinCommonInstructions::Standard" />
<Conditions />
<Actions>
<Action>
<Type value="ChangeDirection" />
<Parametre value="Obj" />
<Parametre value="+" />
<Parametre value="TimeDelta()*10" />
</Action>
</Actions>
</Event>
<Event disabled="false" folded="false">
<Type value="BuiltinCommonInstructions::ForEach" />
<Object value="Obj" />
<Conditions />
<Actions>
<Action>
<Type value="Create" />
<Parametre value="" />
<Parametre value="Pt" />
<Parametre value="Obj.PointX(Pt)" />
<Parametre value="Obj.PointY(Pt)" />
<Parametre value="" />
</Action>
<Action>
<Type value="Create" />
<Parametre value="" />
<Parametre value="Pt" />
<Parametre value="Obj.PointX(Centre)" />
<Parametre value="Obj.PointY(Centre)" />
<Parametre value="" />
</Action>
<Action>
<Type value="Create" />
<Parametre value="" />
<Parametre value="Pt" />
<Parametre value="Obj.X()" />
<Parametre value="Obj.Y()" />
<Parametre value="" />
</Action>
<Action>
<Type value="Create" />
<Parametre value="" />
<Parametre value="Pt" />
<Parametre value="Obj.PointX(Origin)" />
<Parametre value="Obj.PointY(Origin)" />
<Parametre value="" />
</Action>
</Actions>
</Event>
</Events>
</Scene>
</Scenes>
<ExternalEvents />
<ExternalLayouts />
<ExternalSourceFiles />
</Project>

View File

@@ -0,0 +1,729 @@
{
"firstLayout": "",
"gdVersion": {
"build": 98,
"major": 4,
"minor": 0,
"revision": 0
},
"properties": {
"adMobAppId": "",
"folderProject": false,
"linuxExecutableFilename": "",
"macExecutableFilename": "",
"orientation": "default",
"packageName": "",
"projectFile": "/Users/florian/Projects/F/GD/GDJS/tests/games/Sprite flipping and resizing.json",
"scaleMode": "linear",
"sizeOnStartupMode": "",
"useExternalSourceFiles": false,
"version": "1.0.0",
"winExecutableFilename": "",
"winExecutableIconFile": "",
"name": "Projet",
"author": "",
"windowWidth": 800,
"windowHeight": 600,
"latestCompilationDirectory": "",
"maxFPS": 60,
"minFPS": 10,
"verticalSync": false,
"platformSpecificAssets": {},
"loadingScreen": {
"showGDevelopSplash": true
},
"extensions": [
{
"name": "BuiltinObject"
},
{
"name": "BuiltinAudio"
},
{
"name": "BuiltinVariables"
},
{
"name": "BuiltinTime"
},
{
"name": "BuiltinMouse"
},
{
"name": "BuiltinKeyboard"
},
{
"name": "BuiltinJoystick"
},
{
"name": "BuiltinCamera"
},
{
"name": "BuiltinWindow"
},
{
"name": "BuiltinFile"
},
{
"name": "BuiltinNetwork"
},
{
"name": "BuiltinScene"
},
{
"name": "BuiltinAdvanced"
},
{
"name": "Sprite"
},
{
"name": "BuiltinCommonInstructions"
},
{
"name": "BuiltinCommonConversions"
},
{
"name": "BuiltinStringInstructions"
},
{
"name": "BuiltinMathematicalTools"
},
{
"name": "BuiltinExternalLayouts"
}
],
"platforms": [
{
"name": "GDevelop JS platform"
}
],
"currentPlatform": "GDevelop JS platform"
},
"resources": {
"resources": [
{
"alwaysLoaded": false,
"file": "Elisa_standing.png",
"kind": "image",
"metadata": "",
"name": "Elisa_standing.png",
"smoothed": true,
"userAdded": true
},
{
"alwaysLoaded": false,
"file": "Camera.png",
"kind": "image",
"metadata": "",
"name": "Camera.png",
"smoothed": true,
"userAdded": true
}
],
"resourceFolders": []
},
"objects": [],
"objectsGroups": [],
"variables": [],
"layouts": [
{
"b": 209,
"disableInputWhenNotFocused": true,
"mangledName": "Nouvelle_32sc_232ne",
"name": "Nouvelle scène",
"oglFOV": 90,
"oglZFar": 500,
"oglZNear": 1,
"r": 209,
"standardSortMethod": true,
"stopSoundsOnStartup": true,
"title": "",
"v": 209,
"uiSettings": {
"grid": false,
"gridB": 255,
"gridG": 180,
"gridHeight": 32,
"gridOffsetX": 0,
"gridOffsetY": 0,
"gridR": 158,
"gridWidth": 32,
"snap": true,
"windowMask": true,
"zoomFactor": 0.72
},
"objectsGroups": [],
"variables": [],
"instances": [
{
"angle": 2.60682,
"customSize": true,
"height": 179,
"layer": "",
"locked": false,
"name": "Obj",
"width": 80,
"x": 472,
"y": 282,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "rotate",
"value": "1"
},
{
"name": "flipX",
"value": "1"
},
{
"name": "flipY",
"value": "1"
}
]
},
{
"angle": 0.5887,
"customSize": true,
"height": 198,
"layer": "",
"locked": false,
"name": "Obj",
"width": 89,
"x": 112,
"y": 260,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "rotate",
"value": "1"
},
{
"name": "flipY",
"value": "1"
}
]
},
{
"angle": 1.6612,
"customSize": true,
"height": 167,
"layer": "",
"locked": false,
"name": "Obj",
"width": 75,
"x": 453,
"y": 51,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "rotate",
"value": "1"
},
{
"name": "flipX",
"value": "1"
}
]
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 732,
"y": 497,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "flipX",
"value": "1"
},
{
"name": "flipY",
"value": "1"
}
]
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 14,
"y": 492,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "flipY",
"value": "1"
}
]
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 360,
"y": 11,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "flipX",
"value": "1"
}
]
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 359,
"y": 9,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 723,
"y": 13,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "flipX",
"value": "1"
}
]
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Obj",
"width": 0,
"x": 22.4948,
"y": 8.86682,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": -0.882869,
"customSize": true,
"height": 168,
"layer": "",
"locked": false,
"name": "Obj",
"width": 75,
"x": 111.491,
"y": 54.8682,
"zOrder": -1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": [
{
"name": "rotate",
"value": "1"
}
]
}
],
"objects": [
{
"name": "Obj",
"tags": "",
"type": "Sprite",
"updateIfNotVisible": true,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "Elisa_standing.png",
"points": [
{
"name": "Pt",
"x": 29,
"y": 32
}
],
"originPoint": {
"name": "origine",
"x": 1,
"y": 8
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 34,
"y": 44
},
"customCollisionMask": [
[
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
}
]
]
}
]
}
]
}
]
},
{
"name": "Pt",
"tags": "",
"type": "Sprite",
"updateIfNotVisible": true,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "Camera.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 5,
"y": 5
},
"centerPoint": {
"automatic": true,
"name": "centre",
"x": 0,
"y": 0
},
"customCollisionMask": [
[
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
}
]
]
}
]
}
]
}
]
}
],
"events": [
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [],
"actions": [
{
"type": {
"inverted": false,
"value": "Delete"
},
"parameters": [
"Pt",
""
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "DepartScene"
},
"parameters": [
""
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "VarObjet"
},
"parameters": [
"Obj",
"flipX",
"=",
"1"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "FlipX"
},
"parameters": [
"Obj",
"oui"
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "DepartScene"
},
"parameters": [
""
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "VarObjet"
},
"parameters": [
"Obj",
"flipY",
"=",
"1"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "FlipY"
},
"parameters": [
"Obj",
"oui"
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "VarObjet"
},
"parameters": [
"Obj",
"rotate",
"=",
"1"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "ChangeDirection"
},
"parameters": [
"Obj",
"+",
"TimeDelta()*10"
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::ForEach",
"object": "Obj",
"conditions": [],
"actions": [
{
"type": {
"inverted": false,
"value": "Create"
},
"parameters": [
"",
"Pt",
"Obj.PointX(\"Pt\")",
"Obj.PointY(\"Pt\")",
""
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Create"
},
"parameters": [
"",
"Pt",
"Obj.PointX(\"Centre\")",
"Obj.PointY(\"Centre\")",
""
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Create"
},
"parameters": [
"",
"Pt",
"Obj.X()",
"Obj.Y()",
""
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Create"
},
"parameters": [
"",
"Pt",
"Obj.PointX(\"Origin\")",
"Obj.PointY(\"Origin\")",
""
],
"subInstructions": []
}
],
"events": []
}
],
"layers": [
{
"name": "",
"visibility": true,
"cameras": [
{
"defaultSize": true,
"defaultViewport": true,
"height": 0,
"viewportBottom": 1,
"viewportLeft": 0,
"viewportRight": 1,
"viewportTop": 0,
"width": 0
}
],
"effects": []
}
],
"behaviorsSharedData": []
}
],
"externalEvents": [],
"eventsFunctionsExtensions": [],
"externalLayouts": [],
"externalSourceFiles": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

View File

@@ -0,0 +1,581 @@
{
"firstLayout": "",
"gdVersion": {
"build": 98,
"major": 4,
"minor": 0,
"revision": 0
},
"properties": {
"adMobAppId": "",
"folderProject": false,
"linuxExecutableFilename": "",
"macExecutableFilename": "",
"orientation": "landscape",
"packageName": "com.example.gamename",
"projectFile": "/Users/florian/Projects/F/GD/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json",
"scaleMode": "linear",
"sizeOnStartupMode": "adaptWidth",
"useExternalSourceFiles": false,
"version": "1.0.0",
"winExecutableFilename": "",
"winExecutableIconFile": "",
"name": "Project",
"author": "",
"windowWidth": 800,
"windowHeight": 600,
"latestCompilationDirectory": "",
"maxFPS": 60,
"minFPS": 20,
"verticalSync": false,
"platformSpecificAssets": {},
"loadingScreen": {
"showGDevelopSplash": true
},
"extensions": [
{
"name": "BuiltinObject"
},
{
"name": "BuiltinAudio"
},
{
"name": "BuiltinVariables"
},
{
"name": "BuiltinTime"
},
{
"name": "BuiltinMouse"
},
{
"name": "BuiltinKeyboard"
},
{
"name": "BuiltinJoystick"
},
{
"name": "BuiltinCamera"
},
{
"name": "BuiltinWindow"
},
{
"name": "BuiltinFile"
},
{
"name": "BuiltinNetwork"
},
{
"name": "BuiltinScene"
},
{
"name": "BuiltinAdvanced"
},
{
"name": "Sprite"
},
{
"name": "BuiltinCommonInstructions"
},
{
"name": "BuiltinCommonConversions"
},
{
"name": "BuiltinStringInstructions"
},
{
"name": "BuiltinMathematicalTools"
},
{
"name": "BuiltinExternalLayouts"
}
],
"platforms": [
{
"name": "GDevelop JS platform"
}
],
"currentPlatform": "GDevelop JS platform"
},
"resources": {
"resources": [
{
"alwaysLoaded": false,
"file": "Ship-1.png",
"kind": "image",
"metadata": "",
"name": "Ship-1.png",
"smoothed": true,
"userAdded": false
},
{
"alwaysLoaded": false,
"file": "Ship-1-0.png",
"kind": "image",
"metadata": "",
"name": "Ship-1-0.png",
"smoothed": true,
"userAdded": true
},
{
"alwaysLoaded": false,
"file": "NewObject-1.png",
"kind": "image",
"metadata": "",
"name": "NewObject-1.png",
"smoothed": true,
"userAdded": false
}
],
"resourceFolders": []
},
"objects": [],
"objectsGroups": [],
"variables": [],
"layouts": [
{
"b": 209,
"disableInputWhenNotFocused": true,
"mangledName": "NewScene",
"name": "NewScene",
"oglFOV": 90,
"oglZFar": 500,
"oglZNear": 1,
"r": 209,
"standardSortMethod": true,
"stopSoundsOnStartup": true,
"title": "",
"v": 209,
"uiSettings": {
"grid": false,
"gridB": 255,
"gridG": 180,
"gridHeight": 32,
"gridOffsetX": 0,
"gridOffsetY": 0,
"gridR": 158,
"gridWidth": 32,
"snap": true,
"windowMask": false,
"zoomFactor": 1
},
"objectsGroups": [],
"variables": [],
"instances": [
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Ship",
"width": 0,
"x": 365,
"y": 276,
"zOrder": 1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 36,
"layer": "",
"locked": false,
"name": "Collider",
"width": 31,
"x": 505,
"y": 385,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "Collider",
"width": 0,
"x": 472,
"y": 168,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 36,
"layer": "",
"locked": false,
"name": "Collider",
"width": 31,
"x": 149,
"y": 179,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
}
],
"objects": [
{
"name": "Ship",
"tags": "",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "Ship",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 0.08,
"sprites": [
{
"hasCustomCollisionMask": true,
"image": "Ship-1-0.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 0,
"y": 0
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 24,
"y": 29
},
"customCollisionMask": [
[
{
"x": 66.832,
"y": 15.2109
},
{
"x": 82.5742,
"y": 12.3047
},
{
"x": 81.5078,
"y": 45.1836
},
{
"x": 66.1602,
"y": 41.5391
}
]
]
}
]
}
]
}
]
},
{
"name": "Collider",
"tags": "",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 0.08,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "NewObject-1.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 0,
"y": 0
},
"centerPoint": {
"automatic": true,
"name": "centre",
"x": 0,
"y": 0
},
"customCollisionMask": []
}
]
}
]
}
]
}
],
"events": [
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Comment",
"color": {
"b": 109,
"g": 230,
"r": 255,
"textB": 0,
"textG": 0,
"textR": 0
},
"comment": "The spaceship center point should always stay at the same position when the ship is flipped. In other words, there should be no \"jump\" when changing direction or rotating.",
"comment2": ""
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "KeyPressed"
},
"parameters": [
"Ship",
"Up"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "AddForceAL"
},
"parameters": [
"Ship",
"Ship.Direction()-90",
"100",
""
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "KeyPressed"
},
"parameters": [
"Ship",
"Down"
],
"subInstructions": []
}
],
"actions": [],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "KeyPressed"
},
"parameters": [
"Ship",
"Left"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "FlipX"
},
"parameters": [
"Ship",
"no"
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Rotate"
},
"parameters": [
"Ship",
"-90",
""
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "KeyPressed"
},
"parameters": [
"Ship",
"Right"
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "FlipX"
},
"parameters": [
"Ship",
"yes"
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Rotate"
},
"parameters": [
"Ship",
"90",
""
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Comment",
"color": {
"b": 109,
"g": 230,
"r": 255,
"textB": 0,
"textG": 0,
"textR": 0
},
"comment": "Only the arm of the ship should trigger a collision with \"Collider\" objects",
"comment2": ""
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [],
"actions": [
{
"type": {
"inverted": false,
"value": "SceneBackground"
},
"parameters": [
"",
"\"147;147;147\""
],
"subInstructions": []
}
],
"events": []
},
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Standard",
"conditions": [
{
"type": {
"inverted": false,
"value": "CollisionNP"
},
"parameters": [
"Ship",
"Collider",
"",
"",
""
],
"subInstructions": []
}
],
"actions": [
{
"type": {
"inverted": false,
"value": "SceneBackground"
},
"parameters": [
"",
"\"0;193;1\""
],
"subInstructions": []
}
],
"events": []
}
],
"layers": [
{
"name": "",
"visibility": true,
"cameras": [
{
"defaultSize": true,
"defaultViewport": true,
"height": 0,
"viewportBottom": 1,
"viewportLeft": 0,
"viewportRight": 1,
"viewportTop": 0,
"width": 0
}
],
"effects": []
}
],
"behaviorsSharedData": []
}
],
"externalEvents": [],
"eventsFunctionsExtensions": [],
"externalLayouts": [],
"externalSourceFiles": []
}

View File

@@ -32,9 +32,7 @@ module.exports = function(config) {
'../Runtime/variable.js',
'../Runtime/variablescontainer.js',
'../Runtime/oncetriggers.js',
'../Runtime/runtimescene.js',
'../Runtime/runtimebehavior.js',
'../Runtime/runtimeobject.js',
'../Runtime/spriteruntimeobject.js',
'../Runtime/events-tools/commontools.js',
'../Runtime/events-tools/runtimescenetools.js',
@@ -59,13 +57,17 @@ module.exports = function(config) {
'./tests/Extensions/**.js',
//All tests files:
'./tests/init.js',
'./tests-utils/init.gdjs.js',
'./tests-utils/init.pixiruntimegamewithassets.js',
'../../Extensions/**/tests/**.spec.js',
'./tests/**/*.js',
//All benchmark files:
'./benchmarks/init.js',
'./benchmarks/**/*.js'
'./benchmarks/**/*.js',
// Assets
{pattern: './tests-utils/assets/*.jpg', watched: false, included: false, served: true, nocache: false}
]
});
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,39 @@
/**
* Create and return a game with a few assets loaded, to be used in tests
* needing real images.
*/
gdjs.getPixiRuntimeGameWithAssets = () => {
if (gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets) {
return Promise.resolve(gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets);
}
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
properties: { windowWidth: 800, windowHeight: 600 },
resources: {
resources: [
{
kind: 'image',
name: 'base/tests-utils/assets/64x64.jpg',
metadata: '',
file: 'base/tests-utils/assets/64x64.jpg',
},
],
},
});
return new Promise(resolve => {
runtimeGame.loadAllAssets(
() => {
console.info('Done loading assets for test game');
gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets = runtimeGame;
resolve(gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets);
},
() => {/* Ignore progress */}
);
});
};
/** @type gdjs.RuntimeGame */
gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets = null;

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