Compare commits

...

89 Commits

Author SHA1 Message Date
Florian Rival
1ab023e211 Add a comment explaining how to generate latest.yml files if necessary (#3946)
Don't show in changelog
2022-05-23 11:14:20 +02:00
AlexandreS
aa4e9e25bf Bump newIDE version 2022-05-23 11:02:13 +02:00
Clément Pasteau
bc235ef492 Revert publish declaration in package.json for latest.yml generation (#3944)
Do not show in changelog
2022-05-23 10:40:39 +02:00
AlexandreS
eabd1a4f23 Compute object variable suggestions on each object change in the expression builder 2022-05-23 08:58:30 +02:00
Florian Rival
62ef3e729b Speed up the editor development server startup time by 3x (#3941)
* Patch Webpack config to avoid processing the locale message files (translation files)

Only show in developer changelog
2022-05-22 21:24:49 +02:00
Florian Rival
802ee0c03f Upgrade Electron to 18.2 and React dev tools to latest version (#3893)
Only show in developer changelog
2022-05-21 17:00:47 +02:00
Florian Rival
7ef2050de7 Normalise imports to avoid .js suffix (#3938)
Don't show in changelog
2022-05-21 12:54:50 +02:00
AlexandreS
eeea1b99f4 New variables list improvements
* Fix drop down styling on light theme for selected rows
* Make sure the toolbar is fixed on every dialog the dialog is used in
* When adding a variable, scroll to the new row and focus name
* When searching, display children of collections to enable the user path "Search by collection name > Find child of interest > change child value".
  * If one changes a variable name that justifies its display during the search, the row does not disappear as soon as one is done with editing the name
2022-05-20 15:33:09 +02:00
Florian Rival
e75d49ea51 Upgrade to latest React v16 and react-scripts v4 (#3934)
* This enables "Fast Refresh" of React components (hot reloading after a change is made)
* This prepares the codebase for a migration to React v17/v18 later.

Only show in developer changelog
2022-05-20 15:04:23 +02:00
Florian Rival
048674a7d0 Fix ObjectGroupsList story not working
Don't show in changelog
2022-05-20 13:18:23 +02:00
Florian Rival
0502bd1f7a Fix properties of a custom behavior edited in an extension not taking the full width of the dialog 2022-05-20 13:01:53 +02:00
Florian Rival
66b84441ea Fix the GDJS development watcher making the electron renderer process crash when reloading (#3931)
Only show in developer changelog
2022-05-19 21:46:16 +02:00
D8H
d9e27dc4f3 Review change: rename the function (#3926)
Do not show in changelog
2022-05-19 14:20:39 +02:00
D8H
a3c3ffc3bb Fix missing exported extension functions (#3908) 2022-05-19 11:36:39 +02:00
Fannie Yan
dc33b6cc01 Fix typo in Leaderboard sort order (#3925)
Don't show in changelog
2022-05-19 11:16:23 +02:00
github-actions[bot]
4a0c1c5ad2 Update translations [skip ci] (#3891)
Do not show in changelog
2022-05-19 10:14:29 +02:00
AlexandreS
5820c5dd3e Bump newIDE version 2022-05-19 10:13:04 +02:00
D8H
606f5aff1b Fix: resource folder won't open on Windows (#3922)
Don't show in changelogs
2022-05-18 17:56:51 +02:00
AlexandreS
1f42374417 Reorder effects with drag n drop
Effects are applied in order and different orders may give different results, so ordering them is important. You can now do it with drag n drop.
Also, when renaming effects, a few checks are now done in order to prevent unexpected results.
2022-05-18 16:25:11 +02:00
AlexandreS
3356400026 Redesign of the variables editor
* Each row should be easier to read in a glance
* You can now collapse collection variables for a better readability
* Reorder and move of variables is now available with drag n drop
* You can now undo and redo changes
* Paste one or more variables at any level of depth you want
* Search in the names and values of each variables with a filter effect
2022-05-18 15:18:54 +02:00
Clément Pasteau
b811b1e873 Track asset packs opening analytics (#3919) 2022-05-18 14:24:13 +02:00
Florian Rival
e985b11971 Prevent layout shift when opening the asset store
Don't show in changelog
2022-05-16 21:16:48 +02:00
Florian Rival
aefc5e2fed Fix warning
Don't show in changelog
2022-05-16 20:57:43 +02:00
Clément Pasteau
0ccb7c3216 Add an asset pack gallery in the asset store (#3896) 2022-05-16 19:09:07 +02:00
Florian Rival
33c2c9c6cf Make GDevelop.js build happen on branches/Pull Requests (but not from forks) (#3914) 2022-05-16 19:04:44 +02:00
AlexandreS
55a6306f05 Fix range of volume on fade sound/music action
* ⚠️ This action was introduced with a buggy range (0 to 1 instead of 0 to 100). If you happen to use it, please update the range of volume used once the GDevelop new version is installed
2022-05-16 17:00:02 +02:00
AlexandreS
d766e32a18 Fix: Align image frame and full-size collision mask in collision mask editor 2022-05-16 16:47:39 +02:00
AlexandreS
7cdf4e1184 Fix: Prevent users to type a game slug longer than the limit 2022-05-16 16:19:29 +02:00
Fannie Yan
2540bf3c3e Allow user to limit score values saved in leaderboards (#3910)
Add a Dialog in a game's dashboard leaderboard tab allowing users to chose sort direction and limit possible scores for their games.
2022-05-16 16:10:59 +02:00
Florian Rival
6adec363d5 Enable C++ tests by default in CMake
Only show in developer changelog
2022-05-13 17:47:16 +00:00
D8H
91a02d132f Remove the dependency to SFML (#3865)
* This allows to run Core tests more easily.
2022-05-13 13:27:41 +02:00
Florian Rival
67718a364b Fix missing https in wiki links
Don't show in changelog
2022-05-13 12:49:02 +02:00
Florian Rival
e9b600d885 Update links to the new wiki
Don't show in changelog
2022-05-13 12:43:25 +02:00
Florian Rival
696dfcb746 Allow to choose installation directory when installing GDevelop on Windows (#3889)
* Also allow to choose if GDevelop must be installed system-wide or just for the user.
2022-05-13 12:06:33 +02:00
Fannie Yan
437edd1fe7 Make anchor behavior more intuitive (#3892)
* Anchor an edge of an object to set its distance from the window edges. Single anchor will not stretch the object anymore; you will need to set anchors to object's opposite edges for this effect.
* Add a button to switch between legacy and new behavior.
2022-05-13 10:50:40 +02:00
Florian Rival
48599ae9ca Improve consistency of some dialogs
Don't show in changelog
2022-05-12 22:30:57 +02:00
Florian Rival
c288700f2d Fix warnings
Don't show in changelog
2022-05-12 21:55:08 +02:00
Florian Rival
fbfb5dbaa6 Improve design of the alerts/messages and make margins consistent in the app (#3897) 2022-05-12 21:50:18 +02:00
Florian Rival
dfa27df64b Fix parameters of extensions actions/conditions made in the editor not having a default proper name
* This was creating a crash, because of infinite warnings, if clicking on a parameter with an empty name and then clicking on another parameter also having an empty name.

Fix #3888
2022-05-12 15:44:05 +02:00
Florian Rival
1ef404b9a9 Update the preview to create the BrowserWindow from the electron main process (#3890)
Only show in the developer changelog
2022-05-11 16:58:40 +02:00
github-actions[bot]
785dd6f08b Update translations [skip ci] (#3867)
Co-authored-by: fannieyan <fannieyan@users.noreply.github.com>
2022-05-11 10:33:46 +02:00
Fannie Yan
59bc76e144 Add tutorials in app (#3870)
- Added dismissable tutorials in events sheet, variable editors and object variable tab, add object dialog, sprite animations tab, export game for web tab, add new extension dialog.

- Added a tutorial button.
2022-05-10 17:42:45 +02:00
Florian Rival
d873d9747a Display all autocompletions in the expression editor when listing them (#3886)
* The list is not truncated anymore so you can scroll or use the arrow keys to browse all the completions (useful to see everything: objects, expressions, but also the available easing for tweens, etc...)
2022-05-10 13:07:38 +02:00
Florian Rival
a7fe36f351 Fix web-app recent files wrongly containing URLs to examples (#3885) 2022-05-10 09:15:52 +02:00
Florian Rival
252eeb86b1 Upgrade the underlying runtime running the desktop app, and exported desktop games, to Electron 18 (#3806) (#3854)
* This brings improved performance and compatibility for the desktop app with Windows, macOS and Linux.
* In particular, tilemap performance should be greatly improved both in the scene editor, the preview and exported games on Linux.
* This should also improve performance of both the editor and games on macOS when running on Apple Silicon (M1 cpus).
* Thanks to @nilaymajorwar for the ground work on this upgrade.

Only show the rest in developer changelog:

* More work is needed to isolate the Electron "renderer" process from the "main" process (both for the editor and for games), by using `contextBridge` and preload scripts (https://www.electronjs.org/docs/latest/tutorial/process-model). Your help is welcome to progressively do this migration.

Co-authored-by: Nilay Majorwar <nilaymajorwar@gmail.com>
2022-05-08 17:24:49 +02:00
Clément Pasteau
66145ce506 Specify supported languages for the Microsoft Store version (appx) (#3873) 2022-05-07 22:23:49 +02:00
Clément Pasteau
0dd10d2fce Fix history not working well on the Events Sheet
* Fix changes not being detected for history when editing an event Inline
* Fix losing focus when switching tabs
2022-05-06 12:15:31 +02:00
Clément Pasteau
5d35241c4c Fix not setting authors correctly when publishing to Liluo
* Also fix project being correctly saved
* And disabled the fields while publishing
2022-05-06 12:07:53 +02:00
AlexandreS
ba368d9eed Improve points and collision masks editors
- Make coordinates inputs more user friendly
  - Removal of unnecessary decimal places
  - Do not replace empty value with 0 to allow whole value erasing
- Better understanding of which point is which
  - Add possibility to select points in the list so that it is highlighted in the preview (same for hovered points)
  - Allow drag n drop for custom collision mask points
- Open image preview at a zoom factor that is adapted to the sprite size so that one doesn't have to zoom at each opening.
2022-05-06 09:56:06 +02:00
Florian Rival
fbeeb589a2 Fix package-lock.json
Don't show in changelog
2022-05-05 15:04:34 +02:00
Florian Rival
3ec4e3b6dd Add missing metric about desktop or web-app
* Also fix the dev version sending analytics

Don't show in changelog
2022-05-05 12:13:32 +02:00
Florian Rival
11ccaf2ea4 Add Posthog for following app usage analytics
* Fix UUID not reset when a user was logging out
* This should allow to create public dashboard showing various app usage (like popular extensions), with more accurate numbers than using Keen.io
2022-05-05 11:40:38 +02:00
Florian Rival
274d31f563 Update help links for the Time extension
Don't show in changelog
2022-05-04 18:56:40 +02:00
AlexandreS
c37049cd72 Add new settings to leaderboards
* Add parameter to control number of entries to display
* Add possibility to set a default leaderboard, displayed on Liluo game page by default
2022-05-04 10:37:01 +02:00
Florian Rival
7cdc92c776 Introduce the "Wait X seconds" action (#3852)
* This action allow to wait for a few seconds before continuing to run the next actions and sub-events.
* It is perfect to create cut scenes, advanced logic or just run things progressively without relying on timers. In a lot of cases, it's simpler and faster to use this new action.
* The action remember the picked objects: it works like an usual event, but run the actions (and sub-events) a bit later in time. While the action wait, other events continue to run as usual.
* Thanks to @arthuro555 for the ground work and follow up on this new feature.

Only show the rest in developer changelog:

* Add support for asynchronous actions (including for objects).
* Add exhaustive test cases for asynchronous actions. 

Co-authored-by: Arthur Pacaud <arthur.pacaud@hotmail.fr>
2022-05-02 19:44:15 +02:00
Florian Rival
471cd61d82 Add test for "Or" condition with objects
Don't show in changelog
2022-05-02 18:23:56 +02:00
Fannie Yan
afb66f213d Add tutorials in behavior, objects, conditions and actions (#3869)
* Add tutorials in behavior, objects, conditions and actions
2022-05-02 17:49:48 +02:00
Clément Pasteau
8ad70e57cc Encode thumbnail URL before sending it to the GDevelop services
* This prevents a bug where the thumbnail would not work when the filename has non-conventional characters
2022-04-29 15:40:54 +02:00
Arthur Pacaud
d9ea04e059 Allow webp images to be selected for object resources, and webm for videos (#3862)
* These formats are providing better compression than PNG or MP4, but they might not work on older devices - be sure to double check if the devices or browsers of your audience support them.
2022-04-29 11:40:07 +02:00
github-actions[bot]
bb3abdb1fa Update translations [skip ci] (#3831)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-04-28 16:40:10 +02:00
Oxey405
cdddcafa68 Add an action to fade a sound or a music on a channel to a new volume (#3724) 2022-04-28 11:17:46 +02:00
AlexandreS
14175c334e Do not show in changelog - Trim player name when formatting it 2022-04-28 11:02:12 +02:00
D8H
6bd4dff03e Remove variable parameters from SavePlayerScore action (#3860)
Do not show in changelog
2022-04-28 11:01:36 +02:00
Clément Pasteau
ceec39f6a2 Bump version to 5.0.132
Do not show in changelog
2022-04-27 17:59:58 +02:00
AlexandreS
5abf80a0c9 Add possibility to customize leaderboard score column appearance (#3857) 2022-04-27 17:46:15 +02:00
AlexandreS
7f955d8703 Add expressions to specify decimal place where to round, floor and ceil 2022-04-27 16:47:14 +02:00
D8H
781dd42ccb Add analytics for extensions (#3848) 2022-04-26 09:16:01 +02:00
Fannie Yan
e584fa952d Make loader screen cover input fields (#3850)
Don't show in changelog
2022-04-25 09:57:21 +02:00
Florian Rival
5681667dde Add a gitpod.io configuration file (experimental)
* This is useful to edit some part of the codebase on gitpod.io

Only show in developer changelog
2022-04-24 14:29:11 +00:00
Florian Rival
d16f04f4a2 Change AlertMessage design to be flat
Avoid shadows if not really needed.

Don't show in changelog
2022-04-24 14:23:08 +00:00
Aurélien Vivet
e9b464beba Improve wording consistency (#3851) 2022-04-23 16:35:51 +02:00
Florian Rival
7597dbe0d1 Add (new) conditions and expressions to compare the number of instances picked or living on the scene (#3842)
* Add `PickedInstancesCount` and `SceneInstancesCount` expressions, to replace `Count`. These expressions don't do any "picking" of instances, so they are safe to use anywhere without "weird" side effects.
* Equivalent conditions are available for all objects. These conditions allow to check at any point in your events the number of instances living on the scene or picked by actions/conditions.
  * This is useful to check if enough objects are picked by a condition before launching an action. 
  * Because this condition does not change the already picked objects, it's safe to use anywhere without any side effect.

Only show the rest in the developer changelog:
* Allow to read events missing some fields (like `disabled`, `folded`).
* Reduce the useless information stored in project JSON files by not storing the fields if they have their default value.
2022-04-23 16:35:10 +02:00
Clément Pasteau
4777f0a824 Save project after creation on Desktop app 2022-04-22 19:11:34 +02:00
AlexandreS
5b2532f8f3 Remove unused variables
Do not show in changelog
2022-04-21 15:02:41 +02:00
AlexandreS
2c43de5120 Improve objects and groups lists
* Use same drag and drop effect for both lists
* Add possibility to duplicate group
* Add possibility to move multiple objects at once
2022-04-21 14:26:42 +02:00
Florian Rival
124ce1101d Fix leaderboards category icon 2022-04-20 14:27:38 +02:00
AlexandreS
eb3d6c2670 Send events when opening and finalizing events extraction as function
Don't show in changelog
2022-04-20 12:17:09 +02:00
Clément Pasteau
f737fa479f Don't allow picking image URLs that will fail when building the game on the web application
- targeting specifically images with failed CORS
2022-04-19 17:45:48 +02:00
Florian Rival
f93b3bc3b4 Fix crash when an empty effect is added to a layer
Fix #3835
2022-04-19 12:47:20 +02:00
Elairyx
5c6eb2dadb Polish the wording of particle emitter actions, conditions and expressions (#3829) 2022-04-15 16:24:51 +01:00
AlexandreS
3e6ca186f8 Add action to change image for particle emitter using project resources 2022-04-15 15:37:38 +02:00
github-actions[bot]
860e9d36e4 Update translations [skip ci] (#3808)
Co-authored-by: fannieyan <fannieyan@users.noreply.github.com>
2022-04-14 11:40:38 +02:00
Fannie Yan
c8b461cc5f Merge game management buttons in games dashboard (#3826)
* Merge game management button into one in Games dashboard
2022-04-14 10:51:48 +02:00
AlexandreS
36cdc5720a Bump newIDE version 2022-04-14 10:47:33 +02:00
AlexandreS
a7cd53b921 Add built-in leaderboard feature (actions, conditions, administration) (#3759) 2022-04-14 10:25:48 +02:00
D8H
01a25400ff New touch event functions that allow to get them several times (#3820)
* It allows extensions to handle touch events without conflicting with the scene event sheets.
* Existing touch functions continue to work and can be used alongside new ones without any issue.
2022-04-13 12:00:33 +02:00
D8H
2b484c0cf1 The url slug of a game can be verified and changed right before publication (#3811) 2022-04-12 20:39:34 +02:00
Fannie Yan
13204e4b53 Add a button "Access public profile" in one's profile details (#3819)
Add a button in profile details that opens the user's public profile on Liluo.io
2022-04-11 14:41:08 +02:00
D8H
c6d6466d54 Fix the input checkboxes "Playable with [...]" randomly checked when publishing a game for the first time (#3818) 2022-04-08 11:45:20 +01:00
568 changed files with 54526 additions and 45545 deletions

View File

@@ -2,10 +2,17 @@
# on the Electron runtime (newIDE/electron-app) for macOS and Linux.
# For Windows, see the appveyor.yml file.
# This also builds GDevelop.js and store it on a S3 so it can be used to run
# GDevelop without building it from scratch.
# Note that these CircleCI builds/tests are not launched on Pull Requests from forks,
# to avoid sharing secrets.
version: 2.1
orbs:
aws-cli: circleci/aws-cli@2.0.6
jobs:
# Build the **entire** app for macOS.
build-macos:
macos:
xcode: 12.5.1
@@ -75,6 +82,7 @@ jobs:
name: Deploy to S3 (latest)
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the **entire** app for Linux.
build-linux:
# CircleCI docker workers are failing if they don't have enough memory (no swap)
resource_class: xlarge
@@ -153,10 +161,67 @@ jobs:
name: Deploy to S3 (latest)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the WebAssembly library only (so that it's cached on a S3 and easy to re-use).
build-gdevelop_js-wasm-only:
docker:
- image: cimg/node:16.13
working_directory: ~/GDevelop
steps:
- checkout
- aws-cli/setup
# System dependencies (for Emscripten)
- run:
name: Install dependencies for Emscripten
command: sudo apt-get update && sudo apt install cmake
- run:
name: Install Python3 dependencies for Emscripten
command: sudo apt install python-is-python3 python3-distutils -y
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
# GDevelop.js dependencies
- restore_cache:
keys:
- gdevelop.js-linux-nodejs-dependencies-{{ checksum "GDevelop.js/package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- gdevelop.js-linux-nodejs-dependencies-
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
- save_cache:
paths:
- GDevelop.js/node_modules
key: gdevelop.js-linux-nodejs-dependencies-{{ checksum "GDevelop.js/package-lock.json" }}
# Upload artifacts (CircleCI)
- store_artifacts:
path: Binaries/embuild/GDevelop.js
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/
- run:
name: Deploy to S3 (latest)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/latest/
workflows:
builds:
jobs:
- build-gdevelop_js-wasm-only
- build-macos:
filters:
branches:

View File

@@ -12,7 +12,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](https://wiki.gdevelop.io/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose known beta 105 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@
/Binaries/.embuild*
/Binaries/build*
/Binaries/embuild*
/emsdk
*.dll
*.exe
*.a

25
.gitpod.yml Normal file
View File

@@ -0,0 +1,25 @@
# This is a configuration file allowing to quickly set up a development environment
# on GitPod (https://www.gitpod.io/).
# Also check GitHub codespaces if you're interested in working
# on a remote development server.
# This works well for:
# - The editor web-app, including the C++ classes.
# This is not yet adapted for:
# - Working on the game engine or extensions, as they can't be easily tested on the web-app.
# - Working on the desktop app (Electron).
tasks:
- name: Install dependencies for Emscripten and build GDevelop.js
init: |
sudo apt-get update
sudo apt install cmake python-is-python3 python3-distutils -y
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
cd GDevelop.js
npm install
source ../emsdk/emsdk_env.sh && npm run build -- --dev
cd ..
- name: Install GDevelop IDE dependencies
init: cd newIDE/app && npm install && cd ../electron-app && npm install

View File

@@ -1,9 +1,6 @@
# Travis CI configuration to build and run all tests
# (and typing/formatting) for the Core, newIDE, GDJS.
#
# This builds GDevelop.js and store it on a S3 so it can be used to run
# GDevelop without building it.
#
# See also Semaphore CI for quick tests (not building GDevelop.js, so
# faster but not always reliable).
@@ -17,18 +14,7 @@ cache:
directories:
- $HOME/.npm
services:
# Virtual Framebuffer 'fake' X server for SFML
- xvfb
addons:
artifacts:
s3_region: "us-east-1"
target_paths:
- /$(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/embuild/GDevelop.js
apt:
sources:
- ubuntu-toolchain-r-test
@@ -36,20 +22,8 @@ addons:
# Build dependencies:
- cmake
- p7zip-full
# SFML dependencies:
- libopenal-dev
- libjpeg-dev
- libglew-dev
- libudev-dev
- libxrandr-dev
- libsndfile1-dev
- libglu1-mesa-dev
- libfreetype6-dev
before_install:
#Activate X Virtual Framebuffer to allow tests to
#use SFML.
- "export DISPLAY=:99.0"
# This workaround is required to avoid libstdc++ errors (Emscripten requires a recent version of libstdc++)
- wget -q -O libstdc++6 http://security.ubuntu.com/ubuntu/pool/main/g/gcc-5/libstdc++6_5.4.0-6ubuntu1~16.04.12_amd64.deb
- sudo dpkg --force-all -i libstdc++6

View File

@@ -7,11 +7,9 @@
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"defines": [
@@ -27,7 +25,6 @@
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
"/usr/local/include",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
"/usr/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
@@ -48,7 +45,6 @@
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
@@ -78,7 +74,6 @@
"${workspaceRoot}/GDJS",
"${workspaceRoot}/Extensions",
"${workspaceRoot}/Core",
"${workspaceRoot}/ExtLibs/SFML/include",
"${workspaceRoot}"
],
"defines": [
@@ -101,4 +96,4 @@
}
],
"version": 4
}
}

14
.vscode/launch.json vendored
View File

@@ -15,6 +15,20 @@
"disableOptimisticBPs": true,
"cwd": "${workspaceFolder}/GDevelop.js"
},
{
"type": "node",
"request": "launch",
"name": "newIDE/app Jest tests (current file)",
"program": "${workspaceFolder}/newIDE/app/node_modules/.bin/react-scripts",
"args": [
"test", "--env=node",
"${fileBasenameNoExtension}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"cwd": "${workspaceFolder}/newIDE/app"
},
{
"type": "node",
"request": "launch",

View File

@@ -115,7 +115,6 @@
"files.exclude": {
"Binaries/*build*": true,
"Binaries/Output": true,
"ExtLibs/SFML": true,
"GDJS/Runtime-dist": true,
"docs": true,
"newIDE/electron-app/dist": true,

View File

@@ -17,7 +17,7 @@ endmacro()
gd_set_option(BUILD_CORE TRUE BOOL "TRUE to build GDevelop Core library")
gd_set_option(BUILD_GDJS TRUE BOOL "TRUE to build GDevelop JS Platform")
gd_set_option(BUILD_EXTENSIONS TRUE BOOL "TRUE to build the extensions")
gd_set_option(BUILD_TESTS FALSE BOOL "TRUE to build the tests")
gd_set_option(BUILD_TESTS TRUE BOOL "TRUE to build the tests")
# Disable deprecated code
set(NO_GUI TRUE CACHE BOOL "" FORCE) #Force disable old GUI related code.

View File

@@ -14,7 +14,6 @@ set(GDCORE_lib_dir ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SY
#Dependencies on external libraries:
###
include_directories(${sfml_include_dir})
#Defines
###
@@ -68,14 +67,6 @@ set(LIBRARY_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMA
set(ARCHIVE_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
set(RUNTIME_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
#Linker files
###
IF(EMSCRIPTEN)
#Nothing.
ELSE()
target_link_libraries(GDCore ${sfml_LIBRARIES})
ENDIF()
#Tests
###
if(BUILD_TESTS)
@@ -88,5 +79,5 @@ if(BUILD_TESTS)
add_executable(GDCore_tests ${test_source_files})
set_target_properties(GDCore_tests PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE) #Allow finding dependencies directly from build path on Mac OS X.
target_link_libraries(GDCore_tests GDCore)
target_link_libraries(GDCore_tests ${sfml_LIBRARIES})
target_link_libraries(GDCore_tests ${CMAKE_DL_LIBS})
endif()

View File

@@ -13,7 +13,6 @@
#include <string>
#include <vector>
#include "Utf8/utf8.h"
#include <SFML/System/String.hpp>
#ifdef __GNUC__
#define GD_DEPRECATED __attribute__((deprecated))

View File

@@ -0,0 +1,36 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "AsyncEvent.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace std;
namespace gd {
AsyncEvent::AsyncEvent() : BaseEvent() {}
AsyncEvent::~AsyncEvent(){};
vector<const gd::InstructionsList *> AsyncEvent::GetAllActionsVectors() const {
vector<const gd::InstructionsList *> allActions;
allActions.push_back(&actions);
return allActions;
}
vector<gd::InstructionsList *> AsyncEvent::GetAllActionsVectors() {
vector<gd::InstructionsList *> allActions;
allActions.push_back(&actions);
return allActions;
}
} // namespace gd

View File

@@ -0,0 +1,61 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_ASYNCEVENT_H
#define GDCORE_ASYNCEVENT_H
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/InstructionsList.h"
namespace gd {
class Instruction;
class Project;
} // namespace gd
namespace gd {
/**
* \brief Internal event for asynchronous actions.
* This event gets added internally to the events tree when an
* asynchronous action is used.
*/
class GD_CORE_API AsyncEvent : public gd::BaseEvent {
public:
AsyncEvent();
AsyncEvent(const gd::Instruction &asyncAction_,
const gd::InstructionsList &actions_,
const gd::EventsList &subEvents_)
: asyncAction(asyncAction_), actions(actions_), subEvents(subEvents_) {
SetType("BuiltinAsync::Async");
};
virtual ~AsyncEvent();
virtual gd::AsyncEvent *Clone() const { return new AsyncEvent(*this); }
virtual bool IsExecutable() const { return true; }
virtual bool CanHaveSubEvents() const { return true; }
virtual const gd::EventsList &GetSubEvents() const { return subEvents; };
virtual gd::EventsList &GetSubEvents() { return subEvents; };
const gd::InstructionsList &GetActions() const { return actions; };
gd::InstructionsList &GetActions() { return actions; };
const gd::Instruction &GetInstruction() const { return asyncAction; };
gd::Instruction &GetInstruction() { return asyncAction; };
virtual std::vector<const gd::InstructionsList *>
GetAllActionsVectors() const;
virtual std::vector<gd::InstructionsList *> GetAllActionsVectors();
private:
gd::Instruction asyncAction;
gd::InstructionsList actions;
EventsList subEvents;
};
} // namespace gd
#endif // GDCORE_STANDARDEVENT_H

View File

@@ -63,8 +63,12 @@ void StandardEvent::UnserializeFrom(gd::Project& project,
project, conditions, element.GetChild("conditions", 0, "Conditions"));
gd::EventsListSerialization::UnserializeInstructionsFrom(
project, actions, element.GetChild("actions", 0, "Actions"));
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
events.Clear();
if (element.HasChild("events", "Events")) {
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}
}
} // namespace gd

View File

@@ -4,7 +4,9 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include <set>
#include "GDCore/CommonTools.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
@@ -14,7 +16,7 @@ using namespace std;
namespace gd {
void EventsCodeGenerationContext::InheritsFrom(
const EventsCodeGenerationContext& parent_) {
EventsCodeGenerationContext& parent_) {
parent = &parent_;
// Objects lists declared by parent became "already declared" in the child
@@ -24,8 +26,8 @@ void EventsCodeGenerationContext::InheritsFrom(
parent_.objectsListsToBeDeclared.end(),
std::inserter(alreadyDeclaredObjectsLists,
alreadyDeclaredObjectsLists.begin()));
std::copy(parent_.objectsListsWithoutPickingToBeDeclared.begin(),
parent_.objectsListsWithoutPickingToBeDeclared.end(),
std::copy(parent_.objectsListsOrEmptyToBeDeclared.begin(),
parent_.objectsListsOrEmptyToBeDeclared.end(),
std::inserter(alreadyDeclaredObjectsLists,
alreadyDeclaredObjectsLists.begin()));
std::copy(parent_.emptyObjectsListsToBeDeclared.begin(),
@@ -33,6 +35,8 @@ void EventsCodeGenerationContext::InheritsFrom(
std::inserter(alreadyDeclaredObjectsLists,
alreadyDeclaredObjectsLists.begin()));
nearestAsyncParent = parent_.IsAsyncCallback() ? &parent_ : parent_.nearestAsyncParent;
asyncDepth = parent_.asyncDepth;
depthOfLastUse = parent_.depthOfLastUse;
customConditionDepth = parent_.customConditionDepth;
contextDepth = parent_.GetContextDepth() + 1;
@@ -42,33 +46,59 @@ void EventsCodeGenerationContext::InheritsFrom(
}
}
void EventsCodeGenerationContext::InheritsAsAsyncCallbackFrom(
EventsCodeGenerationContext& parent_) {
// Increasing the async depth is enough to mark the context as an async callback.
InheritsFrom(parent_);
asyncDepth = parent_.asyncDepth + 1;
}
void EventsCodeGenerationContext::Reuse(
const EventsCodeGenerationContext& parent_) {
EventsCodeGenerationContext& parent_) {
InheritsFrom(parent_);
if (parent_.CanReuse())
contextDepth = parent_.GetContextDepth(); // Keep same context depth
}
void EventsCodeGenerationContext::NotifyAsyncParentsAboutDeclaredObject(const gd::String& objectName) {
gd::EventsCodeGenerationContext* asyncContext = IsAsyncCallback() ? this : nearestAsyncParent;
for (;
asyncContext != nullptr;
asyncContext = asyncContext->parent->nearestAsyncParent)
asyncContext->allObjectsListToBeDeclaredAcrossChildren.insert(objectName);
}
void EventsCodeGenerationContext::ObjectsListNeeded(
const gd::String& objectName) {
if (!IsToBeDeclared(objectName))
if (!IsToBeDeclared(objectName)) {
objectsListsToBeDeclared.insert(objectName);
if (IsInsideAsync()) {
NotifyAsyncParentsAboutDeclaredObject(objectName);
}
}
depthOfLastUse[objectName] = GetContextDepth();
}
void EventsCodeGenerationContext::ObjectsListWithoutPickingNeeded(
void EventsCodeGenerationContext::ObjectsListNeededOrEmptyIfJustDeclared(
const gd::String& objectName) {
if (!IsToBeDeclared(objectName))
objectsListsWithoutPickingToBeDeclared.insert(objectName);
if (!IsToBeDeclared(objectName)) {
objectsListsOrEmptyToBeDeclared.insert(objectName);
if (IsInsideAsync()) {
NotifyAsyncParentsAboutDeclaredObject(objectName);
}
}
depthOfLastUse[objectName] = GetContextDepth();
}
void EventsCodeGenerationContext::EmptyObjectsListNeeded(
const gd::String& objectName) {
if (!IsToBeDeclared(objectName))
if (!IsToBeDeclared(objectName)) {
emptyObjectsListsToBeDeclared.insert(objectName);
}
depthOfLastUse[objectName] = GetContextDepth();
}
@@ -77,8 +107,8 @@ std::set<gd::String> EventsCodeGenerationContext::GetAllObjectsToBeDeclared()
const {
std::set<gd::String> allObjectListsToBeDeclared(
objectsListsToBeDeclared.begin(), objectsListsToBeDeclared.end());
allObjectListsToBeDeclared.insert(objectsListsWithoutPickingToBeDeclared.begin(),
objectsListsWithoutPickingToBeDeclared.end());
allObjectListsToBeDeclared.insert(objectsListsOrEmptyToBeDeclared.begin(),
objectsListsOrEmptyToBeDeclared.end());
allObjectListsToBeDeclared.insert(emptyObjectsListsToBeDeclared.begin(),
emptyObjectsListsToBeDeclared.end());
@@ -102,4 +132,21 @@ bool EventsCodeGenerationContext::IsSameObjectsList(
otherContext.GetLastDepthObjectListWasNeeded(objectName);
}
bool EventsCodeGenerationContext::ShouldUseAsyncObjectsList(
const gd::String& objectName) const {
if (!IsInsideAsync()) return false;
// Check if the objects list was used after (or in) the nearest async callback context.
const gd::EventsCodeGenerationContext* asyncContext = IsAsyncCallback() ? this : nearestAsyncParent;
if (parent->GetLastDepthObjectListWasNeeded(objectName) >= asyncContext->GetContextDepth()) {
// The object was used in a context after (or in) the nearest async parent context, so we're not getting it from the
// async object lists (it was already gotten from there in this previous context).
return false;
}
// If the objects list is declared in a parent of the nearest async context, it means
// the async context had to use an async objects list to access it.
return asyncContext->ObjectAlreadyDeclaredByParents(objectName);
};
} // namespace gd

View File

@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <set>
#include "GDCore/String.h"
namespace gd {
@@ -33,11 +34,7 @@ class GD_CORE_API EventsCodeGenerationContext {
* updated to contain the maximal scope depth reached.
*/
EventsCodeGenerationContext(unsigned int* maxDepthLevel_ = nullptr)
: contextDepth(0),
customConditionDepth(0),
maxDepthLevel(maxDepthLevel_),
parent(NULL),
reuseExplicitlyForbidden(false){};
: maxDepthLevel(maxDepthLevel_){};
virtual ~EventsCodeGenerationContext(){};
/**
@@ -45,7 +42,13 @@ class GD_CORE_API EventsCodeGenerationContext {
* another one. The child will then for example not declare again objects
* already declared by its parent.
*/
void InheritsFrom(const EventsCodeGenerationContext& parent);
void InheritsFrom(EventsCodeGenerationContext& parent);
/**
* Call this method to make an EventsCodeGenerationContext as a "child" of
* another one, but in the context of an async function.
*/
void InheritsAsAsyncCallbackFrom(EventsCodeGenerationContext& parent);
/**
* \brief As InheritsFrom, mark the context as being the child of another one,
@@ -53,7 +56,7 @@ class GD_CORE_API EventsCodeGenerationContext {
*
* Used for example for optimizing the last event of a list.
*/
void Reuse(const EventsCodeGenerationContext& parent);
void Reuse(EventsCodeGenerationContext& parent);
/**
* \brief Forbid any optimization that would reuse and modify the object list
@@ -88,19 +91,19 @@ class GD_CORE_API EventsCodeGenerationContext {
const EventsCodeGenerationContext* GetParentContext() const { return parent; }
/**
* Mark the object has being the object being handled by the instruction
* Mark the object as being the object being handled by the instruction.
*/
void SetCurrentObject(const gd::String& objectName) {
currentObject = objectName;
};
/**
* Set that no particular object is being handled by an instruction
* Set that no particular object is being handled by an instruction.
*/
void SetNoCurrentObject() { currentObject = ""; };
/**
* Get the object being handled by the instruction
* Get the object being handled by the instruction.
*/
const gd::String& GetCurrentObject() const { return currentObject; };
@@ -109,7 +112,7 @@ class GD_CORE_API EventsCodeGenerationContext {
*
* The list will be filled with objects from the scene if it is the first time
* it is requested, unless there is already an object list with this name
* (i.e. `ObjectAlreadyDeclared(objectName)` returns true).
* (i.e. `ObjectAlreadyDeclaredByParents(objectName)` returns true).
*/
void ObjectsListNeeded(const gd::String& objectName);
@@ -121,7 +124,7 @@ class GD_CORE_API EventsCodeGenerationContext {
* from the scene. If there is already an objects list with this name, no new
* list will be declared again.
*/
void ObjectsListWithoutPickingNeeded(const gd::String& objectName);
void ObjectsListNeededOrEmptyIfJustDeclared(const gd::String& objectName);
/**
* Call this when an instruction in the event needs an empty object list,
@@ -134,29 +137,21 @@ class GD_CORE_API EventsCodeGenerationContext {
void EmptyObjectsListNeeded(const gd::String& objectName);
/**
* Return true if an object list has already been declared (or is going to be
* declared).
* Return true if an object list has already been declared by the parent contexts.
*/
bool ObjectAlreadyDeclared(const gd::String& objectName) const {
bool ObjectAlreadyDeclaredByParents(const gd::String& objectName) const {
return (alreadyDeclaredObjectsLists.find(objectName) !=
alreadyDeclaredObjectsLists.end());
};
/**
* \brief Consider that \a objectName is now declared in the context.
*/
void SetObjectDeclared(const gd::String& objectName) {
alreadyDeclaredObjectsLists.insert(objectName);
}
/**
* Return all the objects lists which will be declared by the current context
* ( the non empty as well as the empty objects lists )
* (normal, potentially empty or empty).
*/
std::set<gd::String> GetAllObjectsToBeDeclared() const;
/**
* Return the objects lists which will be declared by the current context
* Return the objects lists which will be declared by the current context.
*/
const std::set<gd::String>& GetObjectsListsToBeDeclared() const {
return objectsListsToBeDeclared;
@@ -166,9 +161,9 @@ class GD_CORE_API EventsCodeGenerationContext {
* Return the objects lists which will be will be declared, without filling
* them with objects from the scene.
*/
const std::set<gd::String>& GetObjectsListsToBeDeclaredWithoutPicking()
const std::set<gd::String>& GetObjectsListsToBeEmptyIfJustDeclared()
const {
return objectsListsWithoutPickingToBeDeclared;
return objectsListsOrEmptyToBeDeclared;
};
/**
@@ -184,7 +179,7 @@ class GD_CORE_API EventsCodeGenerationContext {
* Return the objects lists which are already declared and can be used in the
* current context without declaration.
*/
const std::set<gd::String>& GetObjectsListsAlreadyDeclared() const {
const std::set<gd::String>& GetObjectsListsAlreadyDeclaredByParents() const {
return alreadyDeclaredObjectsLists;
};
@@ -227,22 +222,55 @@ class GD_CORE_API EventsCodeGenerationContext {
*/
size_t GetCurrentConditionDepth() const { return customConditionDepth; }
private:
/**
* \brief Returns the list of all objects declared in this context
* and subcontexts.
* \warning Only works on an async callback's context.
*
* It is to be used by the Async event code generator to know what objects
* lists to backup for the async callback and async callbacks after it.
*/
const std::set<gd::String>& GetAllDeclaredObjectsAcrossChildren() {
return allObjectsListToBeDeclaredAcrossChildren;
};
/**
* Returns true if an object list should be gotten from a backed up
* objects list instead of the parent context. This can happen inside an
* asynchronous callback or a child context of an asynchronous callback (i.e:
* the `asyncDepth` is not 0).
*/
bool ShouldUseAsyncObjectsList(const gd::String& objectName) const;
/**
* Returns true if the code currently being generated is inside an
* asynchronous context (either in an asynchronous callback, or in a children of
* an asynchronous callback).
*/
bool IsInsideAsync() const { return asyncDepth != 0; };
/**
* Returns true if the code currently being generated is an asynchronous
* callback (but not a child of an asynchronous callback).
*/
bool IsAsyncCallback() const { return parent != nullptr && parent->asyncDepth != asyncDepth; }
/**
* \brief Returns true if the given object is already going to be declared
* (either as a traditional objects list, or one without picking, or one
* empty).
*
* in this context (either as a traditional objects list, or an empty one).
*/
bool IsToBeDeclared(const gd::String& objectName) {
return objectsListsToBeDeclared.find(objectName) !=
objectsListsToBeDeclared.end() ||
objectsListsWithoutPickingToBeDeclared.find(objectName) !=
objectsListsWithoutPickingToBeDeclared.end() ||
objectsListsOrEmptyToBeDeclared.find(objectName) !=
objectsListsOrEmptyToBeDeclared.end() ||
emptyObjectsListsToBeDeclared.find(objectName) !=
emptyObjectsListsToBeDeclared.end();
};
private:
void NotifyAsyncParentsAboutDeclaredObject(const gd::String& objectName);
std::set<gd::String>
alreadyDeclaredObjectsLists; ///< Objects lists already needed in a
///< parent context.
@@ -250,7 +278,7 @@ class GD_CORE_API EventsCodeGenerationContext {
objectsListsToBeDeclared; ///< Objects lists that will be declared in
///< this context.
std::set<gd::String>
objectsListsWithoutPickingToBeDeclared; ///< Objects lists that will be
objectsListsOrEmptyToBeDeclared; ///< Objects lists that will be
///< declared in this context,
///< but not filled with scene's
///< objects.
@@ -260,21 +288,40 @@ class GD_CORE_API EventsCodeGenerationContext {
///< but not filled with scene's
///< objects and not filled with any
///< previously existing objects list.
std::set<gd::String>
allObjectsListToBeDeclaredAcrossChildren; ///< This is only to be used by
///< the async callback
///< contexts to know all
///< objects declared across
///< all children, so that the
///< necessary objects can be
///< backed up.
std::map<gd::String, unsigned int>
depthOfLastUse; ///< The context depth when an object was last used.
gd::String
currentObject; ///< The object being used by an action or condition.
unsigned int contextDepth; ///< The depth of the context : 0 for a newly
///< created context, n+1 for any context
///< inheriting from context with depth n.
unsigned int
customConditionDepth; ///< The depth of the conditions being generated.
unsigned int contextDepth = 0; ///< The depth of the context: 0 for a newly
///< created context, n+1 for any context
///< inheriting from context with depth n.
unsigned int customConditionDepth =
0; ///< The depth of the conditions being generated.
unsigned int asyncDepth =
0; ///< If higher than 0, the current context is an asynchronous callback,
///< or a child context of an asynchronous callback:
///< - If the parent's async depth != the current context async depth,
///< then the current context is an asynchronous callback context.
///< - Otherwise, it's a child of an asynchronous callback.
unsigned int* maxDepthLevel; ///< A pointer to a unsigned int updated with
///< the maximum depth reached.
const EventsCodeGenerationContext*
parent; ///< The parent of the current context. Can be NULL.
bool reuseExplicitlyForbidden; ///< If set to true, forbid children context
///< to reuse this one without inheriting.
const EventsCodeGenerationContext* parent =
nullptr; ///< The parent of the current context. Can be NULL.
EventsCodeGenerationContext* nearestAsyncParent =
nullptr; ///< The nearest parent context that is an async callback
///< context.
bool reuseExplicitlyForbidden =
false; ///< If set to true, forbid children contexts
///< to reuse this one without inheriting.
};
} // namespace gd

View File

@@ -450,7 +450,9 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
* Generate code for an action.
*/
gd::String EventsCodeGenerator::GenerateActionCode(
gd::Instruction& action, EventsCodeGenerationContext& context) {
gd::Instruction& action,
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
gd::String actionCode;
const gd::InstructionMetadata& instrInfos =
@@ -518,8 +520,12 @@ gd::String EventsCodeGenerator::GenerateActionCode(
// Prepare arguments and generate the whole action code
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateObjectAction(
realObjects[i], objInfo, arguments, instrInfos, context);
actionCode += GenerateObjectAction(realObjects[i],
objInfo,
arguments,
instrInfos,
context,
optionalAsyncCallbackName);
context.SetNoCurrentObject();
}
@@ -552,7 +558,8 @@ gd::String EventsCodeGenerator::GenerateActionCode(
autoInfo,
arguments,
instrInfos,
context);
context,
optionalAsyncCallbackName);
context.SetNoCurrentObject();
}
@@ -560,12 +567,72 @@ gd::String EventsCodeGenerator::GenerateActionCode(
} else {
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateFreeAction(arguments, instrInfos, context);
actionCode +=
GenerateFreeAction(arguments, instrInfos, context, optionalAsyncCallbackName);
}
return actionCode;
}
const EventsCodeGenerator::CallbackDescriptor
EventsCodeGenerator::GenerateCallback(
const gd::String& callbackID,
gd::EventsCodeGenerationContext& parentContext,
gd::InstructionsList& actions,
gd::EventsList* subEvents) {
gd::EventsCodeGenerationContext callbackContext;
callbackContext.InheritsAsAsyncCallbackFrom(parentContext);
const gd::String callbackFunctionName =
GetCodeNamespaceAccessor() + "asyncCallback" + callbackID;
const gd::String callbackFunctionArguments =
GenerateEventsParameters(callbackContext);
// Generate actions
gd::String actionsCode = GenerateActionsListCode(actions, callbackContext);
// Generate subevents
if (subEvents != nullptr) // Sub events
{
actionsCode += "\n{ //Subevents\n";
actionsCode += GenerateEventsListCode(*subEvents, callbackContext);
actionsCode += "} //End of subevents\n";
}
// Compose the callback function and add outside main
const gd::String actionsDeclarationsCode =
GenerateObjectsDeclarationCode(callbackContext);
const gd::String callbackCode = callbackFunctionName + " = function (" +
GenerateEventsParameters(callbackContext) +
") {\n" + actionsDeclarationsCode +
actionsCode + "}\n";
AddCustomCodeOutsideMain(callbackCode);
std::set<gd::String> requiredObjects;
// Build the list of all objects required by the callback. Any object that has
// already been declared could have gone through previous object picking, so
// if such an object is used by the actions or subevents of this callback, we
// must ask the caller to pass the already existing objects lists through a
// `LongLivedObjectsList` to the callback function.
for (const auto& objectUsedInSubTree :
callbackContext.GetAllDeclaredObjectsAcrossChildren()) {
if (callbackContext.ObjectAlreadyDeclaredByParents(objectUsedInSubTree))
requiredObjects.insert(objectUsedInSubTree);
};
return CallbackDescriptor(
callbackFunctionName, callbackFunctionArguments, requiredObjects);
};
const gd::String EventsCodeGenerator::GenerateEventsParameters(
const gd::EventsCodeGenerationContext& context) {
gd::String parameters = "runtimeScene";
if (!HasProjectAndLayout()) parameters += ", eventsFunctionContext";
if (context.IsInsideAsync()) parameters += ", asyncObjectsList";
return parameters;
};
/**
* Generate actions code.
*/
@@ -743,23 +810,21 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String declarationsCode;
for (auto object : context.GetObjectsListsToBeDeclared()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration = "std::vector<RuntimeObject*> " +
GetObjectListName(object, context) +
" = runtimeContext->GetObjectsRawPointers(\"" +
ConvertToString(object) + "\");\n";
context.SetObjectDeclared(object);
} else
objectListDeclaration = declareObjectList(object, context);
declarationsCode += objectListDeclaration + "\n";
}
for (auto object : context.GetObjectsListsToBeDeclaredWithoutPicking()) {
for (auto object : context.GetObjectsListsToBeEmptyIfJustDeclared()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration = "std::vector<RuntimeObject*> " +
GetObjectListName(object, context) + ";\n";
context.SetObjectDeclared(object);
} else
objectListDeclaration = declareObjectList(object, context);
@@ -767,10 +832,9 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
}
for (auto object : context.GetObjectsListsToBeDeclaredEmpty()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration = "std::vector<RuntimeObject*> " +
GetObjectListName(object, context) + ";\n";
context.SetObjectDeclared(object);
} else
objectListDeclaration = "std::vector<RuntimeObject*> " +
GetObjectListName(object, context) + ";\n";
@@ -785,7 +849,7 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
* Generate events list code.
*/
gd::String EventsCodeGenerator::GenerateEventsListCode(
gd::EventsList& events, const EventsCodeGenerationContext& parentContext) {
gd::EventsList& events, EventsCodeGenerationContext& parentContext) {
gd::String output;
for (std::size_t eId = 0; eId < events.size(); ++eId) {
// Each event has its own context : Objects picked in an event are totally
@@ -801,6 +865,8 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
// operation.
bool reuseParentContext =
parentContext.CanReuse() && eId == events.size() - 1;
// TODO: avoid creating if useless.
gd::EventsCodeGenerationContext reusedContext;
reusedContext.Reuse(parentContext);
@@ -1015,7 +1081,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
gd::String EventsCodeGenerator::GenerateFreeAction(
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context) {
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
// Generate call
gd::String call;
if (instrInfos.codeExtraInformation.type == "number" ||
@@ -1042,6 +1109,11 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
call = instrInfos.codeExtraInformation.functionCallName + "(" +
GenerateArgumentsList(arguments) + ")";
}
if (!optionalAsyncCallbackName.empty())
call = "runtimeScene.getAsyncTasksManager().addTask(" + call + ", " +
optionalAsyncCallbackName + ")";
return call + ";\n";
}
@@ -1050,7 +1122,8 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::ObjectMetadata& objInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context) {
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
// Create call
gd::String call;
if ((instrInfos.codeExtraInformation.type == "number" ||
@@ -1077,8 +1150,11 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ").\n";
argumentsStr + ")" +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}
@@ -1088,7 +1164,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
const gd::BehaviorMetadata& autoInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context) {
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
// Create call
gd::String call;
if ((instrInfos.codeExtraInformation.type == "number" ||
@@ -1114,8 +1191,11 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" + " for behavior \"" + behaviorName + "\".\n";
argumentsStr + ")" + " for behavior \"" + behaviorName + "\"" +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}

View File

@@ -77,7 +77,7 @@ class GD_CORE_API EventsCodeGenerator {
* \return Code
*/
virtual gd::String GenerateEventsListCode(
gd::EventsList& events, const EventsCodeGenerationContext& context);
gd::EventsList& events, EventsCodeGenerationContext& context);
/**
* \brief Generate code for executing a condition list
@@ -155,7 +155,51 @@ class GD_CORE_API EventsCodeGenerator {
* \return Code
*/
gd::String GenerateActionCode(gd::Instruction& action,
EventsCodeGenerationContext& context);
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
struct CallbackDescriptor {
CallbackDescriptor(const gd::String functionName_,
const gd::String argumentsList_,
const std::set<gd::String> requiredObjects_)
: functionName(functionName_),
argumentsList(argumentsList_),
requiredObjects(requiredObjects_){};
/**
* The name by which the function can be invoked.
*/
const gd::String functionName;
/**
* The comma separated list of arguments that the function takes.
*/
const gd::String argumentsList;
/**
* A set of all objects that need to be backed up to be passed to the callback code.
*/
const std::set<gd::String> requiredObjects;
};
/**
* \brief Generates actions and events as a callback.
*
* This is used by asynchronous functions to run the code out of the normal
* events flow.
*
* \returns A set with all objects required by the callback code.
* The caller must take care of backing them up in a LongLivedObjectsList,
* and to pass it to the callback function as the last argument.
*/
virtual const CallbackDescriptor GenerateCallback(
const gd::String& callbackFunctionName,
gd::EventsCodeGenerationContext& parentContext,
gd::InstructionsList& actions,
gd::EventsList* subEvents = nullptr);
/**
* \brief Generates the parameters list of an event's generated function.
*/
const gd::String GenerateEventsParameters(
const gd::EventsCodeGenerationContext& context);
/**
* \brief Generate code for declaring objects lists.
@@ -462,17 +506,10 @@ class GD_CORE_API EventsCodeGenerator {
* Other standard parameters type that should be implemented by platforms:
* - currentScene: Reference to the current runtime scene.
* - objectList : a map containing lists of objects which are specified by the
object name in another parameter. Example:
* \code
AddExpression("Count", _("Object count"), _("Count the number of picked
objects"), _("Objects"), "res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"))
.SetFunctionName("getPickedObjectsCount");
* \endcode
* - objectListWithoutPicking : Same as objectList but do not pick object if
object name in another parameter.
* - objectListOrEmptyIfJustDeclared : Same as `objectList` but do not pick object if
they are not already picked.
* - objectPtr : Return a reference to the object specified by the object name in
* - objectPtr: Return a reference to the object specified by the object name in
another parameter. Example:
* \code
.AddParameter("object", _("Object"))
@@ -665,14 +702,16 @@ class GD_CORE_API EventsCodeGenerator {
virtual gd::String GenerateFreeAction(
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
virtual gd::String GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
virtual gd::String GenerateBehaviorAction(
const gd::String& objectName,
@@ -680,7 +719,8 @@ class GD_CORE_API EventsCodeGenerator {
const gd::BehaviorMetadata& autoInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
gd::String GenerateRelationalOperatorCall(
const gd::InstructionMetadata& instrInfos,

View File

@@ -5,8 +5,11 @@
*/
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Builtin/AsyncEvent.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
@@ -65,10 +68,40 @@ gd::String BaseEvent::GenerateEventCode(
return "";
}
void BaseEvent::PreprocessAsyncActions(const gd::Platform& platform) {
if (!CanHaveSubEvents()) return;
for (const auto& actionsList : GetAllActionsVectors())
for (std::size_t aId = 0; aId < actionsList->size(); ++aId) {
const auto& action = actionsList->at(aId);
const gd::InstructionMetadata& actionMetadata =
gd::MetadataProvider::GetActionMetadata(platform, action.GetType());
if (actionMetadata.IsAsync()) {
gd::InstructionsList remainingActions;
remainingActions.InsertInstructions(
*actionsList, aId + 1, actionsList->size() - 1);
gd::AsyncEvent asyncEvent(action, remainingActions, GetSubEvents());
// Ensure that the local event no longer has any of the actions/subevent
// after the async function
actionsList->RemoveAfter(aId);
GetSubEvents().Clear();
GetSubEvents().InsertEvent(asyncEvent);
// We just moved all the rest, there's nothing left to do in this event.
return;
}
}
};
void BaseEvent::Preprocess(gd::EventsCodeGenerator& codeGenerator,
gd::EventsList& eventList,
std::size_t indexOfTheEventInThisList) {
if (IsDisabled() || !MustBePreprocessed()) return;
if (IsDisabled()) return;
PreprocessAsyncActions(codeGenerator.GetPlatform());
if (!MustBePreprocessed()) return;
try {
if (type.empty()) return;

View File

@@ -10,6 +10,7 @@
#include <iostream>
#include <memory>
#include <vector>
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/InstructionsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
@@ -23,7 +24,7 @@ class EventsCodeGenerationContext;
class Platform;
class SerializerElement;
class Instruction;
}
} // namespace gd
namespace gd {
@@ -136,13 +137,15 @@ class GD_CORE_API BaseEvent {
* \note Used to preprocess or search in the expressions of the event.
*/
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
GetAllExpressionsWithMetadata() {
GetAllExpressionsWithMetadata() {
std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> > noExpr;
return noExpr;
};
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
GetAllExpressionsWithMetadata() const {
std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> > noExpr;
virtual std::vector<
std::pair<const gd::Expression*, const gd::ParameterMetadata> >
GetAllExpressionsWithMetadata() const {
std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
noExpr;
return noExpr;
};
@@ -206,6 +209,11 @@ class GD_CORE_API BaseEvent {
gd::EventsList& eventList,
std::size_t indexOfTheEventInThisList);
/**
* A function that turns all async member actions into an Async subevent for code generation.
*/
void PreprocessAsyncActions(const gd::Platform& platform);
/**
* \brief If MustBePreprocessed is redefined to return true, the
* gd::EventMetadata::preprocessing associated to the event will be called to

View File

@@ -5,6 +5,7 @@
*/
#include "InstructionsList.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Project/Project.h"
#include "Serialization.h"
@@ -31,6 +32,10 @@ void InstructionsList::InsertInstructions(const InstructionsList& list,
}
}
void InstructionsList::RemoveAfter(const size_t position) {
elements.resize(position);
}
void InstructionsList::SerializeTo(SerializerElement& element) const {
EventsListSerialization::SerializeInstructionsTo(*this, element);
}

View File

@@ -6,9 +6,10 @@
#ifndef GDCORE_INSTRUCTIONSLIST_H
#define GDCORE_INSTRUCTIONSLIST_H
#include "GDCore/Tools/SPtrList.h"
#include <memory>
#include <vector>
#include "GDCore/Tools/SPtrList.h"
namespace gd {
class Instruction;
}
@@ -22,11 +23,11 @@ class SerializerElement;
namespace gd {
class InstructionsList : public SPtrList<gd::Instruction> {
public:
void InsertInstructions(const InstructionsList& list,
size_t begin,
size_t end,
size_t position = (size_t)-1);
public:
void InsertInstructions(const InstructionsList &list, size_t begin,
size_t end, size_t position = (size_t)-1);
void RemoveAfter(size_t position);
/** \name Serialization
*/
@@ -35,17 +36,17 @@ class InstructionsList : public SPtrList<gd::Instruction> {
* \brief Serialize the instructions to the specified element
* \see EventsListSerialization
*/
void SerializeTo(gd::SerializerElement& element) const;
void SerializeTo(gd::SerializerElement &element) const;
/**
* \brief Load the instructions from the specified element
* \see EventsListSerialization
*/
void UnserializeFrom(gd::Project& project,
const gd::SerializerElement& element);
void UnserializeFrom(gd::Project &project,
const gd::SerializerElement &element);
///@}
};
} // namespace gd
} // namespace gd
#endif

View File

@@ -218,8 +218,8 @@ void EventsListSerialization::UnserializeEventsFrom(
event = std::make_shared<EmptyEvent>();
}
event->SetDisabled(eventElem.GetBoolAttribute("disabled"));
event->SetFolded(eventElem.GetBoolAttribute("folded"));
event->SetDisabled(eventElem.GetBoolAttribute("disabled", false));
event->SetFolded(eventElem.GetBoolAttribute("folded", false));
list.InsertEvent(event, list.GetEventsCount());
}
@@ -232,8 +232,8 @@ void EventsListSerialization::SerializeEventsTo(const EventsList& list,
const gd::BaseEvent& event = list.GetEvent(j);
SerializerElement& eventElem = events.AddChild("event");
eventElem.SetAttribute("disabled", event.IsDisabled());
eventElem.SetAttribute("folded", event.IsFolded());
if (event.IsDisabled()) eventElem.SetAttribute("disabled", event.IsDisabled());
if (event.IsFolded()) eventElem.SetAttribute("folded", event.IsFolded());
eventElem.AddChild("type").SetValue(event.GetType());
event.SerializeTo(eventElem);

View File

@@ -42,6 +42,7 @@ class GD_CORE_API BuiltinExtensionsImplementer {
static void ImplementsTimeExtension(gd::PlatformExtension& extension);
static void ImplementsVariablesExtension(gd::PlatformExtension& extension);
static void ImplementsWindowExtension(gd::PlatformExtension& extension);
static void ImplementsAsyncExtension(gd::PlatformExtension& extension);
};
} // namespace gd

View File

@@ -0,0 +1,32 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "AllBuiltinExtensions.h"
#include "GDCore/Events/Builtin/AsyncEvent.h"
#include "GDCore/Tools/Localization.h"
using namespace std;
namespace gd {
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAsyncExtension(
gd::PlatformExtension &extension) {
extension
.SetExtensionInformation(
"BuiltinAsync",
_("Async functions"),
_("Functions that defer the execution of the events after it."),
"Arthur Pacaud (arthuro555)",
"Open source (MIT License)")
.SetCategory("Advanced");
extension.AddEvent("Async",
_("Async event"),
_("Internal event for asynchronous actions"),
"",
"res/eventaddicon.png",
std::make_shared<gd::AsyncEvent>());
}
} // namespace gd

View File

@@ -343,6 +343,34 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
"res/actions/music.png")
.AddCodeOnlyParameter("currentScene", "")
.MarkAsComplex();
extension
.AddAction(
"FadeSoundVolume",
_("Fade the volume of a sound played on a channel."),
_("Fade the volume of a sound played on a channel to the specified volume within the specified duration."),
_("Fade the sound on channel _PARAM1_ to volume _PARAM2_ within _PARAM3_ seconds"),
_("Sounds on channels"),
"res/actions/son24.png",
"res/actions/son.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("expression", _("Final volume (0-100)"))
.AddParameter("expression", _("Fading time in seconds"))
.MarkAsAdvanced();
extension
.AddAction(
"FadeMusicVolume",
_("Fade the volume of a music played on a channel."),
_("Fade the volume of a music played on a channel to the specified volume within the specified duration."),
_("Fade the music on channel _PARAM1_ to volume _PARAM2_ within _PARAM3_ seconds"),
_("Music on channels"),
"res/actions/music24.png",
"res/actions/music.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Channel identifier"))
.AddParameter("expression", _("Final volume (0-100)"))
.AddParameter("expression", _("Fading time in seconds"))
.MarkAsAdvanced();
extension
.AddCondition("MusicPlaying",

View File

@@ -1252,7 +1252,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListWithoutPicking", _("Object to create"))
.AddParameter("objectListOrEmptyIfJustDeclared", _("Object to create"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
@@ -1270,7 +1270,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListWithoutPicking", _("Group of potential objects"))
.AddParameter("objectListOrEmptyIfJustDeclared", _("Group of potential objects"))
.SetParameterLongDescription(
_("Group containing objects that can be created by the action."))
.AddParameter("string", _("Name of the object to create"))
@@ -1418,7 +1418,33 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"))
.UseStandardRelationalOperatorParameters("number")
.MarkAsSimple();
.MarkAsSimple()
.SetHidden();
extension.AddExpressionAndCondition(
"number",
"SceneInstancesCount",
_("Number of object instances on the scene"),
_("the number of instances of the specified objects living on the scene"),
_("the number of _PARAM1_ living on the scene"),
_("Objects"),
"res/conditions/nbObjet24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListOrEmptyWithoutPicking", _("Object"))
.UseStandardParameters("number")
.MarkAsSimple();
extension.AddExpressionAndCondition(
"number",
"PickedInstancesCount",
_("Number of object instances currently picked"),
_("the number of instances picked by the previous conditions (or actions)"),
_("the number of _PARAM0_ currently picked"),
_("Objects"),
"res/conditions/nbObjet24.png")
.AddParameter("objectListOrEmptyWithoutPicking", _("Object"))
.UseStandardParameters("number")
.MarkAsSimple();
extension
.AddCondition(
@@ -1526,7 +1552,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"currently picked in the event"),
"",
"res/conditions/nbObjet.png")
.AddParameter("objectList", _("Object"));
.AddParameter("objectList", _("Object"))
.SetHidden(); // Deprecated
obj.AddStrExpression("ObjectName",
_("Object name"),

View File

@@ -229,6 +229,15 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
extension
.AddExpression("ceilTo",
_("Ceil (round up) to a decimal point"),
_("Round number up to the Nth decimal place"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"))
.AddParameter("expression", _("Expression"), "", true);
extension
.AddExpression("floor",
_("Floor (round down)"),
@@ -237,6 +246,15 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
extension
.AddExpression("floorTo",
_("Floor (round down) to a decimal point"),
_("Round number down to the Nth decimal place"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"))
.AddParameter("expression", _("Expression"), "", true);
extension
.AddExpression("cos",
_("Cosine"),
@@ -295,6 +313,15 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
extension
.AddExpression("roundTo",
_("Round to a decimal point"),
_("Round a number to the Nth decimal place"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"))
.AddParameter("expression", _("Expression"), "", true);
extension
.AddExpression("exp",
_("Exponential"),

View File

@@ -311,7 +311,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
extension
.AddCondition(
@@ -326,8 +327,54 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
extension
.AddCondition(
"HasAnyTouchStarted",
_("A new touch has started"),
_("Check if a touch has just started on this frame. The touch identifiers can be "
"accessed using StartedTouchId() and StartedTouchCount()."),
_("A new touch has started"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddExpression(
"StartedTouchCount",
_("Started touch count"),
_("The number of touches that have just started on this frame. The touch identifiers can be "
"accessed using StartedTouchId()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.AddExpression(
"StartedTouchId",
_("Started touch identifier"),
_("The identifier of the touch that has just started on this frame. The touch number of touches can be "
"accessed using StartedTouchCount()."),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch index"));
extension
.AddCondition(
"HasTouchEnded",
_("A touch has ended"),
_("Check if a touch has ended."),
_("The touch with identifier _PARAM1_ has ended"),
_("Multitouch"),
"res/conditions/touch24.png",
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Touch identifier"));
extension
.AddExpression("MouseWheelDelta",
_("Mouse wheel: Displacement"),
@@ -342,7 +389,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
_("Identifier of the last touch"),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
extension
.AddExpression("LastEndedTouchId",
@@ -350,7 +398,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
_("Identifier of the last ended touch"),
_("Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
}
} // namespace gd

View File

@@ -121,7 +121,7 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
polygonElement.GetChild(k);
polygon.vertices.push_back(
sf::Vector2f(verticeElement.GetDoubleAttribute("x"),
gd::Vector2f(verticeElement.GetDoubleAttribute("x"),
verticeElement.GetDoubleAttribute("y")));
}

View File

@@ -4,7 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#include "Polygon2d.h"
#include <SFML/System/Vector2.hpp>
#include "GDCore/Vector2.h"
#include <cmath>
#include <iostream>
@@ -28,7 +28,7 @@ void Polygon2d::Move(float x, float y) {
}
void Polygon2d::ComputeEdges() const {
sf::Vector2f v1, v2;
gd::Vector2f v1, v2;
edges.clear();
for (std::size_t i = 0; i < vertices.size(); i++) {
@@ -62,8 +62,8 @@ bool Polygon2d::IsConvex() const {
return true;
}
sf::Vector2f Polygon2d::ComputeCenter() const {
sf::Vector2f center;
gd::Vector2f Polygon2d::ComputeCenter() const {
gd::Vector2f center;
for (std::size_t i = 0; i < vertices.size(); i++) {
center.x += vertices[i].x;
@@ -77,10 +77,10 @@ sf::Vector2f Polygon2d::ComputeCenter() const {
Polygon2d Polygon2d::CreateRectangle(float width, float height) {
Polygon2d rect;
rect.vertices.push_back(sf::Vector2f(-width / 2.0f, -height / 2.0f));
rect.vertices.push_back(sf::Vector2f(+width / 2.0f, -height / 2.0f));
rect.vertices.push_back(sf::Vector2f(+width / 2.0f, +height / 2.0f));
rect.vertices.push_back(sf::Vector2f(-width / 2.0f, +height / 2.0f));
rect.vertices.push_back(gd::Vector2f(-width / 2.0f, -height / 2.0f));
rect.vertices.push_back(gd::Vector2f(+width / 2.0f, -height / 2.0f));
rect.vertices.push_back(gd::Vector2f(+width / 2.0f, +height / 2.0f));
rect.vertices.push_back(gd::Vector2f(-width / 2.0f, +height / 2.0f));
return rect;
}

View File

@@ -5,7 +5,7 @@
*/
#ifndef GDCORE_POLYGON_H
#define GDCORE_POLYGON_H
#include <SFML/System/Vector2.hpp>
#include "GDCore/Vector2.h"
#include <vector>
/**
@@ -22,19 +22,19 @@ class GD_CORE_API Polygon2d {
Polygon2d(){};
virtual ~Polygon2d(){};
std::vector<sf::Vector2f> vertices; ///< The vertices composing the polygon
mutable std::vector<sf::Vector2f>
std::vector<gd::Vector2f> vertices; ///< The vertices composing the polygon
mutable std::vector<gd::Vector2f>
edges; ///< Edges. Can be computed from vertices using ComputeEdges()
/**
* \brief Get the vertices composing the polygon.
*/
std::vector<sf::Vector2f>& GetVertices() { return vertices; }
std::vector<gd::Vector2f>& GetVertices() { return vertices; }
/**
* \brief Get the vertices composing the polygon.
*/
const std::vector<sf::Vector2f>& GetVertices() const { return vertices; }
const std::vector<gd::Vector2f>& GetVertices() const { return vertices; }
/**
* \brief Moves each vertices from the given amount.
@@ -68,7 +68,7 @@ class GD_CORE_API Polygon2d {
/**
* \brief Return the position of the center of the polygon
*/
sf::Vector2f ComputeCenter() const;
gd::Vector2f ComputeCenter() const;
/** \name Tools
* Tool functions

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
#include <SFML/Graphics/Sprite.hpp>
#include <iostream>
#include "GDCore/Extensions/Builtin/SpriteExtension/Polygon2d.h"

View File

@@ -6,7 +6,6 @@
#ifndef SPRITE_H
#define SPRITE_H
#include <SFML/Graphics/Sprite.hpp>
#include <memory>
#include "GDCore/Extensions/Builtin/SpriteExtension/Point.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Polygon2d.h"

View File

@@ -6,7 +6,6 @@
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include <SFML/Graphics.hpp>
#include <algorithm>
#include "GDCore/CommonTools.h"

View File

@@ -20,7 +20,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"for slow motion effects).",
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/timers");
.SetExtensionHelpPath("/all-features/timers-and-time");
extension.AddInstructionOrExpressionGroupMetadata(
_("Timers and time")
)
@@ -141,7 +141,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
_("Change time scale"),
_("Change the time scale of the scene."),
_("Set the time scale of the scene to _PARAM1_"),
"",
"res/actions/time24.png",
"res/actions/time.png")
@@ -149,6 +148,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
.AddParameter("expression",
_("Scale (1: Default, 2: 2x faster, 0.5: 2x slower...)"));
extension
.AddAction("Wait",
_("Wait X seconds (experimental)"),
_("Waits a number of seconds before running "
"the next actions (and sub-events)."),
_("Wait _PARAM0_ seconds"),
"",
"res/timer.svg",
"res/timer.svg")
.AddParameter("expression", "Time to wait in seconds")
.SetHelpPath("/all-features/timers-and-time/wait-action")
.SetAsync();
extension
.AddExpression("TimeDelta",
_("Time elapsed since the last frame"),

View File

@@ -8,6 +8,7 @@
#include <functional>
#include <map>
#include <memory>
#include <algorithm>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"

View File

@@ -22,6 +22,7 @@ InstructionMetadata::InstructionMetadata()
canHaveSubInstructions(false),
hidden(true),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}
@@ -45,6 +46,7 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
extensionNamespace(extensionNamespace_),
hidden(false),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}

View File

@@ -9,6 +9,7 @@
#include <functional>
#include <map>
#include <memory>
#include <algorithm>
#include "GDCore/Events/Instruction.h"
#include "GDCore/String.h"
@@ -98,6 +99,23 @@ class GD_CORE_API InstructionMetadata {
return *this;
}
/**
* Check if the instruction is asynchronous - it will be running in the
* background, executing the instructions following it before the frame after
* it resolved.
*/
bool IsAsync() const { return isAsync; }
/**
* Set that the instruction is asynchronous - it will be running in the
* background, executing the instructions following it before the frame after
* it resolved.
*/
InstructionMetadata &SetAsync() {
isAsync = true;
return *this;
}
/**
* Notify that the instruction can have sub instructions.
*/
@@ -461,6 +479,7 @@ class GD_CORE_API InstructionMetadata {
int usageComplexity; ///< Evaluate the instruction from 0 (simple&easy to
///< use) to 10 (complex to understand)
bool isPrivate;
bool isAsync;
bool isObjectInstruction;
bool isBehaviorInstruction;
gd::String requiredBaseObjectCapability;

View File

@@ -9,6 +9,7 @@
#if defined(GD_IDE_ONLY)
#include <map>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class Project;
@@ -151,15 +152,16 @@ class GD_CORE_API ParameterMetadata {
}
/**
* \brief Return true if the type of the parameter is "object", "objectPtr" or
* "objectList".
* \brief Return true if the type of the parameter is representing one object
* (or more, i.e: an object group).
*
* \see gd::ParameterMetadata::GetType
*/
static bool IsObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
parameterType == "objectList" ||
parameterType == "objectListWithoutPicking";
parameterType == "objectListOrEmptyIfJustDeclared" ||
parameterType == "objectListOrEmptyWithoutPicking";
}
/**
@@ -196,7 +198,8 @@ class GD_CORE_API ParameterMetadata {
parameterType == "objectPointName" ||
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName";
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";

View File

@@ -117,6 +117,16 @@ void EffectsContainer::SwapEffects(std::size_t firstEffectIndex,
effects[secondEffectIndex] = temp;
}
void EffectsContainer::MoveEffect(std::size_t oldIndex, std::size_t newIndex) {
if (oldIndex >= effects.size() || newIndex >= effects.size() ||
newIndex == oldIndex)
return;
auto effect = effects[oldIndex];
effects.erase(effects.begin() + oldIndex);
effects.insert(effects.begin() + newIndex, effect);
}
void EffectsContainer::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("effect");
for (std::size_t i = 0; i < GetEffectsCount(); ++i) {

View File

@@ -7,6 +7,7 @@
#define GDCORE_EFFECTS_CONTAINER_H
#include <memory>
#include <vector>
#include <algorithm>
#include "GDCore/String.h"
@@ -89,6 +90,11 @@ class GD_CORE_API EffectsContainer {
*/
void RemoveEffect(const gd::String& name);
/**
* \brief Move the specified effect at a new position in the list.
*/
void MoveEffect(std::size_t oldIndex, std::size_t newIndex);
/**
* Swap the position of two effects.
*/

View File

@@ -5,7 +5,7 @@
*/
#ifndef GDCORE_OBJECT_H
#define GDCORE_OBJECT_H
#include <SFML/System/Vector2.hpp>
#include "GDCore/Vector2.h"
#include <map>
#include <memory>
#include <vector>

View File

@@ -7,6 +7,7 @@
#ifndef GDCORE_OBJECTGROUPSCONTAINER_H
#define GDCORE_OBJECTGROUPSCONTAINER_H
#include <vector>
#include <algorithm>
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/String.h"
namespace gd {
@@ -77,7 +78,6 @@ class GD_CORE_API ObjectGroupsContainer {
*/
bool IsEmpty() const { return objectGroups.empty(); };
#if defined(GD_IDE_ONLY)
/**
* \brief return the position of the group called "name" in the group list
*/
@@ -107,7 +107,6 @@ class GD_CORE_API ObjectGroupsContainer {
* \brief Move the specified group at a new position in the list.
*/
void Move(std::size_t oldIndex, std::size_t newIndex);
#endif
/**
* \brief Clear all groups of the container.

View File

@@ -9,7 +9,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <SFML/System/Utf.hpp>
#include <cctype>
#include <fstream>
#include <map>
@@ -66,6 +65,9 @@ Project::Project()
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
currentPlatform(NULL),
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),

View File

@@ -92,7 +92,7 @@ double Variable::GetValue() const {
return value;
} else if (type == Type::String) {
double retVal = str.empty() ? 0.0 : str.To<double>();
if(std::isnan(retVal)) retVal = 0.0;
if (std::isnan(retVal)) retVal = 0.0;
return retVal;
} else if (type == Type::Boolean) {
return boolVal ? 1.0 : 0.0;
@@ -188,6 +188,15 @@ const Variable& Variable::GetAtIndex(const size_t index) const {
return *childrenArray.at(index);
};
void Variable::MoveChildInArray(const size_t oldIndex, const size_t newIndex) {
if (oldIndex >= childrenArray.size() || newIndex >= childrenArray.size())
return;
std::shared_ptr<gd::Variable> object = std::move(childrenArray[oldIndex]);
childrenArray.erase(childrenArray.begin() + oldIndex);
childrenArray.insert(childrenArray.begin() + newIndex, std::move(object));
}
Variable& Variable::PushNew() { return GetAtIndex(GetChildrenCount()); };
void Variable::RemoveAtIndex(const size_t index) {
@@ -195,8 +204,29 @@ void Variable::RemoveAtIndex(const size_t index) {
childrenArray.erase(childrenArray.begin() + index);
};
bool Variable::InsertAtIndex(const gd::Variable& variable, const size_t index) {
if (type != Type::Array) return false;
auto newVariable = std::make_shared<gd::Variable>(variable);
if (index < childrenArray.size()) {
childrenArray.insert(childrenArray.begin() + index, newVariable);
} else {
childrenArray.push_back(newVariable);
}
return true;
};
bool Variable::InsertChild(const gd::String& name,
const gd::Variable& variable) {
if (type != Type::Structure || HasChild(name)) {
return false;
}
children[name] = std::make_shared<gd::Variable>(variable);
return true;
};
void Variable::SerializeTo(SerializerElement& element) const {
element.SetStringAttribute("type", TypeAsString(GetType()));
if (IsFolded()) element.SetBoolAttribute("folded", true);
if (type == Type::String) {
element.SetStringAttribute("value", GetString());
@@ -234,6 +264,7 @@ void Variable::UnserializeFrom(const SerializerElement& element) {
if (element.HasChild("children", "Children") && IsPrimitive(type))
type = Type::Structure;
// end of compatibility code
SetFolded(element.GetBoolAttribute("folded", false));
if (IsPrimitive(type)) {
if (type == Type::String) {
@@ -305,6 +336,7 @@ void Variable::RemoveRecursively(const gd::Variable& variableToRemove) {
Variable::Variable(const Variable& other)
: value(other.value),
str(other.str),
folded(other.folded),
boolVal(other.boolVal),
type(other.type) {
CopyChildren(other);
@@ -314,6 +346,7 @@ Variable& Variable::operator=(const Variable& other) {
if (this != &other) {
value = other.value;
str = other.str;
folded = other.folded;
boolVal = other.boolVal;
type = other.type;
CopyChildren(other);

View File

@@ -6,10 +6,10 @@
#ifndef GDCORE_VARIABLE_H
#define GDCORE_VARIABLE_H
#include <cmath>
#include <map>
#include <memory>
#include <vector>
#include <cmath>
#include "GDCore/String.h"
namespace gd {
@@ -98,7 +98,7 @@ class GD_CORE_API Variable {
void SetValue(double val) {
value = val;
// NaN values are not supported by GDevelop nor the serializer.
if(std::isnan(value)) value = 0.0;
if (std::isnan(value)) value = 0.0;
type = Type::Number;
}
@@ -185,9 +185,9 @@ class GD_CORE_API Variable {
* \brief Get the count of children that the variable has.
*/
size_t GetChildrenCount() const {
return type == Type::Structure
? children.size()
: type == Type::Array ? childrenArray.size() : 0;
return type == Type::Structure ? children.size()
: type == Type::Array ? childrenArray.size()
: 0;
};
/** \name Structure
@@ -290,12 +290,38 @@ class GD_CORE_API Variable {
*/
void RemoveAtIndex(const size_t index);
/**
* \brief Move child in array.
*/
void MoveChildInArray(const size_t oldIndex, const size_t newIndex);
/**
* \brief Insert child in array.
*/
bool InsertAtIndex(const gd::Variable& variable, const size_t index);
/**
* \brief Insert a child in a structure.
*/
bool InsertChild(const gd::String& name, const gd::Variable& variable);
/**
* \brief Get the vector containing all the children.
*/
const std::vector<std::shared_ptr<Variable>>& GetAllChildrenArray() const {
return childrenArray;
}
/**
* \brief Set if the children must be folded.
*/
void SetFolded(bool fold = true) { folded = fold; }
/**
* \brief True if the children should be folded in the variables editor.
*/
bool IsFolded() const { return folded; }
///@}
///@}
@@ -325,6 +351,7 @@ class GD_CORE_API Variable {
*/
static Type StringAsType(const gd::String& str);
bool folded;
mutable Type type;
mutable gd::String str;
mutable double value;

View File

@@ -4,8 +4,10 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/VariablesContainer.h"
#include <algorithm>
#include <iostream>
#include "GDCore/Project/Variable.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -88,7 +90,6 @@ Variable& VariablesContainer::Insert(const gd::String& name,
}
}
#if defined(GD_IDE_ONLY)
void VariablesContainer::Remove(const gd::String& varName) {
variables.erase(
std::remove_if(
@@ -151,13 +152,14 @@ void VariablesContainer::Swap(std::size_t firstVariableIndex,
}
void VariablesContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
if (oldIndex >= variables.size() || newIndex >= variables.size()) return;
if (oldIndex >= variables.size() || newIndex >= variables.size() ||
oldIndex == newIndex)
return;
auto nameAndVariable = variables[oldIndex];
variables.erase(variables.begin() + oldIndex);
variables.insert(variables.begin() + newIndex, nameAndVariable);
}
#endif
void VariablesContainer::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("variable");

View File

@@ -77,8 +77,6 @@ bool SerializerElement::GetBoolAttribute(const gd::String& name,
}
}
std::cout << "Bool attribute \"" << name << "\" not found, returning "
<< defaultValue;
return defaultValue;
}

View File

@@ -9,7 +9,6 @@
#include <algorithm>
#include <string.h>
#include <SFML/System/String.hpp>
#include "GDCore/CommonTools.h"
#include "GDCore/Utf8/utf8proc.h"
@@ -28,11 +27,6 @@ String::String(const char *characters) : m_string()
*this = characters;
}
String::String(const sf::String &string) : m_string()
{
*this = string;
}
String::String(const std::u32string &string) : m_string()
{
*this = string;
@@ -44,26 +38,6 @@ String& String::operator=(const char *characters)
return *this;
}
String& String::operator=(const sf::String &string)
{
m_string.clear();
//In theory, an UTF8 character can be up to 6 bytes (even if in the current Unicode standard,
//the last character is 4 bytes long when encoded in UTF8).
//So, reserve the maximum possible size to avoid reallocations.
m_string.reserve( string.getSize() * 6 );
//Push_back all characters inside the string.
for( sf::String::ConstIterator it = string.begin(); it != string.end(); ++it )
{
push_back( *it );
}
m_string.shrink_to_fit();
return *this;
}
String& String::operator=(const std::u32string &string)
{
m_string.clear();
@@ -112,7 +86,7 @@ String::const_iterator String::end() const
String String::FromLocale( const std::string &localizedString )
{
#if defined(WINDOWS)
return FromSfString(sf::String(localizedString)); //Don't need to use the current locale, on Windows, std::locale is always the C locale
return FromUTF8(localizedString); //Don't need to use the current locale, on Windows, std::locale is always the C locale
#elif defined(MACOS)
return FromUTF8(localizedString); //Assume UTF8 is the current locale
#elif defined(EMSCRIPTEN)
@@ -124,7 +98,7 @@ String String::FromLocale( const std::string &localizedString )
std::locale("").name().find("UTF8") != std::string::npos)
return FromUTF8(localizedString); //UTF8 is already the current locale
else
return FromSfString(sf::String(localizedString, std::locale(""))); //Use the current locale (std::locale("")) for conversion
return FromUTF8(localizedString); //Use the current locale (std::locale("")) for conversion
#endif
}
@@ -136,11 +110,6 @@ String String::FromUTF32( const std::u32string &string )
return str;
}
String String::FromSfString( const sf::String &sfString )
{
return String(sfString);
}
String String::FromUTF8( const std::string &utf8Str )
{
String str(utf8Str.c_str());
@@ -164,7 +133,7 @@ String String::FromWide( const std::wstring &wstr )
std::string String::ToLocale() const
{
#if defined(WINDOWS)
return ToSfString().toAnsiString();
return m_string;
#elif defined(MACOS)
return m_string;
#elif defined(EMSCRIPTEN)
@@ -176,7 +145,7 @@ std::string String::ToLocale() const
std::locale("").name().find("UTF8") != std::string::npos)
return m_string; //UTF8 is already the current locale on Linux
else
return ToSfString().toAnsiString(std::locale("")); //Use the current locale for conversion
return m_string; //Use the current locale for conversion
#endif
}
@@ -191,20 +160,6 @@ std::u32string String::ToUTF32() const
return u32str;
}
sf::String String::ToSfString() const
{
sf::String str;
for(const_iterator it = begin(); it != end(); ++it)
str += sf::String(static_cast<sf::Uint32>(*it));
return str;
}
String::operator sf::String() const
{
return ToSfString();
}
std::string String::ToUTF8() const
{
return m_string;

View File

@@ -13,12 +13,9 @@
#include <sstream>
#include <string>
#include <vector>
#include <SFML/System/String.hpp>
#include "GDCore/Utf8/utf8.h"
namespace sf {class String;};
namespace gd
{
@@ -121,11 +118,6 @@ public:
*/
String(const std::u32string &string);
/**
* Constructs a string from an sf::String.
*/
String(const sf::String &string);
/**
* \}
*/
@@ -146,8 +138,6 @@ public:
*/
String& operator=(const char *characters);
String& operator=(const sf::String &string);
String& operator=(const std::u32string &string);
/**
@@ -229,7 +219,6 @@ public:
static String From(T value)
{
static_assert(!std::is_same<T, std::string>::value, "Can't use gd::String::From with std::string.");
static_assert(!std::is_same<T, sf::String>::value, "Can't use gd::String::From with sf::String.");
std::ostringstream oss;
oss << value;
@@ -244,7 +233,6 @@ public:
T To() const
{
static_assert(!std::is_same<T, std::string>::value, "Can't use gd::String::To with std::string.");
static_assert(!std::is_same<T, sf::String>::value, "Can't use gd::String::To with sf::String.");
T value;
std::istringstream oss(m_string);
@@ -274,13 +262,6 @@ public:
*/
static String FromUTF32( const std::u32string &string );
/**
* \return a String created from a sf::String (UTF32).
*
* See \ref Conversions1 for more information.
*/
static String FromSfString( const sf::String &sfString );
/**
* \return a String created an UTF8 encoded std::string.
*/
@@ -312,20 +293,6 @@ public:
*/
std::u32string ToUTF32() const;
/**
* \return a sf::String from the current string.
*
* See \ref Conversions1 for more information.
*/
sf::String ToSfString() const;
/**
* Implicit conversion operator to sf::String.
*
* See \ref Conversions1 for more information.
*/
operator sf::String() const;
/**
* \return a UTF8 encoded std::string from the current string.
*/
@@ -885,7 +852,7 @@ namespace std
* on the string size and so is the operator[]().
*
* \section Conversion Conversions from/to other string types
* The String handles implicit conversion with sf::String (implicit constructor and implicit conversion
* The String handles implicit conversion with std::String (implicit constructor and implicit conversion
* operator).
*
* **However, this is not the case with std::string** as this conversion is not often lossless (mostly on Windows).
@@ -894,16 +861,6 @@ namespace std
* directly use the operator=() or the constructor as they are supporting const char* as argument (it assumes the string
* literal is encoded in UTF8, so you'll need to put the u8 prefix).
*
* \subsection Conversions1 Implicit conversion from/to sf::String
* \code
* //Get a String from sf::String
* sf::String sfmlStr("This is a test ! ");
* gd::String str1(sfmlStr); //Now contains "This is a test ! " encoded in UTF8
*
* //Get a sf::String from String
* sf::String anotherSfmlString = str; //anotherSfmlString now contains "Another test ! "
* \endcode
*
* \subsection Conversions2 Conversion from/to std::string
* \code
* //Get a String from a std::string encoded in the current locale

View File

@@ -1,200 +0,0 @@
#include "GDCore/Tools/FileStream.h"
#if defined(WINDOWS)
#if __GLIBCXX__
#include <ext/stdio_filebuf.h>
#endif
#endif
namespace gd {
namespace {
#if FSTREAM_WINDOWS_MINGW
#define MODE(in_val, out_val, trunc_val, app_val) \
((((mode & std::ios_base::in) != 0) == in_val) && \
(((mode & std::ios_base::out) != 0) == out_val) && \
(((mode & std::ios_base::trunc) != 0) == trunc_val) && \
(((mode & std::ios_base::app) != 0) == app_val))
std::wstring GetStdioMode(std::ios_base::openmode mode) {
std::wstring strMode;
/// Thanks to https://gcc.gnu.org/ml/libstdc++/2007-06/msg00013.html
if (MODE(false, true, false, false))
strMode += L"w";
else if (MODE(false, true, false, true))
strMode += L"a";
else if (MODE(true, true, false, true))
strMode += L"a+";
else if (MODE(false, true, true, false))
strMode += L"w";
else if (MODE(true, false, false, false))
strMode += L"r";
else if (MODE(true, true, false, false))
strMode += L"r+";
else if (MODE(true, true, true, false))
strMode += L"w+";
if ((mode & std::ios_base::binary) != 0) strMode += L"b";
return strMode;
}
#endif
/**
* Open the given file into a filebuf and return it.
* On Windows, return the associated FILE* inside the file argument.
*/
FileStream::InternalBufferType* OpenBuffer(const gd::String& path,
std::ios_base::openmode mode,
FILE** file) {
#if FSTREAM_WINDOWS_MINGW
*file = _wfopen(path.ToWide().c_str(), GetStdioMode(mode).c_str());
if (!(*file)) return nullptr;
return new __gnu_cxx::stdio_filebuf<char>(*file, mode);
#else
auto* filebuffer = new std::filebuf();
return filebuffer->open(path.ToLocale().c_str(), mode);
#endif
}
} // namespace
FileStream::FileStream() : std::iostream(nullptr) {}
FileStream::FileStream(const gd::String& path, std::ios_base::openmode mode)
: std::iostream(nullptr),
m_file(nullptr),
m_buffer(OpenBuffer(path, mode, &m_file)) {
setstate(ios_base::goodbit);
if (m_buffer) {
std::iostream::init(m_buffer.get());
if ((mode & std::ios_base::ate) != 0) seekg(0, end);
} else
setstate(ios_base::badbit);
}
FileStream::~FileStream() {
if (is_open()) close();
}
/*
WILL WORK with GCC>=5 (not 4.9 used on Windows)
FileStream::FileStream(FileStream && other) :
std::iostream(std::move(other)),
m_buffer(std::move(other.m_buffer))
{
}*/
/*FileStream& FileStream::operator=(FileStream && other)
{
std::iostream::operator=(std::move(other));
m_buffer = std::move(other.m_buffer);
}*/
void FileStream::open(const gd::String& path, std::ios_base::openmode mode) {
setstate(ios_base::goodbit);
if (is_open()) {
setstate(ios_base::failbit);
std::cout << "is_open true when trying to open!" << std::endl;
} else {
auto* newBuffer = OpenBuffer(path, mode, &m_file);
if (newBuffer) {
m_buffer.reset(newBuffer);
std::iostream::init(m_buffer.get());
if ((mode & std::ios_base::ate) != 0) seekg(0, end);
} else {
setstate(ios_base::badbit);
}
}
}
bool FileStream::is_open() const {
if (!m_buffer) return false;
return m_buffer->is_open();
}
void FileStream::close() {
#if FSTREAM_WINDOWS_MINGW
if (m_buffer) m_buffer->close();
if (m_file && fclose(m_file) != 0) {
setstate(ios_base::failbit);
}
m_buffer.reset(nullptr);
m_file = nullptr;
#else
if (!m_buffer || m_buffer->close() == nullptr) {
setstate(ios_base::failbit);
} else {
m_buffer.reset(nullptr);
}
#endif
}
/*void FileStream::swap(FileStream & other) //WILL WORK with GCC>=5 (not 4.9
used on Windows)
{
std::iostream::swap(other);
std::swap(m_buffer, other.m_buffer);
}*/
SFMLFileStream::SFMLFileStream() : m_file(nullptr) {}
SFMLFileStream::~SFMLFileStream() {
if (m_file) fclose(m_file);
}
bool SFMLFileStream::open(const gd::String& filename) {
if (m_file) fclose(m_file);
#if FSTREAM_WINDOWS_MINGW
m_file = _wfopen(filename.ToWide().c_str(), L"rb");
#else
m_file = fopen(filename.ToLocale().c_str(), "rb");
#endif
return m_file != NULL;
}
sf::Int64 SFMLFileStream::read(void* data, sf::Int64 size) {
if (m_file)
return fread(data, 1, static_cast<std::size_t>(size), m_file);
else
return -1;
}
sf::Int64 SFMLFileStream::seek(sf::Int64 position) {
if (m_file) {
fseek(m_file, static_cast<std::size_t>(position), SEEK_SET);
return tell();
} else {
return -1;
}
}
sf::Int64 SFMLFileStream::tell() {
if (m_file)
return ftell(m_file);
else
return -1;
}
sf::Int64 SFMLFileStream::getSize() {
if (m_file) {
sf::Int64 position = tell();
fseek(m_file, 0, SEEK_END);
sf::Int64 size = tell();
seek(position);
return size;
} else {
return -1;
}
}
} // namespace gd

View File

@@ -1,82 +0,0 @@
#ifndef GDCORE_FSTREAMTOOLS
#define GDCORE_FSTREAMTOOLS
#include <iostream>
#include <memory>
#include <SFML/System.hpp>
#include "GDCore/String.h"
#if defined(WINDOWS) && __GLIBCXX__
#include <ext/stdio_filebuf.h>
#else
#include <fstream> //for std::filebuf
#endif
namespace gd {
/**
* Similar to std::i/ofstream except that it can open file with
* gd::String paths (useful on Windows where fstream doesn't
* support wide paths).
*/
class GD_CORE_API FileStream : public std::iostream {
public:
#if defined(WINDOWS) && __GLIBCXX__
using InternalBufferType = std::basic_filebuf<char>;
#else
using InternalBufferType = std::filebuf;
#endif
FileStream();
FileStream(const gd::String& path, std::ios_base::openmode mode);
~FileStream();
FileStream(const FileStream& other) = delete;
FileStream(FileStream&& other) = delete; // HACK for GCC 4.9 (Windows)
// FileStream(FileStream && other); WILL WORK with GCC>=5 (not 4.9 used on
// Windows)
FileStream& operator=(const FileStream& other) = delete;
FileStream& operator=(FileStream&& other) =
delete; // HACK for GCC 4.9 (Windows)
// FileStream& operator=(FileStream && other); WILL WORK with GCC>=5 (not 4.9
// used on Windows)
void open(const gd::String& path, std::ios_base::openmode mode);
bool is_open() const;
void close();
// void swap(FileStream & other); //WILL WORK with GCC>=5 (not 4.9 used on
// Windows)
private:
FILE* m_file;
std::unique_ptr<InternalBufferType> m_buffer;
};
class GD_CORE_API SFMLFileStream : public sf::InputStream {
public:
SFMLFileStream();
~SFMLFileStream();
bool open(const gd::String& filename);
virtual sf::Int64 read(void* data, sf::Int64 size);
virtual sf::Int64 seek(sf::Int64 position);
virtual sf::Int64 tell();
virtual sf::Int64 getSize();
private:
FILE* m_file;
};
} // namespace gd
#endif

View File

@@ -1,19 +0,0 @@
#include <SFML/OpenGL.hpp>
#include <cmath>
namespace OpenGLTools {
void GD_CORE_API PerspectiveGL(GLdouble fovY,
GLdouble aspect,
GLdouble zNear,
GLdouble zFar) {
const GLdouble pi = 3.1415926535897932384626433832795;
GLdouble fW, fH;
fH = std::tan(fovY / 360 * pi) * zNear;
fW = fH * aspect;
glFrustum(-fW, fW, -fH, fH, zNear, zFar);
}
} // namespace OpenGLTools

View File

@@ -1,9 +0,0 @@
#include <SFML/OpenGL.hpp>
namespace OpenGLTools {
void GD_CORE_API PerspectiveGL(GLdouble fovY,
GLdouble aspect,
GLdouble zNear,
GLdouble zFar);
}

360
Core/GDCore/Vector2.h Normal file
View File

@@ -0,0 +1,360 @@
// This is adapted from SFML (https://github.com/SFML/SFML).
#ifndef GDCORE_VECTOR2_H
#define GDCORE_VECTOR2_H
namespace gd
{
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2016 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/// \brief Utility template class for manipulating
/// 2-dimensional vectors
///
////////////////////////////////////////////////////////////
template <typename T>
class Vector2
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates a Vector2(0, 0).
///
////////////////////////////////////////////////////////////
inline Vector2() :
x(0),
y(0)
{
}
////////////////////////////////////////////////////////////
/// \brief Construct the vector from its coordinates
///
/// \param X X coordinate
/// \param Y Y coordinate
///
////////////////////////////////////////////////////////////
inline Vector2(T X, T Y) :
x(X),
y(Y)
{
}
////////////////////////////////////////////////////////////
/// \brief Construct the vector from another type of vector
///
/// This constructor doesn't replace the copy constructor,
/// it's called only when U != T.
/// A call to this constructor will fail to compile if U
/// is not convertible to T.
///
/// \param vector Vector to convert
///
////////////////////////////////////////////////////////////
template <typename U>
inline explicit Vector2(const Vector2<U>& vector) :
x(static_cast<T>(vector.x)),
y(static_cast<T>(vector.y))
{
}
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
T x; ///< X coordinate of the vector
T y; ///< Y coordinate of the vector
};
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of unary operator -
///
/// \param right Vector to negate
///
/// \return Memberwise opposite of the vector
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator -(const Vector2<T>& right)
{
return Vector2<T>(-right.x, -right.y);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator +=
///
/// This operator performs a memberwise addition of both vectors,
/// and assigns the result to \a left.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to \a left
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T>& operator +=(Vector2<T>& left, const Vector2<T>& right)
{
left.x += right.x;
left.y += right.y;
return left;
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator -=
///
/// This operator performs a memberwise subtraction of both vectors,
/// and assigns the result to \a left.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to \a left
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T>& operator -=(Vector2<T>& left, const Vector2<T>& right)
{
left.x -= right.x;
left.y -= right.y;
return left;
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator +
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Memberwise addition of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator +(const Vector2<T>& left, const Vector2<T>& right)
{
return Vector2<T>(left.x + right.x, left.y + right.y);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator -
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Memberwise subtraction of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator -(const Vector2<T>& left, const Vector2<T>& right)
{
return Vector2<T>(left.x - right.x, left.y - right.y);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator *
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Memberwise multiplication by \a right
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator *(const Vector2<T>& left, T right)
{
return Vector2<T>(left.x * right, left.y * right);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator *
///
/// \param left Left operand (a scalar value)
/// \param right Right operand (a vector)
///
/// \return Memberwise multiplication by \a left
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator *(T left, const Vector2<T>& right)
{
return Vector2<T>(right.x * left, right.y * left);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator *=
///
/// This operator performs a memberwise multiplication by \a right,
/// and assigns the result to \a left.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to \a left
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T>& operator *=(Vector2<T>& left, T right)
{
left.x *= right;
left.y *= right;
return left;
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator /
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Memberwise division by \a right
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T> operator /(const Vector2<T>& left, T right)
{
return Vector2<T>(left.x / right, left.y / right);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator /=
///
/// This operator performs a memberwise division by \a right,
/// and assigns the result to \a left.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to \a left
///
////////////////////////////////////////////////////////////
template <typename T>
inline Vector2<T>& operator /=(Vector2<T>& left, T right)
{
left.x /= right;
left.y /= right;
return left;
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator ==
///
/// This operator compares strict equality between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return True if \a left is equal to \a right
///
////////////////////////////////////////////////////////////
template <typename T>
inline bool operator ==(const Vector2<T>& left, const Vector2<T>& right)
{
return (left.x == right.x) && (left.y == right.y);
}
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary operator !=
///
/// This operator compares strict difference between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return True if \a left is not equal to \a right
///
////////////////////////////////////////////////////////////
template <typename T>
inline bool operator !=(const Vector2<T>& left, const Vector2<T>& right)
{
return (left.x != right.x) || (left.y != right.y);
}
// Define the most common types
typedef Vector2<int> Vector2i;
typedef Vector2<unsigned int> Vector2u;
typedef Vector2<float> Vector2f;
} // namespace gd
#endif // GDCORE_VECTOR2_H
////////////////////////////////////////////////////////////
/// \class gd::Vector2
/// \ingroup CommonProgrammingTools
///
/// gd::Vector2 is a simple class that defines a mathematical
/// vector with two coordinates (x and y). It can be used to
/// represent anything that has two dimensions: a size, a point,
/// a velocity, etc.
///
/// The template parameter T is the type of the coordinates. It
/// can be any type that supports arithmetic operations (+, -, /, *)
/// and comparisons (==, !=), for example int or float.
///
/// You generally don't have to care about the templated form (gd::Vector2<T>),
/// the most common specializations have special typedefs:
/// \li gd::Vector2<float> is gd::Vector2f
/// \li gd::Vector2<int> is gd::Vector2i
/// \li gd::Vector2<unsigned int> is gd::Vector2u
///
/// The gd::Vector2 class has a small and simple interface, its x and y members
/// can be accessed directly (there are no accessors like setX(), getX()) and it
/// contains no mathematical function like dot product, cross product, length, etc.
///
/// Usage example:
/// \code
/// gd::Vector2f v1(16.5f, 24.f);
/// v1.x = 18.2f;
/// float y = v1.y;
///
/// gd::Vector2f v2 = v1 * 5.f;
/// gd::Vector2f v3;
/// v3 = v1 + v2;
///
/// bool different = (v2 != v3);
/// \endcode
///
/// Note: for 3-dimensional vectors, see gd::Vector3.
///
////////////////////////////////////////////////////////////

View File

@@ -75,7 +75,11 @@ TEST_CASE("EventsList", "[common][events]") {
size_t endMemory = gd::SystemStats::GetUsedVirtualMemory();
INFO("Memory used: " << endMemory - startMemory << "KB");
REQUIRE(1500 >= endMemory - startMemory);
#if defined(WINDOWS)
REQUIRE(3000 >= endMemory - startMemory);
#else
REQUIRE(1500 >= endMemory - startMemory);
#endif
}
}

View File

@@ -31,7 +31,7 @@ TEST_CASE("EventsCodeGenerationContext", "[common][events]") {
gd::EventsCodeGenerationContext c1(&maxDepth);
c1.ObjectsListNeeded("c1.object1");
c1.ObjectsListNeeded("c1.object2");
c1.ObjectsListWithoutPickingNeeded("c1.noPicking1");
c1.ObjectsListNeededOrEmptyIfJustDeclared("c1.noPicking1");
gd::EventsCodeGenerationContext c2;
c2.InheritsFrom(c1);
@@ -47,7 +47,7 @@ TEST_CASE("EventsCodeGenerationContext", "[common][events]") {
gd::EventsCodeGenerationContext c5;
c5.InheritsFrom(c2);
c5.ObjectsListWithoutPickingNeeded("c5.noPicking1");
c5.ObjectsListNeededOrEmptyIfJustDeclared("c5.noPicking1");
c5.ObjectsListNeeded("c5.object1");
c5.ObjectsListNeeded("c1.object2");
c5.EmptyObjectsListNeeded("c5.empty1");
@@ -70,36 +70,36 @@ TEST_CASE("EventsCodeGenerationContext", "[common][events]") {
}
SECTION("Object list needed") {
REQUIRE(c1.GetObjectsListsAlreadyDeclared() == std::set<gd::String>());
REQUIRE(c1.GetObjectsListsAlreadyDeclaredByParents() == std::set<gd::String>());
REQUIRE(c1.GetObjectsListsToBeDeclared() ==
std::set<gd::String>({"c1.object1", "c1.object2"}));
REQUIRE(c1.GetObjectsListsToBeDeclaredWithoutPicking() ==
REQUIRE(c1.GetObjectsListsToBeEmptyIfJustDeclared() ==
std::set<gd::String>({"c1.noPicking1"}));
REQUIRE(c1.GetAllObjectsToBeDeclared() ==
std::set<gd::String>({"c1.object1", "c1.object2", "c1.noPicking1"}));
REQUIRE(c2.GetObjectsListsAlreadyDeclared() ==
REQUIRE(c2.GetObjectsListsAlreadyDeclaredByParents() ==
std::set<gd::String>({"c1.object1", "c1.object2", "c1.noPicking1"}));
REQUIRE(c2.GetObjectsListsToBeDeclared() ==
std::set<gd::String>({"c2.object1"}));
REQUIRE(c2.GetObjectsListsToBeDeclaredWithoutPicking() == std::set<gd::String>());
REQUIRE(c2.GetObjectsListsToBeEmptyIfJustDeclared() == std::set<gd::String>());
REQUIRE(c2.GetAllObjectsToBeDeclared() ==
std::set<gd::String>({"c2.object1"}));
REQUIRE(c3.GetObjectsListsAlreadyDeclared() ==
REQUIRE(c3.GetObjectsListsAlreadyDeclaredByParents() ==
std::set<gd::String>({"c1.object1", "c1.object2", "c1.noPicking1"}));
REQUIRE(c3.GetObjectsListsToBeDeclared() ==
std::set<gd::String>({"c3.object1", "c1.object2"}));
REQUIRE(c3.GetObjectsListsToBeDeclaredWithoutPicking() == std::set<gd::String>());
REQUIRE(c3.GetObjectsListsToBeEmptyIfJustDeclared() == std::set<gd::String>());
REQUIRE(c3.GetAllObjectsToBeDeclared() ==
std::set<gd::String>({"c3.object1", "c1.object2"}));
REQUIRE(c5.GetObjectsListsAlreadyDeclared() ==
REQUIRE(c5.GetObjectsListsAlreadyDeclaredByParents() ==
std::set<gd::String>(
{"c1.object1", "c1.object2", "c1.noPicking1", "c2.object1"}));
REQUIRE(c5.GetObjectsListsToBeDeclared() ==
std::set<gd::String>({"c5.object1", "c1.object2"}));
REQUIRE(c5.GetObjectsListsToBeDeclaredWithoutPicking() ==
REQUIRE(c5.GetObjectsListsToBeEmptyIfJustDeclared() ==
std::set<gd::String>({"c5.noPicking1"}));
REQUIRE(c5.GetObjectsListsToBeDeclaredEmpty() ==
std::set<gd::String>({"c5.empty1"}));
@@ -107,22 +107,18 @@ TEST_CASE("EventsCodeGenerationContext", "[common][events]") {
std::set<gd::String>({"c5.object1", "c5.noPicking1", "c1.object2", "c5.empty1"}));
}
SECTION("ObjectAlreadyDeclared") {
REQUIRE(c1.ObjectAlreadyDeclared("c1.object1") == false);
REQUIRE(c2.ObjectAlreadyDeclared("c1.object1") == true);
REQUIRE(c3.ObjectAlreadyDeclared("c1.object1") == true);
REQUIRE(c4.ObjectAlreadyDeclared("c1.object1") == true);
REQUIRE(c5.ObjectAlreadyDeclared("c1.object1") == true);
SECTION("ObjectAlreadyDeclaredByParents") {
REQUIRE(c1.ObjectAlreadyDeclaredByParents("c1.object1") == false);
REQUIRE(c2.ObjectAlreadyDeclaredByParents("c1.object1") == true);
REQUIRE(c3.ObjectAlreadyDeclaredByParents("c1.object1") == true);
REQUIRE(c4.ObjectAlreadyDeclaredByParents("c1.object1") == true);
REQUIRE(c5.ObjectAlreadyDeclaredByParents("c1.object1") == true);
REQUIRE(c2.ObjectAlreadyDeclared("c2.object1") == false);
REQUIRE(c1.ObjectAlreadyDeclared("c2.object1") == false);
REQUIRE(c3.ObjectAlreadyDeclared("c2.object1") == false);
REQUIRE(c4.ObjectAlreadyDeclared("c2.object1") == true);
REQUIRE(c5.ObjectAlreadyDeclared("c2.object1") == true);
REQUIRE(c3.ObjectAlreadyDeclared("some object") == false);
c3.SetObjectDeclared("some object");
REQUIRE(c3.ObjectAlreadyDeclared("some object") == true);
REQUIRE(c1.ObjectAlreadyDeclaredByParents("c2.object1") == false);
REQUIRE(c2.ObjectAlreadyDeclaredByParents("c2.object1") == false);
REQUIRE(c3.ObjectAlreadyDeclaredByParents("c2.object1") == false);
REQUIRE(c4.ObjectAlreadyDeclaredByParents("c2.object1") == true);
REQUIRE(c5.ObjectAlreadyDeclaredByParents("c2.object1") == true);
}
SECTION("Object list last depth") {
@@ -186,4 +182,87 @@ TEST_CASE("EventsCodeGenerationContext", "[common][events]") {
REQUIRE(c7.IsSameObjectsList("c6.object3", c6) == false);
REQUIRE(c7.IsSameObjectsList("c5.empty1", c5) == false);
}
SECTION("Async") {
gd::EventsCodeGenerationContext c1;
c1.ObjectsListNeeded("c1.object1");
c1.ObjectsListNeeded("c1.object2");
c1.ObjectsListNeededOrEmptyIfJustDeclared("c1.possiblyEmpty1");
c1.EmptyObjectsListNeeded("c1.empty1");
gd::EventsCodeGenerationContext c2;
c2.InheritsAsAsyncCallbackFrom(c1);
c2.ObjectsListNeeded("c2.object1");
c2.ObjectsListNeededOrEmptyIfJustDeclared("c2.possiblyEmpty1");
c2.EmptyObjectsListNeeded("c2.empty1");
c2.ObjectsListNeeded("c1.object1");
gd::EventsCodeGenerationContext c3;
c3.InheritsAsAsyncCallbackFrom(c2);
c3.ObjectsListNeeded("c3.object1");
c3.ObjectsListNeededOrEmptyIfJustDeclared("c3.possiblyEmpty1");
c3.EmptyObjectsListNeeded("c3.empty1");
c3.ObjectsListNeeded("c1.object1");
gd::EventsCodeGenerationContext c4;
c4.InheritsFrom(c3);
c4.ObjectsListNeeded("c4.object1");
c4.ObjectsListNeededOrEmptyIfJustDeclared("c4.possiblyEmpty1");
c4.EmptyObjectsListNeeded("c4.empty1");
c4.ObjectsListNeeded("c1.object1");
c4.ObjectsListNeeded("c1.object2");
gd::EventsCodeGenerationContext c5;
c5.InheritsFrom(c4);
c5.ObjectsListNeeded("c5.object1");
c5.ObjectsListNeededOrEmptyIfJustDeclared("c5.possiblyEmpty1");
c5.EmptyObjectsListNeeded("c5.empty1");
c5.ObjectsListNeeded("c1.object1");
c5.ObjectsListNeeded("c1.object2");
REQUIRE(c1.ShouldUseAsyncObjectsList("c1.object1") == false);
REQUIRE(c1.ShouldUseAsyncObjectsList("c1.possiblyEmpty1") == false);
REQUIRE(c1.ShouldUseAsyncObjectsList("c1.empty1") == false);
// Objects declared in async callback are used traditionally:
REQUIRE(c2.ShouldUseAsyncObjectsList("c2.object1") == false);
REQUIRE(c2.ShouldUseAsyncObjectsList("c2.possiblyEmpty1") == false);
REQUIRE(c2.ShouldUseAsyncObjectsList("c2.empty1") == false);
// Objects declared in c1 are gotten from the async list in c2 and c3
// because these contexts use them and are async.
REQUIRE(c2.ShouldUseAsyncObjectsList("c1.object1") == true);
REQUIRE(c3.ShouldUseAsyncObjectsList("c1.object1") == true);
REQUIRE(c4.ShouldUseAsyncObjectsList("c1.object1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c1.object1") == false);
// Objects declared in c1 are gotten from the async list in c4
// because c3 is async (but is not using them)
REQUIRE(c4.ShouldUseAsyncObjectsList("c1.object2") == true);
// Objects declared in c1 but gotten from the async list in c4
// is used traditionnally:
REQUIRE(c5.ShouldUseAsyncObjectsList("c1.object2") == false);
// Objects declared in or after c3 are used traditionally:
REQUIRE(c3.ShouldUseAsyncObjectsList("c3.object1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c3.object1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c3.object1") == false);
REQUIRE(c3.ShouldUseAsyncObjectsList("c3.possiblyEmpty1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c3.possiblyEmpty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c3.possiblyEmpty1") == false);
REQUIRE(c3.ShouldUseAsyncObjectsList("c3.empty1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c3.empty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c3.empty1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c4.object1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c4.possiblyEmpty1") == false);
REQUIRE(c4.ShouldUseAsyncObjectsList("c4.empty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c4.object1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c4.possiblyEmpty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c4.empty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c5.object1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c5.possiblyEmpty1") == false);
REQUIRE(c5.ShouldUseAsyncObjectsList("c5.empty1") == false);
}
}

View File

@@ -1,90 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering FileStream class of GDevelop Core.
*/
#include "GDCore/Tools/FileStream.h"
#include <fstream>
#include <memory>
#include "GDCore/CommonTools.h"
#include "catch.hpp"
TEST_CASE("FileStream", "[common][fstream]") {
// Creating the test file
std::ofstream testFile("FileStreamTest.test");
REQUIRE(testFile.is_open());
testFile << "this is the first line of the test file!\n";
testFile << "the last line!";
testFile.close();
SECTION("Input file") {
gd::FileStream f;
f.open("FileStreamTest.test", std::ios_base::in);
REQUIRE(f.is_open() == true);
REQUIRE(f.tellg() == 0);
std::string lineContent;
REQUIRE(f.eof() == false);
std::getline(f, lineContent);
REQUIRE(lineContent == "this is the first line of the test file!");
REQUIRE(f.eof() == false);
std::getline(f, lineContent);
REQUIRE(lineContent == "the last line!");
REQUIRE(f.eof() == true);
REQUIRE(f.fail() == false);
f.close();
REQUIRE(f.is_open() == false);
REQUIRE(f.fail() == false);
}
SECTION("Output/Input file with special characters in filepath") {
gd::FileStream output("\xEA\x88\xA1.txt", std::ios_base::out);
REQUIRE(output.is_open() == true);
output << "TEST";
output.close();
gd::FileStream input("\xEA\x88\xA1.txt", std::ios_base::in);
REQUIRE(input.is_open() == true);
std::string lineContent;
REQUIRE(input.eof() == false);
std::getline(input, lineContent);
REQUIRE(lineContent == "TEST");
REQUIRE(input.eof() == true);
input.close();
REQUIRE(input.is_open() == false);
REQUIRE(input.fail() == false);
}
SECTION("File opening failure") {
gd::FileStream f("\xE4\x84\xA2",
std::ios_base::in); // A file that doesn't exist
REQUIRE(f.is_open() == false);
REQUIRE(f.fail() ==
true); // As the opening failed, the "fail" flag should be set
f.close();
REQUIRE(f.bad() == true); // As no files are opened, this should set the
// "bad" flag of the stream
}
SECTION("std::ios_base::ate") { // As ate is "emulated", needs testing
gd::FileStream f;
f.open("FileStreamTest.test", std::ios_base::in | std::ios_base::ate);
REQUIRE(f.is_open() == true);
REQUIRE(f.tellg() > 0);
}
}

View File

@@ -7,7 +7,6 @@
* @file Tests covering utf8 features from GDevelop Core.
*/
#include <SFML/System/String.hpp>
#include <exception>
#include <iostream>
#include <string>
@@ -19,10 +18,8 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
SECTION("ctor & conversions") {
gd::String str = u8"UTF8 a été testé !";
sf::String sfStr = str;
std::u32string u32str = str.ToUTF32();
REQUIRE(str == gd::String::FromSfString(sfStr));
REQUIRE(str == gd::String::FromUTF32(u32str));
}
@@ -42,8 +39,10 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
REQUIRE(str.substr(5, 7) == u8"a été t");
REQUIRE(str.substr(5, gd::String::npos) == u8"a été testé !");
REQUIRE_THROWS_AS(str.substr(50, 5), std::out_of_range);
// Windows doesn't seems to like exceptions.
#if !defined(WINDOWS)
REQUIRE_THROWS_AS(str.substr(50, 5), std::out_of_range);
#endif
}
SECTION("insert") {
@@ -51,7 +50,9 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
str.insert(25, u8"vraiment ");
REQUIRE(str == u8"Une fonctionnalité a été vraiment testée !");
REQUIRE_THROWS_AS(str.insert(150, u8"This gonna fail"), std::out_of_range);
#if !defined(WINDOWS)
REQUIRE_THROWS_AS(str.insert(150, u8"This gonna fail"), std::out_of_range);
#endif
}
SECTION("replace") {
@@ -62,8 +63,10 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
REQUIRE(str.replace(11, gd::String::npos, u8"vraiment très testé !") ==
u8"UTF8 a été vraiment très testé !");
REQUIRE_THROWS_AS(str.replace(50, 5, u8"Cela va planter."),
std::out_of_range);
#if !defined(WINDOWS)
REQUIRE_THROWS_AS(str.replace(50, 5, u8"Cela va planter."),
std::out_of_range);
#endif
// Testing the iterator version of replace
gd::String str2 = u8"UTF8 a été testé !";
@@ -94,8 +97,10 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
REQUIRE(str == "UTF8");
}
{
gd::String str = u8"UTF8 a été testé !";
REQUIRE_THROWS_AS(str.erase(100, 5), std::out_of_range);
#if !defined(WINDOWS)
gd::String str = u8"UTF8 a été testé !";
REQUIRE_THROWS_AS(str.erase(100, 5), std::out_of_range);
#endif
}
{
gd::String str = u8"UTF8 a été testé !";

View File

@@ -1,54 +0,0 @@
# Clone SFML from its official directory using Git. Only
# do it if not already cloned to avoid triggering a whole
# recompilation every time CMake is run.
find_package(Git)
if(GIT_FOUND)
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
message( "Cloning SFML in ExtLibs/SFML with Git..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
OUTPUT_QUIET)
message( "Resetting SFML source code to version 2.4.1..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
message( "Applying the patches..." )
file(GLOB SFML_PATCHES
LIST_DIRECTORIES FALSE
${CMAKE_CURRENT_SOURCE_DIR}/SFML-patches/*.patch)
if(SFML_PATCHES)
list(SORT SFML_PATCHES)
foreach(SFML_PATCH ${SFML_PATCHES})
message( "Applying patch: ${SFML_PATCH}..." )
execute_process(
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
OUTPUT_QUIET)
endforeach()
endif()
else()
message( "SFML already downloaded." )
endif()
else()
message( "Git not found, make sure you have SFML >= 2.4 in ExtLibs/SFML and you applied the needed patches (from ExtLibs/SFML-patches)!" )
endif()
#SFML:
IF(NOT EMSCRIPTEN) #Don't build SFML binaries when compiling with emscripten (but keep include files!)
add_subdirectory(SFML)
set(sfml_lib_dir ${CMAKE_BINARY_DIR}/ExtLibs/SFML/lib PARENT_SCOPE)
set(sfml_LIBRARIES sfml-audio sfml-graphics sfml-window sfml-network sfml-system)
IF(WIN32)
set(sfml_LIBRARIES "${sfml_LIBRARIES}" ws2_32 user32 opengl32 glu32 psapi)
ELSEIF(NOT APPLE)
set(sfml_LIBRARIES "${sfml_LIBRARIES}" GLU GL)
ENDIF()
set(sfml_LIBRARIES "${sfml_LIBRARIES}" PARENT_SCOPE)
ENDIF()
set(sfml_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/SFML/include PARENT_SCOPE)

View File

@@ -1,19 +0,0 @@
diff --git a/extlibs/headers/stb_image/stb_image.h b/extlibs/headers/stb_image/stb_image.h
index c3945c2..5fe1050 100644
--- a/extlibs/headers/stb_image/stb_image.h
+++ b/extlibs/headers/stb_image/stb_image.h
@@ -671,14 +671,9 @@ static int stbi__sse2_available()
static int stbi__sse2_available()
{
-#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later
- // GCC 4.8+ has a nice way to do this
- return __builtin_cpu_supports("sse2");
-#else
// portable way to do this, preferably without using GCC inline ASM?
// just bail for now.
return 0;
-#endif
}
#endif
#endif

View File

@@ -1,29 +0,0 @@
From f55ee73a73398bb77ce9c63ff1a13d8053cb1d18 Mon Sep 17 00:00:00 2001
From: Florian Rival <Florian.Rival@gmail.com>
Date: Tue, 3 Jan 2017 21:15:11 +0100
Subject: [PATCH] Fix crash with SFML embeded in wxWidgets on OS X
---
src/SFML/Window/OSX/SFViewController.mm | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/SFML/Window/OSX/SFViewController.mm b/src/SFML/Window/OSX/SFViewController.mm
index 7d736e3..280ec2b 100644
--- a/src/SFML/Window/OSX/SFViewController.mm
+++ b/src/SFML/Window/OSX/SFViewController.mm
@@ -82,8 +82,12 @@ -(id)initWithView:(NSView *)view
////////////////////////////////////////////////////////
-(void)dealloc
{
+ NSLog(@"SFViewController::dealloc called on %@", self);
[self closeWindow];
+
+ // See https://github.com/SFML/SFML/issues/824
+ [[NSNotificationCenter defaultCenter] removeObserver:m_oglView];
[m_view release];
[m_oglView release];
--
2.7.4 (Apple Git-66)

View File

@@ -1,13 +0,0 @@
diff --git a/include/SFML/Config.hpp b/include/SFML/Config.hpp
index 9c68d84..2522d88 100644
--- a/include/SFML/Config.hpp
+++ b/include/SFML/Config.hpp
@@ -76,7 +76,7 @@
// Android
#define SFML_SYSTEM_ANDROID
- #elif defined(__linux__)
+ #elif defined(__linux__) || defined(EMSCRIPTEN)
// Linux
#define SFML_SYSTEM_LINUX

View File

@@ -45,6 +45,7 @@ module.exports = {
)
.addParameter('yesorno', _('Focus the window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -61,6 +62,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -79,6 +81,7 @@ module.exports = {
)
.addParameter('yesorno', _('Show window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -95,6 +98,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -113,6 +117,7 @@ module.exports = {
)
.addParameter('yesorno', _('Maximize window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -129,6 +134,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -147,6 +153,7 @@ module.exports = {
)
.addParameter('yesorno', _('Minimize window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -163,6 +170,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -181,6 +189,7 @@ module.exports = {
)
.addParameter('yesorno', _('Enable window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -197,6 +206,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -215,6 +225,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow resizing?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -231,6 +242,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -249,6 +261,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow moving?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -265,6 +278,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -283,6 +297,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow maximizing?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -299,6 +314,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -317,6 +333,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow minimizing?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -333,6 +350,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -351,6 +369,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow full-screening?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -367,6 +386,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -385,6 +405,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow closing?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -401,6 +422,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -442,6 +464,7 @@ module.exports = {
'above the taskbar on Windows. ' +
'This parameter is ignored on linux.'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -458,6 +481,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -478,6 +502,7 @@ module.exports = {
)
.addParameter('yesorno', _('Enable kiosk mode?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -494,6 +519,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -512,6 +538,7 @@ module.exports = {
)
.addParameter('yesorno', _('Enable shadow?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -528,6 +555,7 @@ module.exports = {
'res/actions/window24.png',
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -548,6 +576,7 @@ module.exports = {
)
.addParameter('yesorno', _('Enable content protection?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -566,6 +595,7 @@ module.exports = {
)
.addParameter('yesorno', _('Allow focus?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -584,6 +614,7 @@ module.exports = {
)
.addParameter('yesorno', _('Flash the window?'), '', false)
.setDefaultValue('true')
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -604,6 +635,7 @@ module.exports = {
.setParameterLongDescription(
'A number between 0 (fully transparent) and 1 (fully opaque).'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -622,6 +654,7 @@ module.exports = {
)
.addParameter('expression', _('X position'), '', false)
.addParameter('expression', _('Y position'), '', false)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -636,6 +669,7 @@ module.exports = {
_('Windows, Linux, macOS'),
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -650,6 +684,7 @@ module.exports = {
_('Windows, Linux, macOS'),
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
@@ -666,6 +701,7 @@ module.exports = {
_('Windows, Linux, macOS'),
'res/actions/window.png'
)
.addCodeOnlyParameter('currentScene', '')
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'

View File

@@ -6,16 +6,23 @@ namespace gdjs {
*/
export namespace evtTools {
export namespace advancedWindow {
/**
* The game's BrowserWindow instance (or null on
* non-electron platforms).
*/
let electronBrowserWindow: any = null;
const getElectronBrowserWindow = (runtimeScene: gdjs.RuntimeScene) => {
const electronRemote = runtimeScene
.getGame()
.getRenderer()
.getElectronRemote();
if (electronRemote) {
return electronRemote.getCurrentWindow();
}
if (typeof require === 'function') {
electronBrowserWindow = require('electron').remote.getCurrentWindow();
}
export const focus = function (activate: boolean) {
return null;
};
export const focus = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
if (activate) {
electronBrowserWindow.focus();
@@ -25,14 +32,21 @@ namespace gdjs {
}
};
export const isFocused = function (): boolean {
export const isFocused = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isFocused();
}
return false;
};
export const show = function (activate: boolean) {
export const show = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
if (activate) {
electronBrowserWindow.showInactive();
@@ -42,14 +56,21 @@ namespace gdjs {
}
};
export const isVisible = function (): boolean {
export const isVisible = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isVisible();
}
return false;
};
export const maximize = function (activate: boolean) {
export const maximize = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
if (activate) {
electronBrowserWindow.maximize();
@@ -59,14 +80,21 @@ namespace gdjs {
}
};
export const isMaximized = function (): boolean {
export const isMaximized = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isMaximized();
}
return false;
};
export const minimize = function (activate: boolean) {
export const minimize = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
if (activate) {
electronBrowserWindow.minimize();
@@ -76,98 +104,150 @@ namespace gdjs {
}
};
export const isMinimized = function (): boolean {
export const isMinimized = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isMinimized();
}
return false;
};
export const enable = function (activate: boolean) {
export const enable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setEnabled(activate);
}
};
export const isEnabled = function (): boolean {
export const isEnabled = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isEnabled();
}
return false;
};
export const setResizable = function (activate: boolean) {
export const setResizable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setResizable(activate);
}
};
export const isResizable = function (): boolean {
export const isResizable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isResizable();
}
return false;
};
export const setMovable = function (activate: boolean) {
export const setMovable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setMovable(activate);
}
};
export const isMovable = function (): boolean {
export const isMovable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isMovable();
}
return false;
};
export const setMaximizable = function (activate: boolean) {
export const setMaximizable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setMaximizable(activate);
}
};
export const isMaximizable = function (): boolean {
export const isMaximizable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isMaximizable();
}
return false;
};
export const setMinimizable = function (activate: boolean) {
export const setMinimizable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setMinimizable(activate);
}
};
export const isMinimizable = function (): boolean {
export const isMinimizable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isMinimizable();
}
return false;
};
export const setFullScreenable = function (activate: boolean) {
export const setFullScreenable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setFullScreenable(activate);
}
};
export const isFullScreenable = function (): boolean {
export const isFullScreenable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isFullScreenable();
}
return false;
};
export const setClosable = function (activate: boolean) {
export const setClosable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setClosable(activate);
}
};
export const isClosable = function (): boolean {
export const isClosable = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isClosable();
}
@@ -184,93 +264,142 @@ namespace gdjs {
| 'main-menu'
| 'status'
| 'pop-up-menu'
| 'screen-saver'
| 'screen-saver',
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setAlwaysOnTop(activate, level);
}
};
export const isAlwaysOnTop = function (): boolean {
export const isAlwaysOnTop = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isAlwaysOnTop();
}
return false;
};
export const setPosition = function (x: float, y: float) {
export const setPosition = function (
x: float,
y: float,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
// Convert x and y to (32 bit) integers to avoid Electron errors.
electronBrowserWindow.setPosition(~~x, ~~y);
}
};
export const getPositionX = function (): number {
export const getPositionX = function (
runtimeScene: gdjs.RuntimeScene
): number {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.getPosition()[0];
}
return 0;
};
export const getPositionY = function (): number {
export const getPositionY = function (
runtimeScene: gdjs.RuntimeScene
): number {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.getPosition()[1];
}
return 0;
};
export const setKiosk = function (activate: boolean) {
export const setKiosk = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setKiosk(activate);
}
};
export const isKiosk = function (): boolean {
export const isKiosk = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.isKiosk();
}
return false;
};
export const flash = function (activate: boolean) {
export const flash = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.flashFrame(activate);
}
};
export const setHasShadow = function (activate: boolean) {
export const setHasShadow = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setHasShadow(activate);
}
};
export const hasShadow = function (): boolean {
export const hasShadow = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.hasShadow();
}
return false;
};
export const setOpacity = function (opacity: float) {
export const setOpacity = function (
opacity: float,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setOpacity(opacity);
}
};
export const getOpacity = function (): number {
export const getOpacity = function (
runtimeScene: gdjs.RuntimeScene
): number {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
return electronBrowserWindow.getOpacity();
}
return 1;
};
export const setContentProtection = function (activate: boolean) {
export const setContentProtection = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setContentProtection(activate);
}
};
export const setFocusable = function (activate: boolean) {
export const setFocusable = function (
activate: boolean,
runtimeScene: gdjs.RuntimeScene
) {
const electronBrowserWindow = getElectronBrowserWindow(runtimeScene);
if (electronBrowserWindow) {
electronBrowserWindow.setFocusable(activate);
}

View File

@@ -5,7 +5,9 @@ Copyright (c) 2016 Victor Levasseur (victorlevasseur52@gmail.com)
This project is released under the MIT License.
*/
#include "AnchorBehavior.h"
#include <map>
#include "GDCore/CommonTools.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -20,6 +22,7 @@ void AnchorBehavior::InitializeContent(gd::SerializerElement& content) {
content.SetAttribute("topEdgeAnchor", static_cast<int>(ANCHOR_VERTICAL_NONE));
content.SetAttribute("bottomEdgeAnchor",
static_cast<int>(ANCHOR_VERTICAL_NONE));
content.SetAttribute("useLegacyBottomAndRightAnchors", false);
}
#if defined(GD_IDE_ONLY)
@@ -68,7 +71,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window left"))
.AddExtraInfo(_("Window right"))
.AddExtraInfo(_("Proportional"))
.SetDescription(_("Use this to anchor the object on X axis."));
.SetDescription(_("Anchor the left edge of the object on X axis."));
properties[_("Right edge anchor")]
.SetValue(GetAnchorAsString(static_cast<HorizontalAnchor>(
@@ -77,7 +80,8 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("No anchor"))
.AddExtraInfo(_("Window left"))
.AddExtraInfo(_("Window right"))
.AddExtraInfo(_("Proportional"));
.AddExtraInfo(_("Proportional"))
.SetDescription(_("Anchor the right edge of the object on X axis."));
properties[_("Top edge anchor")]
.SetValue(GetAnchorAsString(static_cast<VerticalAnchor>(
@@ -87,7 +91,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window top"))
.AddExtraInfo(_("Window bottom"))
.AddExtraInfo(_("Proportional"))
.SetDescription(_("Use this to anchor the object on Y axis."));
.SetDescription(_("Anchor the top edge of the object on Y axis."));
properties[_("Bottom edge anchor")]
.SetValue(GetAnchorAsString(static_cast<VerticalAnchor>(
@@ -96,7 +100,20 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("No anchor"))
.AddExtraInfo(_("Window top"))
.AddExtraInfo(_("Window bottom"))
.AddExtraInfo(_("Proportional"));
.AddExtraInfo(_("Proportional"))
.SetDescription(_("Anchor the bottom edge of the object on Y axis."));
properties[("useLegacyBottomAndRightAnchors")]
.SetLabel(_(
"Stretch object when anchoring right or bottom ledge (deprecated, "
"it's recommended to let this unchecked and anchor both sides if you "
"want Sprite to stretch instead.)"))
.SetGroup(_("Deprecated options (advanced)"))
.SetValue(behaviorContent.GetBoolAttribute(
"useLegacyBottomAndRightAnchors", true)
? "true"
: "false")
.SetType("Boolean");
return properties;
}
@@ -147,6 +164,9 @@ bool AnchorBehavior::UpdateProperty(gd::SerializerElement& behaviorContent,
behaviorContent.SetAttribute(
"bottomEdgeAnchor",
static_cast<int>(GetVerticalAnchorFromString(value)));
else if (name == "useLegacyBottomAndRightAnchors")
behaviorContent.SetAttribute("useLegacyBottomAndRightAnchors",
(value == "1"));
else
return false;

View File

@@ -15,6 +15,7 @@ namespace gdjs {
_rightEdgeDistance: number = 0;
_topEdgeDistance: number = 0;
_bottomEdgeDistance: number = 0;
_useLegacyBottomAndRightAnchors: boolean = false;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
@@ -23,6 +24,10 @@ namespace gdjs {
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
this._topEdgeAnchor = behaviorData.topEdgeAnchor;
this._bottomEdgeAnchor = behaviorData.bottomEdgeAnchor;
this._useLegacyBottomAndRightAnchors =
behaviorData.useLegacyBottomAndRightAnchors === undefined
? true
: behaviorData.useLegacyBottomAndRightAnchors;
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
@@ -40,6 +45,13 @@ namespace gdjs {
) {
this._bottomEdgeAnchor = newBehaviorData.bottomEdgeAnchor;
}
if (
oldBehaviorData.useLegacyTrajectory !==
newBehaviorData.useLegacyTrajectory
) {
this._useLegacyBottomAndRightAnchors =
newBehaviorData.useLegacyBottomAndRightAnchors;
}
if (
oldBehaviorData.relativeToOriginalWindowSize !==
newBehaviorData.relativeToOriginalWindowSize
@@ -262,28 +274,94 @@ namespace gdjs {
bottomPixel
);
//Move and resize the object according to the anchors
if (
this._rightEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
// Compatibility with GD <= 5.0.133
if (this._useLegacyBottomAndRightAnchors) {
//Move and resize the object according to the anchors
if (
this._rightEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
}
if (
this._bottomEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
}
if (
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
);
}
if (
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
);
}
}
if (
this._bottomEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
}
if (
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
);
}
if (this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE) {
this.owner.setY(
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
);
// End of compatibility code
else {
// Resize if right and left anchors are set
if (
this._rightEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE &&
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
this.owner.setX(topLeftCoord[0]);
} else {
if (
this._leftEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
);
}
if (
this._rightEdgeAnchor !==
AnchorRuntimeBehavior.HorizontalAnchor.NONE
) {
this.owner.setX(
bottomRightCoord[0] +
this.owner.getX() -
this.owner.getDrawableX() -
this.owner.getWidth()
);
}
}
// Resize if top and bottom anchors are set
if (
this._bottomEdgeAnchor !==
AnchorRuntimeBehavior.VerticalAnchor.NONE &&
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
this.owner.setY(topLeftCoord[1]);
} else {
if (
this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
);
}
if (
this._bottomEdgeAnchor !==
AnchorRuntimeBehavior.VerticalAnchor.NONE
) {
this.owner.setY(
bottomRightCoord[1] +
this.owner.getY() -
this.owner.getDrawableY() -
this.owner.getHeight()
);
}
}
}
}
}

View File

@@ -0,0 +1,204 @@
// @ts-check
describe.only('gdjs.AnchorRuntimeBehavior', function () {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
// @ts-ignore
properties: { windowWidth: 1000, windowHeight: 1000 },
});
const anchorBehaviorName = 'Anchor';
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
cameras: [],
effects: [],
ambientLightColorR: 127,
ambientLightColorB: 127,
ambientLightColorG: 127,
isLightingLayer: false,
followBaseLayerCamera: false,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
instances: [],
});
function createObject(behaviorProperties) {
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
name: anchorBehaviorName,
type: 'AnchorBehavior::AnchorBehavior',
// @ts-ignore - properties are not typed
rightEdgeAnchor: 0,
leftEdgeAnchor: 0,
topEdgeAnchor: 0,
bottomEdgeAnchor: 0,
relativeToOriginalWindowSize: true,
useLegacyBottomAndRightAnchors: false,
...behaviorProperties,
},
],
variables: [],
effects: [],
});
object.setCustomWidthAndHeight(10, 10);
runtimeScene.addObject(object);
return object;
}
function getAnchorBehavior(object) {
const behavior = object.getBehavior(anchorBehaviorName);
if (!(behavior instanceof gdjs.AnchorRuntimeBehavior)) {
throw new Error(
'Expected behavior to be an instance of gdjs.AnchorBehavior'
);
}
return behavior;
}
describe('(anchor horizontal edge)', function () {
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {
const object = createObject({ [objectEdge]: 1 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(500);
expect(object.getWidth()).to.equal(10);
});
});
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window right (fixed)`, function () {
const object = createObject({ [objectEdge]: 2 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(1500);
expect(object.getY()).to.equal(500);
expect(object.getWidth()).to.equal(10);
});
});
it('anchors the right and left edge of object (fixed)', function () {
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(500);
expect(object.getWidth()).to.equal(1010);
});
it('anchors the left edge of object (proportional)', function () {
const object = createObject({ leftEdgeAnchor: 3 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(1000);
expect(object.getY()).to.equal(500);
expect(object.getWidth()).to.equal(10);
});
});
describe('(anchor vertical edge)', function () {
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, function () {
const object = createObject({ [objectEdge]: 1 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(500);
expect(object.getWidth()).to.equal(10);
});
});
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, function () {
const object = createObject({ [objectEdge]: 2 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(1500);
expect(object.getWidth()).to.equal(10);
});
});
it('anchors the top and bottom edge of object (fixed)', function () {
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(500);
expect(object.getHeight()).to.equal(1010);
});
it('anchors the top edge of object (proportional)', function () {
const object = createObject({ topEdgeAnchor: 3 });
runtimeGame.setGameResolutionSize(1000, 1000);
object.setPosition(500, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.setGameResolutionSize(2000, 2000);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.equal(500);
expect(object.getY()).to.equal(1000);
expect(object.getWidth()).to.equal(10);
});
});
});

View File

@@ -4,7 +4,6 @@
# consist in a few lines containing calls to these functions.
macro(gd_add_extension_includes)
include_directories(${sfml_include_dir})
include_directories(${GDCORE_include_dir})
endmacro()
@@ -81,7 +80,6 @@ function(gd_extension_link_libraries target_name)
#Nothing.
ELSE()
target_link_libraries(${target_name} GDCore)
target_link_libraries(${target_name} ${sfml_LIBRARIES})
ENDIF()
endfunction()

View File

@@ -7,7 +7,7 @@ This project is released under the MIT License.
#ifndef DRAGGABLEBEHAVIOR_H
#define DRAGGABLEBEHAVIOR_H
#include <SFML/System/Vector2.hpp>
#include "GDCore/Vector2.h"
#include <map>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Object.h"

View File

@@ -307,7 +307,7 @@ module.exports = {
.setLabel(_('Color map texture for the effect'))
.setDescription(
_(
'You can change colors of pixels by modifing a reference color image, containing each colors, called the *Color Map Texture*. To get started, **download** [a default color map texture here](http://wiki.compilgames.net/doku.php/gdevelop5/interface/scene-editor/layer-effects).'
'You can change colors of pixels by modifing a reference color image, containing each colors, called the *Color Map Texture*. To get started, **download** [a default color map texture here](https://wiki.gdevelop.io/gdevelop5/interface/scene-editor/layer-effects).'
)
);
colorMapProperties
@@ -442,7 +442,7 @@ module.exports = {
.setLabel(_('Displacement map texture'))
.setDescription(
_(
'Displacement map texture for the effect. To get started, **download** [a default displacement map texture here](http://wiki.compilgames.net/doku.php/gdevelop5/interface/scene-editor/layer-effects).'
'Displacement map texture for the effect. To get started, **download** [a default displacement map texture here](https://wiki.gdevelop.io/gdevelop5/interface/scene-editor/layer-effects).'
)
);
displacementProperties

View File

@@ -68,7 +68,7 @@ module.exports = {
.setFullName(_('Dummy effect example'))
.setDescription(
_(
'This is an example of an effect ("shader") with an [external link to the wiki](http://wiki.compilgames.net/doku.php/gdevelop5/) and **bold letters**.'
'This is an example of an effect ("shader") with an [external link to the wiki](https://wiki.gdevelop.io/gdevelop5/) and **bold letters**.'
)
)
.addIncludeFile('Extensions/ExampleJsExtension/dummyeffect.js');

View File

@@ -8,22 +8,18 @@ namespace gdjs {
/** Get the Node.js path module, or null if it can't be loaded */
export const _getPath = function () {
if (!gdjs.fileSystem._path) {
// @ts-ignore
gdjs.fileSystem._path =
typeof require !== 'undefined' ? require('path') : null;
if (!_path) {
_path = typeof require !== 'undefined' ? require('path') : null;
}
return gdjs.fileSystem._path;
return _path;
};
/** Get the Node.js fs module, or null if it can't be loaded */
export const _getFs = function () {
if (!gdjs.fileSystem._fs) {
// @ts-ignore
gdjs.fileSystem._fs =
typeof require !== 'undefined' ? require('fs') : null;
if (!_fs) {
_fs = typeof require !== 'undefined' ? require('fs') : null;
}
return gdjs.fileSystem._fs;
return _fs;
};
export const getDirectoryName = function (fileOrFolderPath: string) {
@@ -58,9 +54,10 @@ namespace gdjs {
export const getDesktopPath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('desktop') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('desktop') || '';
} else {
return '';
}
@@ -74,9 +71,10 @@ namespace gdjs {
export const getDocumentsPath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('documents') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('documents') || '';
} else {
return '';
}
@@ -90,9 +88,10 @@ namespace gdjs {
export const getPicturesPath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('pictures') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('pictures') || '';
} else {
return '';
}
@@ -106,9 +105,10 @@ namespace gdjs {
export const getExecutablePath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('exe') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('exe') || '';
} else {
return '';
}
@@ -138,9 +138,10 @@ namespace gdjs {
export const getUserdataPath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('userData') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('userData') || '';
} else {
return '';
}
@@ -153,9 +154,10 @@ namespace gdjs {
export const getUserHomePath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('home') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('home') || '';
} else {
return '';
}
@@ -169,9 +171,10 @@ namespace gdjs {
export const getTempPath = function (
runtimeScene: gdjs.RuntimeScene
): string {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
return electron.remote.app.getPath('temp') || '';
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
const app = remote ? remote.app : null;
if (app) {
return app.getPath('temp') || '';
} else {
return '';
}

View File

@@ -1324,7 +1324,7 @@ module.exports = {
true
)
.setParameterLongDescription(
'See the shape of the returned data on [the wiki page](http://wiki.compilgames.net/doku.php/gdevelop5/all-features/firebase/firestore#the_query_result).'
'See the shape of the returned data on [the wiki page](https://wiki.gdevelop.io/gdevelop5/all-features/firebase/firestore#the_query_result).'
)
.addParameter(
'scenevar',

View File

@@ -1,39 +1,40 @@
describe('Inventory', function () {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
it('Inventories can be serialized then unserialized with no data loss', () => {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
properties: { windowWidth: 800, windowHeight: 600 },
resources: { resources: [] },
});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'sword');
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'sword');
gdjs.evtTools.inventory.equip(runtimeScene, 'MyInventory', 'sword', true);
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'armor');
gdjs.evtTools.inventory.setMaximum(runtimeScene, 'MyInventory', 'armor', 1);
var variable = new gdjs.Variable();
gdjs.evtTools.inventory.serializeToVariable(
runtimeScene,
'MyInventory',
variable
);
gdjs.evtTools.inventory.unserializeFromVariable(
runtimeScene,
'MyInventory2',
variable
);
expect(
gdjs.evtTools.inventory.count(runtimeScene, 'MyInventory2', 'sword')
).to.be(2);
expect(
gdjs.evtTools.inventory.isEquipped(runtimeScene, 'MyInventory2', 'sword')
).to.be(true);
expect(
gdjs.evtTools.inventory.count(runtimeScene, 'MyInventory2', 'armor')
).to.be(1);
expect(
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory2', 'armor')
).to.be(false);
});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'sword');
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'sword');
gdjs.evtTools.inventory.equip(runtimeScene, 'MyInventory', 'sword', true);
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory', 'armor');
gdjs.evtTools.inventory.setMaximum(runtimeScene, 'MyInventory', 'armor', 1);
var variable = new gdjs.Variable();
gdjs.evtTools.inventory.serializeToVariable(
runtimeScene,
'MyInventory',
variable
);
gdjs.evtTools.inventory.unserializeFromVariable(
runtimeScene,
'MyInventory2',
variable
);
expect(
gdjs.evtTools.inventory.count(runtimeScene, 'MyInventory2', 'sword')
).to.be(2);
expect(
gdjs.evtTools.inventory.isEquipped(runtimeScene, 'MyInventory2', 'sword')
).to.be(true);
expect(
gdjs.evtTools.inventory.count(runtimeScene, 'MyInventory2', 'armor')
).to.be(1);
expect(
gdjs.evtTools.inventory.add(runtimeScene, 'MyInventory2', 'armor')
).to.be(false);
});

View File

@@ -0,0 +1,258 @@
// @flow
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'Leaderboards',
_('Leaderboards (experimental)'),
_('Allow your game to send scores to your leaderboards.'),
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/leaderboards')
.setCategory('Leaderboards')
.addInstructionOrExpressionGroupMetadata(_('Leaderboards (experimental)'))
.setIcon('JsPlatform/Extensions/leaderboard.svg');
extension
.addAction(
'SavePlayerScore',
_('Save player score'),
_("Save the player's score to the given leaderboard."),
_(
'Send to leaderboard _PARAM1_ the score _PARAM2_ with player name: _PARAM3_.'
),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('leaderboardId', _('Leaderboard'), '', false)
.addParameter(
'expression',
_('Score to register for the player'),
'',
false
)
.addParameter('string', _('Name to register for the player'), '', false)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/sha256.js')
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.savePlayerScore');
extension
.addCondition(
'HasLastSaveErrored',
_('Last score save has errored'),
_('Check if the last attempt to save a score has errored.'),
_('Last score save in leaderboard _PARAM0_ has errored'),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('leaderboardId', _('Leaderboard'), '', true)
.setParameterLongDescription(
_(
'If no leaderboard is specified, will return the value related to the last leaderboard save action.'
)
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.hasSavingErrored');
extension
.addCondition(
'HasLastSaveSucceeded',
_('Last score save has succeeded'),
_('Check if the last attempt to save a score has succeeded.'),
_('Last score save in leaderboard _PARAM0_ has succeeded'),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('leaderboardId', _('Leaderboard'), '', true)
.setParameterLongDescription(
_(
'If no leaderboard is specified, will return the value related to the last leaderboard save action that successfully ended.'
)
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.hasBeenSaved');
extension
.addCondition(
'IsSaving',
_('Score is saving'),
_('Check if a score is currently being saved in leaderboard.'),
_('Score is saving in leaderboard _PARAM0_'),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('leaderboardId', _('Leaderboard'), '', true)
.setParameterLongDescription(
_(
'If no leaderboard is specified, will return the value related to the last leaderboard save action.'
)
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.isSaving');
extension
.addStrExpression(
'LastSaveError',
_('Error of last save attempt'),
_('Get the error of the last save attempt.'),
_('Error of last save attempt in leaderboard _PARAM0_'),
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('leaderboardId', _('Leaderboard'), '', true)
.setParameterLongDescription(
_(
'If no leaderboard is specified, will return the value related to the last leaderboard save action.'
)
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.getLastSaveError');
extension
.addCondition(
'IsLeaderboardViewErrored',
_('Leaderboard display has errored'),
_('Check if the display of the leaderboard errored.'),
_('Leaderboard display has errored'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.isLeaderboardViewErrored');
extension
.addCondition(
'IsLeaderboardViewLoaded',
_('Leaderboard display has loaded'),
_(
'Check if the display of the leaderboard has finished loading and been displayed on screen.'
),
_('Leaderboard display has loaded and is displayed on screen'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.isLeaderboardViewLoaded');
extension
.addCondition(
'IsLeaderboardViewLoading',
_('Leaderboard display is loading'),
_('Check if the display of the leaderboard is loading.'),
_('Leaderboard display is loading'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.isLeaderboardViewLoading');
extension
.addStrExpression(
'FormatPlayerName',
_('Format player name'),
_('Formats a name so that it can be submitted to a leaderboard.'),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('string', _('Raw player name'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.formatPlayerName');
extension
.addAction(
'DisplayLeaderboard',
_('Display leaderboard'),
_(
'Display the specified leaderboard on top of the game. If a leaderboard was already displayed on top of the game, the new leaderboard will replace it.'
),
_('Display leaderboard _PARAM1_ (display a loader: _PARAM2_)'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('leaderboardId', _('Leaderboard'), '', false)
.addParameter(
'yesorno',
_('Display loader while leaderboard is loading'),
'',
false
)
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.displayLeaderboard');
extension
.addAction(
'CloseLeaderboardView',
_('Close current leaderboard'),
_('Close the leaderboard currently displayed on top of the game.'),
_('Close current leaderboard displayed on top of the game'),
_('Display leaderboard'),
'JsPlatform/Extensions/leaderboard.svg',
'JsPlatform/Extensions/leaderboard.svg'
)
.addCodeOnlyParameter('currentScene', '')
.setHelpPath('/all-features/leaderboards')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.closeLeaderboardView');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
return [];
},
};

View File

@@ -0,0 +1,645 @@
/// <reference path="sha256.d.ts" />
namespace gdjs {
const logger = new gdjs.Logger('Leaderboards');
export namespace evtTools {
export namespace leaderboards {
const computeDigest = (payload: string): string => {
const shaObj = new jsSHA('SHA-256', 'TEXT', { encoding: 'UTF8' });
shaObj.update(payload);
return shaObj.getHash('B64');
};
// Score saving
class ScoreSavingState {
lastScoreSavingStartedAt: number | null;
lastScoreSavingSucceededAt: number | null;
currentlySavingScore: number | null;
currentlySavingPlayerName: string | null;
lastSavedScore: number | null;
lastSavedPlayerName: string | null;
lastSaveError: string | null;
isScoreSaving: boolean;
hasScoreBeenSaved: boolean;
hasScoreSavingErrored: boolean;
constructor() {
this.lastScoreSavingStartedAt = null;
this.lastScoreSavingSucceededAt = null;
this.currentlySavingScore = null;
this.currentlySavingPlayerName = null;
this.lastSavedScore = null;
this.lastSavedPlayerName = null;
this.lastSaveError = null;
this.isScoreSaving = false;
this.hasScoreBeenSaved = false;
this.hasScoreSavingErrored = false;
}
isSameAsLastScore(playerName: string, score: number): boolean {
return (
this.lastSavedPlayerName === playerName &&
this.lastSavedScore === score
);
}
isAlreadySavingThisScore(playerName: string, score: number): boolean {
return (
this.isScoreSaving &&
this.currentlySavingPlayerName === playerName &&
this.currentlySavingScore === score
);
}
isTooSoonToSaveAnotherScore(): boolean {
return (
!!this.lastScoreSavingSucceededAt &&
Date.now() - this.lastScoreSavingSucceededAt < 500
);
}
startSaving(playerName: string, score: number): void {
this.lastScoreSavingStartedAt = Date.now();
this.isScoreSaving = true;
this.hasScoreBeenSaved = false;
this.hasScoreSavingErrored = false;
this.currentlySavingScore = score;
this.currentlySavingPlayerName = playerName;
}
closeSaving(): void {
this.lastScoreSavingSucceededAt = Date.now();
this.lastSavedScore = this.currentlySavingScore;
this.lastSavedPlayerName = this.currentlySavingPlayerName;
this.isScoreSaving = false;
this.hasScoreBeenSaved = true;
}
setError(errorCode: string): void {
this.lastSaveError = errorCode;
this.isScoreSaving = false;
this.hasScoreBeenSaved = false;
this.hasScoreSavingErrored = true;
}
}
let _scoreSavingStateByLeaderboard: {
[leaderboardId: string]: ScoreSavingState;
} = {};
// Leaderboard display
let _requestedLeaderboardId: string | null;
let _leaderboardViewIframe: HTMLIFrameElement | null = null;
let _leaderboardViewIframeErrored: boolean = false;
let _leaderboardViewIframeLoading: boolean = false;
let _leaderboardViewIframeLoaded: boolean = false;
let _errorTimeoutId: NodeJS.Timeout | null = null;
let _leaderboardViewClosingCallback:
| ((event: MessageEvent) => void)
| null = null;
const _loaderContainer: HTMLDivElement = document.createElement('div');
_loaderContainer.style.backgroundColor = '#000000';
_loaderContainer.style.display = 'flex';
_loaderContainer.style.height = '100%';
_loaderContainer.style.width = '100%';
_loaderContainer.style.justifyContent = 'center';
_loaderContainer.style.alignItems = 'center';
_loaderContainer.style.position = 'relative';
_loaderContainer.style.zIndex = '2';
const _loader = document.createElement('img');
_loader.setAttribute('width', '50px');
_loader.setAttribute(
'src',
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iNCI+PC9jaXJjbGU+CjxwYXRoIG9wYWNpdHk9JzAuNzUnIGZpbGw9IiNGRkZGRkYiIGQ9Ik00IDEyYTggOCAwIDAxOC04VjBDNS4zNzMgMCAwIDUuMzczIDAgMTJoNHptMiA1LjI5MUE3Ljk2MiA3Ljk2MiAwIDAxNCAxMkgwYzAgMy4wNDIgMS4xMzUgNS44MjQgMyA3LjkzOGwzLTIuNjQ3eiI+PC9wYXRoPgo8L3N2Zz4='
);
try {
_loader.animate(
[{ transform: 'rotate(0deg)' }, { transform: 'rotate(359deg)' }],
{
duration: 3000,
iterations: Infinity,
}
);
} catch {
logger.warn('Animation not supported, loader will be fixed.');
}
_loaderContainer.appendChild(_loader);
const getLastScoreSavingState = function ({
hasSucceeded,
}: {
hasSucceeded: boolean;
}): ScoreSavingState | null {
const getDateField = (scoreSavingState: ScoreSavingState) =>
hasSucceeded
? scoreSavingState.lastScoreSavingSucceededAt
: scoreSavingState.lastScoreSavingStartedAt;
const scoreSavingStates = Object.values(
_scoreSavingStateByLeaderboard
).filter((scoreSavingState) => !!getDateField(scoreSavingState));
if (scoreSavingStates.length === 0) return null;
let lastScoreSavingState = scoreSavingStates[0];
scoreSavingStates.forEach((scoreSavingState) => {
const currentItemDate = getDateField(scoreSavingState);
const lastItemDate = getDateField(lastScoreSavingState);
if (
currentItemDate &&
lastItemDate &&
currentItemDate > lastItemDate
) {
lastScoreSavingState = scoreSavingState;
}
});
return lastScoreSavingState;
};
export const savePlayerScore = function (
runtimeScene: gdjs.RuntimeScene,
leaderboardId: string,
score: float,
playerName: string
) {
let scoreSavingState: ScoreSavingState;
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
if (scoreSavingState.isAlreadySavingThisScore(playerName, score)) {
logger.warn(
'There is already a request to save with this player name and this score. Ignoring this one.'
);
return;
}
if (scoreSavingState.isSameAsLastScore(playerName, score)) {
logger.warn(
'The player and score to be sent are the same as previous one. Ignoring this one.'
);
const errorCode = 'SAME_AS_PREVIOUS';
scoreSavingState.setError(errorCode);
return;
}
if (scoreSavingState.isTooSoonToSaveAnotherScore()) {
logger.warn(
'Last entry was sent too little time ago. Ignoring this one.'
);
const errorCode = 'TOO_FAST';
scoreSavingState.setError(errorCode);
return;
}
} else {
scoreSavingState = new ScoreSavingState();
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
}
scoreSavingState.startSaving(playerName, score);
const baseUrl = 'https://api.gdevelop-app.com/play';
const game = runtimeScene.getGame();
const payload = JSON.stringify({
playerName: formatPlayerName(playerName),
score: score,
sessionId: game.getSessionId(),
clientPlayerId: game.getPlayerId(),
location:
typeof window !== 'undefined' && (window as any).location
? (window as any).location.href
: '',
});
fetch(
`${baseUrl}/game/${gdjs.projectData.properties.projectUuid}/leaderboard/${leaderboardId}/entry`,
{
body: payload,
method: 'POST',
headers: {
'Content-Type': 'application/json',
Digest: computeDigest(payload),
},
}
).then(
(response) => {
if (!response.ok) {
const errorCode = response.status.toString();
logger.error(
'Server responded with an error:',
errorCode,
response.statusText
);
scoreSavingState.setError(errorCode);
return;
}
scoreSavingState.closeSaving();
return response.text().then(
(text) => {},
(error) => {
logger.warn(
'An error occurred when reading response but score has been saved:',
error
);
}
);
},
(error) => {
logger.error('Error while submitting a leaderboard score:', error);
const errorCode = 'REQUEST_NOT_SENT';
scoreSavingState.setError(errorCode);
}
);
};
export const isSaving = function (leaderboardId?: string): boolean {
if (leaderboardId) {
return _scoreSavingStateByLeaderboard[leaderboardId]
? _scoreSavingStateByLeaderboard[leaderboardId].isScoreSaving
: false;
}
const lastScoreSavingState = getLastScoreSavingState({
hasSucceeded: false,
});
return lastScoreSavingState
? lastScoreSavingState.isScoreSaving
: false;
};
export const hasBeenSaved = function (leaderboardId?: string): boolean {
if (leaderboardId) {
return _scoreSavingStateByLeaderboard[leaderboardId]
? _scoreSavingStateByLeaderboard[leaderboardId].hasScoreBeenSaved
: false;
}
const lastScoreSavingState = getLastScoreSavingState({
hasSucceeded: true,
});
return lastScoreSavingState
? lastScoreSavingState.hasScoreBeenSaved
: false;
};
export const hasSavingErrored = function (
leaderboardId?: string
): boolean {
if (leaderboardId) {
return _scoreSavingStateByLeaderboard[leaderboardId]
? _scoreSavingStateByLeaderboard[leaderboardId]
.hasScoreSavingErrored
: false;
}
const lastScoreSavingState = getLastScoreSavingState({
hasSucceeded: false,
});
return lastScoreSavingState
? lastScoreSavingState.hasScoreSavingErrored
: false;
};
export const getLastSaveError = function (
leaderboardId?: string
): string | null {
if (leaderboardId) {
return _scoreSavingStateByLeaderboard[leaderboardId]
? _scoreSavingStateByLeaderboard[leaderboardId].lastSaveError
: 'NO_DATA_ERROR';
}
const lastScoreSavingState = getLastScoreSavingState({
hasSucceeded: false,
});
return lastScoreSavingState
? lastScoreSavingState.lastSaveError
: 'NO_DATA_ERROR';
};
export const formatPlayerName = function (rawName: string): string {
if (
!rawName ||
typeof rawName !== 'string' ||
(typeof rawName === 'string' && rawName.length === 0)
) {
return `Player${Math.round(
(Math.random() * 9 + 1) * 10000 // Number between 10,000 and 99,999
)}`;
}
return rawName
.trim()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/\s/g, '_')
.replace(/[^\w|-]/g, '')
.slice(0, 30);
};
const checkLeaderboardAvailability = function (
url: string
): Promise<boolean> {
return fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}).then(
(response) => {
if (!response.ok) {
logger.error(
`Error while fetching leaderboard view, server returned: ${response.status} ${response.statusText}`
);
return false;
}
return true;
},
(err) => {
logger.error('Error while fetching leaderboard view:', err);
return false;
}
);
};
const receiveMessageFromLeaderboardView = function (
runtimeScene: gdjs.RuntimeScene,
displayLoader: boolean,
event: MessageEvent
) {
switch (event.data) {
case 'closeLeaderboardView':
closeLeaderboardView(runtimeScene);
break;
case 'leaderboardViewLoaded':
if (displayLoader) {
if (_errorTimeoutId) clearTimeout(_errorTimeoutId);
displayLoaderInLeaderboardView(false, runtimeScene, {
callOnErrorIfDomElementContainerMissing: false,
});
}
if (!_leaderboardViewIframe) {
handleErrorDisplayingLeaderboard(
runtimeScene,
"The leaderboard view couldn't be found. Doing nothing."
);
return;
}
_leaderboardViewIframe.style.opacity = '1';
_leaderboardViewIframeLoaded = true;
_leaderboardViewIframeLoading = false;
break;
}
};
const handleErrorDisplayingLeaderboard = function (
runtimeScene: gdjs.RuntimeScene,
message: string
) {
logger.error(message);
_leaderboardViewIframeErrored = true;
_leaderboardViewIframeLoading = false;
closeLeaderboardView(runtimeScene);
};
const resetLeaderboardDisplayErrorTimeout = (
runtimeScene: gdjs.RuntimeScene
) => {
if (_errorTimeoutId) clearTimeout(_errorTimeoutId);
_errorTimeoutId = setTimeout(() => {
if (!_leaderboardViewIframeLoaded) {
handleErrorDisplayingLeaderboard(
runtimeScene,
'Leaderboard page did not send message in time. Closing leaderboard view.'
);
}
}, 5000);
};
const displayLoaderInLeaderboardView = function (
yesOrNo: boolean,
runtimeScene: gdjs.RuntimeScene,
options: { callOnErrorIfDomElementContainerMissing: boolean }
): boolean {
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
if (options.callOnErrorIfDomElementContainerMissing) {
handleErrorDisplayingLeaderboard(
runtimeScene,
"The div element covering the game couldn't be found, the leaderboard cannot be displayed."
);
}
return false;
}
if (yesOrNo) {
if (
domElementContainer.children &&
domElementContainer.children.length > 0
) {
domElementContainer.insertBefore(
_loaderContainer,
domElementContainer.children[0]
);
} else {
domElementContainer.appendChild(_loaderContainer);
}
if (_leaderboardViewIframe) {
_leaderboardViewIframe.style.opacity = '0';
}
} else {
try {
domElementContainer.removeChild(_loaderContainer);
if (_leaderboardViewIframe) {
_leaderboardViewIframe.style.opacity = '1';
}
} catch {}
}
return true;
};
const computeLeaderboardDisplayingIframe = function (
url: string
): HTMLIFrameElement {
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.id = 'leaderboard-view';
iframe.style.position = 'absolute';
// To trigger iframe loading and be able to listen to its events, use `opacity: 0` instead of `visibility: hidden` or `display: none`
iframe.style.opacity = '0';
iframe.style.pointerEvents = 'all';
iframe.style.backgroundColor = '#FFFFFF';
iframe.style.top = '0px';
iframe.style.height = '100%';
iframe.style.left = '0px';
iframe.style.width = '100%';
iframe.style.border = 'none';
return iframe;
};
export const displayLeaderboard = function (
runtimeScene: gdjs.RuntimeScene,
leaderboardId: string,
displayLoader: boolean
) {
// First ensure we're not trying to display multiple times the same leaderboard (in which case
// we "de-duplicate" the request to display it).
if (leaderboardId === _requestedLeaderboardId) {
if (_leaderboardViewIframeLoading) {
logger.warn(
`Already loading the view for the requested loader (${leaderboardId}), ignoring.`
);
return;
}
if (_leaderboardViewIframeLoaded) {
logger.warn(
`Already loaded the view for the requested loader (${leaderboardId}), ignoring.`
);
return;
}
}
// We are now assured we want to display a new (or different) leaderboard: start loading it.
_requestedLeaderboardId = leaderboardId;
_leaderboardViewIframeErrored = false;
_leaderboardViewIframeLoaded = false;
_leaderboardViewIframeLoading = true;
if (displayLoader) {
displayLoaderInLeaderboardView(true, runtimeScene, {
callOnErrorIfDomElementContainerMissing: true,
});
}
const gameId = gdjs.projectData.properties.projectUuid;
const targetUrl = `https://liluo.io/games/${gameId}/leaderboard/${leaderboardId}?inGameEmbedded=true`;
checkLeaderboardAvailability(targetUrl).then(
(isAvailable) => {
if (leaderboardId !== _requestedLeaderboardId) {
logger.warn(
`Received a response for leaderboard ${leaderboardId} though the last leaderboard requested is ${_requestedLeaderboardId}, ignoring this response.`
);
return;
}
if (!isAvailable) {
handleErrorDisplayingLeaderboard(
runtimeScene,
'Leaderboard data could not be fetched. Closing leaderboard view if there is one.'
);
return;
}
if (_leaderboardViewIframe) {
resetLeaderboardDisplayErrorTimeout(runtimeScene);
if (displayLoader) {
displayLoaderInLeaderboardView(true, runtimeScene, {
callOnErrorIfDomElementContainerMissing: false,
});
}
_leaderboardViewIframe.src = targetUrl;
} else {
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
handleErrorDisplayingLeaderboard(
runtimeScene,
"The div element covering the game couldn't be found, the leaderboard cannot be displayed."
);
return;
}
resetLeaderboardDisplayErrorTimeout(runtimeScene);
_leaderboardViewIframe = computeLeaderboardDisplayingIframe(
targetUrl
);
if (typeof window !== 'undefined') {
_leaderboardViewClosingCallback = (event: MessageEvent) => {
receiveMessageFromLeaderboardView(
runtimeScene,
displayLoader,
event
);
};
(window as any).addEventListener(
'message',
_leaderboardViewClosingCallback,
true
);
}
domElementContainer.appendChild(_leaderboardViewIframe);
}
},
(err) => {
logger.error(err);
handleErrorDisplayingLeaderboard(
runtimeScene,
'An error occurred when fetching leaderboard data. Closing leaderboard view if there is one.'
);
return;
}
);
};
export const isLeaderboardViewErrored = function (): boolean {
return _leaderboardViewIframeErrored;
};
export const isLeaderboardViewLoaded = function (): boolean {
return _leaderboardViewIframeLoaded;
};
export const isLeaderboardViewLoading = function (): boolean {
return _leaderboardViewIframeLoading;
};
export const closeLeaderboardView = function (
runtimeScene: gdjs.RuntimeScene
) {
try {
displayLoaderInLeaderboardView(false, runtimeScene, {
callOnErrorIfDomElementContainerMissing: false,
});
if (!_leaderboardViewIframe) {
logger.info(
"The iframe displaying the current leaderboard couldn't be found, the leaderboard view must be already closed."
);
return;
}
const domElementContainer = runtimeScene
.getGame()
.getRenderer()
.getDomElementContainer();
if (!domElementContainer) {
logger.info(
"The div element covering the game couldn't be found, the leaderboard view must be already closed."
);
return;
}
if (typeof window !== 'undefined') {
(window as any).removeEventListener(
'message',
_leaderboardViewClosingCallback,
true
);
_leaderboardViewClosingCallback = null;
}
domElementContainer.removeChild(_leaderboardViewIframe);
_leaderboardViewIframe = null;
} finally {
// Don't reset the loading flag (the view of another leaderboard might be loading)
// or the error flag (we want to persist the error flag even after the view is closed),
// but reset the flag indicating the view is loaded (if it was).
_leaderboardViewIframeLoaded = false;
const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();
if (gameCanvas) gameCanvas.focus();
}
};
}
}
}

234
Extensions/Leaderboards/sha256.d.ts vendored Normal file
View File

@@ -0,0 +1,234 @@
declare type EncodingType = 'UTF8' | 'UTF16BE' | 'UTF16LE';
declare type FormatNoTextType =
| 'HEX'
| 'B64'
| 'BYTES'
| 'ARRAYBUFFER'
| 'UINT8ARRAY';
declare type FormatType = 'TEXT' | FormatNoTextType;
declare type GenericInputType =
| {
value: string;
format: 'TEXT';
encoding?: EncodingType;
}
| {
value: string;
format: 'B64' | 'HEX' | 'BYTES';
}
| {
value: ArrayBuffer;
format: 'ARRAYBUFFER';
}
| {
value: Uint8Array;
format: 'UINT8ARRAY';
};
declare type FixedLengthOptionsNoEncodingType =
| {
hmacKey?: GenericInputType;
}
| {
numRounds?: number;
};
declare type FixedLengthOptionsEncodingType =
| {
hmacKey?: GenericInputType;
encoding?: EncodingType;
}
| {
numRounds?: number;
encoding?: EncodingType;
};
interface packedValue {
value: number[];
binLen: number;
}
declare abstract class jsSHABase<StateT, VariantT> {
/**
* @param variant The desired SHA variant.
* @param inputFormat The input format to be used in future `update` calls.
* @param options Hashmap of extra input options.
*/
protected readonly shaVariant: VariantT;
protected readonly inputFormat: FormatType;
protected readonly utfType: EncodingType;
protected readonly numRounds: number;
protected abstract intermediateState: StateT;
protected keyWithIPad: number[];
protected keyWithOPad: number[];
protected remainder: number[];
protected remainderLen: number;
protected updateCalled: boolean;
protected processedLen: number;
protected macKeySet: boolean;
protected abstract readonly variantBlockSize: number;
protected abstract readonly bigEndianMod: -1 | 1;
protected abstract readonly outputBinLen: number;
protected abstract readonly isVariableLen: boolean;
protected abstract readonly HMACSupported: boolean;
protected abstract readonly converterFunc: (
input: any,
existingBin: number[],
existingBinLen: number
) => packedValue;
protected abstract readonly roundFunc: (block: number[], H: StateT) => StateT;
protected abstract readonly finalizeFunc: (
remainder: number[],
remainderBinLen: number,
processedBinLen: number,
H: StateT,
outputLen: number
) => number[];
protected abstract readonly stateCloneFunc: (state: StateT) => StateT;
protected abstract readonly newStateFunc: (variant: VariantT) => StateT;
protected abstract readonly getMAC:
| ((options: { outputLen: number }) => number[])
| null;
protected constructor(
variant: VariantT,
inputFormat: 'TEXT',
options?: FixedLengthOptionsEncodingType
);
protected constructor(
variant: VariantT,
inputFormat: FormatNoTextType,
options?: FixedLengthOptionsNoEncodingType
);
/**
* Hashes as many blocks as possible. Stores the rest for either a future update or getHash call.
*
* @param srcString The input to be hashed.
*/
update(srcString: string | ArrayBuffer | Uint8Array): void;
/**
* Returns the desired SHA hash of the input fed in via `update` calls.
*
* @param format The desired output formatting
* @param options Hashmap of output formatting options. `outputLen` must be specified for variable length hashes.
* `outputLen` replaces the now deprecated `shakeLen` key.
* @returns The hash in the format specified.
*/
getHash(
format: 'HEX',
options?: {
outputUpper?: boolean;
outputLen?: number;
shakeLen?: number;
}
): string;
getHash(
format: 'B64',
options?: {
b64Pad?: string;
outputLen?: number;
shakeLen?: number;
}
): string;
getHash(
format: 'BYTES',
options?: {
outputLen?: number;
shakeLen?: number;
}
): string;
getHash(
format: 'UINT8ARRAY',
options?: {
outputLen?: number;
shakeLen?: number;
}
): Uint8Array;
getHash(
format: 'ARRAYBUFFER',
options?: {
outputLen?: number;
shakeLen?: number;
}
): ArrayBuffer;
/**
* Sets the HMAC key for an eventual `getHMAC` call. Must be called immediately after jsSHA object instantiation.
*
* @param key The key used to calculate the HMAC
* @param inputFormat The format of key.
* @param options Hashmap of extra input options.
*/
setHMACKey(
key: string,
inputFormat: 'TEXT',
options?: {
encoding?: EncodingType;
}
): void;
setHMACKey(key: string, inputFormat: 'B64' | 'HEX' | 'BYTES'): void;
setHMACKey(key: ArrayBuffer, inputFormat: 'ARRAYBUFFER'): void;
setHMACKey(key: Uint8Array, inputFormat: 'UINT8ARRAY'): void;
/**
* Internal function that sets the MAC key.
*
* @param key The packed MAC key to use
*/
protected _setHMACKey(key: packedValue): void;
/**
* Returns the the HMAC in the specified format using the key given by a previous `setHMACKey` call.
*
* @param format The desired output formatting.
* @param options Hashmap of extra outputs options.
* @returns The HMAC in the format specified.
*/
getHMAC(
format: 'HEX',
options?: {
outputUpper?: boolean;
}
): string;
getHMAC(
format: 'B64',
options?: {
b64Pad?: string;
}
): string;
getHMAC(format: 'BYTES'): string;
getHMAC(format: 'UINT8ARRAY'): Uint8Array;
getHMAC(format: 'ARRAYBUFFER'): ArrayBuffer;
/**
* Internal function that returns the "raw" HMAC
*/
protected _getHMAC(): number[];
}
declare type VariantType = 'SHA-224' | 'SHA-256';
declare class jsSHA extends jsSHABase<number[], VariantType> {
intermediateState: number[];
variantBlockSize: number;
bigEndianMod: -1 | 1;
outputBinLen: number;
isVariableLen: boolean;
HMACSupported: boolean;
converterFunc: (
input: any,
existingBin: number[],
existingBinLen: number
) => packedValue;
roundFunc: (block: number[], H: number[]) => number[];
finalizeFunc: (
remainder: number[],
remainderBinLen: number,
processedBinLen: number,
H: number[]
) => number[];
stateCloneFunc: (state: number[]) => number[];
newStateFunc: (variant: VariantType) => number[];
getMAC: () => number[];
constructor(
variant: VariantType,
inputFormat: 'TEXT',
options?: FixedLengthOptionsEncodingType
);
constructor(
variant: VariantType,
inputFormat: FormatNoTextType,
options?: FixedLengthOptionsNoEncodingType
);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,68 @@
// @ts-check
describe('Leaderboards', () => {
describe('formatPlayerName', () => {
it('it returns name if correct', () => {
expect(gdjs.evtTools.leaderboards.formatPlayerName('PlayerName')).to.be(
'PlayerName'
);
});
it('it returns name with underscores instead of whitespaces except for leading and trailing ones that are removed', () => {
expect(
gdjs.evtTools.leaderboards.formatPlayerName('\tMy Player Name ')
).to.be('My_Player_Name');
});
it("it doesn't change a name with vertical bars and hyphens", () => {
expect(gdjs.evtTools.leaderboards.formatPlayerName('Pla-yer|Name')).to.be(
'Pla-yer|Name'
);
});
it('it truncates name if longer than 30', () => {
expect(
gdjs.evtTools.leaderboards.formatPlayerName(
'aPlayerNameTh4tIsT00LongToBeSaved'
)
).to.be('aPlayerNameTh4tIsT00LongToBeSa');
});
it('it generates a predefined player name with a random number if input is void/wrong type/empty', () => {
// @ts-ignore
expect(gdjs.evtTools.leaderboards.formatPlayerName(null)).to.match(
/^Player\d{5}/
);
// @ts-ignore
expect(gdjs.evtTools.leaderboards.formatPlayerName(5)).to.match(
/^Player\d{5}/
);
// @ts-ignore
expect(gdjs.evtTools.leaderboards.formatPlayerName(undefined)).to.match(
/^Player\d{5}/
);
// @ts-ignore
expect(gdjs.evtTools.leaderboards.formatPlayerName(() => {})).to.match(
/^Player\d{5}/
);
// @ts-ignore
expect(gdjs.evtTools.leaderboards.formatPlayerName('')).to.match(
/^Player\d{5}/
);
});
it('it removes accents from latin letters', () => {
expect(gdjs.evtTools.leaderboards.formatPlayerName('plâyèrÏonisé')).to.be(
'playerIonise'
);
});
it('it removes non-accepted characters in a long name', () => {
expect(
gdjs.evtTools.leaderboards.formatPlayerName(
'aιΥÉᚱÀeThatsTooonToBeՏaѵÊĐThisPartAppears'
)
).to.be('aEAeThatsTooonToBeaEThisPartAp');
});
});
});

View File

@@ -5,7 +5,6 @@ Copyright (c) 2012-2016 Victor Levasseur (victorlevasseur01@orange.fr)
This project is released under the MIT License.
*/
#include <SFML/Graphics.hpp>
#include "GDCore/Tools/Localization.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Project/InitialInstance.h"

View File

@@ -16,30 +16,30 @@ This project is released under the MIT License.
*/
void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction("ParticleColor1",
_("Initial color"),
_("Modify initial color of particles."),
_("Put initial color of particles of _PARAM0_ to _PARAM1_"),
_("Start color"),
_("Modify start color of particles."),
_("Change particles start color of _PARAM0_ to _PARAM1_"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter")
.AddParameter("color", _("Initial color"));
.AddParameter("color", _("Start color"));
obj.AddAction("ParticleColor2",
_("Final color"),
_("Modify final color of particles."),
_("Put final color of particles of _PARAM0_ to _PARAM1_"),
_("End color"),
_("Modify end color of particles."),
_("Change particles end color of _PARAM0_ to _PARAM1_"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter")
.AddParameter("color", _("Final color"));
.AddParameter("color", _("End color"));
obj.AddAction(
"ParticleRed1",
_("Red color, parameter 1"),
_("Modify parameter 1 of the red color."),
_("the parameter 1 of red color"),
_("Start color red component"),
_("Modify the start color red component."),
_("the start color red component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -48,9 +48,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleRed1",
_("Red color, parameter 1"),
_("Test parameter 1 of the red color"),
_("the parameter 1 of red color"),
_("Start color red component"),
_("Compare the start color red component."),
_("the start color red component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -59,9 +59,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleRed2",
_("Red color, parameter 2"),
_("Modify parameter 2 of the red color"),
_("the parameter 2 of red color"),
_("End color red component"),
_("Modify the end color red component."),
_("the end color red component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -70,9 +70,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleRed2",
_("Red color, parameter 2"),
_("Test parameter 2 of the red color"),
_("the parameter 2 of red color"),
_("End color red component"),
_("Compare the end color red component."),
_("the end color red component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -81,9 +81,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleBlue1",
_("Blue color, parameter 1"),
_("Modify parameter 1 of blue color"),
_("the parameter 1 of blue color"),
_("Start color blue component"),
_("Modify the start color blue component."),
_("the start color blue component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -92,9 +92,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleBlue1",
_("Blue color, parameter 1"),
_("Test parameter 1 of blue color"),
_("the parameter 1 of blue color"),
_("Start color blue component"),
_("Compare the start color blue component."),
_("the start color blue component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -103,9 +103,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleBlue2",
_("Blue color, parameter 2"),
_("Modify parameter 2 of blue color"),
_("the parameter 2 of blue color"),
_("End color blue component"),
_("Modify the end color blue component."),
_("the end color blue component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -114,9 +114,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleBlue2",
_("Blue color, parameter 2"),
_("Test parameter 2 of blue color"),
_("the parameter 2 of blue color"),
_("End color blue component"),
_("Compare the end color blue component."),
_("the end color blue component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -125,9 +125,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleGreen1",
_("Green color, parameter 1"),
_("Modify parameter 1 of green color"),
_("the parameter 1 of green color"),
_("Start color green component"),
_("Modify the start color green component."),
_("the start color green component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -136,9 +136,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleGreen1",
_("Green color, parameter 1"),
_("Test parameter 1 of green color"),
_("the parameter 1 of green color"),
_("Start color green component"),
_("Compare the start color green component."),
_("the start color green component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -147,9 +147,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleGreen2",
_("Green color, parameter 2"),
_("Modify the parameter 2 of the green color"),
_("the parameter 2 of green color"),
_("End color green component"),
_("Modify the end color green component."),
_("the end color green component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -158,9 +158,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleGreen2",
_("Green color, parameter 2"),
_("Test the parameter 2 of the green color"),
_("the parameter 2 of green color"),
_("End color green component"),
_("Compare the end color green component."),
_("the end color green component"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -168,9 +168,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardRelationalOperatorParameters("number");
obj.AddAction("ParticleSize1",
_("Size, parameter 1"),
_("Modify parameter 1 of the size of particles"),
_("the parameter 1 of size"),
_("Start size"),
_("Modify the particle start size."),
_("the start size"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -178,9 +178,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardOperatorParameters("number");
obj.AddCondition("ParticleSize1",
_("Size, parameter 1"),
_("Test parameter 1 of the size of particles"),
_("the parameter 1 of the size"),
_("Start size"),
_("Compare the particle start size."),
_("the start size"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -188,9 +188,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardRelationalOperatorParameters("number");
obj.AddAction("ParticleSize2",
_("Size, parameter 2"),
_("Modify parameter 2 of the size of particles"),
_("the parameter 2 of size"),
_("End size"),
_("Modify the particle end size."),
_("the end size"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -198,9 +198,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardOperatorParameters("number");
obj.AddCondition("ParticleSize2",
_("Size, parameter 2"),
_("Test parameter 2 of the size of particles"),
_("the parameter 2 of the size"),
_("End size"),
_("Compare the particle end size."),
_("the end size"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -210,7 +210,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddAction(
"ParticleAngle1",
_("Angle, parameter 1"),
_("Modify parameter 1 of the angle of particles"),
_("Modify parameter 1 of the angle of particles."),
_("the parameter 1 of angle"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
@@ -220,7 +220,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition("ParticleAngle1",
_("Angle, parameter 1"),
_("Test parameter 1 of the angle of particles"),
_("Compare parameter 1 of the angle of particles."),
_("the parameter 1 of angle"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
@@ -241,7 +241,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition("ParticleAngle2",
_("Angle, parameter 2"),
_("Test parameter 2 of the angle of particles"),
_("Compare parameter 2 of the angle of particles."),
_("the parameter 2 of angle"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
@@ -250,9 +250,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardRelationalOperatorParameters("number");
obj.AddAction("ParticleAlpha1",
_("Transparency, parameter 1"),
_("Modify parameter 1 of the transparency of particles"),
_("the parameter 1 of the transparency"),
_("Start opacity"),
_("Modify the start opacity of particles."),
_("the start opacity"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -261,9 +261,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleAlpha1",
_("Transparency, parameter 1"),
_("Test parameter 1 of the transparency of particles"),
_("the parameter 1 of the transparency"),
_("Start opacity"),
_("Compare the start opacity of particles."),
_("the start opacity"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -271,9 +271,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
.UseStandardRelationalOperatorParameters("number");
obj.AddAction("ParticleAlpha2",
_("Transparency, parameter 2"),
_("Modify parameter 2 of the transparency of particles"),
_("the parameter 2 of the transparency"),
_("End opacity"),
_("Modify the end opacity of particles."),
_("the end opacity"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
@@ -282,9 +282,9 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
obj.AddCondition(
"ParticleAlpha2",
_("Transparency, parameter 2"),
_("Test parameter 2 of the transparency of particles"),
_("the parameter 2 of the transparency"),
_("Start opacity"),
_("Compare the end opacity of particles."),
_("the end opacity"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")

View File

@@ -28,7 +28,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddAction(
"RendererParam1",
_("Rendering first parameter"),
_("Modify first parameter of rendering ( Size/Length ).\nParticles "
_("Modify first parameter of rendering (Size/Length).\nParticles "
"have to be recreated in order to take changes in account."),
_("the rendering 1st parameter"),
_("Setup"),
@@ -40,7 +40,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddCondition(
"RendererParam1",
_("Rendering first parameter"),
_("Test the first parameter of rendering ( Size/Length )."),
_("Test the first parameter of rendering (Size/Length)."),
_("the 1st rendering parameter"),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon24.png",
@@ -51,7 +51,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddAction("RendererParam2",
_("Rendering second parameter"),
_("Modify the second parameter of rendering ( Size/Length "
_("Modify the second parameter of rendering (Size/Length"
").\nParticles have to be recreated in order to take changes "
"in account."),
_("the rendering 2nd parameter"),
@@ -64,7 +64,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddCondition(
"RendererParam2",
_("Rendering second parameter"),
_("Test the second parameter of rendering ( Size/Length )."),
_("Test the second parameter of rendering (Size/Length)."),
_("the 2nd rendering parameter"),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon24.png",
@@ -135,14 +135,25 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
.UseStandardRelationalOperatorParameters("number");
obj.AddAction("Texture",
_("Image"),
_("Change the image of particles ( if displayed )."),
_("Change image (using an expression)"),
_("Change the image of particles (if displayed)."),
_("Change the image of particles of _PARAM0_ to _PARAM1_"),
_("Advanced"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter")
.AddParameter("string", _("Image to use"))
.SetParameterLongDescription("Indicate the name of the resource");
obj.AddAction("SetTextureFromResource",
_("Change image"),
_("Change the image of particles (if displayed)."),
_("Change the image of particles of _PARAM0_ to _PARAM1_"),
_("Common"),
"CppPlatform/Extensions/particleSystemicon24.png",
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter")
.AddParameter("string", _("New image"));
.AddParameter("imageResource", _("Image file (or image resource name)"));
obj.AddCondition(
"Texture",
@@ -157,7 +168,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddStrExpression("Texture",
_("Particles image"),
_("Name of the image displayed by particles"),
_("Name of the image displayed by particles."),
_("Particles"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
@@ -172,7 +183,7 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddExpression("CurrentParticleCount",
_("Particles count"),
_("Number of particles currently displayed"),
_("Number of particles currently displayed."),
_("Particles"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
@@ -193,35 +204,35 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
obj.AddExpression("Tank",
_("Capacity"),
_("Capacity"),
_("Capacity of the particle tank."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("Flow",
_("Flow"),
_("Flow"),
_("Flow of the particles (particles/second)."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("EmitterForceMin",
_("Emission minimal force"),
_("Emission minimal force"),
_("The minimal emission force of the particles."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("EmitterForceMax",
_("Emission maximal force"),
_("Emission maximal force"),
_("The maximal emission force of the particles."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("EmitterAngle",
_("Emission angle"),
_("Emission angle"),
_("Emission angle of the particles."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
@@ -238,104 +249,104 @@ void ExtensionSubDeclaration3(gd::ObjectMetadata& obj) {
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ZoneRadius",
_("Radius of the emission zone"),
_("Radius of the emission zone"),
_("Radius of emission zone"),
_("The radius of the emission zone."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGravityX",
_("X Gravity of particles"),
_("X Gravity of particles"),
_("X gravity"),
_("Gravity of particles applied on X-axis."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGravityY",
_("Y Gravity of particles"),
_("Y Gravity of particles"),
_("Y gravity"),
_("Gravity of particles applied on Y-axis."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGravityAngle",
_("Gravity angle"),
_("Gravity angle"),
_("Angle of gravity."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGravityLength",
_("Gravity"),
_("Gravity value"),
_("Value of gravity."),
_("Common"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleLifeTimeMin",
_("Minimum lifetime of particles"),
_("Minimum lifetime of particles"),
_("Minimum lifetime of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleLifeTimeMax",
_("Maximum lifetime of particles"),
_("Maximum lifetime of particles"),
_("Maximum lifetime of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleRed1",
_("Parameter 1 of red color"),
_("Parameter 1 of red color"),
_("Start color red component"),
_("The start color red component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleRed2",
_("Parameter 2 of red color"),
_("Parameter 2 of red color"),
_("End color red component"),
_("The end color red component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleBlue1",
_("Parameter 1 of blue color"),
_("Parameter 1 of blue color"),
_("Start color blue component"),
_("The start color blue component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleBlue2",
_("Parameter 2 of blue color"),
_("Parameter 2 of blue color"),
_("End color blue component"),
_("The end color blue component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGreen1",
_("Parameter 1 of green color"),
_("Parameter 1 of green color"),
_("Start color green component"),
_("The start color green component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleGreen2",
_("Parameter 2 of green color"),
_("Parameter 2 of green color"),
_("End color green component"),
_("The end color green component of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleAlpha1",
_("Parameter 1 of transparency"),
_("Parameter 1 of transparency"),
_("Start opacity"),
_("Start opacity of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleAlpha2",
_("Parameter 2 of transparency"),
_("Parameter 2 of transparency"),
_("End opacity"),
_("End opacity of the particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleSize1",
_("Parameter 1 of size"),
_("Parameter 1 of size"),
_("Start size"),
_("Start size of particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);
obj.AddExpression("ParticleSize2",
_("Parameter 2 of size"),
_("Parameter 2 of size"),
_("End size"),
_("End size of particles."),
_("Setup"),
"CppPlatform/Extensions/particleSystemicon16.png")
.AddParameter("object", _("Object"), "ParticleEmitter", false);

View File

@@ -169,6 +169,10 @@ class ParticleSystemJsExtension : public gd::PlatformExtension {
actions["ParticleSystem::Flow"].SetFunctionName("setFlow").SetGetter(
"getFlow");
conditions["ParticleSystem::Flow"].SetFunctionName("getFlow");
actions["ParticleSystem::SetTextureFromResource"]
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("setTexture")
.SetGetter("getTexture");
actions["ParticleSystem::Texture"]
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("setTexture")

View File

@@ -7,7 +7,7 @@ This project is released under the MIT License.
#ifndef PATHFINDINGBEHAVIOR_H
#define PATHFINDINGBEHAVIOR_H
#include <SFML/System/Vector2.hpp>
#include "GDCore/Vector2.h"
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Object.h"

View File

@@ -33,7 +33,7 @@ void PhysicsBehavior::InitializeContent(
behaviorContent.SetAttribute("polygonWidth", 200);
behaviorContent.SetAttribute("polygonHeight", 200);
std::vector<sf::Vector2f> polygonCoords;
std::vector<gd::Vector2f> polygonCoords;
behaviorContent.SetAttribute(
"coordsList",
PhysicsBehavior::GetStringFromCoordsVector(polygonCoords, '/', ';'));
@@ -128,7 +128,7 @@ bool PhysicsBehavior::UpdateProperty(gd::SerializerElement &behaviorContent,
#endif
gd::String PhysicsBehavior::GetStringFromCoordsVector(
const std::vector<sf::Vector2f> &vec,
const std::vector<gd::Vector2f> &vec,
char32_t coordsSep,
char32_t composantSep) {
gd::String coordsStr;

View File

@@ -13,7 +13,7 @@ This project is released under the MIT License.
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/Object.h"
#include "SFML/System/Vector2.hpp"
#include "GDCore/Vector2.h"
namespace gd {
class Project;
class SerializerElement;
@@ -43,7 +43,7 @@ class GD_EXTENSION_API PhysicsBehavior : public gd::Behavior {
gd::SerializerElement &behaviorContent) override;
private:
gd::String GetStringFromCoordsVector(const std::vector<sf::Vector2f> &vec,
gd::String GetStringFromCoordsVector(const std::vector<gd::Vector2f> &vec,
char32_t coordsSep,
char32_t composantSep);
enum ShapeType {

View File

@@ -11,9 +11,8 @@ namespace gdjs {
runtimeScene: gdjs.RuntimeScene,
savePath: string
) {
const electron = runtimeScene.getGame().getRenderer().getElectron();
if (electron) {
const fileSystem = electron.remote.require('fs');
const fs = typeof require !== 'undefined' ? require('fs') : null;
if (fs) {
const canvas = runtimeScene.getGame().getRenderer().getCanvas();
if (canvas) {
const content = canvas
@@ -22,7 +21,7 @@ namespace gdjs {
if (savePath.toLowerCase().indexOf('.png') == -1) {
savePath += '.png';
}
fileSystem.writeFile(savePath, content, 'base64', (err) => {
fs.writeFile(savePath, content, 'base64', (err) => {
if (err) {
logger.error(
'Unable to save the screenshot at path: ' + savePath

View File

@@ -4,7 +4,6 @@ cmake_policy(SET CMP0015 NEW)
project(GDJS)
#Dependencies on external libraries:
###
include_directories(${sfml_include_dir})
include_directories(${GDCORE_include_dir})
#Defines
@@ -75,5 +74,4 @@ IF(EMSCRIPTEN)
#Nothing.
ELSE()
target_link_libraries(GDJS GDCore)
target_link_libraries(GDJS ${sfml_LIBRARIES})
ENDIF()

View File

@@ -18,8 +18,8 @@
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/SceneNameMangler.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
@@ -83,7 +83,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
wholeEventsCode + "\n" +
functionReturnCode + "\n" +
"}\n";
// clang-format on
// clang-format on
return output;
}
@@ -233,7 +233,11 @@ gd::String EventsCodeGenerator::GenerateFreeEventsFunctionContext(
gd::String objectsGettersMap;
gd::String objectArraysMap;
gd::String behaviorNamesMap;
return GenerateEventsFunctionContext(parameters, onceTriggersVariable, objectsGettersMap, objectArraysMap, behaviorNamesMap);
return GenerateEventsFunctionContext(parameters,
onceTriggersVariable,
objectsGettersMap,
objectArraysMap,
behaviorNamesMap);
}
gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionContext(
@@ -260,35 +264,38 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionContext(
}
if (!thisBehaviorName.empty()) {
// If we have a behavior considered as the current behavior ("this") (usually
// called Behavior in behavior events function), generate a slightly more
// optimized getter for it.
// If we have a behavior considered as the current behavior ("this")
// (usually called Behavior in behavior events function), generate a
// slightly more optimized getter for it.
behaviorNamesMap += ConvertToStringExplicit(thisBehaviorName) + ": " +
thisBehaviorName + "\n";
// Add required behaviors from properties
for (size_t i = 0; i < eventsBasedBehavior.GetPropertyDescriptors().GetCount(); i++)
{
const gd::NamedPropertyDescriptor& propertyDescriptor = eventsBasedBehavior.GetPropertyDescriptors().Get(i);
const std::vector<gd::String>& extraInfo = propertyDescriptor.GetExtraInfo();
for (size_t i = 0;
i < eventsBasedBehavior.GetPropertyDescriptors().GetCount();
i++) {
const gd::NamedPropertyDescriptor& propertyDescriptor =
eventsBasedBehavior.GetPropertyDescriptors().Get(i);
const std::vector<gd::String>& extraInfo =
propertyDescriptor.GetExtraInfo();
if (propertyDescriptor.GetType() == "Behavior") {
// Generate map that will be used to transform from behavior name used in
// function to the "real" behavior name from the caller.
// Generate map that will be used to transform from behavior name used
// in function to the "real" behavior name from the caller.
gd::String comma = behaviorNamesMap.empty() ? "" : ", ";
behaviorNamesMap += comma + ConvertToStringExplicit(propertyDescriptor.GetName()) +
": this._get" + propertyDescriptor.GetName() + "()\n";
behaviorNamesMap +=
comma + ConvertToStringExplicit(propertyDescriptor.GetName()) +
": this._get" + propertyDescriptor.GetName() + "()\n";
}
}
}
return GenerateEventsFunctionContext(
parameters,
onceTriggersVariable,
objectsGettersMap,
objectArraysMap,
behaviorNamesMap,
thisObjectName,
thisBehaviorName);
return GenerateEventsFunctionContext(parameters,
onceTriggersVariable,
objectsGettersMap,
objectArraysMap,
behaviorNamesMap,
thisObjectName,
thisBehaviorName);
}
gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
@@ -384,7 +391,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
// to create the new object as the object names used in the function
// are not the same as the objects available in the scene.
" createObject: function(objectName) {\n"
" var objectsList = "
" const objectsList = "
"eventsFunctionContext._objectsMap[objectName];\n" +
// TODO: we could speed this up by storing a map of object names, but
// the cost of creating/storing it for each events function might not
@@ -398,13 +405,27 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
// Add the new instance to object lists
" if (object) {\n" +
" objectsList.get(objectsList.firstKey()).push(object);\n" +
" eventsFunctionContext._objectArraysMap[objectName].push(object);\n" +
" }\n" +
" return object;" +
" }\n" +
" "
"eventsFunctionContext._objectArraysMap[objectName].push(object);\n" +
" }\n" + " return object;" + " }\n" +
// Unknown object, don't create anything:
" return null;\n" +
" },\n"
// Function to count instances on the scene. We need it here because
// it needs the objects map to get the object names of the parent context.
" getInstancesCountOnScene: function(objectName) {\n"
" const objectsList = "
"eventsFunctionContext._objectsMap[objectName];\n" +
" let count = 0;\n" +
" if (objectsList) {\n" +
" for(const objectName in objectsList.items)\n" +
" count += parentEventsFunctionContext ?\n" +
"parentEventsFunctionContext.getInstancesCountOnScene(objectName) "
":\n" +
" runtimeScene.getInstancesCountOnScene(objectName);\n" +
" }\n" +
" return count;\n" +
" },\n"
// Allow to get a layer directly from the context for convenience:
" getLayer: function(layerName) {\n"
" return runtimeScene.getLayer(layerName);\n"
@@ -664,7 +685,8 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::ObjectMetadata& objInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context) {
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
gd::String actionCode;
// Prepare call
@@ -701,12 +723,23 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
GenerateArgumentsList(arguments, 1) + ")";
}
if (!optionalAsyncCallbackName.empty()) {
actionCode += "{\nconst asyncTaskGroup = new gdjs.TaskGroup();\n";
call = "asyncTaskGroup.addTask(" + call + ")";
}
actionCode +=
"for(var i = 0, len = " + GetObjectListName(objectName, context) +
".length ;i < len;++i) {\n";
actionCode += " " + call + ";\n";
actionCode += "}\n";
if (!optionalAsyncCallbackName.empty()) {
actionCode +=
"runtimeScene.getAsyncTasksManager().addTask(asyncTaskGroup, " +
optionalAsyncCallbackName + ")\n}";
}
return actionCode;
}
@@ -716,7 +749,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
const gd::BehaviorMetadata& autoInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context) {
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName) {
gd::String actionCode;
// Prepare call
@@ -764,11 +798,22 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
<< "\" requested for object \'" << objectName
<< "\" (action: " << instrInfos.GetFullName() << ")." << endl;
} else {
if (!optionalAsyncCallbackName.empty()) {
actionCode += "{\n const asyncTaskGroup = new gdjs.TaskGroup();\n";
call = "asyncTaskGroup.addTask(" + call + ")";
}
actionCode +=
"for(var i = 0, len = " + GetObjectListName(objectName, context) +
".length ;i < len;++i) {\n";
actionCode += " " + call + ";\n";
actionCode += "}\n";
if (!optionalAsyncCallbackName.empty()) {
actionCode +=
"runtimeScene.getAsyncTasksManager().addTask(asyncTaskGroup, " +
optionalAsyncCallbackName + ");\n };";
}
}
return actionCode;
@@ -792,7 +837,7 @@ gd::String EventsCodeGenerator::GenerateGetBehaviorNameCode(
gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::EventsCodeGenerationContext& context) {
auto declareObjectList = [this](gd::String object,
auto declareObjectListFromParent = [this](gd::String object,
gd::EventsCodeGenerationContext& context) {
gd::String objectListName = GetObjectListName(object, context);
if (!context.GetParentContext()) {
@@ -802,6 +847,11 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
return "/* Could not declare " + objectListName + " */";
}
if (context.ShouldUseAsyncObjectsList(object)) {
gd::String copiedListName = "asyncObjectsList.getObjects(" + ConvertToStringExplicit(object) + ")";
return "gdjs.copyArray(" + copiedListName + ", " + objectListName + ");\n";
}
//*Optimization*: Avoid expensive copy of the object list if we're using
// the same list as the one from the parent context.
if (context.IsSameObjectsList(object, *context.GetParentContext()))
@@ -815,33 +865,30 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String declarationsCode;
for (auto object : context.GetObjectsListsToBeDeclared()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration += "gdjs.copyArray(" +
GenerateAllInstancesGetterCode(object) + ", " +
GetObjectListName(object, context) + ");";
context.SetObjectDeclared(object);
GenerateAllInstancesGetterCode(object, context) +
", " + GetObjectListName(object, context) + ");";
} else
objectListDeclaration = declareObjectList(object, context);
objectListDeclaration = declareObjectListFromParent(object, context);
declarationsCode += objectListDeclaration + "\n";
}
for (auto object : context.GetObjectsListsToBeDeclaredWithoutPicking()) {
for (auto object : context.GetObjectsListsToBeEmptyIfJustDeclared()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
context.SetObjectDeclared(object);
} else
objectListDeclaration = declareObjectList(object, context);
objectListDeclaration = declareObjectListFromParent(object, context);
declarationsCode += objectListDeclaration + "\n";
}
for (auto object : context.GetObjectsListsToBeDeclaredEmpty()) {
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclared(object)) {
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
context.SetObjectDeclared(object);
} else
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
@@ -853,7 +900,7 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
}
gd::String EventsCodeGenerator::GenerateAllInstancesGetterCode(
gd::String& objectName) {
const gd::String& objectName, gd::EventsCodeGenerationContext& context) {
if (HasProjectAndLayout()) {
return "runtimeScene.getObjects(" + ConvertToStringExplicit(objectName) +
")";
@@ -864,7 +911,7 @@ gd::String EventsCodeGenerator::GenerateAllInstancesGetterCode(
}
gd::String EventsCodeGenerator::GenerateEventsListCode(
gd::EventsList& events, const gd::EventsCodeGenerationContext& context) {
gd::EventsList& events, gd::EventsCodeGenerationContext& context) {
// *Optimization*: generating all JS code of events in a single, enormous
// function is badly handled by JS engines and in particular the garbage
// collectors, leading to intermittent lag/freeze while the garbage collector
@@ -875,9 +922,7 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
gd::String code =
gd::EventsCodeGenerator::GenerateEventsListCode(events, context);
gd::String parametersCode = HasProjectAndLayout()
? "runtimeScene"
: "runtimeScene, eventsFunctionContext";
gd::String parametersCode = GenerateEventsParameters(context);
// Generate a unique name for the function.
gd::String uniqueId =
@@ -979,12 +1024,16 @@ gd::String EventsCodeGenerator::GenerateObject(
// avoid re-creating them at runtime. Arrays are passed as reference in JS and
// we always use the same static arrays, making this possible.
auto declareMapOfObjects =
[this](const std::vector<gd::String>& objects,
const gd::EventsCodeGenerationContext& context) {
[this](const std::vector<gd::String>& declaredObjectNames,
const gd::EventsCodeGenerationContext& context,
const std::vector<gd::String>& notDeclaredObjectNames = {}) {
// The map name must be unique for each set of objects lists.
// We generate it from the objects lists names.
gd::String objectsMapName = GetCodeNamespaceAccessor() + "mapOf";
gd::String mapDeclaration;
for (auto& objectName : objects) {
// The map name must be unique for each set of objects lists.
// Map each declared object to its list.
for (auto& objectName : declaredObjectNames) {
objectsMapName +=
ManObjListName(GetObjectListName(objectName, context));
@@ -993,8 +1042,20 @@ gd::String EventsCodeGenerator::GenerateObject(
"\": " + GetObjectListName(objectName, context);
}
// Map each object not declared to an empty list.
// Useful for parameters willing to get objects lists without
// picking the objects for future instructions.
for (auto& objectName : notDeclaredObjectNames) {
objectsMapName += "Empty" + ManObjListName(objectName);
if (!mapDeclaration.empty()) mapDeclaration += ", ";
mapDeclaration += "\"" + ConvertToString(objectName) +
"\": []";
}
// TODO: this should be de-duplicated.
AddCustomCodeOutsideMain(objectsMapName + " = Hashtable.newFrom({" +
mapDeclaration + "});");
mapDeclaration + "});\n");
return objectsMapName;
};
@@ -1006,14 +1067,32 @@ gd::String EventsCodeGenerator::GenerateObject(
gd::String objectsMapName = declareMapOfObjects(realObjects, context);
output = objectsMapName;
} else if (type == "objectListWithoutPicking") {
} else if (type == "objectListOrEmptyIfJustDeclared") {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
for (auto& objectName : realObjects)
context.ObjectsListWithoutPickingNeeded(objectName);
context.ObjectsListNeededOrEmptyIfJustDeclared(objectName);
gd::String objectsMapName = declareMapOfObjects(realObjects, context);
output = objectsMapName;
} else if (type == "objectListOrEmptyWithoutPicking") {
std::vector<gd::String> realObjects = ExpandObjectsName(objectName, context);
// Find the objects not yet declared, and handle them separately so they are
// passed as empty object lists.
std::vector<gd::String> objectToBeDeclaredNames;
std::vector<gd::String> objectNotYetDeclaredNames;
for (auto& objectName : realObjects) {
if (context.ObjectAlreadyDeclaredByParents(objectName) ||
context.IsToBeDeclared(objectName)) {
objectToBeDeclaredNames.push_back(objectName);
} else {
objectNotYetDeclaredNames.push_back(objectName);
}
}
gd::String objectsMapName = declareMapOfObjects(objectToBeDeclaredNames, context, objectNotYetDeclaredNames);
output = objectsMapName;
} else if (type == "objectPtr") {
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);

View File

@@ -77,10 +77,10 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
* \param eventsFunction The events function to be compiled.
* \param codeNamespace Where to store the context used by the function.
* \param includeFiles Will be filled with the necessary include files.
* \param onceTriggersVariable The code to access the variable holding OnceTriggers.
* \param preludeCode The code to run just before the events generated code.
* \param compilationForRuntime Set this to true if the code is generated for
* runtime.
* \param onceTriggersVariable The code to access the variable holding
* OnceTriggers. \param preludeCode The code to run just before the events
* generated code. \param compilationForRuntime Set this to true if the code
* is generated for runtime.
*
* \return JavaScript code
*/
@@ -107,7 +107,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
* \return Code
*/
virtual gd::String GenerateEventsListCode(
gd::EventsList& events, const gd::EventsCodeGenerationContext& context);
gd::EventsList& events, gd::EventsCodeGenerationContext& context);
/**
* Generate code for executing a condition list
@@ -229,7 +229,8 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::ObjectMetadata& objInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
virtual gd::String GenerateBehaviorAction(
const gd::String& objectName,
@@ -237,7 +238,8 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::BehaviorMetadata& autoInfo,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context);
gd::EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
virtual gd::String GenerateGetBehaviorNameCode(
const gd::String& behaviorName);
@@ -279,7 +281,8 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
virtual gd::String GenerateObjectsDeclarationCode(
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateAllInstancesGetterCode(gd::String& objectName);
virtual gd::String GenerateAllInstancesGetterCode(
const gd::String& objectName, gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateProfilerSectionBegin(const gd::String& section);
virtual gd::String GenerateProfilerSectionEnd(const gd::String& section);
@@ -323,18 +326,18 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
bool isBehaviorEventsFunction);
/**
* \brief Generate the "eventsFunctionContext" object that allow a free function
* to provides access objects, object creation and access to arguments from
* the rest of the events.
* \brief Generate the "eventsFunctionContext" object that allow a free
* function to provides access objects, object creation and access to
* arguments from the rest of the events.
*/
gd::String GenerateFreeEventsFunctionContext(
const std::vector<gd::ParameterMetadata>& parameters,
const gd::String& onceTriggersVariable);
/**
* \brief Generate the "eventsFunctionContext" object that allow a behavior function
* to provides access objects, object creation and access to arguments from
* the rest of the events.
* \brief Generate the "eventsFunctionContext" object that allow a behavior
* function to provides access objects, object creation and access to
* arguments from the rest of the events.
*/
gd::String GenerateBehaviorEventsFunctionContext(
const gd::EventsBasedBehavior& eventsBasedBehavior,
@@ -360,7 +363,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
gd::String codeNamespace; ///< Optional namespace for the generated code,
///< used when generating events function.
private:
private:
/**
* \brief Generate the "eventsFunctionContext" object that allow a function
* to provides access objects, object creation and access to arguments from

View File

@@ -0,0 +1,73 @@
/*
* GDevelop JS Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "AsyncExtension.h"
#include "GDCore/Events/Builtin/AsyncEvent.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
#include "GDCore/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
namespace gdjs {
AsyncExtension::AsyncExtension() {
gd::BuiltinExtensionsImplementer::ImplementsAsyncExtension(*this);
GetAllEvents()["BuiltinAsync::Async"].SetCodeGenerator(
[](gd::BaseEvent &event_,
gd::EventsCodeGenerator &codeGenerator,
gd::EventsCodeGenerationContext &parentContext) {
gd::AsyncEvent &event = dynamic_cast<gd::AsyncEvent &>(event_);
// Generate callback code
const auto callbackDescriptor = codeGenerator.GenerateCallback(
gd::String::From(codeGenerator.GenerateSingleUsageUniqueIdFor(
event.GetInstruction().GetOriginalInstruction().lock().get())),
parentContext,
event.GetActions(),
event.HasSubEvents() ? &event.GetSubEvents() : nullptr);
const gd::String callbackCallCode =
"(runtimeScene) => (" + callbackDescriptor.functionName + "(" +
callbackDescriptor.argumentsList + "))";
// Generate the action and store the generated task.
const gd::String asyncActionCode = codeGenerator.GenerateActionCode(
event.GetInstruction(), parentContext, callbackCallCode);
// Generate code to backup the objects lists.
// Do it after generating the code of the action so that it uses the
// same object list as used in the action.
gd::String parentAsyncObjectsListGetter =
parentContext.IsInsideAsync()
? "const parentAsyncObjectsList = asyncObjectsList;\n"
: "";
gd::String asyncObjectsListBuilder =
parentContext.IsInsideAsync()
? "const asyncObjectsList = "
"gdjs.LongLivedObjectsList.from(parentAsyncObjectsList);\n"
: "const asyncObjectsList = new gdjs.LongLivedObjectsList();\n";
for (const gd::String &objectNameToBackup :
callbackDescriptor.requiredObjects) {
if (parentContext.ShouldUseAsyncObjectsList(objectNameToBackup))
asyncObjectsListBuilder +=
"/* Don't save " + objectNameToBackup +
" as it will be provided by the parent asyncObjectsList. */\n";
else
asyncObjectsListBuilder +=
"for (const obj of " +
codeGenerator.GetObjectListName(objectNameToBackup,
parentContext) +
") asyncObjectsList.addObject(" +
codeGenerator.ConvertToStringExplicit(objectNameToBackup) +
", obj);\n";
}
return "{\n" + parentAsyncObjectsListGetter + "{\n" +
asyncObjectsListBuilder + asyncActionCode + "}\n" + "}\n";
});
}
} // namespace gdjs

View File

@@ -0,0 +1,24 @@
/*
* GDevelop JS Platform
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef ASYNCEXTENSION_H
#define ASYNCEXTENSION_H
#include "GDCore/Extensions/PlatformExtension.h"
namespace gdjs {
/**
* \brief Built-in extension providing async functionality.
*
* \ingroup BuiltinExtensions
*/
class AsyncExtension : public gd::PlatformExtension {
public:
AsyncExtension();
virtual ~AsyncExtension(){};
};
} // namespace gdjs
#endif // ASYNCEXTENSION_H

View File

@@ -33,6 +33,8 @@ AudioExtension::AudioExtension() {
"gdjs.evtTools.sound.pauseMusicOnChannel");
GetAllActions()["RePlayMusicCanal"].SetFunctionName(
"gdjs.evtTools.sound.continueMusicOnChannel");
GetAllActions()["FadeMusicVolume"].SetFunctionName(
"gdjs.evtTools.sound.fadeMusicVolume");
GetAllActions()["PreloadMusic"].SetFunctionName(
"gdjs.evtTools.sound.preloadMusic");
@@ -44,6 +46,9 @@ AudioExtension::AudioExtension() {
"gdjs.evtTools.sound.unloadSound");
GetAllActions()["UnloadAllAudio"].SetFunctionName(
"gdjs.evtTools.sound.unloadAllAudio");
GetAllActions()["FadeSoundVolume"].SetFunctionName(
"gdjs.evtTools.sound.fadeSoundVolume");
GetAllConditions()["MusicPlaying"].SetFunctionName(
"gdjs.evtTools.sound.isMusicOnChannelPlaying");

View File

@@ -252,10 +252,21 @@ BaseObjectExtension::BaseObjectExtension() {
"gdjs.evtTools.object.createObjectOnScene");
GetAllActions()["CreateByName"].SetFunctionName(
"gdjs.evtTools.object.createObjectFromGroupOnScene");
GetAllExpressions()["Count"].SetFunctionName(
"gdjs.evtTools.object.pickedObjectsCount");
"gdjs.evtTools.object.pickedObjectsCount"); // Deprecated
GetAllConditions()["NbObjet"].SetFunctionName(
"gdjs.evtTools.object.pickedObjectsCount");
"gdjs.evtTools.object.pickedObjectsCount"); // Deprecated
GetAllExpressions()["SceneInstancesCount"].SetFunctionName(
"gdjs.evtTools.object.getSceneInstancesCount");
GetAllConditions()["SceneInstancesCount"].SetFunctionName(
"gdjs.evtTools.object.getSceneInstancesCount");
GetAllExpressions()["PickedInstancesCount"].SetFunctionName(
"gdjs.evtTools.object.getPickedInstancesCount");
GetAllConditions()["PickedInstancesCount"].SetFunctionName(
"gdjs.evtTools.object.getPickedInstancesCount");
GetAllConditions()["CollisionNP"].SetFunctionName(
"gdjs.evtTools.object.hitBoxesCollisionTest");
GetAllConditions()["Raycast"].SetFunctionName(

View File

@@ -30,6 +30,7 @@
#include "GDCore/String.h"
#include "GDCore/Tools/Localization.h"
#include "GDJS/Events/Builtin/JsCodeEvent.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
using namespace std;
using namespace gd;
@@ -89,8 +90,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
return gd::String("");
});
GetAllConditions()["BuiltinCommonInstructions::CompareNumbers"].codeExtraInformation =
GetAllConditions()["Egal"].codeExtraInformation;
GetAllConditions()["BuiltinCommonInstructions::CompareNumbers"]
.codeExtraInformation = GetAllConditions()["Egal"].codeExtraInformation;
GetAllConditions()["StrEqual"].codeExtraInformation.SetCustomCodeGenerator(
[](gd::Instruction& instruction,
@@ -123,8 +124,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
return gd::String("");
});
GetAllConditions()["BuiltinCommonInstructions::CompareStrings"].codeExtraInformation =
GetAllConditions()["StrEqual"].codeExtraInformation;
GetAllConditions()["BuiltinCommonInstructions::CompareStrings"]
.codeExtraInformation =
GetAllConditions()["StrEqual"].codeExtraInformation;
GetAllEvents()["BuiltinCommonInstructions::Link"]
.SetCodeGenerator([](gd::BaseEvent& event_,
@@ -216,7 +218,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// "MyObject" will both have to declare a "MyObject" object list.
gd::EventsCodeGenerationContext context;
context.InheritsFrom(parentContext);
context.ForbidReuse(); // TODO: This may not be necessary
context.ForbidReuse(); // TODO: This may not be necessary (to be investigated/heavily tested).
gd::String conditionCode = codeGenerator.GenerateConditionCode(
conditions[cId],
@@ -280,7 +282,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
//"OR" condition must declare objects list, but without getting
// the objects from the scene. Lists are either empty or come from
// a parent event.
parentContext.ObjectsListWithoutPickingNeeded(*it);
parentContext.ObjectsListNeededOrEmptyIfJustDeclared(*it);
// We need to duplicate the object lists : The "final" ones will
// be filled with objects by conditions, but they will have no
// incidence on further conditions, as conditions use "normal"
@@ -437,11 +439,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// Prevent code generation if the event is empty, as this would
// get the game stuck in a never ending loop.
if (
event.GetWhileConditions().empty() &&
event.GetConditions().empty() &&
event.GetActions().empty()
)
if (event.GetWhileConditions().empty() &&
event.GetConditions().empty() && event.GetActions().empty())
return gd::String(
"\n// While event not generated to prevent an infinite loop.\n");
@@ -495,6 +494,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
outputCode += "if (" + ifPredicat + ") {\n";
outputCode += actionsCode;
outputCode += "\n{ //Subevents: \n";
// TODO: check (and heavily test) if sub events should be generated before
// the call to GenerateObjectsDeclarationCode.
outputCode +=
codeGenerator.GenerateEventsListCode(event.GetSubEvents(), context);
outputCode += "} //Subevents end.\n";
@@ -553,6 +554,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
!event.GetValueIteratorVariableName().empty();
bool keyIteratorExists = !event.GetKeyIteratorVariableName().empty();
// clang-format off
// Define references to variables (if they exist)
if (keyIteratorExists)
outputCode +=
@@ -599,6 +601,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
" // Arrays are passed by reference like JS objects\n"
" $VALUE_ITERATOR_REFERENCE.replaceChildrenArray($STRUCTURE_CHILD_VARIABLE.getAllChildrenArray());\n"
" } else console.warn(\"Cannot identify type: \", type);\n";
// clang-format on
// Now do the rest of standard event code generation
outputCode += objectDeclaration;
@@ -755,7 +758,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// picked again)
gd::EventsCodeGenerationContext context;
context.InheritsFrom(parentContext);
context.ForbidReuse();
context.ForbidReuse(); // TODO: This may not be necessary (to be investigated/heavily tested).
for (unsigned int i = 0; i < realObjects.size(); ++i)
context.EmptyObjectsListNeeded(realObjects[i]);

View File

@@ -37,7 +37,9 @@ MathematicalToolsExtension::MathematicalToolsExtension() {
GetAllExpressions()["atanh"].SetFunctionName("gdjs.evtTools.common.atanh");
GetAllExpressions()["cbrt"].SetFunctionName("gdjs.evtTools.common.cbrt");
GetAllExpressions()["ceil"].SetFunctionName("Math.ceil");
GetAllExpressions()["ceilTo"].SetFunctionName("gdjs.evtTools.common.ceilTo");
GetAllExpressions()["floor"].SetFunctionName("Math.floor");
GetAllExpressions()["floorTo"].SetFunctionName("gdjs.evtTools.common.floorTo");
GetAllExpressions()["cosh"].SetFunctionName("gdjs.evtTools.common.cosh");
GetAllExpressions()["sinh"].SetFunctionName("gdjs.evtTools.common.sinh");
GetAllExpressions()["tanh"].SetFunctionName("gdjs.evtTools.common.tanh");
@@ -63,6 +65,7 @@ MathematicalToolsExtension::MathematicalToolsExtension() {
GetAllExpressions()["int"].SetFunctionName("Math.round");
GetAllExpressions()["rint"].SetFunctionName("Math.round");
GetAllExpressions()["round"].SetFunctionName("Math.round");
GetAllExpressions()["roundTo"].SetFunctionName("gdjs.evtTools.common.roundTo");
GetAllExpressions()["trunc"].SetFunctionName("gdjs.evtTools.common.trunc");
GetAllExpressions()["lerp"].SetFunctionName("gdjs.evtTools.common.lerp");
GetAllExpressions()["XFromAngleAndDistance"].SetFunctionName("gdjs.evtTools.common.getXFromAngleAndDistance");

View File

@@ -55,9 +55,9 @@ MouseExtension::MouseExtension() {
"gdjs.evtTools.input.getMouseY"); // Deprecated
GetAllConditions()["PopStartedTouch"].SetFunctionName(
"gdjs.evtTools.input.popStartedTouch");
"gdjs.evtTools.input.popStartedTouch"); // Deprecated
GetAllConditions()["PopEndedTouch"].SetFunctionName(
"gdjs.evtTools.input.popEndedTouch");
"gdjs.evtTools.input.popEndedTouch"); // Deprecated
GetAllConditions()["TouchX"].SetFunctionName("gdjs.evtTools.input.getTouchX");
GetAllConditions()["TouchY"].SetFunctionName("gdjs.evtTools.input.getTouchY");
@@ -67,9 +67,18 @@ MouseExtension::MouseExtension() {
"gdjs.evtTools.input.getTouchY");
GetAllExpressions()["LastTouchId"].SetFunctionName(
"gdjs.evtTools.input.getLastTouchId");
"gdjs.evtTools.input.getLastTouchId"); // Deprecated
GetAllExpressions()["LastEndedTouchId"].SetFunctionName(
"gdjs.evtTools.input.getLastEndedTouchId");
"gdjs.evtTools.input.getLastEndedTouchId"); // Deprecated
GetAllConditions()["HasAnyTouchStarted"].SetFunctionName(
"gdjs.evtTools.input.hasAnyTouchStarted");
GetAllConditions()["HasTouchEnded"].SetFunctionName(
"gdjs.evtTools.input.hasTouchEnded");
GetAllExpressions()["StartedTouchCount"].SetFunctionName(
"gdjs.evtTools.input.getStartedTouchCount");
GetAllExpressions()["StartedTouchId"].SetFunctionName(
"gdjs.evtTools.input.getStartedTouchIdentifier");
GetAllExpressions()["MouseWheelDelta"].SetFunctionName(
"gdjs.evtTools.input.getMouseWheelDelta");

View File

@@ -27,6 +27,8 @@ TimeExtension::TimeExtension() {
"gdjs.evtTools.runtimeScene.unpauseTimer");
GetAllActions()["RemoveTimer"].SetFunctionName(
"gdjs.evtTools.runtimeScene.removeTimer");
GetAllActions()["Wait"].SetFunctionName(
"gdjs.evtTools.runtimeScene.wait");
GetAllConditions()["TimeScale"].SetFunctionName(
"gdjs.evtTools.runtimeScene.getTimeScale");
GetAllActions()["ChangeTimeScale"].SetFunctionName(

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