Compare commits

...

137 Commits

Author SHA1 Message Date
Florian Rival
36fb4ec9b2 Bump newIDE version 2021-11-12 11:47:21 +00:00
Florian Rival
124e1f3683 Improve changelog extraction script
Don't show in changelog
2021-11-12 11:46:57 +00:00
github-actions[bot]
9c350729a8 Update translations [skip ci] (#3235) 2021-11-12 11:43:27 +00:00
Florian Rival
9186daa782 Update Platformer starter to allow the character to walk on rotated platforms
Don't show in changelog
2021-11-11 17:18:14 +00:00
Florian Rival
c6161c4752 Add an option in the Sprite editor to allow animations to play even when the object is hidden/outside the screen 2021-11-11 17:08:40 +00:00
D8H
5d3f207216 Add major improvements to the platformer engine to better handle slopes and moving platforms (#3009)
* The characters on platform are no long stopping when going from one rotated platform to another.
* Platforms going up and down are now properly handled by characters - they won't fall or vibrate like before. You can now freely use platforms doing any movement and characters will stay on them and can move freely on them.
* Characters Y position will now stay stable when moving on a flat platform and between jumps.
* Note that if you use the collision condition to check if an object is touching a platform, you should instead use the condition "Is object on given floor": this will always work consistently.
2021-11-11 16:11:02 +00:00
Florian Rival
cf462f6c6e Fix volume sounds and musics not clamped at exactly 0 and 1 (#3234)
Don't show in changelog
2021-11-10 11:36:20 +00:00
github-actions[bot]
bc979031e3 Update translations (#3202) 2021-11-09 16:08:14 +00:00
Clément Pasteau
406bae5e12 Fix a glitch where a sound being played at a low volume can actually be heard at a high volume for a split second 2021-11-09 16:00:56 +00:00
Florian Rival
5f5f50e039 Fix wording
Don't show in changelog
2021-11-09 14:43:53 +00:00
Florian Rival
394fb4c587 Fix warning
Don't show in changelog
2021-11-08 15:24:07 +00:00
Florian Rival
599d48afca Refactor scan/removal of unused resources
Don't show in changelog
2021-11-07 19:38:50 +00:00
Florian Rival
bccef185cb Clean up some code
Don't show in changelog
2021-11-07 19:38:50 +00:00
Florian Rival
d0f4370026 Add support for blob urls in the web-app export/preview
Experimental support - just so the behavior of the export/preview are correct.

Don't show in changelog
2021-11-07 19:38:50 +00:00
Florian Rival
77d6f0310c Add support for using arbitrary URLs for images/sounds/resources in the web-app 2021-11-07 19:38:50 +00:00
Florian Rival
c73a5a046f Fix semaphore CI npm cache not working because of npm 7+ upgrading the package-lock.json
Using "npm ci" to allow clean installation without changes to the lock files.

Don't show in changelog
2021-11-04 11:48:04 +00:00
Florian Rival
c37e129a5b Implement support for the Debugger in the web-app
* One or more preview windows can be launched and used with the GDevelop Debugger, like on the desktop app.
  * To run the Debugger, click on the button next to the Play button in the toolbar and choose "Start Preview with Debugger and Performance Profiler"
* This is useful to inspect instances of objects, inspect internal messages or run the performance profiler.
* A right click on the Play button will also allow to launch a new preview, in a new window.
* Also fix the loading screen not shown in the preview on the web-app even when asked to be shown (using the game properties preview button)
2021-11-04 11:48:04 +00:00
Florian Rival
aeecb0e29f Revert Rectangle to Line in the Particle Emitter editor
Don't show in changelog
2021-11-02 10:26:57 +00:00
Tristan Rhodes
a6525e5617 Clarify names of particle types in the Particle Emitter editor (#3217) 2021-11-01 14:35:10 +00:00
Arthur Pacaud
f67aeedaeb Update esbuild (#3220)
Only show in developer changelog
2021-11-01 10:01:52 +00:00
Leo_Red
0c2f023c63 Fix contributors list (#3221) 2021-11-01 09:55:03 +00:00
Leo_Red
d6d4569dbf Update naming of events in the menus to make them easier to understand (#3212) 2021-10-29 14:47:09 +01:00
Florian Rival
965ec330cf Improve preview in the web-app so that it opens in a separate window using the size from the game properties 2021-10-28 22:51:27 +01:00
Arthur Pacaud
c09d29a959 Show the preview window, when corresponding preference is enabled, above the editor but not above all windows on the screen (#3203) 2021-10-28 20:11:03 +01:00
Florian Rival
67612009d1 Add CrazyGames.com in the links where to publish a HTML5 game (#3211) 2021-10-28 18:05:35 +01:00
Florian Rival
2da5194672 Fix crash in the Debugger
Don't show in the changelog
2021-10-28 17:32:42 +01:00
Florian Rival
7f5821a299 Fix user not always logged when opening export after relaunching the app (#3205)
* Also fix signup/login dialog closing before the signup/login is entirely finished
2021-10-27 15:43:39 +01:00
Florian Rival
a3fdeec6a7 Refactor gdjs.Logger to allow disabling specific log groups in the console (#3204)
* This reduces the logs during GDJS tests, as this was cluttering the
terminal.

Only show in developer changelog
2021-10-27 11:39:36 +01:00
Florian Rival
852ad1d92b Do not make the preview window always on top by default in preferences
Don't show in changelog
2021-10-25 22:30:21 +01:00
Florian Rival
8fdba503ab Fix potential crash in the scene editor 2021-10-25 22:21:17 +01:00
Florian Rival
50d7bec375 Bump newIDE version 2021-10-25 15:30:31 +01:00
Clément Pasteau
0c85e9bf30 Prevent sending session hits if the session is not correctly created 2021-10-25 15:58:30 +02:00
github-actions[bot]
08c41ece71 Update translations (#3197) 2021-10-25 14:27:17 +01:00
Aurélien Vivet
bd9fffba3f Remove unused test games from GDevelop 4 (#3192)
Don't show in changelog
2021-10-24 19:22:31 +01:00
github-actions[bot]
413caf6f62 Update translations (#3182) 2021-10-22 22:15:43 +01:00
Aurélien Vivet
530d0baffe Remove beta term from the docs (#3187)
Don't show in changelog
2021-10-22 22:13:14 +01:00
Aurélien Vivet
e78d2c6962 Add automation to automatically close stale issues (#3184)
Only show in developer changelog
2021-10-22 11:26:31 +01:00
Arthur Pacaud
bc606ed1be Add a console to the games debugger (#2770)
* When launching the Debugger to inspect a game, open the Console to see internal messages sent by the game, JavaScript code or the game engine.
* This is an advanced feature that is useful to find issues in your game or to see if your game is displaying any internal error.
2021-10-21 19:47:28 +01:00
Clément Pasteau
c705f89de8 Feature/external layout options (#3178)
Improve scene selection and help text for an external layout
2021-10-21 14:18:44 +02:00
github-actions[bot]
3b73b5eb6d Update translations 2021-10-21 12:40:29 +01:00
D8H
107410f0a4 Improve typing and simplify implementation of Linked Objects' getObjectsLinkedWith function (#3180)
Only show in developer changelog
2021-10-21 09:42:34 +01:00
D8H
b7b95d5e09 Add new tests for the platformer engine to prepare for the upcoming changes (#3176)
Only show in developer changelog
2021-10-20 15:40:01 +02:00
AlexandreS
a470e9b86c Fixes an issue with ghost instances of behaviors (light obstacle and pathfinder obstacle) 2021-10-20 10:45:01 +02:00
Clément Pasteau
cf5c8ae631 Improve Expression autocomplete for functions 2021-10-18 13:22:01 +02:00
AlexandreS
8f8ac2fd1e Show index of current focused result in Event Sheet 2021-10-15 09:34:48 +02:00
Clément Pasteau
cdac70425e Prevent debugger server being launched twice, causing a crash
Do not show in changelog
2021-10-14 16:58:46 +02:00
AlexandreS
378f0a48ad Add possibility to search in events sentences
* Better highlight of search results
* Remove highlights when closing the search panel
2021-10-14 15:52:30 +02:00
Clément Pasteau
e653639366 Improve Expression autocomplete (#3167)
Improve Expression Autocomplete to be more intuitive
2021-10-14 15:08:18 +02:00
Florian Rival
e105d4c9f6 Disable history when publishing the web-app to GitHub pages
Don't show in changelog
2021-10-11 16:43:33 +01:00
Florian Rival
5b80bed305 Fix crash happening when modifying "dead" instances after removing a layer and instances that were on it
Fix #3164
2021-10-11 16:39:35 +01:00
AlexandreS
a4ac323e63 Only show the operators that can be actually used in actions/conditions for strings (#3156)
* For strings/texts, only = and ≠ can be used for comparisons, and "set to"/"add to" for modifications.
2021-10-08 17:14:02 +01:00
Clément Pasteau
bc23d6a084 Fix effects being applied multiple times on objects when creating and destroying a lot of them (#3149)
* Effects from destroyed objects could be cleaned incorrectly and applied to new objects, creating glitches on newly created objects.
2021-10-08 16:14:23 +01:00
Florian Rival
2c24359fba Skip CI for Pull Request automatically open to update translations
We can always remove this tag before merging. This is to avoid doubling the usage of Travis/Semaphore CIs at every commit on master.

Don't show in changelog
2021-10-08 11:48:32 +01:00
Clément Pasteau
a6b01fc01d Add conditions for mouse key pressed from text 2021-10-08 12:15:35 +02:00
github-actions[bot]
44b81f52ea Update translations (#3154)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2021-10-07 23:06:41 +01:00
Florian Rival
cfdf13538e Automate update of translations (#3153)
* Automatically download and build latest translations from Crowdin at each commit on master, opening a PR with the changes (if any).
2021-10-07 22:58:50 +01:00
Florian Rival
7ee38a50bf Fix translations
Launched npm run compile-translations *after* launching npm run extract-all-translations, so that the English .po file was up-to-date to avoid missing translations in English language.

Don't show in changelog
2021-10-07 19:28:01 +01:00
Florian Rival
e2b8620b83 Fix translations
Don't show in changelog
2021-10-07 17:18:57 +01:00
AlexandreS
7ed8660edc Fix translations - Don't show in changelog 2021-10-07 18:10:18 +02:00
AlexandreS
75cc70368c Bump newIDE version and update translations 2021-10-07 16:38:19 +02:00
AlexandreS
0d3dfe5cf4 Display another user's profile when clicking their badge 2021-10-07 15:57:49 +02:00
Clément Pasteau
e7aa75bcd7 Add possibility to change one's email with confirmation 2021-10-07 13:28:44 +02:00
Clément Pasteau
c5ad127e83 Fix opening up the parameters dialog for object or behaviors expressions having no parameters 2021-10-07 12:28:27 +02:00
Clément Pasteau
acfdebfc0f Prevent calling a user update on every authState change
This fixes a race condition where the fetchUser would be called too early, causing a conflict with the signup function while creating the user in the database.

Do not show in changelog
2021-10-06 12:15:56 +02:00
Leo_Red
d3f8b410b0 Fix wording of "empty game" to "empty project" for consistency (#3136) 2021-10-06 09:04:53 +01:00
Florian Rival
4b7d67ce97 Fix very long object names overflowing or messing up some dialogs in the editor 2021-10-05 23:46:08 +01:00
Florian Rival
46a81ef4be Fix icons aligment and text ellipsis for long resource names in resources selector 2021-10-05 23:46:08 +01:00
Florian Rival
fe2812b8e8 Update caching by service worker of libGD.js/wasm
Don't show in changelog
2021-10-05 23:46:08 +01:00
Clément Pasteau
042cf49b3b Allow user to verify their email (#3132)
Allow users to verify their email
2021-10-05 18:23:29 +02:00
Florian Rival
7cf334ad1c Fix Edit My Profile and Logout buttons not working anymore
This was because of a stale "state" used by React. Fix by using the functional version of setState.

Also fix some Flow types that are not stricts.

Don't show in changelog
2021-10-04 17:47:10 +01:00
AlexandreS
f999bee387 Fix loss of user login between IDE refreshes 2021-10-04 17:09:43 +02:00
AlexandreS
0627d4b865 Show user's bio on My Profile window 2021-10-04 16:12:22 +02:00
Clément Pasteau
92e6a5e67f Improve user autocomplete (#3129)
Prevent saving projects and extensions with non-existing authors
2021-10-04 11:44:40 +02:00
Aurélien Vivet
d980400c2b Add "FontSize" expression for Text objects (#2974) 2021-10-03 15:50:30 +01:00
MechanicalPen
e235694fac Fix conditions comparing Dialogue Tree variables to work even if the dialogue is not running (#3127) 2021-10-03 12:07:33 +01:00
AlexandreS
cdf00d10f1 Give possibility to enter one's bio 2021-10-01 17:07:40 +02:00
Clément Pasteau
218520b836 Update translations (#3124) 2021-10-01 15:56:10 +01:00
Clément Pasteau
3ce71813ba Bump newIDE version (#3122) 2021-10-01 14:03:39 +01:00
Clément Pasteau
90300f895c Validate the username format in the create & edit forms 2021-10-01 14:56:14 +02:00
Florian Rival
9c8aa57fb6 Fix alignment of a background text in profile dialog
Don't show in changelog
2021-10-01 13:50:03 +01:00
Clément Pasteau
84876a1dff Display contributions (examples & extensions) on the user profile (#3110)
Display contributions (examples & extensions) on the user profile
2021-10-01 13:27:03 +02:00
Florian Rival
19ef8742f0 Extract translation sources and upload them to Crowdin at each commit on master (#3114)
* Extract but without upload for other branches

Don't show in changelog
2021-10-01 11:25:36 +01:00
Clément Pasteau
567efafa70 Display error if username is not valid 2021-10-01 11:41:59 +02:00
AlexandreS
c70685ccc7 Improve changelog extraction
Don't show in changelog
2021-09-30 17:10:15 +02:00
ClementPasteau
ec8daa7d8d Change profile.id calls to firebaseUser.id (#3112)
This ensures we don't prevent an action because the profile is
not fetched yet

Don't show in changelog
2021-09-30 12:32:43 +01:00
Florian Rival
0d817f4dae Fix warning
Don't show in changelog
2021-09-30 10:44:14 +01:00
AlexandreS
9dbbaada01 Update translations 2021-09-30 10:10:37 +01:00
AlexandreSi
342a6dc56f Bump newIDE version 2021-09-30 09:24:17 +01:00
Florian Rival
baae910fe8 Remove useless postfixing of the libGD.js/wasm files
Don't show in changelog
2021-09-30 09:06:38 +01:00
Florian Rival
2c6d30b28e Fix warning
Don't show in changelog
2021-09-29 22:49:35 +01:00
AlexandreS
9321f0ec7c Do not open expression parameters dialog if no parameters to configure 2021-09-29 18:23:46 +02:00
AlexandreS
e463f352b7 Add several UI improvements on the IDE 2021-09-29 18:16:47 +02:00
AlexandreS
5e3430aea5 Don't show in changelog - Remove unused #ifdef 2021-09-29 16:26:03 +02:00
AlexandreS
2d899d7c52 Add possibility to start and stop particle emitter 2021-09-29 09:25:03 +02:00
Florian Rival
0b933a569e Fix Bitmap Text objects crashing the editor if the font was renamed in resources 2021-09-24 20:24:06 +01:00
Florian Rival
4904e7e7fb Fix crash at game loading when a resource file was not found 2021-09-24 19:23:55 +01:00
Florian Rival
bfb1b6b15d Fix crash when moving a parameter of a function of an extension (or a custom behavior) after adding it
Fix #3093, fix #2729
2021-09-24 18:12:35 +01:00
ClementPasteau
9364a485cd Fix Folder being set for both Examples and Starters 2021-09-24 17:02:28 +02:00
ClementPasteau
176a2a0b47 Modify calls to Example & Extension with new endpoints
Do not show in changelog
2021-09-24 16:42:28 +02:00
AlexandreS
a10c9362dd Add condition to test if platformer is on a given platform 2021-09-24 14:15:06 +02:00
AlexandreS
3a0f55ee1b Fix windows notifications title 2021-09-24 14:01:54 +02:00
ClementPasteau
0b6bddc5a4 Feature/display authors on examples (#3090)
Display Examples authors on the search list and the details popup
2021-09-24 13:55:18 +02:00
Florian Rival
bf910e0cba Fix Scan for Images/Fonts/Audios in the resources editor wrongly adding resources with files already in other resources
* This could be visible when using the asset store or resources that have been renamed.
2021-09-24 00:09:32 +01:00
Florian Rival
763d8e8175 Fix renaming resources breaking objects or events using it
* Also fix the Bitmap Font field not shown in the parameters of the action to change it.
2021-09-24 00:09:32 +01:00
D8H
135ba2b4df Add expressions for properties of type "color" for custom behaviors (#3086) 2021-09-23 17:39:55 +01:00
ClementPasteau
d4a3722ec8 Fix authorIds being saved in the prop elements of a project (#3084) 2021-09-23 13:01:35 +00:00
ClementPasteau
477e88d4ce Allow entering the authors usernames in the Project properties 2021-09-23 12:46:31 +02:00
AlexandreS
023ed8f7b5 Add possibility to zoom in or out on events sheet 2021-09-23 09:37:33 +02:00
Florian Rival
55020a3d15 Fix the deployed storybook not properly loading libGD.js
Don't show in changelog
2021-09-22 17:24:07 +01:00
Florian Rival
552219e48f Add CI to deploy automatically the Storybook for each commit (#3081)
* Useful to quickly test components without having to fetch and build locally a branch.
* Can optionally be deployed to Chromatic if the GitHub action is launched manually.

Only show in developer changelog
2021-09-22 16:58:47 +01:00
ClementPasteau
4dfac41d81 Improve userAutocomplete help text (#3079) 2021-09-22 17:28:52 +02:00
Florian Rival
33deca92e3 Upgrade to Storybook v6 (#3078)
* Add a dropdown to choose the theme in the stories, useful to quickly check a component with all themes.
* Add support for build-storybook (to build a static version of the Storybook). Will be useful to share the storybook on PRs later or even do visual regression testing.

Test: yarn && yarn start, yarn storybook and yarn build-storybook works
Test: npm i && npm start, npm run storybook and npm run build-storybook works
Test: npm run build in newIDE/app works

Only show in developer changelog
2021-09-22 16:10:34 +01:00
AlexandreS
adc7584981 Fix particle emitter unused parameter friction removing it 2021-09-22 13:13:13 +02:00
ClementPasteau
23d5296a52 Feature/authors extension autocomplete (#3073)
Allow authors to be specified when creating an Extension
2021-09-22 12:57:01 +02:00
AlexandreS
2febbf439f Fix some particle emitter actions and conditions 2021-09-22 10:49:47 +02:00
D8H
169a49a246 Optimize Linked Objects actions/conditions, up to 80% faster on games using it extensively with a lot of objects (#3022) 2021-09-21 22:21:16 +01:00
ClementPasteau
c8c4322ece Feature/display extension username (#3056)
Display Authors on Extensions list and details popup
2021-09-20 09:16:14 +02:00
Florian Rival
349703e287 Add a test checking that groups are working correctly inside functions.
Don't show in changelog
2021-09-19 21:43:35 +01:00
D8H
1326ffd3b6 Fix regression in Draggable behavior (objects could be dragged only vertically or horizontally) (#3068)
Don't show in changelog
2021-09-19 12:42:47 +01:00
Florian Rival
baff4d3cb0 Fix long description fields for parameters shown when changing from one function to another after adding a long description
Fix #3067
2021-09-19 10:09:28 +01:00
D8H
b40e2d3fdf Improve Draggable behavior to drag the frontmost object under the cursor/touch (#3066)
* In previous versions, the order was not guaranteed, which could result in a non intuitive result for the player.
2021-09-19 01:36:49 +01:00
Florian Rival
8e6ba3abce Add autocompletion for effect and effect parameter names in actions and conditions (#3060) 2021-09-19 00:36:39 +01:00
D8H
aaebbe47d5 Improve the Draggable behavior to reduce memory usage. (#3064) 2021-09-19 00:25:10 +01:00
AlexandreS
a51003040c Set the default dimensions of a new collision mask to the dimensions of the sprite 2021-09-16 16:48:08 +02:00
Florian Rival
cfce635419 Add automatic addition of extensions, examples and assets commits in the changelog (#3051)
* Also ensure scripts are type checked.

Don't show in changelog
2021-09-16 14:49:03 +01:00
Florian Rival
3d299c5a14 Add back debug task to launch the editor in Chrome (and debug from VSCode) [skip ci]
Don't show in changelog
2021-09-16 09:48:48 +01:00
AlexandreS
f33196dc85 Fix unexpected custom loading screen image removal when removing unused images 2021-09-15 17:06:25 +02:00
AlexandreS
7b2dc2223c Merge pull request #3045 from 4ian/tween-object-color-parameter-type
Add color picker to Add Object Color Tween action 'To color' parameter
2021-09-14 11:48:01 +02:00
AlexandreSi
b37d05f78c Add color picker to Add Object Color Tween action 'To color' parameter 2021-09-14 11:11:52 +02:00
Florian Rival
dcba0b45a6 Remove an ignored (though innocuous) clang warning 2021-09-13 20:29:24 +02:00
Florian Rival
f2ec46ca1e Enable more warnings from Clang when compiling C++ sources (#3042)
Only show in developer changelog
2021-09-13 20:02:03 +02:00
ClementPasteau
5c33e9e8d0 Allow user to enter their username when registering (#3024)
Allow user to add their username when registering
2021-09-13 18:06:16 +02:00
ClementPasteau
fc23517bae Upgrade Pixi.js to 6.1.2 (#3026) 2021-09-13 17:45:27 +02:00
Florian Rival
24c74af79b Fix effects of objects not always working, depending on layers/object position in the list 2021-09-13 13:35:26 +02:00
Florian Rival
2a19ea5182 Bump newIDE version 2021-09-09 12:30:40 +01:00
Florian Rival
705c7af134 Improve typings in the extract changelog script
Don't show in changelog
2021-09-09 12:25:25 +01:00
Florian Rival
028eebefab Fix the regression when using Linked Objects in behaviors by temporarily removing the optimization (#3023) 2021-09-09 12:23:35 +01:00
541 changed files with 36265 additions and 34601 deletions

15
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# Automatically close issues with certain tags indicating that we need more information,
# after some days have passed.
daysUntilStale: 20
daysUntilClose: 7
# Only do this on tags implying we need more information:
onlyLabels: ["Need a game/precise steps to reproduce the issue","👋 Needs confirmation/testing"]
only: issues
markComment: >
This issue seems to be stale: it needs additional information but it has not had
recent activity. It will be closed in 7 days if no further activity occurs. Thank you
for your contributions.

71
.github/workflows/build-storybook.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
# GitHub Action to build the Storybook of the editor and publish it for testing.
#
# Note that only the Storybook is built and GDevelop.js is not rebuilt (for speed concerns),
# so changes in the C++ source could not be reflected if the CI run by Travis-CI
# did not upload a freshly built GDevelop.js.
name: Build Storybook
on:
# Launch on all commits.
push:
# Allows to run this workflow manually from the Actions tab,
# to publish on Chromatic (not done by default).
workflow_dispatch:
jobs:
build-storybook:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.BUILD_STORYBOOK_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.BUILD_STORYBOOK_AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- uses: actions/checkout@v2
with:
fetch-depth: 50
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Install newIDE dependencies
run: npm install
working-directory: newIDE/app
- name: Build Storybook
run: npm run build-storybook
working-directory: newIDE/app
# Publish on S3 to allow quick testing of components.
- name: Publish Storybook to S3 bucket (specific commit)
run: aws s3 sync ./build-storybook/ s3://gdevelop-storybook/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/ --delete
working-directory: newIDE/app
- name: Publish Storybook to S3 bucket (latest)
run: aws s3 sync ./build-storybook/ s3://gdevelop-storybook/$(git rev-parse --abbrev-ref HEAD)/latest/ --delete
working-directory: newIDE/app
- name: Log urls to the Storybook
run: |
echo "Find the latest Storybook for this branch on http://gdevelop-storybook.s3-website-us-east-1.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/latest/index.html"
echo "Find the Storybook for this commit on http://gdevelop-storybook.s3-website-us-east-1.amazonaws.com/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/index.html"
# Publish on Chromatic, only when manually launched (too costly to run on every commit).
- name: Publish Storybook to Chromatic
if: github.event_name == 'workflow_dispatch'
uses: chromaui/action@v1
with:
workingDir: newIDE/app
storybookBuildDir: "build-storybook"
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

View File

@@ -0,0 +1,47 @@
# GitHub Action to extract translations and (later) upload them to Crowdin.
name: Extract translations
on:
# Execute for all commits (to ensure translations extraction works)
push:
# Allows to run this workflow manually from the Actions tab.
workflow_dispatch:
jobs:
extract-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Install gettext
run: sudo apt update && sudo apt install gettext -y
- name: Install newIDE dependencies
run: npm install
working-directory: newIDE/app
- name: Extract translations
run: npm run extract-all-translations
working-directory: newIDE/app
# Only upload on Crowdin for the master branch
- name: Install Crowdin CLI
if: github.ref == 'refs/heads/master'
run: npm i -g @crowdin/cli
- name: Upload translations to Crowdin
run: crowdin upload sources
if: github.ref == 'refs/heads/master'
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -0,0 +1,73 @@
# GitHub Action to update translations by downloading them from Crowdin,
# and open a Pull Request with the changes.
name: Update translations
on:
# Execute only on master
push:
branches:
- master
# Allows to run this workflow manually from the Actions tab.
workflow_dispatch:
jobs:
update-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Install gettext
run: sudo apt update && sudo apt install gettext -y
- name: Install newIDE dependencies
run: npm install
working-directory: newIDE/app
# We need to extract translations first to make sure all the source strings
# are included in the English catalogs. Otherwise, missing source strings
# with parameters (like "My name is {0}.") would be shown as-is when
# the app is built (but not in development - unclear why, LinguiJS issue?).
- name: Extract translations
run: npm run extract-all-translations
working-directory: newIDE/app
# (Build and) download the most recent translations (PO files) from Crowdin.
- name: Install Crowdin CLI
run: npm i -g @crowdin/cli
- name: Download new translations from Crowdin
run: crowdin download
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
# Seems like the three letters code is not handled properly by LinguiJS?
# Do without this language while we find a solution.
- name: Remove catalogs not handled properly by LinguiJS compile command.
run: rm -rf newIDE/app/src/locales/pcm_NG/
- name: Compile translations into .js files that are read by LinguiJS
run: npm run compile-translations
working-directory: newIDE/app
- name: Create a Pull Request with the changes
uses: peter-evans/create-pull-request@v3.10.1
with:
commit-message: Update translations [skip ci]
branch: chore/update-translations
delete-branch: true
title: '[Auto PR] Update translations'
body: |
This updates the translations by downloading them from Crowdin and compiling them for usage by the app.
Please double check the values in `newIDE/app/src/locales/LocalesMetadata.js` to ensure the changes are sensible.

View File

@@ -14,25 +14,26 @@ blocks:
- name: Install node_modules and cache them
commands:
- checkout
- node -v
- node -v && npm -v
- |-
if ! cache has_key newIDE-app-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum newIDE/app/package-lock.json); then
cd newIDE/app
npm i
npm ci
cd ../..
cache store newIDE-app-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum newIDE/app/package-lock.json) newIDE/app/node_modules
fi
- |-
if ! cache has_key GDJS-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/package-lock.json); then
cd GDJS
npm i
git checkout package-lock.json # Ensure no changes was made by newIDE post-install tasks.
npm ci
cd ..
cache store GDJS-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/package-lock.json) GDJS/node_modules
fi
- |-
if ! cache has_key GDJS-tests-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/tests/package-lock.json); then
cd GDJS/tests
npm i
npm ci
cd ../..
cache store GDJS-tests-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/tests/package-lock.json) GDJS/tests/node_modules
fi
@@ -50,6 +51,7 @@ blocks:
- cd newIDE/app
- npm run postinstall
- npm run flow
- npm run check-script-types
- cd ../..
- name: GDJS typing and documentation generation
commands:

View File

@@ -102,6 +102,7 @@ script:
- npm test
- npm run flow
- npm run check-format
- npm run check-script-types
- cd ../..
# GDJS tests:
- cd GDJS

29
.vscode/launch.json vendored
View File

@@ -4,13 +4,36 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "GDevelop.js Jest tests (all)",
"program": "${workspaceFolder}/GDevelop.js/node_modules/.bin/jest",
"args": ["--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"cwd": "${workspaceFolder}/GDevelop.js"
},
{
"type": "node",
"request": "launch",
"name": "GDevelop.js Jest tests (current file)",
"program": "${workspaceFolder}/GDevelop.js/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"cwd": "${workspaceFolder}/GDevelop.js"
},
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"name": "Debug with Chrome (web-app, local development server)",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}",
"preLaunchTask": "Start development server"
"webRoot": "${workspaceFolder}"
}
]
}

2
.vscode/tasks.json vendored
View File

@@ -80,7 +80,7 @@
},
{
"type": "typescript",
"tsconfig": "GDJS/tsconfig.json",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": ["$tsc-watch"],
"group": "test",

View File

@@ -47,7 +47,6 @@ IF(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
ENDIF()
#Sanity checks
IF ("${CMAKE_BUILD_TYPE}" STREQUAL "")
message( "CMAKE_BUILD_TYPE is empty, assuming build type is Release" )
@@ -72,6 +71,13 @@ endif()
# Mark some warnings as errors
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Activate as much warnings as possible to avoid errors like
# uninitialized variables or other hard to debug bugs.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder-ctor -Wno-reorder -Wno-pessimizing-move -Wno-unused-variable -Wno-unused-private-field")
# Make as much warnings considered as errors as possible (only one for now).
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-stack-address")
endif()

View File

@@ -37,7 +37,7 @@ void ExposeProjectEffects(
}
}
for (std::size_t i; i < layout.GetObjectsCount(); i++) {
for (std::size_t i = 0; i < layout.GetObjectsCount(); i++) {
auto& object = layout.GetObject(i);
auto& effects = object.GetEffects();
for (std::size_t e = 0; e < effects.GetEffectsCount(); e++) {

View File

@@ -621,8 +621,14 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = GenerateGetBehaviorNameCode(parameter);
} else if (metadata.type == "key") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
} else if (metadata.type == "password" || // Deprecated
metadata.type ==
"musicfile" || // Should be renamed "largeAudioResource"
metadata.type ==
"soundfile" || // Should be renamed "audioResource"
metadata.type == "police" || // Should be renamed "fontResource"
metadata.type == "bitmapFontResource" ||
metadata.type == "imageResource") {
argOutput = "\"" + ConvertToString(parameter) + "\"";
} else if (metadata.type == "mouse") {
argOutput = "\"" + ConvertToString(parameter) + "\"";

View File

@@ -67,13 +67,13 @@ class GD_CORE_API ExpressionParser2 {
}
/**
* Given an object name (or empty if none) and a behavior name (or empty if none),
* return the index of the first parameter that is inside the parenthesis:
* 0, 1 or 2.
*
* For example, in an expression like `Object.MyBehavior::Method("hello")`, the
* parameter "hello" is the second parameter (the first being by convention Object,
* and the second MyBehavior, also by convention).
* Given an object name (or empty if none) and a behavior name (or empty if
* none), return the index of the first parameter that is inside the
* parenthesis: 0, 1 or 2.
*
* For example, in an expression like `Object.MyBehavior::Method("hello")`,
* the parameter "hello" is the second parameter (the first being by
* convention Object, and the second MyBehavior, also by convention).
*/
static size_t WrittenParametersFirstIndex(const gd::String &objectName,
const gd::String &behaviorName) {
@@ -403,9 +403,18 @@ class GD_CORE_API ExpressionParser2 {
const gd::ExpressionMetadata &metadata =
MetadataProvider::GetAnyExpressionMetadata(platform, functionFullName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions to
// be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode = Parameters(metadata.parameters);
auto function =
gd::make_unique<FunctionCallNode>(metadata.GetReturnType(),
gd::make_unique<FunctionCallNode>(returnType,
std::move(parametersNode.parameters),
metadata,
functionFullName);
@@ -458,9 +467,18 @@ class GD_CORE_API ExpressionParser2 {
MetadataProvider::GetObjectAnyExpressionMetadata(
platform, objectType, objectFunctionOrBehaviorName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions
// to be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode = Parameters(metadata.parameters, objectName);
auto function = gd::make_unique<FunctionCallNode>(
metadata.GetReturnType(),
returnType,
objectName,
std::move(parametersNode.parameters),
metadata,
@@ -520,10 +538,19 @@ class GD_CORE_API ExpressionParser2 {
MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, functionName);
// In case we can't find a valid expression, ensure the node has the type
// that is requested by the parent, so we avoid putting "unknown" (which
// would be also correct, but less precise and would prevent completions
// to be shown to the user)
const gd::String returnType =
gd::MetadataProvider::IsBadExpressionMetadata(metadata) == true
? type
: metadata.GetReturnType();
auto parametersNode =
Parameters(metadata.parameters, objectName, behaviorName);
auto function = gd::make_unique<FunctionCallNode>(
metadata.GetReturnType(),
returnType,
objectName,
behaviorName,
std::move(parametersNode.parameters),

View File

@@ -43,6 +43,7 @@ struct ExpressionParserLocation {
* \brief A diagnostic that can be attached to a gd::ExpressionNode.
*/
struct ExpressionParserDiagnostic {
virtual ~ExpressionParserDiagnostic() = default;
virtual bool IsError() { return false; }
virtual const gd::String &GetMessage() { return noMessage; }
virtual size_t GetStartPosition() { return 0; }

View File

@@ -7,21 +7,21 @@
#define GDCORE_EXPRESSIONPARSER2NODEWORKER_H
namespace gd {
class ExpressionNode;
class SubExpressionNode;
class OperatorNode;
class UnaryOperatorNode;
class NumberNode;
class TextNode;
class VariableNode;
class VariableAccessorNode;
class VariableBracketAccessorNode;
class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
class IdentifierNode;
class FunctionCallOrObjectFunctionNameOrEmptyNode;
class ObjectFunctionNameNode;
class FunctionCallNode;
class EmptyNode;
struct ExpressionNode;
struct SubExpressionNode;
struct OperatorNode;
struct UnaryOperatorNode;
struct NumberNode;
struct TextNode;
struct VariableNode;
struct VariableAccessorNode;
struct VariableBracketAccessorNode;
struct IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
struct IdentifierNode;
struct FunctionCallOrObjectFunctionNameOrEmptyNode;
struct ObjectFunctionNameNode;
struct FunctionCallNode;
struct EmptyNode;
} // namespace gd
namespace gd {
@@ -34,21 +34,21 @@ namespace gd {
* \see gd::ExpressionNode
*/
class GD_CORE_API ExpressionParser2NodeWorker {
friend class ExpressionNode;
friend class SubExpressionNode;
friend class OperatorNode;
friend class UnaryOperatorNode;
friend class NumberNode;
friend class TextNode;
friend class VariableNode;
friend class VariableAccessorNode;
friend class VariableBracketAccessorNode;
friend class IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
friend class IdentifierNode;
friend class FunctionCallOrObjectFunctionNameOrEmptyNode;
friend class ObjectFunctionNameNode;
friend class FunctionCallNode;
friend class EmptyNode;
friend struct ExpressionNode;
friend struct SubExpressionNode;
friend struct OperatorNode;
friend struct UnaryOperatorNode;
friend struct NumberNode;
friend struct TextNode;
friend struct VariableNode;
friend struct VariableAccessorNode;
friend struct VariableBracketAccessorNode;
friend struct IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode;
friend struct IdentifierNode;
friend struct FunctionCallOrObjectFunctionNameOrEmptyNode;
friend struct ObjectFunctionNameNode;
friend struct FunctionCallNode;
friend struct EmptyNode;
public:
virtual ~ExpressionParser2NodeWorker();

View File

@@ -18,7 +18,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"Florian Rival",
"Open source (MIT License)");
#if defined(GD_IDE_ONLY)
extension
.AddCondition("Toujours",
_("Always"),
@@ -104,7 +103,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
_("Functions"),
"res/function16.png")
.AddParameter("string", "Parameter name");
#endif
}
} // namespace gd

View File

@@ -21,7 +21,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/audio");
#if defined(GD_IDE_ONLY)
extension
.AddAction("PlaySoundCanal",
_("Play a sound on a channel"),
@@ -579,7 +578,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
"res/conditions/volume.png")
.AddCodeOnlyParameter("currentScene", "");
#endif
}
} // namespace gd

View File

@@ -25,7 +25,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
gd::ObjectMetadata& obj = extension.AddObject<gd::Object>(
"", _("Base object"), _("Base object"), "res/objeticon24.png");
#if defined(GD_IDE_ONLY)
obj.AddCondition("PosX",
_("X position"),
_("Compare the X position of the object."),
@@ -84,9 +83,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("operator", _("Modification's sign"), "number")
.AddParameter("expression", _("X position"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("operator", _("Modification's sign"), "number")
.AddParameter("expression", _("Y position"))
.MarkAsSimple();
@@ -99,9 +98,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/position24.png",
"res/actions/position.png")
.AddParameter("object", _("Object"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("operator", _("Modification's sign"), "number")
.AddParameter("expression", _("X position"))
.AddParameter("operator", _("Modification's sign"))
.AddParameter("operator", _("Modification's sign"), "number")
.AddParameter("expression", _("Y position"))
.MarkAsSimple();
@@ -1080,7 +1079,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/effect24.png",
"res/actions/effect.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Effect Name"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("yesorno", _("Enable?"))
.MarkAsSimple();
@@ -1094,8 +1093,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/effect24.png",
"res/actions/effect.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Effect Name"))
.AddParameter("string", _("Parameter name"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("expression", _("New value"))
.MarkAsSimple();
@@ -1109,8 +1108,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/effect24.png",
"res/actions/effect.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Effect Name"))
.AddParameter("string", _("Parameter name"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("string", _("New value"))
.MarkAsSimple();
@@ -1124,8 +1123,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/effect24.png",
"res/actions/effect.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Effect Name"))
.AddParameter("string", _("Parameter Name"))
.AddParameter("objectEffectName", _("Effect name"))
.AddParameter("objectEffectParameterName", _("Parameter name"))
.AddParameter("yesorno", _("Enable?"))
.MarkAsSimple();
@@ -1137,7 +1136,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/effect24.png",
"res/actions/effect.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Effect Name"))
.AddParameter("objectEffectName", _("Effect name"))
.MarkAsSimple();
extension
@@ -1441,7 +1440,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Objects"),
"res/actions/layer.png")
.AddParameter("object", _("Object"));
#endif
}
} // namespace gd

View File

@@ -24,7 +24,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/interface/scene-editor/layers-and-cameras");
#if defined(GD_IDE_ONLY)
extension
.AddExpressionAndConditionAndAction(
"number",
@@ -342,8 +341,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("string", _("Parameter name"))
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("expression", _("New value"))
.MarkAsAdvanced();
@@ -361,8 +360,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("string", _("Parameter name"))
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("string", _("New value"))
.MarkAsAdvanced();
@@ -380,8 +379,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("string", _("Parameter name"))
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("layerEffectParameterName", _("Parameter name"))
.AddParameter("yesorno", _("Enable this parameter"))
.MarkAsAdvanced();
@@ -396,7 +395,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("layerEffectName", _("Effect name"))
.MarkAsAdvanced();
extension
@@ -410,7 +409,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
.SetDefaultValue("\"\"")
.AddParameter("string", _("Effect"))
.AddParameter("layerEffectName", _("Effect name"))
.AddParameter("yesorno", _("Enable"), "", true)
.MarkAsAdvanced();
@@ -567,7 +566,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"res/actions/camera.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("layer", _("Layer"));
#endif
}
} // namespace gd

View File

@@ -21,7 +21,6 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/common-conversions");
#if defined(GD_IDE_ONLY)
extension
.AddExpression("ToNumber",
@@ -65,7 +64,6 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
_("Conversion"),
"res/conditions/toujours24.png")
.AddParameter("expression", _("Angle, in radians"));
#endif
}
} // namespace gd

View File

@@ -5,7 +5,6 @@
*/
#include "AllBuiltinExtensions.h"
#include "GDCore/Tools/Localization.h"
#if defined(GD_IDE_ONLY)
#include "GDCore/Events/Builtin/CommentEvent.h"
#include "GDCore/Events/Builtin/ForEachChildVariableEvent.h"
#include "GDCore/Events/Builtin/ForEachEvent.h"
@@ -15,7 +14,6 @@
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Builtin/WhileEvent.h"
#include "GDCore/Events/Event.h"
#endif
using namespace std;
namespace gd {
@@ -33,7 +31,6 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/advanced-conditions");
#if defined(GD_IDE_ONLY)
extension
.AddCondition("Or",
_("Or"),
@@ -86,29 +83,29 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
std::make_shared<gd::StandardEvent>());
extension.AddEvent("Link",
_("Link"),
_("Link to some external events"),
_("Link external events"),
_("Link to external events."),
"",
"res/lienaddicon.png",
std::make_shared<gd::LinkEvent>());
extension.AddEvent("Comment",
_("Comment"),
_("Event displaying a text in the events editor"),
_("Event displaying a text in the events editor."),
"",
"res/comment.png",
std::make_shared<gd::CommentEvent>());
extension.AddEvent("While",
_("While"),
_("The event is repeated while the conditions are true"),
_("Repeat the event while the conditions are true."),
"",
"res/while.png",
std::make_shared<gd::WhileEvent>());
extension.AddEvent("Repeat",
_("Repeat"),
_("Event repeated a number of times"),
_("Repeat the event for a specified number of times."),
"",
"res/repeat.png",
std::make_shared<gd::RepeatEvent>());
@@ -129,12 +126,11 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
std::make_shared<gd::ForEachChildVariableEvent>());
extension.AddEvent("Group",
_("Group"),
_("Group containing events"),
_("Event group"),
_("Group containing events."),
"",
"res/foreach.png",
std::make_shared<gd::GroupEvent>());
#endif
}
} // namespace gd

View File

@@ -21,7 +21,6 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/interface/scene-editor/external-layouts");
#if defined(GD_IDE_ONLY)
extension
.AddAction("CreateObjectsFromExternalLayout",
_("Create objects from an external layout"),
@@ -37,7 +36,6 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
.AddParameter("expression", _("Y position of the origin"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
#endif
}
} // namespace gd

View File

@@ -22,7 +22,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/storage");
#if defined(GD_IDE_ONLY)
extension
.AddCondition(
"GroupExists",
@@ -194,7 +193,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
"res/actions/launchFile.png")
.AddParameter("string", _("Command"))
.MarkAsAdvanced();
#endif
}
} // namespace gd

View File

@@ -23,11 +23,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/keyboard");
#if defined(GD_IDE_ONLY)
extension
.AddCondition("KeyPressed",
_("Key pressed"),
_("Test if a key is pressed"),
_("Check if a key is pressed"),
_("_PARAM1_ key is pressed"),
_("Keyboard"),
"res/conditions/keyboard24.png",
@@ -38,7 +37,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
extension
.AddCondition("KeyReleased",
_("Key released"),
_("Test if a key was just released"),
_("Check if a key was just released"),
_("_PARAM1_ key is released"),
_("Keyboard"),
"res/conditions/keyboard24.png",
@@ -49,33 +48,33 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
extension
.AddCondition("KeyFromTextPressed",
_("Key pressed (text expression)"),
_("Test if a key, retrieved from the result of the "
_("Check if a key, retrieved from the result of the "
"expression, is pressed"),
_("_PARAM1_ key is pressed"),
_("Keyboard"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to test"))
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
extension
.AddCondition("KeyFromTextReleased",
_("Key released (text expression)"),
_("Test if a key, retrieved from the result of the "
_("Check if a key, retrieved from the result of the "
"expression, was just released"),
_("_PARAM1_ key is released"),
_("Keyboard"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to test"))
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
extension
.AddCondition("AnyKeyPressed",
_("Any key pressed"),
_("Test if any key is pressed"),
_("Check if any key is pressed"),
_("Any key is pressed"),
_("Keyboard"),
"res/conditions/keyboard24.png",
@@ -85,7 +84,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
extension
.AddCondition("AnyKeyReleased",
_("Any key released"),
_("Test if any key is released"),
_("Check if any key is released"),
_("Any key is released"),
_("Keyboard"),
"res/conditions/keyboard24.png",
@@ -100,7 +99,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
_("Keyboard"),
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "");
#endif
}
} // namespace gd

View File

@@ -19,7 +19,6 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
"Florian Rival",
"Open source (MIT License)");
#if defined(GD_IDE_ONLY)
extension
.AddExpression("normalize",
@@ -403,7 +402,6 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
.AddParameter("expression", _("Angle, in degrees"))
.AddParameter("expression", _("Distance"));
#endif
}
} // namespace gd

View File

@@ -25,7 +25,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/mouse-touch");
#if defined(GD_IDE_ONLY)
extension
.AddCondition(
"IsMouseWheelScrollingUp",
@@ -217,6 +216,39 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddParameter("mouse", _("Button to check"))
.MarkAsSimple();
extension
.AddCondition(
"MouseButtonFromTextPressed",
_("Mouse button pressed or touch held (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, is pressed."),
_("_PARAM1_ mouse button is pressed"),
_("Mouse and touch"),
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the button to check"))
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
extension
.AddCondition(
"MouseButtonFromTextReleased",
_("Mouse button released (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, was just released."),
_("_PARAM1_ mouse button is released"),
_("Mouse and touch"),
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string",
_("Expression generating the mouse button to check"))
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
extension
.AddExpressionAndCondition("number",
"TouchX",
@@ -302,8 +334,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
_("Mouse and touch/Multitouch"),
"res/conditions/touch.png")
.AddCodeOnlyParameter("currentScene", "");
#endif
}
} // namespace gd

View File

@@ -21,7 +21,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/network");
#if defined(GD_IDE_ONLY)
extension
.AddAction(
"SendRequest",
@@ -196,7 +195,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
"res/conditions/toujours24.png")
.AddParameter("objectPtr", _("The object with the variable"))
.AddParameter("objectvar", _("The object variable to be stringified"));
#endif
}
} // namespace gd

View File

@@ -20,7 +20,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
#if defined(GD_IDE_ONLY)
extension
.AddExpression("Random",
@@ -196,7 +195,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
"res/conditions/egal.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddParameter("expression", _("First expression"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("relationalOperator", _("Sign of the test"), "number")
.AddParameter("expression", _("Second expression"))
.MarkAsAdvanced();
@@ -210,10 +209,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
"res/conditions/egal.png")
.SetHelpPath("/all-features/advanced-conditions")
.AddParameter("string", _("First string expression"))
.AddParameter("relationalOperator", _("Sign of the test"))
.AddParameter("relationalOperator", _("Sign of the test"), "string")
.AddParameter("string", _("Second string expression"))
.MarkAsAdvanced();
#endif
}
} // namespace gd

View File

@@ -75,11 +75,9 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
SetTimeBetweenFrames(
element.GetDoubleAttribute("timeBetweenFrames", 1, "tempsEntre"));
SetLoop(element.GetBoolAttribute("looping", false, "boucle"));
#if defined(GD_IDE_ONLY)
SetMetadata(element.HasAttribute("metadata") || element.HasChild("metadata")
? element.GetStringAttribute("metadata")
: "");
#endif
const gd::SerializerElement& spritesElement =
element.GetChild("sprites", 0, "Sprites");
@@ -135,7 +133,6 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
}
};
#if defined(GD_IDE_ONLY)
void SavePoint(const Point& point, gd::SerializerElement& element) {
element.SetAttribute("name", point.GetName());
element.SetAttribute("x", point.GetX());
@@ -190,6 +187,5 @@ void Direction::SerializeTo(gd::SerializerElement& element) const {
if (!GetMetadata().empty()) element.SetAttribute("metadata", GetMetadata());
SaveSpritesDirection(sprites, element.AddChild("sprites"));
}
#endif
} // namespace gd

View File

@@ -113,7 +113,6 @@ class GD_CORE_API Direction {
*/
void MoveSprite(std::size_t oldIndex, std::size_t newIndex);
#if defined(GD_IDE_ONLY)
/**
* \brief Set the metadata (any string) associated to the Direction.
* \note Can be used by external editors to store extra information.
@@ -124,20 +123,15 @@ class GD_CORE_API Direction {
* \brief Return the (optional) metadata associated to the Direction.
*/
virtual const gd::String& GetMetadata() const { return metadata; }
#endif
void UnserializeFrom(const gd::SerializerElement& element);
#if defined(GD_IDE_ONLY)
void SerializeTo(gd::SerializerElement& element) const;
#endif
private:
bool loop; ///< true if the animation must loop.
double timeBetweenFrame; ///< The time between each sprite of the animation.
std::vector<Sprite> sprites; ///< The sprites of the direction.
#if defined(GD_IDE_ONLY)
gd::String metadata;
#endif
};
} // namespace gd

View File

@@ -29,7 +29,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
_("Animated object which can be used for most elements of a game"),
"CppPlatform/Extensions/spriteicon.png");
#if defined(GD_IDE_ONLY)
obj.AddAction("Opacity",
_("Change sprite opacity"),
_("Change the opacity of a Sprite. 0 is fully transparent, 255 "
@@ -536,7 +535,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.AddParameter("objectList", _("Object 1"), "Sprite")
.AddParameter("objectList", _("Object 2"), "Sprite")
.AddCodeOnlyParameter("conditionInverted", "");
#endif
}
} // namespace gd

View File

@@ -17,11 +17,9 @@
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#if defined(GD_IDE_ONLY)
#include <SFML/Graphics.hpp>
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#endif
namespace gd {
@@ -81,7 +79,6 @@ void SpriteObject::DoUnserializeFrom(gd::Project& project,
}
}
#if defined(GD_IDE_ONLY)
void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("updateIfNotVisible", updateIfNotVisible);
@@ -157,7 +154,6 @@ bool SpriteObject::UpdateInitialInstanceProperty(gd::InitialInstance& position,
return true;
}
#endif
const Animation& SpriteObject::GetAnimation(std::size_t nb) const {
if (nb >= animations.size()) return badAnimation;

View File

@@ -44,7 +44,6 @@ class GD_CORE_API SpriteObject : public gd::Object {
return gd::make_unique<SpriteObject>(*this);
}
#if defined(GD_IDE_ONLY)
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
@@ -60,7 +59,6 @@ class GD_CORE_API SpriteObject : public gd::Object {
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
#endif
/** \name Animations
* Methods related to animations management
@@ -118,14 +116,24 @@ class GD_CORE_API SpriteObject : public gd::Object {
* animation of the object.
*/
const std::vector<Animation>& GetAllAnimations() const { return animations; }
/**
* \brief Set if the object animation should be played even if the object is hidden
* or far from the camera.
*/
void SetUpdateIfNotVisible(bool updateIfNotVisible_) { updateIfNotVisible = updateIfNotVisible_; }
/**
* \brief Check if the object animation should be played even if the object is hidden
* or far from the camera (false by default).
*/
bool GetUpdateIfNotVisible() const { return updateIfNotVisible; }
///@}
private:
void DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) override;
#if defined(GD_IDE_ONLY)
void DoSerializeTo(gd::SerializerElement& element) const override;
#endif
mutable std::vector<Animation> animations;
bool updateIfNotVisible; ///< If set to true, ask the game engine to play

View File

@@ -21,7 +21,6 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
#if defined(GD_IDE_ONLY)
extension.AddStrExpression("NewLine",
_("Insert a new line"),
_("Insert a new line"),
@@ -182,7 +181,6 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Position of the last character in the string to be "
"considered in the search"));
#endif
}
} // namespace gd

View File

@@ -22,7 +22,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/timers");
#if defined(GD_IDE_ONLY)
extension
.AddCondition("Timer",
@@ -206,7 +205,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
"\"yday\", \"timestamp\"]");
#endif
}
} // namespace gd

View File

@@ -23,7 +23,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/variables");
#if defined(GD_IDE_ONLY)
extension
.AddCondition("VarScene",
_("Value of a scene variable"),
@@ -458,7 +457,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Variables"),
"res/actions/var.png")
.AddParameter("globalvar", _("Variable"));
#endif
}
} // namespace gd

View File

@@ -22,7 +22,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/window");
#if defined(GD_IDE_ONLY)
extension
.AddAction(
"SetFullScreen",
@@ -215,7 +214,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
_("Screen"),
"res/window.png")
.AddCodeOnlyParameter("currentScene", "");
#endif
}
} // namespace gd

View File

@@ -34,7 +34,7 @@ ExpressionMetadata& ExpressionMetadata::SetHidden() {
gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
const gd::String& type,
const gd::String& description,
const gd::String& optionalObjectType,
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
gd::ParameterMetadata info;
info.type = type;
@@ -46,15 +46,15 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
? (optionalObjectType.empty()
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
optionalObjectType //... so prefix it with the extension
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: optionalObjectType; // Otherwise don't change anything
: supplementaryInformation; // Otherwise don't change anything
// TODO: Assert against optionalObjectType === "emsc" (when running with
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
parameters.push_back(info);

View File

@@ -190,7 +190,7 @@ class GD_CORE_API ExpressionMetadata {
gd::ExpressionMetadata& AddParameter(
const gd::String& type,
const gd::String& description,
const gd::String& optionalObjectType = "",
const gd::String& supplementaryInformation = "",
bool parameterIsOptional = false);
/**

View File

@@ -51,7 +51,7 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
InstructionMetadata& InstructionMetadata::AddParameter(
const gd::String& type,
const gd::String& description,
const gd::String& optionalObjectType,
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
ParameterMetadata info;
info.type = type;
@@ -63,15 +63,15 @@ InstructionMetadata& InstructionMetadata::AddParameter(
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
? (optionalObjectType.empty()
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
optionalObjectType //... so prefix it with the extension
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: optionalObjectType; // Otherwise don't change anything
: supplementaryInformation; // Otherwise don't change anything
// TODO: Assert against optionalObjectType === "emsc" (when running with
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
parameters.push_back(info);
@@ -93,7 +93,7 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
AddParameter("operator", _("Modification's sign"));
AddParameter("operator", _("Modification's sign"), type);
AddParameter(type == "number" ? "expression" : type, _("Value"));
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;
@@ -129,7 +129,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
AddParameter("relationalOperator", _("Sign of the test"));
AddParameter("relationalOperator", _("Sign of the test"), type);
AddParameter(type == "number" ? "expression" : type, _("Value to compare"));
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;

View File

@@ -6,7 +6,6 @@
#ifndef INSTRUCTIONMETADATA_H
#define INSTRUCTIONMETADATA_H
#if defined(GD_IDE_ONLY)
#include <functional>
#include <map>
#include <memory>
@@ -137,8 +136,11 @@ class GD_CORE_API InstructionMetadata {
* will also determine the type of the argument used when calling the function
* in the generated code.
* \param description Description for parameter
* \param optionalObjectType If type is "object", this parameter will describe
* which objects are allowed. If it is empty, all objects are allowed.
* \param supplementaryInformation Additional information that can be used for
* rendering or logic. For example:
* - If type is "object", this argument will describe which objects are allowed.
* If this argument is empty, all objects are allowed.
* - If type is "operator", this argument will be used to display only pertinent operators.
* \param parameterIsOptional true if the parameter must be optional, false
* otherwise.
*
@@ -146,7 +148,7 @@ class GD_CORE_API InstructionMetadata {
*/
InstructionMetadata &AddParameter(const gd::String &type,
const gd::String &label,
const gd::String &optionalObjectType = "",
const gd::String &supplementaryInformation = "",
bool parameterIsOptional = false);
/**
@@ -319,7 +321,7 @@ class GD_CORE_API InstructionMetadata {
* "CppPlatform/Extensions/text.png");
*
* .AddParameter("object", _("Object"), "Text", false)
* .AddParameter("operator", _("Modification operator"))
* .AddParameter("operator", _("Modification operator"), "string")
* .AddParameter("string", _("String"))
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString").SetIncludeFile("MyExtension/TextObject.h");
*
@@ -452,5 +454,4 @@ class GD_CORE_API InstructionMetadata {
} // namespace gd
#endif
#endif // INSTRUCTIONMETADATA_H

View File

@@ -38,19 +38,17 @@ class GD_CORE_API MultipleInstructionMetadata {
MultipleInstructionMetadata &AddParameter(
const gd::String &type,
const gd::String &label,
const gd::String &optionalObjectType = "",
const gd::String &supplementaryInformation = "",
bool parameterIsOptional = false) {
#if defined(GD_IDE_ONLY)
if (expression)
expression->AddParameter(
type, label, optionalObjectType, parameterIsOptional);
type, label, supplementaryInformation, parameterIsOptional);
if (condition)
condition->AddParameter(
type, label, optionalObjectType, parameterIsOptional);
type, label, supplementaryInformation, parameterIsOptional);
if (action)
action->AddParameter(
type, label, optionalObjectType, parameterIsOptional);
#endif
type, label, supplementaryInformation, parameterIsOptional);
return *this;
}
@@ -59,13 +57,11 @@ class GD_CORE_API MultipleInstructionMetadata {
*/
MultipleInstructionMetadata &AddCodeOnlyParameter(
const gd::String &type, const gd::String &supplementaryInformation) {
#if defined(GD_IDE_ONLY)
if (expression)
expression->AddCodeOnlyParameter(type, supplementaryInformation);
if (condition)
condition->AddCodeOnlyParameter(type, supplementaryInformation);
if (action) action->AddCodeOnlyParameter(type, supplementaryInformation);
#endif
return *this;
}
@@ -73,11 +69,9 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::SetDefaultValue
*/
MultipleInstructionMetadata &SetDefaultValue(const gd::String &defaultValue) {
#if defined(GD_IDE_ONLY)
if (expression) expression->SetDefaultValue(defaultValue);
if (condition) condition->SetDefaultValue(defaultValue);
if (action) action->SetDefaultValue(defaultValue);
#endif
return *this;
};
@@ -86,11 +80,9 @@ class GD_CORE_API MultipleInstructionMetadata {
*/
MultipleInstructionMetadata &SetParameterLongDescription(
const gd::String &longDescription) {
#if defined(GD_IDE_ONLY)
if (expression) expression->SetParameterLongDescription(longDescription);
if (condition) condition->SetParameterLongDescription(longDescription);
if (action) action->SetParameterLongDescription(longDescription);
#endif
return *this;
};
@@ -98,11 +90,9 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::SetHidden
*/
MultipleInstructionMetadata &SetHidden() {
#if defined(GD_IDE_ONLY)
if (expression) expression->SetHidden();
if (condition) condition->SetHidden();
if (action) action->SetHidden();
#endif
return *this;
};
@@ -111,50 +101,40 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::UseStandardRelationalOperatorParameters
*/
MultipleInstructionMetadata &UseStandardParameters(const gd::String &type) {
#if defined(GD_IDE_ONLY)
if (condition) condition->UseStandardRelationalOperatorParameters(type);
if (action) action->UseStandardOperatorParameters(type);
#endif
return *this;
}
MultipleInstructionMetadata &SetFunctionName(const gd::String &functionName) {
#if defined(GD_IDE_ONLY)
if (expression) expression->SetFunctionName(functionName);
if (condition) condition->SetFunctionName(functionName);
if (action) action->GetCodeExtraInformation().SetFunctionName(functionName);
#endif
return *this;
}
MultipleInstructionMetadata &SetGetter(const gd::String &getter) {
#if defined(GD_IDE_ONLY)
if (expression) expression->SetFunctionName(getter);
if (condition) condition->SetFunctionName(getter);
if (action) action->GetCodeExtraInformation().SetGetter(getter);
#endif
return *this;
}
MultipleInstructionMetadata &SetIncludeFile(const gd::String &includeFile) {
#if defined(GD_IDE_ONLY)
if (expression)
expression->GetCodeExtraInformation().SetIncludeFile(includeFile);
if (condition)
condition->GetCodeExtraInformation().SetIncludeFile(includeFile);
if (action) action->GetCodeExtraInformation().SetIncludeFile(includeFile);
#endif
return *this;
}
MultipleInstructionMetadata &AddIncludeFile(const gd::String &includeFile) {
#if defined(GD_IDE_ONLY)
if (expression)
expression->GetCodeExtraInformation().AddIncludeFile(includeFile);
if (condition)
condition->GetCodeExtraInformation().AddIncludeFile(includeFile);
if (action) action->GetCodeExtraInformation().AddIncludeFile(includeFile);
#endif
return *this;
}
@@ -162,10 +142,8 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::MarkAsSimple
*/
MultipleInstructionMetadata &MarkAsSimple() {
#if defined(GD_IDE_ONLY)
if (condition) condition->MarkAsSimple();
if (action) action->MarkAsSimple();
#endif
return *this;
}
@@ -173,10 +151,8 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::MarkAsAdvanced
*/
MultipleInstructionMetadata &MarkAsAdvanced() {
#if defined(GD_IDE_ONLY)
if (condition) condition->MarkAsAdvanced();
if (action) action->MarkAsAdvanced();
#endif
return *this;
}
@@ -184,10 +160,8 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::MarkAsComplex
*/
MultipleInstructionMetadata &MarkAsComplex() {
#if defined(GD_IDE_ONLY)
if (condition) condition->MarkAsComplex();
if (action) action->MarkAsComplex();
#endif
return *this;
}

View File

@@ -302,6 +302,38 @@ ObjectMetadata::AddExpressionAndConditionAndAction(
expression, condition, action);
}
gd::InstructionMetadata& ObjectMetadata::AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName) {
gd::String newNameWithNamespace = extensionNamespace + newActionName;
gd::String copiedNameWithNamespace = extensionNamespace + copiedActionName;
auto copiedAction = actionsInfos.find(copiedNameWithNamespace);
if (copiedAction == actionsInfos.end()) {
gd::LogWarning("Could not find an action with name " +
copiedNameWithNamespace + " to copy.");
} else {
actionsInfos[newNameWithNamespace] = copiedAction->second;
}
return actionsInfos[newNameWithNamespace];
}
gd::InstructionMetadata& ObjectMetadata::AddDuplicatedCondition(
const gd::String& newConditionName, const gd::String& copiedConditionName) {
gd::String newNameWithNamespace = extensionNamespace + newConditionName;
gd::String copiedNameWithNamespace = extensionNamespace + copiedConditionName;
auto copiedCondition = conditionsInfos.find(copiedNameWithNamespace);
if (copiedCondition == conditionsInfos.end()) {
gd::LogWarning("Could not find a condition with name " +
copiedNameWithNamespace + " to copy.");
} else {
conditionsInfos[newNameWithNamespace] = copiedCondition->second;
}
return conditionsInfos[newNameWithNamespace];
}
ObjectMetadata& ObjectMetadata::SetFullName(const gd::String& fullname_) {
#if defined(GD_IDE_ONLY)
fullname = fullname_;

View File

@@ -153,6 +153,25 @@ class GD_CORE_API ObjectMetadata {
const gd::String& group,
const gd::String& icon);
/**
* \brief Create a new action which is the duplicate of the specified one.
*
* Useful for handling a deprecated action that is just a "copy" of the new
* one.
*/
gd::InstructionMetadata& AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName);
/**
* \brief Create a new condition which is the duplicate of the specified one.
*
* Useful for handling a deprecated condition that is just a "copy" of the new
* one.
*/
gd::InstructionMetadata& AddDuplicatedCondition(
const gd::String& newConditionName,
const gd::String& copiedConditionName);
/**
* \brief Set the name shown to the user.
*/

View File

@@ -189,6 +189,10 @@ class GD_CORE_API ParameterMetadata {
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName" ||
parameterType == "layerEffectName" ||
parameterType == "layerEffectParameterName" ||
parameterType == "objectEffectName" ||
parameterType == "objectEffectParameterName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName";
} else if (type == "variable") {

View File

@@ -529,24 +529,6 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressionsForBehavior(
gd::String autoType);
/**
* Called to inventory resources used by conditions
* (and possibly do work on them, like renaming, etc...)
*
* \see gd::PlatformExtension::ExposeActionsResources
*/
virtual void ExposeConditionsResources(Instruction& condition,
gd::ArbitraryResourceWorker& worker){};
/**
* Called to inventory resources used by actions
* (and possibly do work on them, like renaming, etc...)
*
* \see ArbitraryResourceWorker
*/
virtual void ExposeActionsResources(Instruction& action,
gd::ArbitraryResourceWorker& worker){};
/**
* \brief Get all the properties of the extension. Properties
* are shown in the game properties in the editor, and are exported in the

View File

@@ -19,11 +19,14 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
using namespace std;
namespace gd {
const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
/**
* \brief Go through the nodes and change the given object name to a new one.
*
@@ -675,16 +678,27 @@ bool EventsRefactorer::ReplaceStringInConditions(
}
vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
const gd::Platform& platform,
gd::EventsList& events,
gd::String search,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings) {
bool inEventStrings,
bool inEventSentences) {
vector<EventsSearchResult> results;
const gd::String& ignored_characters = EventsRefactorer::searchIgnoredCharacters;
search.replace_if(search.begin(),
search.end(),
[ignored_characters](const char &c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventAddedInResults = false;
@@ -694,7 +708,7 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
if (!eventAddedInResults &&
SearchStringInConditions(
project, layout, *conditionsVectors[j], search, matchCase)) {
platform, *conditionsVectors[j], search, matchCase, inEventSentences)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
@@ -709,7 +723,7 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
if (!eventAddedInResults &&
SearchStringInActions(
project, layout, *actionsVectors[j], search, matchCase)) {
platform, *actionsVectors[j], search, matchCase, inEventSentences)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
@@ -720,7 +734,7 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
if (inEventStrings) {
if (!eventAddedInResults &&
SearchStringInEvent(project, layout, events[i], search, matchCase)) {
SearchStringInEvent(events[i], search, matchCase)) {
results.push_back(EventsSearchResult(
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
&events,
@@ -730,14 +744,14 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
if (events[i].CanHaveSubEvents()) {
vector<EventsSearchResult> subResults =
SearchInEvents(project,
layout,
SearchInEvents(platform,
events[i].GetSubEvents(),
search,
matchCase,
inConditions,
inActions,
inEventStrings);
inEventStrings,
inEventSentences);
std::copy(
subResults.begin(), subResults.end(), std::back_inserter(results));
}
@@ -746,11 +760,12 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
return results;
}
bool EventsRefactorer::SearchStringInActions(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::InstructionsList& actions,
gd::String search,
bool matchCase) {
bool EventsRefactorer::SearchStringInActions(
const gd::Platform& platform,
gd::InstructionsList& actions,
gd::String search,
bool matchCase,
bool inSentences) {
for (std::size_t aId = 0; aId < actions.size(); ++aId) {
for (std::size_t pNb = 0; pNb < actions[aId].GetParameters().size();
++pNb) {
@@ -765,24 +780,60 @@ bool EventsRefactorer::SearchStringInActions(gd::ObjectsContainer& project,
if (foundPosition != gd::String::npos) return true;
}
if (inSentences && SearchStringInFormattedText(
platform, actions[aId], search, matchCase, false))
return true;
if (!actions[aId].GetSubInstructions().empty() &&
SearchStringInActions(project,
layout,
SearchStringInActions(platform,
actions[aId].GetSubInstructions(),
search,
matchCase))
matchCase,
inSentences))
return true;
}
return false;
}
bool EventsRefactorer::SearchStringInFormattedText(
const gd::Platform& platform,
gd::Instruction& instruction,
gd::String search,
bool matchCase,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::String completeSentence = gd::InstructionSentenceFormatter::Get()->GetFullText(instruction, metadata);
const gd::String& ignored_characters = EventsRefactorer::searchIgnoredCharacters;
completeSentence.replace_if(completeSentence.begin(),
completeSentence.end(),
[ignored_characters](const char &c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
completeSentence.RemoveConsecutiveOccurrences(
completeSentence.begin(), completeSentence.end(), ' ');
size_t foundPosition = matchCase
? completeSentence.find(search)
: completeSentence.FindCaseInsensitive(search);
return foundPosition != gd::String::npos;
}
bool EventsRefactorer::SearchStringInConditions(
gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
const gd::Platform& platform,
gd::InstructionsList& conditions,
gd::String search,
bool matchCase) {
bool matchCase,
bool inSentences) {
for (std::size_t cId = 0; cId < conditions.size(); ++cId) {
for (std::size_t pNb = 0; pNb < conditions[cId].GetParameters().size();
++pNb) {
@@ -797,21 +848,23 @@ bool EventsRefactorer::SearchStringInConditions(
if (foundPosition != gd::String::npos) return true;
}
if (inSentences && SearchStringInFormattedText(
platform, conditions[cId], search, matchCase, true))
return true;
if (!conditions[cId].GetSubInstructions().empty() &&
SearchStringInConditions(project,
layout,
SearchStringInConditions(platform,
conditions[cId].GetSubInstructions(),
search,
matchCase))
matchCase,
inSentences))
return true;
}
return false;
}
bool EventsRefactorer::SearchStringInEvent(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::BaseEvent& event,
bool EventsRefactorer::SearchStringInEvent(gd::BaseEvent& event,
gd::String search,
bool matchCase) {
for (gd::String str : event.GetAllSearchableStrings()) {

View File

@@ -41,7 +41,7 @@ class GD_CORE_API EventsSearchResult {
std::size_t positionInList;
bool IsEventsListValid() const { return eventsList != nullptr; }
/**
* \brief Get the events list containing the event pointed by the EventsSearchResult.
* \warning Only call this when IsEventsListValid returns true.
@@ -49,7 +49,7 @@ class GD_CORE_API EventsSearchResult {
const gd::EventsList & GetEventsList() const { return *eventsList; }
std::size_t GetPositionInList() const { return positionInList; }
bool IsEventValid() const { return !event.expired(); }
/**
@@ -72,7 +72,7 @@ class GD_CORE_API EventsSearchResult {
class GD_CORE_API EventsRefactorer {
public:
/**
* Replace all occurences of an object name by another name
* Replace all occurrences of an object name by another name
* ( include : objects in parameters and in math/text expressions of all
* events ).
*/
@@ -98,14 +98,14 @@ class GD_CORE_API EventsRefactorer {
* \return A vector containing EventsSearchResult objects filled with events
* containing the string
*/
static std::vector<EventsSearchResult> SearchInEvents(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
static std::vector<EventsSearchResult> SearchInEvents(const gd::Platform& platform,
gd::EventsList& events,
gd::String search,
bool matchCase,
bool inConditions,
bool inActions,
bool inEventStrings);
bool inEventStrings,
bool inEventSentences);
/**
* Replace all occurrences of a gd::String in events
@@ -123,7 +123,7 @@ class GD_CORE_API EventsRefactorer {
private:
/**
* Replace all occurences of an object name by another name in an action
* Replace all occurrences of an object name by another name in an action
* ( include : objects in parameters and in math/text expressions ).
*
* \return true if something was modified.
@@ -136,7 +136,7 @@ class GD_CORE_API EventsRefactorer {
gd::String newName);
/**
* Replace all occurences of an object name by another name in a condition
* Replace all occurrences of an object name by another name in a condition
* ( include : objects in parameters and in math/text expressions ).
*
* \return true if something was modified.
@@ -185,7 +185,7 @@ class GD_CORE_API EventsRefactorer {
gd::String name);
/**
* Replace all occurences of a gd::String in conditions
* Replace all occurrences of a gd::String in conditions
*
* \return true if something was modified.
*/
@@ -197,7 +197,7 @@ class GD_CORE_API EventsRefactorer {
bool matchCase);
/**
* Replace all occurences of a gd::String in actions
* Replace all occurrences of a gd::String in actions
*
* \return true if something was modified.
*/
@@ -208,21 +208,26 @@ class GD_CORE_API EventsRefactorer {
gd::String newString,
bool matchCase);
static bool SearchStringInActions(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
static bool SearchStringInFormattedText(const gd::Platform& platform,
gd::Instruction& instruction,
gd::String search,
bool matchCase,
bool isCondition);
static bool SearchStringInActions(const gd::Platform& platform,
gd::InstructionsList& actions,
gd::String search,
bool matchCase);
static bool SearchStringInConditions(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
bool matchCase,
bool inSentences);
static bool SearchStringInConditions(const gd::Platform& platform,
gd::InstructionsList& conditions,
gd::String search,
bool matchCase);
static bool SearchStringInEvent(gd::ObjectsContainer& project,
gd::ObjectsContainer& layout,
gd::BaseEvent& events,
gd::String search,
bool matchCase);
bool matchCase,
bool inSentences);
static bool SearchStringInEvent(gd::BaseEvent& events,
gd::String search,
bool matchCase);
static const gd::String searchIgnoredCharacters;
EventsRefactorer(){};
};

View File

@@ -9,12 +9,12 @@
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2.h"
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
#include "GDCore/Events/Parsers/ExpressionParser2.h"
namespace gd {
class Expression;
@@ -326,10 +326,7 @@ class GD_CORE_API ExpressionCompletionFinder
node.type, "", searchedPosition + 1, searchedPosition + 1));
}
void OnVisitOperatorNode(OperatorNode& node) override {
completions.push_back(ExpressionCompletionDescription::ForObject(
node.type, "", searchedPosition + 1, searchedPosition + 1));
completions.push_back(ExpressionCompletionDescription::ForExpression(
node.type, "", searchedPosition + 1, searchedPosition + 1));
// No completions.
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
completions.push_back(ExpressionCompletionDescription::ForObject(
@@ -359,8 +356,9 @@ class GD_CORE_API ExpressionCompletionFinder
}
// Search the parameter metadata index skipping invisible ones.
size_t visibleParameterIndex = 0;
size_t metadataParameterIndex = ExpressionParser2::WrittenParametersFirstIndex(
functionCall->objectName, functionCall->behaviorName);
size_t metadataParameterIndex =
ExpressionParser2::WrittenParametersFirstIndex(
functionCall->objectName, functionCall->behaviorName);
const gd::ParameterMetadata* parameterMetadata = nullptr;
while (metadataParameterIndex <

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
#include <algorithm>
#include <iostream>
@@ -90,6 +89,19 @@ InstructionSentenceFormatter::GetAsFormattedText(
return formattedStr;
}
} // namespace gd
gd::String InstructionSentenceFormatter::GetFullText(
const gd::Instruction &instr, const gd::InstructionMetadata &metadata)
{
const std::vector<std::pair<gd::String, gd::TextFormatting> > formattedText =
GetAsFormattedText(instr, metadata);
#endif
gd::String completeSentence = "";
for (std::size_t id = 0; id < formattedText.size(); ++id) {
completeSentence += formattedText.at(id).first;
}
return completeSentence;
}
} // namespace gd

View File

@@ -4,7 +4,6 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef TRANSLATEACTION_H
#define TRANSLATEACTION_H
#include <map>
@@ -39,6 +38,9 @@ class GD_CORE_API InstructionSentenceFormatter {
return (static_cast<InstructionSentenceFormatter *>(_singleton));
}
gd::String GetFullText(const gd::Instruction &instr,
const gd::InstructionMetadata &metadata);
static void DestroySingleton() {
if (NULL != _singleton) {
delete _singleton;
@@ -55,4 +57,3 @@ class GD_CORE_API InstructionSentenceFormatter {
} // namespace gd
#endif // TRANSLATEACTION_H
#endif

View File

@@ -19,12 +19,15 @@ bool InstructionsParameterMover::DoVisitInstruction(
gd::Instruction& instruction, bool isCondition) {
if (instruction.GetType() == instructionType) {
std::vector<gd::Expression> updatedParameters = instruction.GetParameters();
if (oldIndex < updatedParameters.size() ||
newIndex < updatedParameters.size()) {
gd::Expression movedParameter = updatedParameters[oldIndex];
if (oldIndex < updatedParameters.size()) {
gd::Expression movedParameter = updatedParameters.at(oldIndex);
updatedParameters.erase(updatedParameters.begin() + oldIndex);
updatedParameters.insert(updatedParameters.begin() + newIndex,
movedParameter);
if (newIndex < updatedParameters.size()) {
updatedParameters.insert(updatedParameters.begin() + newIndex,
movedParameter);
} else {
updatedParameters.push_back(movedParameter);
}
instruction.SetParameters(updatedParameters);
}
}

View File

@@ -13,8 +13,11 @@
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ResourcesManager.h"
@@ -23,8 +26,13 @@ using namespace std;
namespace gd {
void ArbitraryResourceWorker::ExposeImage(gd::String& imageName){
// Nothing to do, the image is a reference to a resource that
// is already exposed.
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
@@ -33,12 +41,14 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
if (resources->HasResource(audioName) &&
resources->GetResource(audioName).GetKind() == "audio") {
// Nothing to do, the audio is a reference to a resource that
// is already exposed.
// Nothing to do, the audio is a reference to a proper resource.
return;
}
}
// For compatibility with older projects (where events were refering to files
// directly), we consider that this resource name is a filename, and so expose
// it as a file.
ExposeFile(audioName);
};
@@ -48,30 +58,17 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
if (resources->HasResource(fontName) &&
resources->GetResource(fontName).GetKind() == "font") {
// Nothing to do, the font is a reference to a resource that
// is already exposed.
// Nothing to do, the font is a reference to a proper resource.
return;
}
}
// For compatibility with older projects (where events were refering to files
// directly), we consider that this resource name is a filename, and so expose
// it as a file.
ExposeFile(fontName);
};
void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName) {
for (auto resources : GetResources()) {
if (!resources) continue;
if (resources->HasResource(bitmapFontName) &&
resources->GetResource(bitmapFontName).GetKind() == "bitmapFont") {
// Nothing to do, the font is a reference to a resource that
// is already exposed.
return;
}
}
ExposeFile(bitmapFontName);
};
void ArbitraryResourceWorker::ExposeResources(
gd::ResourcesManager* resourcesManager) {
if (!resourcesManager) return;
@@ -95,101 +92,67 @@ void ArbitraryResourceWorker::ExposeResource(gd::Resource& resource) {
ArbitraryResourceWorker::~ArbitraryResourceWorker() {}
/**
* Launch the specified resource worker on every resource referenced in the
* events.
*/
class ResourceWorkerInEventsWorker : public ArbitraryEventsWorker {
public:
ResourceWorkerInEventsWorker(const gd::Project& project_,
gd::ArbitraryResourceWorker& worker_)
: project(project_), worker(worker_){};
virtual ~ResourceWorkerInEventsWorker() {};
private:
bool DoVisitInstruction(gd::Instruction& instruction, bool isCondition) {
const auto& platform = project.GetCurrentPlatform();
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[this, &instruction](const gd::ParameterMetadata& parameterMetadata,
const gd::String& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
if (parameterMetadata.GetType() ==
"police") { // Should be renamed fontResource
gd::String updatedParameterValue = parameterValue;
worker.ExposeFont(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "soundfile" ||
parameterMetadata.GetType() ==
"musicfile") { // Should be renamed audioResource
gd::String updatedParameterValue = parameterValue;
worker.ExposeAudio(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "bitmapFontResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeBitmapFont(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "imageResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeImage(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
}
});
return false;
};
const gd::Project& project;
gd::ArbitraryResourceWorker& worker;
};
void LaunchResourceWorkerOnEvents(const gd::Project& project,
gd::EventsList& events,
gd::ArbitraryResourceWorker& worker) {
// Get all extensions used
auto allGameExtensions =
project.GetCurrentPlatform().GetAllPlatformExtensions();
for (std::size_t j = 0; j < events.size(); j++) {
vector<gd::InstructionsList*> allActionsVectors =
events[j].GetAllActionsVectors();
for (std::size_t i = 0; i < allActionsVectors.size(); ++i) {
for (std::size_t k = 0; k < allActionsVectors[i]->size(); k++) {
gd::String type = allActionsVectors[i]->Get(k).GetType();
for (std::size_t e = 0; e < allGameExtensions.size(); ++e) {
bool extensionHasAction = false;
const std::map<gd::String, gd::InstructionMetadata>& allActions =
allGameExtensions[e]->GetAllActions();
if (allActions.find(type) != allActions.end())
extensionHasAction = true;
const vector<gd::String>& objects =
allGameExtensions[e]->GetExtensionObjectsTypes();
for (std::size_t o = 0; o < objects.size(); ++o) {
const std::map<gd::String, gd::InstructionMetadata>&
allObjectsActions =
allGameExtensions[e]->GetAllActionsForObject(objects[o]);
if (allObjectsActions.find(type) != allObjectsActions.end())
extensionHasAction = true;
}
const vector<gd::String>& autos =
allGameExtensions[e]->GetBehaviorsTypes();
for (std::size_t a = 0; a < autos.size(); ++a) {
const std::map<gd::String, gd::InstructionMetadata>&
allAutosActions =
allGameExtensions[e]->GetAllActionsForBehavior(autos[a]);
if (allAutosActions.find(type) != allAutosActions.end())
extensionHasAction = true;
}
if (extensionHasAction) {
allGameExtensions[e]->ExposeActionsResources(
allActionsVectors[i]->Get(k), worker);
break;
}
}
}
}
vector<gd::InstructionsList*> allConditionsVector =
events[j].GetAllConditionsVectors();
for (std::size_t i = 0; i < allConditionsVector.size(); ++i) {
for (std::size_t k = 0; k < allConditionsVector[i]->size(); k++) {
gd::String type = allConditionsVector[i]->Get(k).GetType();
for (std::size_t e = 0; e < allGameExtensions.size(); ++e) {
bool extensionHasCondition = false;
const std::map<gd::String, gd::InstructionMetadata>& allConditions =
allGameExtensions[e]->GetAllConditions();
if (allConditions.find(type) != allConditions.end())
extensionHasCondition = true;
const vector<gd::String>& objects =
allGameExtensions[e]->GetExtensionObjectsTypes();
for (std::size_t j = 0; j < objects.size(); ++j) {
const std::map<gd::String, gd::InstructionMetadata>&
allObjectsConditions =
allGameExtensions[e]->GetAllConditionsForObject(objects[j]);
if (allObjectsConditions.find(type) != allObjectsConditions.end())
extensionHasCondition = true;
}
const vector<gd::String>& autos =
allGameExtensions[e]->GetBehaviorsTypes();
for (std::size_t j = 0; j < autos.size(); ++j) {
const std::map<gd::String, gd::InstructionMetadata>&
allAutosConditions =
allGameExtensions[e]->GetAllConditionsForBehavior(autos[j]);
if (allAutosConditions.find(type) != allAutosConditions.end())
extensionHasCondition = true;
}
if (extensionHasCondition)
allGameExtensions[e]->ExposeConditionsResources(
allConditionsVector[i]->Get(k), worker);
}
}
}
if (events[j].CanHaveSubEvents())
LaunchResourceWorkerOnEvents(project, events[j].GetSubEvents(), worker);
}
return;
ResourceWorkerInEventsWorker eventsWorker(project, worker);
eventsWorker.Launch(events);
}
} // namespace gd

View File

@@ -60,19 +60,18 @@ class GD_CORE_API ArbitraryResourceWorker {
/**
* \brief Expose an audio, which is either a reference to an "audio" resource,
* or a filename if no resource with this name exists.
* or a filename if no resource with this name exists (for backward compatibility).
*/
virtual void ExposeAudio(gd::String &audioName);
/**
* \brief Expose a font, which is either a reference to a "font" resource,
* or a filename if no resource with this name exists.
* or a filename if no resource with this name exists (for backward compatibility).
*/
virtual void ExposeFont(gd::String &fontName);
/**
* \brief Expose a bitmap font, which is either a reference to a "bitmapFont" resource,
* or a filename if no resource with this name exists.
* \brief Expose a bitmap font, which is always a reference to a "bitmapFont" resource.
*/
virtual void ExposeBitmapFont(gd::String &bitmapFontName);

View File

@@ -14,25 +14,6 @@ using namespace std;
namespace gd {
bool ProjectResourcesAdder::AddAllMissing(gd::Project& project,
const gd::String& resourceType) {
// Search for resources used in the project
gd::ResourcesInUseHelper resourcesInUse;
project.ExposeResources(resourcesInUse);
ResourcesManager& resourcesManager = project.GetResourcesManager();
for (auto& resourceName : resourcesInUse.GetAll(resourceType)) {
if (!resourcesManager.HasResource(resourceName)) {
std::cout << "Adding missing resource \"" << resourceName
<< "\"to the project." << std::endl;
resourcesManager.AddResource(
resourceName, /*filename=*/resourceName, resourceType);
}
}
return true;
}
std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
gd::Project& project, const gd::String& resourceType) {
std::vector<gd::String> unusedResources;
@@ -42,15 +23,13 @@ std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
std::set<gd::String>& usedResources = resourcesInUse.GetAll(resourceType);
// Search all resources not used
std::vector<gd::String> resources =
project.GetResourcesManager().GetAllResourceNames();
const std::vector<std::shared_ptr<Resource>>& resources =
project.GetResourcesManager().GetAllResources();
for (std::size_t i = 0; i < resources.size(); i++) {
if (project.GetResourcesManager().GetResource(resources[i]).GetKind() !=
resourceType)
continue;
if (resources[i]->GetKind() != resourceType) continue;
if (usedResources.find(resources[i]) == usedResources.end())
unusedResources.push_back(resources[i]);
if (usedResources.find(resources[i]->GetName()) == usedResources.end())
unusedResources.push_back(resources[i]->GetName());
}
return unusedResources;

View File

@@ -20,17 +20,6 @@ namespace gd {
*/
class GD_CORE_API ProjectResourcesAdder {
public:
/**
* \brief Update the project so that all missing resources are added, with an
* filename that is equal to the missing resource name.
*
* \param project The project to be updated.
* \param resourceType The type of the resource the be searched
*
* \return true if no error happened
*/
static bool AddAllMissing(gd::Project& project, const gd::String & resourceType);
/**
* \brief Find all resources of the specified kind that are
* not used by the project.

View File

@@ -22,7 +22,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
resourceFullFilename = gd::AbstractFileSystem::NormalizeSeparator(
resourceFullFilename); // Protect against \ on Linux.
// In the case of absolute filnames that we don't want to preserve, or
// In the case of absolute filenames that we don't want to preserve, or
// in the case of copying files without preserving relative folders, the new
// names will be generated from the filename alone (with collision protection).
auto stripToFilenameOnly = [&]() {

View File

@@ -71,7 +71,7 @@ class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
* Resources merging helper collects all resources filenames and update these
* filenames.
*/
virtual void ExposeFile(gd::String& resource);
virtual void ExposeFile(gd::String& resource) override;
protected:
void SetNewFilename(gd::String oldFilename, gd::String newFilename);

View File

@@ -33,8 +33,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
virtual ~ResourcesRenamer(){};
virtual void ExposeFile(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
virtual void ExposeFile(gd::String& resourceFileName) override{
// Don't do anything: we're renaming resources, not the files they are
// pointing to.
};
virtual void ExposeImage(gd::String& imageResourceName) override {
RenameIfNeeded(imageResourceName);
@@ -45,6 +46,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeFont(gd::String& fontResourceName) override {
RenameIfNeeded(fontResourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontName) override {
RenameIfNeeded(bitmapFontName);
};
private:
void RenameIfNeeded(gd::String& resourceName) {

View File

@@ -238,7 +238,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
};
auto renameBehaviorEventsFunction =
[&project, &eventsFunctionsExtension, &oldName, &newName](
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
@@ -261,7 +261,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
};
auto renameBehaviorPropertyFunctions =
[&project, &eventsFunctionsExtension, &oldName, &newName](
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::NamedPropertyDescriptor& property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
@@ -805,7 +805,6 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
auto renameBehaviorEventsFunction =
[&project,
&eventsFunctionsExtension,
&eventsBasedBehavior,
&oldBehaviorName,
&newBehaviorName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
@@ -832,7 +831,6 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
auto renameBehaviorProperty = [&project,
&eventsFunctionsExtension,
&eventsBasedBehavior,
&oldBehaviorName,
&newBehaviorName](
const gd::NamedPropertyDescriptor&

View File

@@ -36,6 +36,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
fullName = other.fullName;
tags = other.tags;
author = other.author;
authorIds = other.authorIds;
previewIconUrl = other.previewIconUrl;
iconUrl = other.iconUrl;
helpPath = other.helpPath;
@@ -55,6 +56,11 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
for (const auto& tag : tags) {
tagsElement.AddChild("").SetStringValue(tag);
}
auto& authorIdsElement = element.AddChild("authorIds");
authorIdsElement.ConsiderAsArray();
for (const auto& authorId : authorIds) {
authorIdsElement.AddChild("").SetStringValue(authorId);
}
element.SetAttribute("author", author);
element.SetAttribute("previewIconUrl", previewIconUrl);
element.SetAttribute("iconUrl", iconUrl);
@@ -99,6 +105,13 @@ void EventsFunctionsExtension::UnserializeFrom(
}
}
authorIds.clear();
auto& authorIdsElement = element.GetChild("authorIds");
authorIdsElement.ConsiderAsArray();
for (std::size_t i = 0; i < authorIdsElement.GetChildrenCount(); ++i) {
authorIds.push_back(authorIdsElement.GetChild(i).GetStringValue());
}
dependencies.clear();
const auto& dependenciesElement = element.GetChild("dependencies");
dependenciesElement.ConsiderAsArray();

View File

@@ -88,6 +88,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
const std::vector<gd::String>& GetTags() const { return tags; };
std::vector<gd::String>& GetTags() { return tags; };
const std::vector<gd::String>& GetAuthorIds() const { return authorIds; };
std::vector<gd::String>& GetAuthorIds() { return authorIds; };
const gd::String& GetAuthor() const { return author; };
EventsFunctionsExtension& SetAuthor(const gd::String& author_) {
author = author_;
@@ -220,6 +223,7 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::String name;
gd::String fullName;
std::vector<gd::String> tags;
std::vector<gd::String> authorIds;
gd::String author;
gd::String previewIconUrl;
gd::String iconUrl;

View File

@@ -52,12 +52,12 @@ class GD_CORE_API ExtensionProperties {
/**
* \brief Serialize the Extension Properties.
*/
virtual void SerializeTo(SerializerElement& element) const;
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the Extension Properties.
*/
virtual void UnserializeFrom(const SerializerElement& element);
void UnserializeFrom(const SerializerElement& element);
///@}
private:

View File

@@ -1,5 +1,5 @@
#if defined(GD_IDE_ONLY)
#include "GDCore/Project/ExternalEvents.h"
#include "ExternalEvents.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Serialization.h"
@@ -48,4 +48,3 @@ void ExternalEvents::UnserializeFrom(gd::Project& project,
}
} // namespace gd
#endif

View File

@@ -3,12 +3,12 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef GDCORE_EXTERNALEVENTS_H
#define GDCORE_EXTERNALEVENTS_H
#include <ctime>
#include <memory>
#include <vector>
#include "GDCore/Events/EventsList.h"
#include "GDCore/String.h"
namespace gd {
@@ -135,4 +135,3 @@ struct ExternalEventsHasName
} // namespace gd
#endif // GDCORE_EXTERNALEVENTS_H
#endif

View File

@@ -5,6 +5,7 @@
*/
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -15,19 +16,15 @@ namespace gd {
void ExternalLayout::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("name", "", "Name");
instances.UnserializeFrom(element.GetChild("instances", 0, "Instances"));
#if defined(GD_IDE_ONLY)
editorSettings.UnserializeFrom(element.GetChild("editionSettings"));
#endif
associatedLayout = element.GetStringAttribute("associatedLayout");
}
#if defined(GD_IDE_ONLY)
void ExternalLayout::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
instances.SerializeTo(element.AddChild("instances"));
editorSettings.SerializeTo(element.AddChild("editionSettings"));
element.SetAttribute("associatedLayout", associatedLayout);
}
#endif
} // namespace gd

View File

@@ -7,14 +7,13 @@
#ifndef GDCORE_EXTERNALLAYOUT_H
#define GDCORE_EXTERNALLAYOUT_H
#include <memory>
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
#if defined(GD_IDE_ONLY)
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#endif
namespace gd {
@@ -54,7 +53,6 @@ class GD_CORE_API ExternalLayout {
*/
gd::InitialInstancesContainer& GetInitialInstances() { return instances; }
#if defined(GD_IDE_ONLY)
/**
* \brief Get the user settings for the IDE.
*/
@@ -65,10 +63,7 @@ class GD_CORE_API ExternalLayout {
/**
* \brief Get the user settings for the IDE.
*/
gd::EditorSettings& GetAssociatedEditorSettings() {
return editorSettings;
}
#endif
gd::EditorSettings& GetAssociatedEditorSettings() { return editorSettings; }
/**
* \brief Get the name of the layout last used to edit the external layout.
@@ -80,15 +75,13 @@ class GD_CORE_API ExternalLayout {
*/
void SetAssociatedLayout(const gd::String& name) { associatedLayout = name; }
/** \name Serialization
*/
///@{
#if defined(GD_IDE_ONLY)
/** \name Serialization
*/
///@{
/**
* \brief Serialize external layout.
*/
void SerializeTo(SerializerElement& element) const;
#endif
/**
* \brief Unserialize the external layout.
@@ -99,9 +92,7 @@ class GD_CORE_API ExternalLayout {
private:
gd::String name;
gd::InitialInstancesContainer instances;
#if defined(GD_IDE_ONLY)
gd::EditorSettings editorSettings;
#endif
gd::String associatedLayout;
};

View File

@@ -50,6 +50,10 @@ class GD_CORE_API LoadingScreen {
return backgroundImageResourceName;
};
gd::String& GetBackgroundImageResourceName() {
return backgroundImageResourceName;
};
LoadingScreen& SetBackgroundImageResourceName(const gd::String& value) {
backgroundImageResourceName = value;
return *this;

View File

@@ -87,7 +87,6 @@ class GD_CORE_API ObjectsContainer {
*/
std::size_t GetObjectsCount() const;
#if defined(GD_IDE_ONLY)
/**
* \brief Add a new empty object of type \a objectType called \a name at the
* specified position in the list.<br>
@@ -99,7 +98,6 @@ class GD_CORE_API ObjectsContainer {
const gd::String& objectType,
const gd::String& name,
std::size_t position);
#endif
/**
* \brief Add a new object to the list
@@ -176,7 +174,6 @@ class GD_CORE_API ObjectsContainer {
*/
///@{
#if defined(GD_IDE_ONLY)
/**
* \brief Return a reference to the project's objects groups.
*/
@@ -186,7 +183,6 @@ class GD_CORE_API ObjectsContainer {
* \brief Return a const reference to the project's objects groups.
*/
const ObjectGroupsContainer& GetObjectGroups() const { return objectGroups; }
#endif
///@}

View File

@@ -49,14 +49,11 @@ using namespace std;
namespace gd {
Project::Project()
:
#if defined(GD_IDE_ONLY)
name(_("Project")),
: name(_("Project")),
version("1.0.0"),
packageName("com.example.gamename"),
orientation("landscape"),
folderProject(false),
#endif
windowWidth(800),
windowHeight(600),
maxFPS(60),
@@ -67,17 +64,12 @@ Project::Project()
adaptGameResolutionAtRuntime(true),
sizeOnStartupMode("adaptWidth"),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false)
#if defined(GD_IDE_ONLY)
,
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
currentPlatform(NULL),
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build())
#endif
{
}
gdBuildVersion(gd::VersionWrapper::Build()) {}
Project::~Project() {}
@@ -102,7 +94,6 @@ std::unique_ptr<gd::Object> Project::CreateObject(
return nullptr;
}
#if defined(GD_IDE_ONLY)
std::shared_ptr<gd::BaseEvent> Project::CreateEvent(
const gd::String& type, const gd::String& platformName) {
for (std::size_t i = 0; i < platforms.size(); ++i) {
@@ -161,7 +152,6 @@ bool Project::RemovePlatform(const gd::String& platformName) {
return false;
}
#endif
bool Project::HasLayoutNamed(const gd::String& name) const {
return (find_if(scenes.begin(),
@@ -188,13 +178,11 @@ std::size_t Project::GetLayoutPosition(const gd::String& name) const {
}
std::size_t Project::GetLayoutsCount() const { return scenes.size(); }
#if defined(GD_IDE_ONLY)
void Project::SwapLayouts(std::size_t first, std::size_t second) {
if (first >= scenes.size() || second >= scenes.size()) return;
std::iter_swap(scenes.begin() + first, scenes.begin() + second);
}
#endif
gd::Layout& Project::InsertNewLayout(const gd::String& name,
std::size_t position) {
@@ -203,9 +191,7 @@ gd::Layout& Project::InsertNewLayout(const gd::String& name,
new Layout())));
newlyInsertedLayout.SetName(name);
#if defined(GD_IDE_ONLY)
newlyInsertedLayout.UpdateBehaviorsSharedData(*this);
#endif
return newlyInsertedLayout;
}
@@ -216,9 +202,7 @@ gd::Layout& Project::InsertLayout(const gd::Layout& layout,
position < scenes.size() ? scenes.begin() + position : scenes.end(),
new Layout(layout))));
#if defined(GD_IDE_ONLY)
newlyInsertedLayout.UpdateBehaviorsSharedData(*this);
#endif
return newlyInsertedLayout;
}
@@ -231,7 +215,6 @@ void Project::RemoveLayout(const gd::String& name) {
scenes.erase(scene);
}
#if defined(GD_IDE_ONLY)
bool Project::HasExternalEventsNamed(const gd::String& name) const {
return (find_if(externalEvents.begin(),
externalEvents.end(),
@@ -311,7 +294,6 @@ void Project::SwapExternalLayouts(std::size_t first, std::size_t second) {
std::iter_swap(externalLayouts.begin() + first,
externalLayouts.begin() + second);
}
#endif
bool Project::HasExternalLayoutNamed(const gd::String& name) const {
return (find_if(externalLayouts.begin(),
externalLayouts.end(),
@@ -377,7 +359,6 @@ void Project::RemoveExternalLayout(const gd::String& name) {
externalLayouts.erase(externalLayout);
}
#if defined(GD_IDE_ONLY)
void Project::SwapEventsFunctionsExtensions(std::size_t first,
std::size_t second) {
if (first >= eventsFunctionsExtensions.size() ||
@@ -476,11 +457,8 @@ void Project::RemoveEventsFunctionsExtension(const gd::String& name) {
void Project::ClearEventsFunctionsExtensions() {
eventsFunctionsExtensions.clear();
}
#endif
void Project::UnserializeFrom(const SerializerElement& element) {
// Checking version
#if defined(GD_IDE_ONLY)
const SerializerElement& gdVersionElement =
element.GetChild("gdVersion", 0, "GDVersion");
gdMajorVersion =
@@ -513,7 +491,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
"available.");
}
}
#endif
const SerializerElement& propElement =
element.GetChild("properties", 0, "Info");
@@ -534,7 +511,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
#if defined(GD_IDE_ONLY)
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
SetPackageName(propElement.GetStringAttribute("packageName"));
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
@@ -552,6 +528,13 @@ void Project::UnserializeFrom(const SerializerElement& element) {
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
authorIds.clear();
auto& authorIdsElement = propElement.GetChild("authorIds");
authorIdsElement.ConsiderAsArray();
for (std::size_t i = 0; i < authorIdsElement.GetChildrenCount(); ++i) {
authorIds.push_back(authorIdsElement.GetChild(i).GetStringValue());
}
// Compatibility with GD <= 5.0.0-beta101
if (VersionWrapper::IsOlderOrEqual(
gdMajorVersion, gdMinorVersion, gdBuildVersion, 0, 4, 0, 98, 0) &&
@@ -583,9 +566,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
}
// end of compatibility code
#endif
#if defined(GD_IDE_ONLY)
currentPlatform = NULL;
gd::String currentPlatformName =
propElement.GetChild("currentPlatform").GetValue().GetString();
@@ -635,12 +615,9 @@ void Project::UnserializeFrom(const SerializerElement& element) {
if (currentPlatform == NULL && !platforms.empty())
currentPlatform = platforms.back();
#endif
#if defined(GD_IDE_ONLY)
GetObjectGroups().UnserializeFrom(
element.GetChild("objectsGroups", 0, "ObjectGroups"));
#endif
resourcesManager.UnserializeFrom(
element.GetChild("resources", 0, "Resources"));
UnserializeObjectsFrom(*this, element.GetChild("objects", 0, "Objects"));
@@ -658,7 +635,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
layout.UnserializeFrom(*this, layoutElement);
}
#if defined(GD_IDE_ONLY)
externalEvents.clear();
const SerializerElement& externalEventsElement =
element.GetChild("externalEvents", 0, "ExternalEvents");
@@ -690,7 +666,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
newEventsFunctionsExtension.UnserializeFrom(
*this, eventsFunctionsExtensionElement);
}
#endif
externalLayouts.clear();
const SerializerElement& externalLayoutsElement =
@@ -705,7 +680,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
newExternalLayout.UnserializeFrom(externalLayoutElement);
}
#if defined(GD_IDE_ONLY)
externalSourceFiles.clear();
const SerializerElement& externalSourceFilesElement =
element.GetChild("externalSourceFiles", 0, "ExternalSourceFiles");
@@ -718,10 +692,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
gd::SourceFile& newSourceFile = InsertNewSourceFile("", "");
newSourceFile.UnserializeFrom(sourceFileElement);
}
#endif
}
#if defined(GD_IDE_ONLY)
void Project::SerializeTo(SerializerElement& element) const {
SerializerElement& versionElement = element.AddChild("gdVersion");
versionElement.SetAttribute("major", gd::VersionWrapper::Major());
@@ -755,6 +727,12 @@ void Project::SerializeTo(SerializerElement& element) const {
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
auto& authorIdsElement = propElement.AddChild("authorIds");
authorIdsElement.ConsiderAsArray();
for (const auto& authorId : authorIds) {
authorIdsElement.AddChild("").SetStringValue(authorId);
}
// Compatibility with GD <= 5.0.0-beta101
if (useDeprecatedZeroAsDefaultZOrder) {
propElement.SetAttribute("useDeprecatedZeroAsDefaultZOrder", true);
@@ -865,6 +843,10 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
for (std::size_t j = 0; j < GetObjectsCount(); ++j) {
GetObject(j).ExposeResources(worker);
}
// Add loading screen background image if present
if (loadingScreen.GetBackgroundImageResourceName() != "")
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
}
bool Project::HasSourceFile(gd::String name, gd::String language) const {
@@ -915,7 +897,6 @@ gd::SourceFile& Project::InsertNewSourceFile(const gd::String& name,
return newlyInsertedSourceFile;
}
#endif
Project::Project(const Project& other) { Init(other); }
@@ -940,8 +921,8 @@ void Project::Init(const gd::Project& game) {
projectUuid = game.projectUuid;
useDeprecatedZeroAsDefaultZOrder = game.useDeprecatedZeroAsDefaultZOrder;
#if defined(GD_IDE_ONLY)
author = game.author;
authorIds = game.authorIds;
packageName = game.packageName;
orientation = game.orientation;
folderProject = game.folderProject;
@@ -957,33 +938,26 @@ void Project::Init(const gd::Project& game) {
gdBuildVersion = game.gdBuildVersion;
currentPlatform = game.currentPlatform;
#endif
platforms = game.platforms;
resourcesManager = game.resourcesManager;
initialObjects = gd::Clone(game.initialObjects);
scenes = gd::Clone(game.scenes);
#if defined(GD_IDE_ONLY)
externalEvents = gd::Clone(game.externalEvents);
#endif
externalLayouts = gd::Clone(game.externalLayouts);
#if defined(GD_IDE_ONLY)
eventsFunctionsExtensions = gd::Clone(game.eventsFunctionsExtensions);
useExternalSourceFiles = game.useExternalSourceFiles;
externalSourceFiles = gd::Clone(game.externalSourceFiles);
#endif
variables = game.GetVariables();
#if defined(GD_IDE_ONLY)
projectFile = game.GetProjectFile();
#endif
}
} // namespace gd

View File

@@ -77,7 +77,6 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetVersion() const { return version; }
#if defined(GD_IDE_ONLY)
/**
* \brief Change the author of the project.
*/
@@ -88,6 +87,16 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetAuthor() const { return author; }
/**
* \brief Get the author ids of the project.
*/
const std::vector<gd::String>& GetAuthorIds() const { return authorIds; };
/**
* \brief Get the author ids of the project, to modify them (non-const).
*/
std::vector<gd::String>& GetAuthorIds() { return authorIds; };
/**
* \brief Change project package name.
*/
@@ -178,7 +187,6 @@ class GD_CORE_API Project : public ObjectsContainer {
* \brief Return a reference to loading screen setup for the project
*/
const gd::LoadingScreen& GetLoadingScreen() const { return loadingScreen; }
#endif
/**
* Change game's main window default width.
@@ -279,16 +287,13 @@ class GD_CORE_API Project : public ObjectsContainer {
/**
* Return true if pixels rounding option is enabled.
*/
bool GetPixelsRounding() const {
return pixelsRounding;
}
bool GetPixelsRounding() const { return pixelsRounding; }
/**
* Set pixels rounding option to true or false.
*/
void SetPixelsRounding(bool enable) { pixelsRounding = enable; }
/**
* \brief Return if the project should set 0 as Z-order for objects created
* from events (which is deprecated) - instead of the highest Z order that was
@@ -325,7 +330,6 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
void ResetProjectUuid();
#if defined(GD_IDE_ONLY)
/**
* \brief Get the properties set by extensions.
*
@@ -376,7 +380,6 @@ class GD_CORE_API Project : public ObjectsContainer {
* current platform won't be changed.
*/
void SetCurrentPlatform(const gd::String& platformName);
#endif
///@}
@@ -402,7 +405,6 @@ class GD_CORE_API Project : public ObjectsContainer {
const gd::String& name,
const gd::String& platformName = "");
#if defined(GD_IDE_ONLY)
/**
* Create an event of the given type.
*
@@ -418,7 +420,6 @@ class GD_CORE_API Project : public ObjectsContainer {
std::shared_ptr<gd::BaseEvent> CreateEvent(
const gd::String& type, const gd::String& platformName = "");
///@}
#endif
/** \name Layouts management
* Members functions related to layout management.
@@ -456,14 +457,12 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
std::size_t GetLayoutPosition(const gd::String& name) const;
#if defined(GD_IDE_ONLY)
/**
* \brief Swap the specified layouts.
*
* Do nothing if indexes are not correct.
*/
void SwapLayouts(std::size_t first, std::size_t second);
#endif
/**
* \brief Return the number of layouts.
@@ -502,7 +501,6 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
void UnserializeFrom(const SerializerElement& element);
#if defined(GD_IDE_ONLY)
/**
* \brief Serialize the project.
*
@@ -524,13 +522,11 @@ class GD_CORE_API Project : public ObjectsContainer {
* Get the minor version of GDevelop used to save the project.
*/
unsigned int GetLastSaveGDBuildVersion() { return gdBuildVersion; };
#endif
/** \name External events management
* Members functions related to external events management.
*/
///@{
#if defined(GD_IDE_ONLY)
/** \name External events management
* Members functions related to external events management.
*/
///@{
/**
* Return true if external events called "name" exists.
*/
@@ -598,7 +594,6 @@ class GD_CORE_API Project : public ObjectsContainer {
* \brief Delete external events named "name".
*/
void RemoveExternalEvents(const gd::String& name);
#endif
///@}
/** \name External layout management
@@ -639,14 +634,12 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
std::size_t GetExternalLayoutPosition(const gd::String& name) const;
#if defined(GD_IDE_ONLY)
/**
* \brief Swap the specified external layouts.
*
* Do nothing if indexes are not correct.
*/
void SwapExternalLayouts(std::size_t first, std::size_t second);
#endif
/**
* Return the number of external layout.
@@ -690,12 +683,11 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetFirstLayout() { return firstLayout; }
///@}
///@}
/** \name Events functions extensions management
*/
///@{
#if defined(GD_IDE_ONLY)
/** \name Events functions extensions management
*/
///@{
/**
* \brief Check if events functions extension called "name" exists.
*/
@@ -770,7 +762,6 @@ class GD_CORE_API Project : public ObjectsContainer {
* \brief Remove all the events functions extensions.
*/
void ClearEventsFunctionsExtensions();
#endif
///@}
/** \name Resources management
@@ -833,13 +824,12 @@ class GD_CORE_API Project : public ObjectsContainer {
* behavior, events function name, etc...).
*/
static bool ValidateName(const gd::String& name);
///@}
///@}
/** \name External source files
* To manage external C++ or Javascript source files used by the game
*/
///@{
#if defined(GD_IDE_ONLY)
/** \name External source files
* To manage external C++ or Javascript source files used by the game
*/
///@{
/**
* \brief Return true if the game activated the use of external source files.
*/
@@ -884,8 +874,7 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::SourceFile& InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position = -1);
#endif
///@}
///@}
private:
/**
@@ -903,8 +892,8 @@ class GD_CORE_API Project : public ObjectsContainer {
///< are below this number )
bool verticalSync; ///< If true, must activate vertical synchronization.
gd::String scaleMode;
bool pixelsRounding; ///< If true, the rendering should stop pixel interpolation
///< of rendered objects.
bool pixelsRounding; ///< If true, the rendering should stop pixel
///< interpolation of rendered objects.
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
@@ -921,23 +910,22 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::VariablesContainer variables; ///< Initial global variables
std::vector<std::unique_ptr<gd::ExternalLayout> >
externalLayouts; ///< List of all externals layouts
#if defined(GD_IDE_ONLY)
std::vector<std::unique_ptr<gd::EventsFunctionsExtension> >
eventsFunctionsExtensions;
#endif
gd::ResourcesManager
resourcesManager; ///< Contains all resources used by the project
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
#if defined(GD_IDE_ONLY)
bool useExternalSourceFiles; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name
gd::String packageName; ///< Game package name
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.
gd::String packageName; ///< Game package name
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
bool
folderProject; ///< True if folder project, false if single file project.
gd::String
@@ -957,7 +945,6 @@ class GD_CORE_API Project : public ObjectsContainer {
///< time the project was saved.
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
///< time the project was saved.
#endif
};
} // namespace gd

View File

@@ -8,6 +8,7 @@
#include <iostream>
#include <map>
#include <unordered_set>
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Project.h"
@@ -130,6 +131,22 @@ std::vector<gd::String> ResourcesManager::GetAllResourceNames() const {
return allResources;
}
std::vector<gd::String> ResourcesManager::FindFilesNotInResources(
const std::vector<gd::String>& filesToCheck) const {
std::unordered_set<gd::String> resourceFiles;
for (const auto& resource: resources) {
resourceFiles.insert(resource->GetFile());
}
std::vector<gd::String> filesNotInResources;
for(const gd::String& file: filesToCheck) {
if (resourceFiles.find(file) == resourceFiles.end())
filesNotInResources.push_back(file);
}
return filesNotInResources;
}
#if defined(GD_IDE_ONLY)
std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
@@ -159,7 +176,8 @@ bool ImageResource::UpdateProperty(const gd::String& name,
return true;
}
std::map<gd::String, gd::PropertyDescriptor> AudioResource::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> AudioResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Preload as sound")]
.SetValue(preloadAsSound ? "true" : "false")

View File

@@ -243,7 +243,7 @@ class GD_CORE_API AudioResource : public Resource {
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
void SerializeTo(SerializerElement& element) const override;
#endif
@@ -447,11 +447,22 @@ class GD_CORE_API ResourcesManager {
*/
std::shared_ptr<Resource> CreateResource(const gd::String& kind);
/**
* Get a list containing all the resources.
*/
const std::vector<std::shared_ptr<Resource>>& GetAllResources() const { return resources; };
/**
* \brief Get a list containing the names of all resources.
*/
std::vector<gd::String> GetAllResourceNames() const;
/**
* \brief Return a list of the files, from the specified input list,
* that are not used as files by the resources.
*/
std::vector<gd::String> FindFilesNotInResources(const std::vector<gd::String>& filesToCheck) const;
#if defined(GD_IDE_ONLY)
/**
* \brief Return a (smart) pointer to a resource.

View File

@@ -6,6 +6,7 @@
#include "GDCore/String.h"
#include <algorithm>
#include <string.h>
#include <SFML/System/String.hpp>
@@ -283,6 +284,42 @@ String& String::insert( size_type pos, const String &str )
return *this;
}
String& String::replace_if(iterator i1, iterator i2, std::function<bool(char32_t)> p, const String &str)
{
String::size_type offset = 1;
iterator it = i1.base();
while(it < i2.base())
{
if (p(*it)) { replace(std::distance(begin(), it), offset, str); }
else { it++; }
}
return *this;
}
String& String::RemoveConsecutiveOccurrences(iterator i1, iterator i2, const char c)
{
std::vector<std::pair<size_type, size_type>> ranges_to_remove;
for(iterator current_index = i1.base(); current_index < i2.base(); current_index++)
{
if (*current_index == c){
iterator current_subindex = current_index;
std::advance(current_subindex, 1);
if (*current_subindex == c) {
while(current_subindex < end() && *current_subindex == c)
{
current_subindex++;
}
replace(std::distance(begin(), current_index),
std::distance(current_index, current_subindex),
c);
std::advance(current_index, 1);
}
}
}
return *this;
}
String& String::replace( iterator i1, iterator i2, const String &str )
{
m_string.replace(i1.base(), i2.base(), str.m_string);
@@ -290,6 +327,31 @@ String& String::replace( iterator i1, iterator i2, const String &str )
return *this;
}
String& String::replace( iterator i1, iterator i2, size_type n, const char c )
{
m_string.replace(i1.base(), i2.base(), n, c);
return *this;
}
String& String::replace( String::size_type pos, String::size_type len, const char c )
{
if(pos > size())
throw std::out_of_range("[gd::String::replace] starting pos greater than size");
iterator i1 = begin();
std::advance( i1, pos );
iterator i2 = i1;
while(i2 != end() && len > 0) //Increment "len" times and stop if end() is reached
{
++i2;
--len;
}
return replace( i1, i2, 1, c );
}
String& String::replace( String::size_type pos, String::size_type len, const String &str )
{
if(pos > size())

View File

@@ -438,15 +438,52 @@ public:
*/
String& replace( iterator i1, iterator i2, const String &str );
/**
* \brief Replace the portion of the String between **i1** and **i2** (**i2** not
* included) by **n** consecutive copies of character **c**.
* \return *this
*
* **Iterators :** All iterators may be invalidated.
*/
String& replace( iterator i1, iterator i2, size_type n, const char c );
/**
* \brief Replace the portion of the String between **pos** and **pos** + **len**
* (the character at **pos** + **len** is not included)
* (the character at **pos** + **len** is not included) with **str**.
* \return *this
*
* **Iterators :** All iterators may be invalidated.
*/
String& replace( size_type pos, size_type len, const String &str );
/**
* \brief Replace the portion of the String between **pos** and **pos** + **len**
* (the character at **pos** + **len** is not included) with the character **c**.
* \return *this
*
* **Iterators :** All iterators may be invalidated.
*/
String& replace( size_type pos, size_type len, const char c );
/**
* \brief Search in the portion of the String between **i1** and **i2** (**i2** not
* included) for characters matching predicate function **p** and replace them
* by the String **str**.
* \return *this
*
* **Iterators :** All iterators may be invalidated.
*/
String& replace_if( iterator i1, iterator i2, std::function<bool(char32_t)> p, const String &str );
/**
* \brief Remove consecutive occurrences of the character **c** in the portion of the
* between **i1** and **i2** (**i2** not included) to replace it by a single occurrence.
* \return *this
*
* **Iterators :** All iterators may be invalidated.
*/
String& RemoveConsecutiveOccurrences(iterator i1, iterator i2, const char c);
/**
* \brief Erase the characters between **first** and **last** (**last** not included).
* \param first an iterator to the first character to remove

View File

@@ -0,0 +1,132 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include <string>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "DummyPlatform.h"
#include "catch.hpp"
class ArbitraryResourceWorkerTest : public gd::ArbitraryResourceWorker {
public:
virtual void ExposeFile(gd::String& file) { files.push_back(file); };
virtual void ExposeImage(gd::String& imageName) {
images.push_back(imageName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontName) {
bitmapFonts.push_back(bitmapFontName);
};
virtual void ExposeAudio(gd::String& audioName) {
audios.push_back(audioName);
};
std::vector<gd::String> files;
std::vector<gd::String> images;
std::vector<gd::String> bitmapFonts;
std::vector<gd::String> audios;
};
TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
SECTION("Basics") {
gd::Project project;
project.GetResourcesManager().AddResource(
"res1", "path/to/file1.png", "image");
project.GetResourcesManager().AddResource(
"res2", "path/to/file2.png", "image");
project.GetResourcesManager().AddResource(
"res3", "path/to/file3.png", "image");
project.GetResourcesManager().AddResource(
"res4", "path/to/file4.png", "audio");
ArbitraryResourceWorkerTest worker;
project.ExposeResources(worker);
REQUIRE(worker.files.size() == 4);
REQUIRE(std::find(worker.files.begin(),
worker.files.end(),
"path/to/file2.png") != worker.files.end());
REQUIRE(std::find(worker.files.begin(),
worker.files.end(),
"path/to/file4.png") != worker.files.end());
SECTION("Object using a resource") {
gd::SpriteObject obj("myObject");
gd::Animation anim;
gd::Sprite sprite;
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
obj.AddAnimation(anim);
project.InsertObject(obj, 0);
worker.files.clear();
worker.images.clear();
project.ExposeResources(worker);
REQUIRE(worker.files.size() == 4);
REQUIRE(worker.images.size() == 1);
REQUIRE(worker.images[0] == "res1");
SECTION("ProjectResourcesAdder") {
std::vector<gd::String> uselessResources =
gd::ProjectResourcesAdder::GetAllUseless(project, "image");
REQUIRE(uselessResources.size() == 2);
gd::ProjectResourcesAdder::RemoveAllUseless(project, "image");
std::vector<gd::String> remainingResources =
project.GetResourcesManager().GetAllResourceNames();
REQUIRE(remainingResources.size() == 2);
REQUIRE(remainingResources[0] == "res1");
REQUIRE(remainingResources[1] == "res4");
}
}
}
SECTION("With events") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
project.GetResourcesManager().AddResource(
"res1", "path/to/file1.png", "image");
project.GetResourcesManager().AddResource(
"res2", "path/to/file2.png", "image");
project.GetResourcesManager().AddResource(
"res3", "path/to/file3.fnt", "bitmapFont");
project.GetResourcesManager().AddResource(
"res4", "path/to/file4.png", "audio");
ArbitraryResourceWorkerTest worker;
auto& layout = project.InsertNewLayout("Scene", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithResources");
instruction.SetParametersCount(3);
instruction.SetParameter(0, "res3");
instruction.SetParameter(1, "res1");
instruction.SetParameter(2, "res4");
standardEvent.GetActions().Insert(instruction);
layout.GetEvents().InsertEvent(standardEvent);
project.ExposeResources(worker);
REQUIRE(worker.bitmapFonts.size() == 1);
REQUIRE(worker.bitmapFonts[0] == "res3");
REQUIRE(worker.images.size() == 1);
REQUIRE(worker.images[0] == "res1");
REQUIRE(worker.audios.size() == 1);
REQUIRE(worker.audios[0] == "res4");
}
}

View File

@@ -96,6 +96,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
extension->SetExtensionInformation(
"MyExtension", "My testing extension", "", "", "");
extension
->AddAction("DoSomething",
"Do something",
@@ -106,6 +107,20 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"")
.AddParameter("expression", "Parameter 1 (a number)")
.SetFunctionName("doSomething");
extension
->AddAction("DoSomethingWithResources",
"Do something with resources",
"This does something with resources",
"Do something with resources please",
"",
"",
"")
.AddParameter("bitmapFontResource", "Parameter 1 (a bitmap font resource)")
.AddParameter("imageResource", "Parameter 2 (an image resource)")
.AddParameter("soundfile", "Parameter 3 (an audio resource)")
.SetFunctionName("doSomethingWithResources");
extension->AddExpression("GetNumber", "Get me a number", "", "", "")
.SetFunctionName("getNumber");
extension

View File

@@ -87,42 +87,27 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
}
}
SECTION("Operator (number)") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions1{
gd::ExpressionCompletionDescription::ForObject("number", "", 1, 1),
gd::ExpressionCompletionDescription::ForExpression("number", "", 1, 1)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions2{
gd::ExpressionCompletionDescription::ForObject("number", "", 2, 2),
gd::ExpressionCompletionDescription::ForExpression("number", "", 2, 2)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions3{
gd::ExpressionCompletionDescription::ForObject("number", "", 3, 3),
gd::ExpressionCompletionDescription::ForExpression("number", "", 3, 3)};
REQUIRE(getCompletionsFor("number", "1 + ", 1) == expectedCompletions1);
REQUIRE(getCompletionsFor("number", "1 + ", 2) == expectedCompletions2);
REQUIRE(getCompletionsFor("number", "1 + ", 3) == expectedCompletions3);
REQUIRE(getCompletionsFor("number", "1 + ", 1) == expectedEmptyCompletions);
REQUIRE(getCompletionsFor("number", "1 + ", 2) == expectedEmptyCompletions);
REQUIRE(getCompletionsFor("number", "1 + ", 3) == expectedEmptyCompletions);
}
SECTION("Operator (string)") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions3{
gd::ExpressionCompletionDescription::ForObject("string", "", 3, 3),
gd::ExpressionCompletionDescription::ForExpression("string", "", 3, 3)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions4{
gd::ExpressionCompletionDescription::ForObject("string", "", 4, 4),
gd::ExpressionCompletionDescription::ForExpression("string", "", 4, 4)};
std::vector<gd::ExpressionCompletionDescription> expectedCompletions5{
gd::ExpressionCompletionDescription::ForObject("string", "", 5, 5),
gd::ExpressionCompletionDescription::ForExpression("string", "", 5, 5)};
REQUIRE(getCompletionsFor("string", "\"a\" + ", 3) == expectedCompletions3);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 4) == expectedCompletions4);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 5) == expectedCompletions5);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 3) ==
expectedEmptyCompletions);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 4) ==
expectedEmptyCompletions);
REQUIRE(getCompletionsFor("string", "\"a\" + ", 5) ==
expectedEmptyCompletions);
}
SECTION("Free function") {
SECTION("Test 1") {
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Function", 0, 8)};
"string", "Function", 0, 8)};
std::vector<gd::ExpressionCompletionDescription> expectedExactCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Function", 0, 8)
"string", "Function", 0, 8)
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "Function(", 0) ==
expectedCompletions);
@@ -230,17 +215,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject(
"unknown", "MyObject", 0, 8)};
"string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehavior(
"Func", 9, 13, "MyObject"),
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", 9, 13, "MyObject")};
"string", "Func", 9, 13, "MyObject")};
std::vector<gd::ExpressionCompletionDescription>
expectedExactFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", 9, 13, "MyObject")
"string", "Func", 9, 13, "MyObject")
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 0) ==
expectedObjectCompletions);
@@ -329,7 +314,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
std::vector<gd::ExpressionCompletionDescription>
expectedObjectCompletions{
gd::ExpressionCompletionDescription::ForObject(
"unknown", "MyObject", 0, 8)};
"string", "MyObject", 0, 8)};
std::vector<gd::ExpressionCompletionDescription>
expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehavior(
@@ -337,11 +322,11 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
std::vector<gd::ExpressionCompletionDescription>
expectedFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", 21, 25, "MyObject", "MyBehavior")};
"string", "Func", 21, 25, "MyObject", "MyBehavior")};
std::vector<gd::ExpressionCompletionDescription>
expectedExactFunctionCompletions{
gd::ExpressionCompletionDescription::ForExpression(
"unknown", "Func", 21, 25, "MyObject", "MyBehavior")
"string", "Func", 21, 25, "MyObject", "MyBehavior")
.SetIsExact(true)};
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 0) ==
expectedObjectCompletions);

View File

@@ -971,6 +971,35 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
}
SECTION("Unfinished object function name of type string with parentheses") {
auto node = parser.ParseExpression("string", "MyObject.()");
REQUIRE(node != nullptr);
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionCall.objectName == "MyObject");
REQUIRE(objectFunctionCall.functionName == "");
REQUIRE(objectFunctionCall.type == "string");
}
SECTION("Unfinished object function name of type number with parentheses") {
auto node = parser.ParseExpression("number", "MyObject.()");
REQUIRE(node != nullptr);
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionCall.objectName == "MyObject");
REQUIRE(objectFunctionCall.functionName == "");
REQUIRE(objectFunctionCall.type == "number");
}
SECTION(
"Unfinished object function name of type number|string with "
"parentheses") {
auto node = parser.ParseExpression("number|string", "MyObject.()");
REQUIRE(node != nullptr);
auto &objectFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionCall.objectName == "MyObject");
REQUIRE(objectFunctionCall.functionName == "");
REQUIRE(objectFunctionCall.type == "number|string");
}
SECTION("Unfinished object behavior name") {
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
REQUIRE(node != nullptr);
@@ -981,6 +1010,67 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(objectFunctionName.behaviorFunctionName == "");
}
SECTION("Unfinished object behavior name of type string with parentheses") {
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::()");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
REQUIRE(objectFunctionName.functionName == "");
REQUIRE(objectFunctionName.type == "string");
}
SECTION("Unfinished object behavior name of type number with parentheses") {
auto node = parser.ParseExpression("number", "MyObject.MyBehavior::()");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
REQUIRE(objectFunctionName.functionName == "");
REQUIRE(objectFunctionName.type == "number");
}
SECTION(
"Unfinished object behavior name of type number|string with "
"parentheses") {
auto node =
parser.ParseExpression("number|string", "MyObject.MyBehavior::()");
REQUIRE(node != nullptr);
auto &objectFunctionName = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(objectFunctionName.objectName == "MyObject");
REQUIRE(objectFunctionName.behaviorName == "MyBehavior");
REQUIRE(objectFunctionName.functionName == "");
REQUIRE(objectFunctionName.type == "number|string");
}
SECTION("Unfinished free function name of type string with parentheses") {
auto node = parser.ParseExpression("string", "fun()");
REQUIRE(node != nullptr);
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(freeFunctionCall.objectName == "");
REQUIRE(freeFunctionCall.functionName == "fun");
REQUIRE(freeFunctionCall.type == "string");
}
SECTION("Unfinished free function name of type number with parentheses") {
auto node = parser.ParseExpression("number", "fun()");
REQUIRE(node != nullptr);
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(freeFunctionCall.objectName == "");
REQUIRE(freeFunctionCall.functionName == "fun");
REQUIRE(freeFunctionCall.type == "number");
}
SECTION(
"Unfinished free function name of type number|string with parentheses") {
auto node = parser.ParseExpression("number|string", "fun()");
REQUIRE(node != nullptr);
auto &freeFunctionCall = dynamic_cast<gd::FunctionCallNode &>(*node);
REQUIRE(freeFunctionCall.objectName == "");
REQUIRE(freeFunctionCall.functionName == "fun");
REQUIRE(freeFunctionCall.type == "number|string");
}
SECTION("Invalid function calls") {
{
auto node = parser.ParseExpression("number", "Idontexist(12)");

View File

@@ -0,0 +1,77 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include <string>
#include "DummyPlatform.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
TEST_CASE("InstructionsParameterMover", "[common][events]") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto& layout = project.InsertNewLayout("Scene", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomething");
instruction.SetParametersCount(3);
instruction.SetParameter(0, "Param1");
instruction.SetParameter(1, "Param2");
instruction.SetParameter(2, "Param3");
standardEvent.GetActions().Insert(instruction);
layout.GetEvents().InsertEvent(standardEvent);
gd::Instruction& insertedInstruction =
dynamic_cast<gd::StandardEvent&>(layout.GetEvents().GetEvent(0))
.GetActions()
.Get(0);
SECTION("Move a parameter from one valid index to another") {
gd::InstructionsParameterMover mover(
project, "MyExtension::DoSomething", 0, 2);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, mover);
REQUIRE(insertedInstruction.GetParameter(0).GetPlainString() == "Param2");
REQUIRE(insertedInstruction.GetParameter(1).GetPlainString() == "Param3");
REQUIRE(insertedInstruction.GetParameter(2).GetPlainString() == "Param1");
}
SECTION("Move a parameter to an out of bound new index") {
gd::InstructionsParameterMover mover(
project, "MyExtension::DoSomething", 0, 99);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, mover);
REQUIRE(insertedInstruction.GetParameter(0).GetPlainString() == "Param2");
REQUIRE(insertedInstruction.GetParameter(1).GetPlainString() == "Param3");
REQUIRE(insertedInstruction.GetParameter(2).GetPlainString() == "Param1");
}
SECTION("Don't move a parameter if out of bound old index") {
gd::InstructionsParameterMover mover(
project, "MyExtension::DoSomething", 99, 2);
gd::WholeProjectRefactorer::ExposeProjectEvents(project, mover);
REQUIRE(insertedInstruction.GetParameter(0).GetPlainString() == "Param1");
REQUIRE(insertedInstruction.GetParameter(1).GetPlainString() == "Param2");
REQUIRE(insertedInstruction.GetParameter(2).GetPlainString() == "Param3");
}
}

View File

@@ -7,27 +7,13 @@
* @file Tests covering common features of GDevelop Core.
*/
#include <string>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
class ArbitraryResourceWorkerTest : public gd::ArbitraryResourceWorker {
public:
virtual void ExposeFile(gd::String& file) { files.push_back(file); };
virtual void ExposeImage(gd::String& imageName) {
images.push_back(imageName);
};
std::vector<gd::String> files;
std::vector<gd::String> images;
};
TEST_CASE("Resources", "[common][resources]") {
SECTION("Basics") {
gd::ImageResource image;
@@ -46,58 +32,4 @@ TEST_CASE("Resources", "[common][resources]") {
image.SetFile("Lots\\\\Of\\\\\\..\\Backslashs");
REQUIRE(image.GetFile() == "Lots//Of///../Backslashs");
}
SECTION("ArbitraryResourceWorker") {
gd::Project project;
project.GetResourcesManager().AddResource(
"res1", "path/to/file1.png", "image");
project.GetResourcesManager().AddResource(
"res2", "path/to/file2.png", "image");
project.GetResourcesManager().AddResource(
"res3", "path/to/file3.png", "image");
project.GetResourcesManager().AddResource(
"res4", "path/to/file4.png", "audio");
ArbitraryResourceWorkerTest worker;
project.ExposeResources(worker);
REQUIRE(worker.files.size() == 4);
REQUIRE(std::find(worker.files.begin(),
worker.files.end(),
"path/to/file2.png") != worker.files.end());
REQUIRE(std::find(worker.files.begin(),
worker.files.end(),
"path/to/file4.png") != worker.files.end());
SECTION("Object using a resource") {
gd::SpriteObject obj("myObject");
gd::Animation anim;
gd::Sprite sprite;
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
obj.AddAnimation(anim);
project.InsertObject(obj, 0);
worker.files.clear();
worker.images.clear();
project.ExposeResources(worker);
REQUIRE(worker.files.size() == 4);
REQUIRE(worker.images.size() == 1);
REQUIRE(worker.images[0] == "res1");
SECTION("ProjectResourcesAdder") {
std::vector<gd::String> uselessResources =
gd::ProjectResourcesAdder::GetAllUseless(project, "image");
REQUIRE(uselessResources.size() == 2);
gd::ProjectResourcesAdder::RemoveAllUseless(project, "image");
std::vector<gd::String> remainingResources =
project.GetResourcesManager().GetAllResourceNames();
REQUIRE(remainingResources.size() == 2);
REQUIRE(remainingResources[0] == "res1");
REQUIRE(remainingResources[1] == "res4");
}
}
}
}

View File

@@ -2,6 +2,8 @@ namespace gdjs {
declare var admob: any;
export namespace adMob {
const logger = new gdjs.Logger('AdMob');
export enum AdSizeType {
BANNER,
LARGE_BANNER,
@@ -127,13 +129,13 @@ namespace gdjs {
() => {
bannerShowing = true;
bannerLoading = false;
console.info('AdMob banner successfully shown.');
logger.info('AdMob banner successfully shown.');
},
(error) => {
bannerShowing = false;
bannerLoading = false;
bannerErrored = true;
console.error('Error while showing an AdMob banner:', error);
logger.error('Error while showing an AdMob banner:', error);
}
);
};
@@ -190,14 +192,14 @@ namespace gdjs {
})
.then(
() => {
console.info('AdMob interstitial successfully loaded.');
logger.info('AdMob interstitial successfully loaded.');
if (displayWhenLoaded) showInterstitial();
},
(error) => {
interstitialLoading = false;
interstitialReady = false;
interstitialErrored = true;
console.error('Error while loading a interstitial:', error);
logger.error('Error while loading a interstitial:', error);
}
);
};
@@ -216,7 +218,7 @@ namespace gdjs {
(error) => {
interstitialShowing = false;
interstitialErrored = true;
console.error('Error while trying to show an interstitial:', error);
logger.error('Error while trying to show an interstitial:', error);
}
);
};
@@ -270,7 +272,7 @@ namespace gdjs {
})
.then(
() => {
console.info('AdMob reward video successfully loaded.');
logger.info('AdMob reward video successfully loaded.');
if (displayWhenLoaded) showVideo();
},
@@ -278,7 +280,7 @@ namespace gdjs {
videoLoading = false;
videoReady = false;
videoErrored = true;
console.error('Error while loading a reward video:', error);
logger.error('Error while loading a reward video:', error);
}
);
};
@@ -298,7 +300,7 @@ namespace gdjs {
(error) => {
videoShowing = false;
videoErrored = true;
console.error('Error while trying to show a reward video:', error);
logger.error('Error while trying to show a reward video:', error);
}
);
};

View File

@@ -256,11 +256,7 @@ module.exports = {
.addAction(
'SetBitmapFontAndTextureAtlasResourceName',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.') +
' ' +
_(
'The resource name can be found in: `Project Manager > Game settings > Resources`.'
),
_('Change the Bitmap Font and/or the atlas image used by the object.'),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
@@ -269,7 +265,7 @@ module.exports = {
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter('bitmapFont', _('Bitmap font resource name'), '', false)
.addParameter('bitmapFontResource', _('Bitmap font resource name'), '', false)
.setParameterLongDescription(
'The resource name of the font file, without quotes.'
)
@@ -658,6 +654,11 @@ module.exports = {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
// Temporarily go back to the default font, as the PIXI.BitmapText
// object does not support being displayed with a font not installed at all.
// It will be replaced as soon as the proper font is loaded.
this._pixiObject.fontName = getDefaultBitmapFont().font;
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(

View File

@@ -83,6 +83,30 @@ module.exports = {
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
.setFunctionName('gdjs.evtTools.debuggerTools.enableDebugDraw');
extension
.addAction(
'ConsoleLog',
_('Log a message to the console'),
_("Logs a message to the debugger's console."),
_(
'Log message _PARAM0_ of type _PARAM1_ to the console in group _PARAM2_'
),
_('Debugger Tools'),
'res/actions/bug32.png',
'res/actions/bug32.png'
)
.addParameter('string', 'Message to log', '', false)
.addParameter(
'stringWithSelector',
'Message type',
'["info", "warning", "error"]',
true
)
.addParameter('string', 'Group of messages', '', true)
.getCodeExtraInformation()
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
.setFunctionName('gdjs.evtTools.debuggerTools.log');
return extension;
},
runExtensionSanityTests: function (

View File

@@ -13,6 +13,20 @@ namespace gdjs {
runtimeScene.getGame().pause(true);
};
/**
* Logs a message to the console.
* @param message - The message to log.
* @param type - The type of log (info, warning or error).
* @param group - The group of messages it belongs to.
*/
export const log = function (
message: string,
type: 'info' | 'warning' | 'error',
group: string
) {
gdjs.Logger.getLoggerOutput().log(group, message, type, false);
};
/**
* Enable or disable the debug draw.
* @param runtimeScene - The current scene.

View File

@@ -62,7 +62,7 @@ module.exports = {
"JsPlatform/Extensions/orientation_alpha24.png",
"JsPlatform/Extensions/orientation_alpha32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -82,7 +82,7 @@ module.exports = {
"JsPlatform/Extensions/orientation_beta24.png",
"JsPlatform/Extensions/orientation_beta32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -102,7 +102,7 @@ module.exports = {
"JsPlatform/Extensions/orientation_gamma24.png",
"JsPlatform/Extensions/orientation_gamma32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -228,7 +228,7 @@ module.exports = {
"JsPlatform/Extensions/motion_rotation_alpha24.png",
"JsPlatform/Extensions/motion_rotation_alpha32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -248,7 +248,7 @@ module.exports = {
"JsPlatform/Extensions/motion_rotation_beta24.png",
"JsPlatform/Extensions/motion_rotation_beta32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -268,7 +268,7 @@ module.exports = {
"JsPlatform/Extensions/motion_rotation_gamma24.png",
"JsPlatform/Extensions/motion_rotation_gamma32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -288,7 +288,7 @@ module.exports = {
"JsPlatform/Extensions/motion_acceleration_x24.png",
"JsPlatform/Extensions/motion_acceleration_x32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -308,7 +308,7 @@ module.exports = {
"JsPlatform/Extensions/motion_acceleration_y24.png",
"JsPlatform/Extensions/motion_acceleration_y32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(
@@ -328,7 +328,7 @@ module.exports = {
"JsPlatform/Extensions/motion_acceleration_z24.png",
"JsPlatform/Extensions/motion_acceleration_z32.png"
)
.addParameter("relationalOperator", _("Sign of the test"))
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.getCodeExtraInformation()
.setIncludeFile(

View File

@@ -1,6 +1,8 @@
// @ts-nocheck - Weird usage of `this` in this file. Should be refactored.
namespace gdjs {
const logger = new gdjs.Logger('Dialogue tree');
gdjs.dialogueTree = {};
gdjs.dialogueTree.runner = new bondage.Runner();
@@ -21,7 +23,7 @@ namespace gdjs {
gdjs.dialogueTree.startFrom(startDialogueNode);
}
} catch (e) {
console.error(e);
logger.error('Error while loading from scene variable: ', e);
}
};
@@ -42,10 +44,7 @@ namespace gdjs {
.getJsonManager()
.loadJson(jsonResourceName, function (error, content) {
if (error) {
console.error(
'An error happened while loading JSON resource:',
error
);
logger.error('An error happened while loading JSON resource:', error);
} else {
if (!content) {
return;
@@ -54,7 +53,7 @@ namespace gdjs {
try {
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
} catch (error) {
console.error(
logger.error(
'An error happened while loading parsing the dialogue tree data:',
error
);
@@ -155,7 +154,7 @@ namespace gdjs {
this.clipTextEnd >= this.dialogueText.length
) {
if (gdjs.dialogueTree.getVariable('debug')) {
console.warn(
logger.warn(
'Scroll completed:',
this.clipTextEnd,
'/',
@@ -244,7 +243,7 @@ namespace gdjs {
gdjs.dialogueTree.pauseScrolling = false;
commandCalls.splice(index, 1);
if (gdjs.dialogueTree.getVariable('debug')) {
console.info('CMD:', call);
logger.info('CMD:', call);
}
}, parseInt(call.params[1], 10));
}
@@ -252,7 +251,7 @@ namespace gdjs {
gdjs.dialogueTree.commandParameters = call.params;
commandCalls.splice(index, 1);
if (gdjs.dialogueTree.getVariable('debug')) {
console.info('CMD:', call);
logger.info('CMD:', call);
}
return true;
}
@@ -366,7 +365,7 @@ namespace gdjs {
this.dialogueData = this.dialogue.next().value;
gdjs.dialogueTree.goToNextDialogueLine();
} catch (error) {
console.error(
logger.error(
`An error happened when trying to access the dialogue branch!`,
error
);
@@ -562,7 +561,7 @@ namespace gdjs {
this.selectedOption = -1;
this.selectedOptionUpdated = false;
if (gdjs.dialogueTree.getVariable('debug')) {
console.info('parsing:', this.dialogueData);
logger.info('Parsing:', this.dialogueData);
}
if (!this.dialogueData) {
gdjs.dialogueTree.stopRunningDialogue();
@@ -739,7 +738,7 @@ namespace gdjs {
* @param key The name of the variable you want to get the value of
*/
gdjs.dialogueTree.getVariable = function (key: string) {
if (this.dialogueIsRunning && key in this.runner.variables.data) {
if (this.runner.variables && key in this.runner.variables.data) {
return this.runner.variables.get(key);
}
return '';
@@ -754,7 +753,7 @@ namespace gdjs {
key: string,
value: string | boolean | number
) {
if (this.dialogueIsRunning && key in this.runner.variables.data) {
if (this.runner.variables && key in this.runner.variables.data) {
return this.runner.variables.get(key) === value;
}
return false;
@@ -797,7 +796,7 @@ namespace gdjs {
gdjs.dialogueTree.loadState = function (inputVariable: gdjs.Variable) {
const loadedState = inputVariable.toJSObject();
if (!loadedState) {
console.error('Load state variable is empty:', inputVariable);
logger.error('Load state variable is empty:', inputVariable);
return;
}
try {
@@ -808,7 +807,7 @@ namespace gdjs {
gdjs.dialogueTree.runner.variables.set(key, value);
});
} catch (e) {
console.error('Failed to load state from variable:', inputVariable, e);
logger.error('Failed to load state from variable:', inputVariable, e);
}
};

View File

@@ -1,6 +1,6 @@
/*
GDevelop - Draggable Behavior Extension
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
Copyright (c) 2013-2021 Florian Rival (Florian.Rival@gmail.com)
*/
namespace gdjs {
@@ -9,11 +9,11 @@ namespace gdjs {
* moved using the mouse.
*/
export class DraggableRuntimeBehavior extends gdjs.RuntimeBehavior {
_dragged: boolean = false;
_touchId: any = null;
_mouse: boolean = false;
_xOffset: number = 0;
_yOffset: number = 0;
/**
* The manager that currently handles the dragging of the owner if any.
* When the owner is being dragged, no other manager can start dragging it.
*/
_draggedByDraggableManager: DraggableManager | null = null;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
@@ -33,59 +33,47 @@ namespace gdjs {
}
_endDrag() {
if (this._dragged && this._mouse) {
DraggableRuntimeBehavior.mouseDraggingSomething = false;
if (this._draggedByDraggableManager) {
this._draggedByDraggableManager.endDrag();
}
if (this._dragged && this._touchId !== null) {
DraggableRuntimeBehavior.touchDraggingSomething[this._touchId] = false;
}
this._dragged = false;
this._mouse = false;
this._touchId = null;
this._draggedByDraggableManager = null;
}
_dismissDrag() {
this._draggedByDraggableManager = null;
}
_tryBeginDrag(runtimeScene) {
if (this._dragged) {
if (this._draggedByDraggableManager) {
return false;
}
const inputManager = runtimeScene.getGame().getInputManager();
//Try mouse
const mouseDraggableManager = DraggableManager.getMouseManager(
runtimeScene
);
if (
inputManager.isMouseButtonPressed(0) &&
!DraggableRuntimeBehavior.leftPressedLastFrame &&
!DraggableRuntimeBehavior.mouseDraggingSomething
!mouseDraggableManager.isDragging(this)
) {
const mousePos = runtimeScene
.getLayer(this.owner.getLayer())
.convertCoords(inputManager.getMouseX(), inputManager.getMouseY());
if (this.owner.insideObject(mousePos[0], mousePos[1])) {
this._dragged = true;
this._mouse = true;
this._xOffset = mousePos[0] - this.owner.getX();
this._yOffset = mousePos[1] - this.owner.getY();
DraggableRuntimeBehavior.mouseDraggingSomething = true;
if (mouseDraggableManager.tryAndTakeDragging(runtimeScene, this)) {
this._draggedByDraggableManager = mouseDraggableManager;
return true;
}
} else {
//Try touches
const touchIds = inputManager.getStartedTouchIdentifiers();
for (let i = 0; i < touchIds.length; ++i) {
if (DraggableRuntimeBehavior.touchDraggingSomething[touchIds[i]]) {
const touchDraggableManager = DraggableManager.getTouchManager(
runtimeScene,
touchIds[i]
);
if (touchDraggableManager.isDragging(this)) {
continue;
}
const touchPos = runtimeScene
.getLayer(this.owner.getLayer())
.convertCoords(
inputManager.getTouchX(touchIds[i]),
inputManager.getTouchY(touchIds[i])
);
if (this.owner.insideObject(touchPos[0], touchPos[1])) {
this._dragged = true;
this._touchId = touchIds[i];
this._xOffset = touchPos[0] - this.owner.getX();
this._yOffset = touchPos[1] - this.owner.getY();
DraggableRuntimeBehavior.touchDraggingSomething[touchIds[i]] = true;
if (touchDraggableManager.tryAndTakeDragging(runtimeScene, this)) {
this._draggedByDraggableManager = touchDraggableManager;
return true;
}
}
@@ -94,45 +82,17 @@ namespace gdjs {
}
_shouldEndDrag(runtimeScene) {
if (!this._dragged) {
if (!this._draggedByDraggableManager) {
return false;
}
const inputManager = runtimeScene.getGame().getInputManager();
if (this._mouse) {
return !inputManager.isMouseButtonPressed(0);
} else {
if (this._touchId !== null) {
return (
inputManager.getAllTouchIdentifiers().indexOf(this._touchId) === -1
);
}
}
return false;
return this._draggedByDraggableManager.shouldEndDrag(runtimeScene, this);
}
_updateObjectPosition(runtimeScene) {
if (!this._dragged) {
if (!this._draggedByDraggableManager) {
return false;
}
const inputManager = runtimeScene.getGame().getInputManager();
if (this._mouse) {
const mousePos = runtimeScene
.getLayer(this.owner.getLayer())
.convertCoords(inputManager.getMouseX(), inputManager.getMouseY());
this.owner.setX(mousePos[0] - this._xOffset);
this.owner.setY(mousePos[1] - this._yOffset);
} else {
if (this._touchId !== null) {
const touchPos = runtimeScene
.getLayer(this.owner.getLayer())
.convertCoords(
inputManager.getTouchX(this._touchId),
inputManager.getTouchY(this._touchId)
);
this.owner.setX(touchPos[0] - this._xOffset);
this.owner.setY(touchPos[1] - this._yOffset);
}
}
this._draggedByDraggableManager.updateObjectPosition(runtimeScene, this);
return true;
}
@@ -145,25 +105,214 @@ namespace gdjs {
}
doStepPostEvents(runtimeScene) {
DraggableRuntimeBehavior.leftPressedLastFrame = runtimeScene
const mouseDraggableManager = DraggableManager.getMouseManager(
runtimeScene
);
mouseDraggableManager.leftPressedLastFrame = runtimeScene
.getGame()
.getInputManager()
.isMouseButtonPressed(0);
}
isDragged(runtimeScene): boolean {
return this._dragged;
return !!this._draggedByDraggableManager;
}
}
/**
* Handle the dragging
*/
abstract class DraggableManager {
/**
* The object has left its original position.
* When true, the search for the best object to drag has ended.
*/
protected _draggingSomething = false;
/**
* The behavior of the object that is being dragged and that is the best one (i.e: highest Z order) found.
*/
protected _draggableBehavior: gdjs.DraggableRuntimeBehavior | null = null;
protected _xOffset: number = 0;
protected _yOffset: number = 0;
constructor(runtimeScene: gdjs.RuntimeScene) {}
/**
* Get the platforms manager of a scene.
*/
static getMouseManager(
runtimeScene: gdjs.RuntimeScene
): MouseDraggableManager {
// @ts-ignore
if (!runtimeScene.mouseDraggableManager) {
//Create the shared manager if necessary.
// @ts-ignore
runtimeScene.mouseDraggableManager = new MouseDraggableManager(
runtimeScene
);
}
// @ts-ignore
return runtimeScene.mouseDraggableManager;
}
//Static property used to avoid start dragging an object while another is dragged.
static mouseDraggingSomething = false;
/**
* Get the platforms manager of a scene.
*/
static getTouchManager(
runtimeScene: gdjs.RuntimeScene,
touchId: integer
): DraggableManager {
// @ts-ignore
if (!runtimeScene.touchDraggableManagers) {
//Create the shared manager if necessary.
// @ts-ignore
runtimeScene.touchDraggableManagers = [];
}
// @ts-ignore
if (!runtimeScene.touchDraggableManagers[touchId]) {
//Create the shared manager if necessary.
// @ts-ignore
runtimeScene.touchDraggableManagers[
touchId
] = new TouchDraggableManager(runtimeScene, touchId);
}
// @ts-ignore
return runtimeScene.touchDraggableManagers[touchId];
}
//Static property used to avoid start dragging an object while another is dragged by the same touch.
static touchDraggingSomething: Array<boolean> = [];
tryAndTakeDragging(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
) {
if (
this._draggableBehavior &&
draggableRuntimeBehavior.owner.getZOrder() <=
this._draggableBehavior.owner.getZOrder()
) {
return false;
}
const position = this.getPosition(runtimeScene, draggableRuntimeBehavior);
if (
!draggableRuntimeBehavior.owner.insideObject(position[0], position[1])
) {
return false;
}
if (this._draggableBehavior) {
// The previous best object to drag will not be dragged.
this._draggableBehavior._dismissDrag();
}
this._draggableBehavior = draggableRuntimeBehavior;
this._xOffset = position[0] - draggableRuntimeBehavior.owner.getX();
this._yOffset = position[1] - draggableRuntimeBehavior.owner.getY();
return true;
}
//Static property used to only start dragging when clicking.
static leftPressedLastFrame = false;
updateObjectPosition(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
) {
const position = this.getPosition(runtimeScene, draggableRuntimeBehavior);
if (
draggableRuntimeBehavior.owner.getX() != position[0] - this._xOffset ||
draggableRuntimeBehavior.owner.getY() != position[1] - this._yOffset
) {
draggableRuntimeBehavior.owner.setX(position[0] - this._xOffset);
draggableRuntimeBehavior.owner.setY(position[1] - this._yOffset);
this._draggingSomething = true;
}
}
endDrag() {
this._draggingSomething = false;
this._draggableBehavior = null;
}
abstract isDragging(
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean;
abstract shouldEndDrag(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean;
abstract getPosition(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint;
}
/**
* Handle the dragging by mouse
*/
class MouseDraggableManager extends DraggableManager {
/** Used to only start dragging when clicking. */
leftPressedLastFrame = false;
constructor(runtimeScene: gdjs.RuntimeScene) {
super(runtimeScene);
}
isDragging(draggableRuntimeBehavior: DraggableRuntimeBehavior): boolean {
return this.leftPressedLastFrame || this._draggingSomething;
}
getPosition(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint {
const inputManager = runtimeScene.getGame().getInputManager();
return runtimeScene
.getLayer(draggableRuntimeBehavior.owner.getLayer())
.convertCoords(inputManager.getMouseX(), inputManager.getMouseY());
}
shouldEndDrag(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean {
const inputManager = runtimeScene.getGame().getInputManager();
return !inputManager.isMouseButtonPressed(0);
}
}
/**
* Handle the dragging by touch
*/
class TouchDraggableManager extends DraggableManager {
private _touchId: integer;
constructor(runtimeScene: gdjs.RuntimeScene, touchId: integer) {
super(runtimeScene);
this._touchId = touchId;
}
isDragging(draggableRuntimeBehavior: DraggableRuntimeBehavior): boolean {
return this._draggingSomething;
}
getPosition(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): FloatPoint {
const inputManager = runtimeScene.getGame().getInputManager();
return runtimeScene
.getLayer(draggableRuntimeBehavior.owner.getLayer())
.convertCoords(
inputManager.getTouchX(this._touchId),
inputManager.getTouchY(this._touchId)
);
}
shouldEndDrag(
runtimeScene: gdjs.RuntimeScene,
draggableRuntimeBehavior: DraggableRuntimeBehavior
): boolean {
const inputManager = runtimeScene.getGame().getInputManager();
return (
inputManager.getAllTouchIdentifiers().indexOf(this._touchId) === -1
);
}
}
gdjs.registerBehavior(
'DraggableBehavior::Draggable',
gdjs.DraggableRuntimeBehavior

View File

@@ -51,112 +51,210 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
runtimeScene.addObject(object);
runtimeScene.addObject(object2);
it('should handle mouse', function () {
object.setPosition(450, 500);
describe('(mouse)', function () {
it('can drag an object', function () {
object.setPosition(450, 500);
//Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Mouse move with dragging
runtimeGame.getInputManager().onMouseMove(600, 600);
runtimeScene.renderAndStep(1000 / 60);
// Mouse move without dragging
runtimeGame.getInputManager().onMouseMove(600, 600);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Start dragging again
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
// Start dragging again
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
[false, true].forEach((firstInFront) => {
it(`must drag the object in front (${
firstInFront ? '1st object' : '2nd object'
} in front)`, function () {
object.setPosition(450, 500);
object2.setPosition(450, 500);
if (firstInFront) {
object.setZOrder(2);
object2.setZOrder(1);
} else {
object.setZOrder(1);
object2.setZOrder(2);
}
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(450, 500);
runtimeGame
.getInputManager()
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onMouseMove(750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame
.getInputManager()
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
runtimeScene.renderAndStep(1000 / 60);
if (firstInFront) {
// The 1st object moved
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object2.getX()).to.be(450);
expect(object2.getY()).to.be(500);
} else {
// The 2nd object moved
expect(object.getX()).to.be(450);
expect(object.getY()).to.be(500);
expect(object2.getX()).to.be(750);
expect(object2.getY()).to.be(600);
}
});
});
});
it('should handle touches', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
describe('(touch)', function () {
it('can drag an object', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
//Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Move another unrelated touch
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
// Move another unrelated touch
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
//Start drag'n'drop with another touch
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
// Start drag'n'drop with another touch
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onFrameEnded();
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
it('should handle multitouch', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
object2.setPosition(650, 600);
expect(object.getX()).to.be(850);
expect(object.getY()).to.be(700);
});
it('can drag 2 objects with multitouch', function () {
runtimeGame.getInputManager().touchSimulateMouse(false);
object.setPosition(450, 500);
object2.setPosition(650, 600);
//Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(2);
// Drag'n'drop
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(1);
runtimeGame.getInputManager().onTouchEnd(2);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(700);
expect(object2.getX()).to.be(100);
expect(object2.getY()).to.be(200);
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(700);
expect(object2.getX()).to.be(100);
expect(object2.getY()).to.be(200);
// Avoid side effects on the following test cases
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
});
[false, true].forEach((firstInFront) => {
it(`must drag the object in front (${
firstInFront ? '1st object' : '2nd object'
} in front)`, function () {
object.setPosition(450, 500);
object2.setPosition(450, 500);
if (firstInFront) {
object.setZOrder(2);
object2.setZOrder(1);
} else {
object.setZOrder(1);
object2.setZOrder(2);
}
// Drag'n'drop
runtimeGame.getInputManager().touchSimulateMouse(false);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
runtimeGame.getInputManager().onTouchEnd(0);
runtimeGame.getInputManager().onTouchEnd(1);
runtimeScene.renderAndStep(1000 / 60);
runtimeGame.getInputManager().onFrameEnded();
if (firstInFront) {
// The 1st object moved
expect(object.getX()).to.be(750);
expect(object.getY()).to.be(600);
expect(object2.getX()).to.be(450);
expect(object2.getY()).to.be(500);
} else {
// The 2nd object moved
expect(object.getX()).to.be(450);
expect(object.getY()).to.be(500);
expect(object2.getX()).to.be(750);
expect(object2.getY()).to.be(600);
}
});
});
});
});

View File

@@ -1,5 +1,6 @@
//A simple PIXI filter doing some color changes
namespace gdjs {
const logger = new gdjs.Logger('Dummy effect');
import PIXI = GlobalPIXIModule.PIXI;
const DummyPixiFilter = function () {
@@ -46,7 +47,7 @@ namespace gdjs {
// `effectData.stringParameters.someImage`
// `effectData.stringParameters.someColor`
// `effectData.booleanParameters.someBoolean`
console.info(
logger.info(
'The PIXI texture found for the Dummy Effect (not actually used):',
(layer
.getRuntimeScene()

View File

@@ -1,4 +1,6 @@
namespace gdjs {
const logger = new gdjs.Logger('Dummy behavior');
/**
* The DummyRuntimeBehavior changes a variable in the object that is owning
* it, at every tick before events are run, to set it to the string that was
@@ -20,7 +22,7 @@ namespace gdjs {
this._textToSet = behaviorData.property1;
// You can also run arbitrary code at the creation of the behavior:
console.log('DummyRuntimeBehavior was created for object:', owner);
logger.log('DummyRuntimeBehavior was created for object:', owner);
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {

View File

@@ -1,4 +1,6 @@
namespace gdjs {
const logger = new gdjs.Logger('Dummy object');
/**
* A dummy object doing showing a text on screen.
* @ignore
@@ -126,9 +128,9 @@ namespace gdjs {
* A dummy method that can be called from events
*/
myMethod(number1: float, text1: string) {
console.log('Congrats, this method was called on a DummyRuntimeObject');
console.log('Here is the object:', this);
console.log('Here are the arguments passed from events:', number1, text1);
logger.log('Congrats, this method was called on a DummyRuntimeObject');
logger.log('Here is the object:', this);
logger.log('Here are the arguments passed from events:', number1, text1);
}
}
gdjs.registerObject(

View File

@@ -1,4 +1,5 @@
namespace gdjs {
const logger = new gdjs.Logger('Dummy behavior (with shared data)');
export class DummyWithSharedDataRuntimeBehavior extends gdjs.RuntimeBehavior {
_textToSet: string;
@@ -20,11 +21,11 @@ namespace gdjs {
this._textToSet = (sharedData as any).sharedProperty1;
// You can also run arbitrary code at the creation of the behavior:
console.log(
logger.log(
'DummyWithSharedDataRuntimeBehavior was created for object:',
owner
);
console.log('The shared data are:', sharedData);
logger.log('The shared data are:', sharedData);
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {

View File

@@ -1,4 +1,6 @@
namespace gdjs {
const logger = new gdjs.Logger('Example extension');
export namespace evtTools {
/**
* This is an example of some functions that can be used through events.
@@ -24,7 +26,7 @@ namespace gdjs {
* that will be called at this moment.
*/
gdjs.registerRuntimeSceneLoadedCallback(function (runtimeScene) {
console.log('A gdjs.RuntimeScene was loaded:', runtimeScene);
logger.log('A gdjs.RuntimeScene was loaded:', runtimeScene);
});
/**
@@ -32,7 +34,7 @@ namespace gdjs {
* that will be called at this moment.
*/
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
console.log('A gdjs.RuntimeScene was unloaded:', runtimeScene);
logger.log('A gdjs.RuntimeScene was unloaded:', runtimeScene);
});
/**
@@ -42,7 +44,7 @@ namespace gdjs {
runtimeScene,
runtimeObject
) {
console.log(
logger.log(
'A gdjs.RuntimeObject was deleted from a gdjs.RuntimeScene:',
runtimeScene,
runtimeObject
@@ -50,7 +52,7 @@ namespace gdjs {
});
// Finally, note that you can also simply run code here. Most of the time you shouldn't need it though.
console.log(
logger.log(
'gdjs.exampleJsExtension was created, with myGlobalString containing:' +
myGlobalString
);

View File

@@ -1,4 +1,5 @@
namespace gdjs {
const logger = new gdjs.Logger('Facebook instant games');
export namespace evtTools {
export namespace facebookInstantGames {
export let _preloadedInterstitial: any = null;
@@ -153,12 +154,12 @@ namespace gdjs {
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = true;
console.info('Facebook Instant Games interstitial preloaded.');
logger.info('Facebook Instant Games interstitial preloaded.');
})
.catch(function (err) {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = false;
console.error('Interstitial failed to preload: ' + err.message);
logger.error('Interstitial failed to preload: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
});
};
@@ -173,10 +174,10 @@ namespace gdjs {
gdjs.evtTools.facebookInstantGames._preloadedInterstitial
.showAsync()
.then(function () {
console.info('Facebook Instant Games interstitial shown.');
logger.info('Facebook Instant Games interstitial shown.');
})
.catch(function (err) {
console.error('Interstitial failed to show: ' + err.message);
logger.error('Interstitial failed to show: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
})
.then(function () {
@@ -207,12 +208,12 @@ namespace gdjs {
.then(function () {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = true;
console.info('Facebook Instant Games rewarded video preloaded.');
logger.info('Facebook Instant Games rewarded video preloaded.');
})
.catch(function (err) {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoading = false;
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = false;
console.error('Rewarded video failed to preload: ' + err.message);
logger.error('Rewarded video failed to preload: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
});
};
@@ -227,10 +228,10 @@ namespace gdjs {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideo
.showAsync()
.then(function () {
console.info('Facebook Instant Games rewarded video shown.');
logger.info('Facebook Instant Games rewarded video shown.');
})
.catch(function (err) {
console.error('Rewarded video failed to show: ' + err.message);
logger.error('Rewarded video failed to show: ' + err.message);
errorVariable.setString(err.message || 'Unknown error');
})
.then(function () {
@@ -242,7 +243,7 @@ namespace gdjs {
return gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded;
};
if (typeof FBInstant === 'undefined' && typeof window !== 'undefined') {
console.log('Creating a mocked version of Facebook Instant Games.');
logger.log('Creating a mocked version of Facebook Instant Games.');
/**
* A mocked Leaderboard, part of the mock of FBInstant.
@@ -295,7 +296,7 @@ namespace gdjs {
showAsync(): Promise<void> {
if (this._isLoaded) {
console.info(
logger.info(
'In a real Instant Game, a video reward should have been shown to the user.'
);
return Promise.resolve();
@@ -318,7 +319,7 @@ namespace gdjs {
showAsync(): Promise<void> {
if (this._isLoaded) {
console.info(
logger.info(
'In a real Instant Game, an interstitial should have been shown to the user.'
);
return Promise.resolve();

View File

@@ -1,4 +1,5 @@
namespace gdjs {
const logger = new gdjs.Logger('Filesystem');
export namespace fileSystem {
// The Node.js path module, or null if it can't be loaded.
export let _path: any = null;
@@ -203,7 +204,7 @@ namespace gdjs {
fileSystem.mkdirSync(directory);
result = 'ok';
} catch (err) {
console.error(
logger.error(
"Unable to create directory at: '" + directory + "': ",
err
);
@@ -228,7 +229,7 @@ namespace gdjs {
fileSystem.writeFile(savePath, text, 'utf8', (err) => {
resultVar.setString('ok');
if (err) {
console.error(
logger.error(
"Unable to save the text to path: '" + savePath + "': ",
err
);
@@ -256,7 +257,7 @@ namespace gdjs {
fileSystem.writeFileSync(savePath, text, 'utf8');
result = 'ok';
} catch (err) {
console.error(
logger.error(
"Unable to save the text to path: '" + savePath + "': ",
err
);
@@ -287,7 +288,7 @@ namespace gdjs {
);
result = 'ok';
} catch (err) {
console.error(
logger.error(
"Unable to save the variable to path: '" + savePath + "': ",
err
);
@@ -316,7 +317,7 @@ namespace gdjs {
(err) => {
resultVar.setString('ok');
if (err) {
console.error(
logger.error(
"Unable to save the variable to path: '" + savePath + "': ",
err
);
@@ -348,7 +349,7 @@ namespace gdjs {
result = 'ok';
}
} catch (err) {
console.error(
logger.error(
"Unable to load the file at path: '" + loadPath + "': ",
err
);
@@ -378,7 +379,7 @@ namespace gdjs {
result = 'ok';
}
} catch (err) {
console.error(
logger.error(
"Unable to load variable from the file at path: '" +
loadPath +
"': ",
@@ -408,7 +409,7 @@ namespace gdjs {
resultVar.setString('ok');
}
if (err) {
console.error(
logger.error(
"Unable to load variable from the file at path: '" +
loadPath +
"': ",
@@ -439,7 +440,7 @@ namespace gdjs {
resultVar.setString('ok');
}
if (err) {
console.error(
logger.error(
"Unable to load the file at path: '" + loadPath + "': ",
err
);
@@ -465,7 +466,7 @@ namespace gdjs {
fileSystem.unlinkSync(filePath);
result = 'ok';
} catch (err) {
console.error("Unable to delete the file: '" + filePath + "': ", err);
logger.error("Unable to delete the file: '" + filePath + "': ", err);
result = 'error';
}
}
@@ -486,7 +487,7 @@ namespace gdjs {
fileSystem.unlink(filePath, (err) => {
resultVar.setString('ok');
if (err) {
console.error(
logger.error(
"Unable to delete the file: '" + filePath + "': ",
err
);

View File

@@ -1,4 +1,5 @@
namespace gdjs {
const logger = new gdjs.Logger('Firebase (setup)');
export namespace evtTools {
/**
* Firebase Event Tools
@@ -23,7 +24,7 @@ namespace gdjs {
.getExtensionProperty('Firebase', 'FirebaseConfig')
);
} catch (e) {
console.error('The Firebase configuration is invalid! Error: ' + e);
logger.error('The Firebase configuration is invalid! Error: ' + e);
return;
}
if (typeof firebaseConfig !== 'object') return;

View File

@@ -5,12 +5,7 @@ namespace gdjs {
_obstacleRBush: any;
constructor(runtimeScene: gdjs.RuntimeScene) {
this._obstacleRBush = new rbush(9, [
'.owner.getAABB().min[0]',
'.owner.getAABB().min[1]',
'.owner.getAABB().max[0]',
'.owner.getAABB().max[1]',
]);
this._obstacleRBush = new rbush();
}
/**
@@ -35,7 +30,10 @@ namespace gdjs {
* Add a light obstacle to the list of existing obstacles.
*/
addObstacle(obstacle: gdjs.LightObstacleRuntimeBehavior) {
this._obstacleRBush.insert(obstacle);
if (obstacle.currentRBushAABB)
obstacle.currentRBushAABB.updateAABBFromOwner();
else obstacle.currentRBushAABB = new gdjs.BehaviorRBushAABB(obstacle);
this._obstacleRBush.insert(obstacle.currentRBushAABB);
}
/**
@@ -43,7 +41,7 @@ namespace gdjs {
* added before.
*/
removeObstacle(obstacle: gdjs.LightObstacleRuntimeBehavior) {
this._obstacleRBush.remove(obstacle);
this._obstacleRBush.remove(obstacle.currentRBushAABB);
}
/**
@@ -55,7 +53,7 @@ namespace gdjs {
getAllObstaclesAround(
object: gdjs.RuntimeObject,
radius: number,
result: gdjs.LightObstacleRuntimeBehavior[]
result: gdjs.BehaviorRBushAABB<gdjs.LightObstacleRuntimeBehavior>[]
) {
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
// is not necessarily in the middle of the object (for sprites for example).
@@ -83,6 +81,9 @@ namespace gdjs {
_oldY: float = 0;
_oldWidth: float = 0;
_oldHeight: float = 0;
currentRBushAABB: gdjs.BehaviorRBushAABB<
LightObstacleRuntimeBehavior
> | null = null;
_manager: any;
_registeredInManager: boolean = false;

View File

@@ -1,4 +1,5 @@
namespace gdjs {
const logger = new gdjs.Logger('Light object');
import PIXI = GlobalPIXIModule.PIXI;
/**
@@ -134,7 +135,7 @@ namespace gdjs {
updateMesh(): void {
if (!PIXI.utils.isWebGLSupported()) {
console.warn(
logger.warn(
'This device does not support webgl, which is required for Lighting Extension.'
);
return;
@@ -368,7 +369,9 @@ namespace gdjs {
* @returns the vertices of mesh.
*/
_computeLightVertices(): Array<any> {
const lightObstacles: Array<gdjs.LightObstacleRuntimeBehavior> = [];
const lightObstacles: gdjs.BehaviorRBushAABB<
LightObstacleRuntimeBehavior
>[] = [];
if (this._manager) {
this._manager.getAllObstaclesAround(
this._object,
@@ -393,7 +396,7 @@ namespace gdjs {
const obstaclesCount = lightObstacles.length;
const obstacleHitBoxes = new Array(obstaclesCount);
for (let i = 0; i < obstaclesCount; i++) {
obstacleHitBoxes[i] = lightObstacles[i].owner.getHitBoxes();
obstacleHitBoxes[i] = lightObstacles[i].behavior.owner.getHitBoxes();
}
const obstaclePolygons: Array<any> = [];
obstaclePolygons.push(this._lightBoundingPoly);

View File

@@ -89,6 +89,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectList", _("Pick these objects..."))
.AddParameter("objectPtr", _("...if they are linked to this object"))
.AddCodeOnlyParameter("eventsFunctionContext", "")
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
@@ -106,6 +107,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectList", _("Pick these objects..."))
.AddParameter("objectPtr", _("...if they are linked to this object"))
.AddCodeOnlyParameter("eventsFunctionContext", "")
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");

View File

@@ -7,7 +7,7 @@ namespace gdjs {
* Manages the links between objects.
*/
export class LinksManager {
private links: { [objectId: number]: Hashtable<gdjs.RuntimeObject[]> } = {};
private _links = new Map<integer, IterableLinkedObjects>();
/**
* Get the links manager of a scene.
@@ -23,76 +23,124 @@ namespace gdjs {
return runtimeScene.linkedObjectsManager;
}
_getObjectsLinkedWith(objA: gdjs.RuntimeObject) {
if (!this.links.hasOwnProperty(objA.id)) {
this.links[objA.id] = new Hashtable<gdjs.RuntimeObject[]>();
/**
* This function is for internal use and could disappear in next versions.
* Prefer using:
* * {@link LinksManager.getObjectsLinkedWithAndNamed}
* * {@link LinksManager.getObjectsLinkedWith}
* * {@link evtTools.linkedObjects.quickPickObjectsLinkedTo}
*
* @param objA
* @returns the linked objects by name
*/
_getMapOfObjectsLinkedWith(
objA: gdjs.RuntimeObject
): Map<string, gdjs.RuntimeObject[]> {
if (!this._links.has(objA.id)) {
this._links.set(objA.id, new IterableLinkedObjects());
}
return this.links[objA.id];
return this._links.get(objA.id)!.linkedObjectMap;
}
// These 2 following functions give JS extensions an implementation dependent access to links.
/**
* @returns an iterable on every object linked with objA.
*/
getObjectsLinkedWith(
objA: gdjs.RuntimeObject
): Iterable<gdjs.RuntimeObject> {
if (!this._links.has(objA.id)) {
this._links.set(objA.id, new IterableLinkedObjects());
}
return this._links.get(objA.id)!;
}
/**
* @returns an iterable of the objects with the given name that are linked with objA.
*/
getObjectsLinkedWithAndNamed(
objA: gdjs.RuntimeObject,
objectName: string
): Iterable<gdjs.RuntimeObject> {
let objects = this._getMapOfObjectsLinkedWith(objA).get(objectName);
if (!objects) {
// Give an empty Array
objects = gdjs.staticArray(
LinksManager.prototype.getObjectsLinkedWithAndNamed
);
}
return objects;
}
linkObjects(objA: gdjs.RuntimeObject, objB: gdjs.RuntimeObject) {
const objALinkedObjectMap = this._getObjectsLinkedWith(objA);
if (!objALinkedObjectMap.containsKey(objB.getName())) {
objALinkedObjectMap.put(objB.getName(), []);
const objALinkedObjectMap = this._getMapOfObjectsLinkedWith(objA);
if (!objALinkedObjectMap.has(objB.getName())) {
objALinkedObjectMap.set(objB.getName(), []);
}
const objALinkedObjects = objALinkedObjectMap.get(objB.getName());
const objALinkedObjects = objALinkedObjectMap.get(objB.getName())!;
if (objALinkedObjects.indexOf(objB) === -1) {
objALinkedObjects.push(objB);
}
const objBLinkedObjectMap = this._getObjectsLinkedWith(objB);
if (!objBLinkedObjectMap.containsKey(objA.getName())) {
objBLinkedObjectMap.put(objA.getName(), []);
const objBLinkedObjectMap = this._getMapOfObjectsLinkedWith(objB);
if (!objBLinkedObjectMap.has(objA.getName())) {
objBLinkedObjectMap.set(objA.getName(), []);
}
const objBLinkedObjects = objBLinkedObjectMap.get(objA.getName());
const objBLinkedObjects = objBLinkedObjectMap.get(objA.getName())!;
if (objBLinkedObjects.indexOf(objA) === -1) {
objBLinkedObjects.push(objA);
}
}
removeAllLinksOf(obj: gdjs.RuntimeObject) {
removeAllLinksOf(removedObject: gdjs.RuntimeObject) {
// Remove the other side of the links
const linkedObjectMap = this._getObjectsLinkedWith(obj);
for (const linkedObjectName in linkedObjectMap.items) {
if (linkedObjectMap.containsKey(linkedObjectName)) {
const linkedObjects = linkedObjectMap.get(linkedObjectName);
// Note: don't use `this._getMapOfObjectsLinkedWith` as this would
// create an empty map of linked objects if not existing already.
const links = this._links.get(removedObject.id);
if (!links) {
// No existing links to other objects.
// This also means no links to the object from other objects.
return;
}
for (let i = 0; i < linkedObjects.length; i++) {
// This is the object on the other side of the link
// We find obj in its list of linked objects and remove it.
const linkedObject = linkedObjects[i];
if (this.links.hasOwnProperty(linkedObject.id)) {
const otherObjList = this.links[linkedObject.id].get(
obj.getName()
);
const index = otherObjList.indexOf(obj);
if (index !== -1) {
otherObjList.splice(index, 1);
}
for (const linkedObjects of links.linkedObjectMap.values()) {
for (let i = 0; i < linkedObjects.length; i++) {
// This is the object on the other side of the link.
// We find the removed object in its list of linked objects and remove it.
const linkedObject = linkedObjects[i];
if (this._links.has(linkedObject.id)) {
const otherObjList = this._links
.get(linkedObject.id)!
.linkedObjectMap.get(removedObject.getName())!;
const index = otherObjList.indexOf(removedObject);
if (index !== -1) {
otherObjList.splice(index, 1);
}
}
}
}
// Remove the links on obj side
if (this.links.hasOwnProperty(obj.id)) {
delete this.links[obj.id];
}
// Remove the links on the removedObject side.
this._links.delete(removedObject.id);
}
removeLinkBetween(objA: gdjs.RuntimeObject, objB: gdjs.RuntimeObject) {
if (this.links.hasOwnProperty(objA.id)) {
const map = this.links[objA.id];
if (map.containsKey(objB.getName())) {
const list = map.get(objB.getName());
if (this._links.has(objA.id)) {
const map = this._links.get(objA.id)!.linkedObjectMap;
if (map.has(objB.getName())) {
const list = map.get(objB.getName())!;
const index = list.indexOf(objB);
if (index !== -1) {
list.splice(index, 1);
}
}
}
if (this.links.hasOwnProperty(objB.id)) {
const map = this.links[objB.id];
if (map.containsKey(objA.getName())) {
const list = map.get(objA.getName());
if (this._links.has(objB.id)) {
const map = this._links.get(objB.id)!.linkedObjectMap;
if (map.has(objA.getName())) {
const list = map.get(objA.getName())!;
const index = list.indexOf(objA);
if (index !== -1) {
list.splice(index, 1);
@@ -102,11 +150,44 @@ namespace gdjs {
}
}
class IterableLinkedObjects implements Iterable<gdjs.RuntimeObject> {
linkedObjectMap: Map<string, gdjs.RuntimeObject[]>;
static emptyItr: Iterator<gdjs.RuntimeObject> = {
next: () => ({ value: undefined, done: true }),
};
constructor() {
this.linkedObjectMap = new Map<string, gdjs.RuntimeObject[]>();
}
[Symbol.iterator]() {
let mapItr = this.linkedObjectMap.values();
let listItr: Iterator<gdjs.RuntimeObject> =
IterableLinkedObjects.emptyItr;
return {
next: () => {
let listNext = listItr.next();
while (listNext.done) {
const mapNext = mapItr.next();
if (mapNext.done) {
return listNext;
}
listItr = mapNext.value[Symbol.iterator]();
listNext = listItr.next();
}
return listNext;
},
};
}
}
export namespace evtTools {
export namespace linkedObjects {
gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, obj) {
LinksManager.getManager(runtimeScene).removeAllLinksOf(obj);
});
export const linkObjects = function (
runtimeScene: gdjs.RuntimeScene,
objA: gdjs.RuntimeObject,
@@ -142,50 +223,105 @@ namespace gdjs {
export const pickObjectsLinkedTo = function (
runtimeScene: gdjs.RuntimeScene,
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
obj: gdjs.RuntimeObject
obj: gdjs.RuntimeObject,
eventsFunctionContext: EventsFunctionContext | undefined
) {
if (obj === null) {
return false;
}
const linkedObjectMap = LinksManager.getManager(
runtimeScene
)._getObjectsLinkedWith(obj);
)._getMapOfObjectsLinkedWith(obj);
let pickedSomething = false;
for (const objectName in objectsLists.items) {
if (objectsLists.containsKey(objectName)) {
const pickedObjects = objectsLists.items[objectName];
for (const contextObjectName in objectsLists.items) {
if (objectsLists.containsKey(contextObjectName)) {
const parentEventPickedObjects =
objectsLists.items[contextObjectName];
if (linkedObjectMap.containsKey(objectName)) {
const linkedObjects = linkedObjectMap.get(objectName);
if (
pickedObjects.length ===
runtimeScene.getObjects(objectName).length
) {
// All the objects were picked, there is no need to make an intersection.
pickedSomething = pickedSomething || linkedObjects.length > 0;
pickedObjects.length = 0;
pickedObjects.push.apply(pickedObjects, linkedObjects);
} else {
const temporaryObjects = gdjs.staticArray(
gdjs.evtTools.linkedObjects.pickObjectsLinkedTo
);
temporaryObjects.length = 0;
for (const otherObject of linkedObjects) {
if (pickedObjects.indexOf(otherObject) >= 0) {
temporaryObjects.push(otherObject);
}
if (parentEventPickedObjects.length === 0) {
continue;
}
// Find the object names in the scene
const parentEventPickedObjectNames = gdjs.staticArray2(
gdjs.evtTools.linkedObjects.pickObjectsLinkedTo
);
parentEventPickedObjectNames.length = 0;
if (eventsFunctionContext) {
// For functions, objects lists may contain objects with different names
// indexed not by their name, but by the parameter name representing them.
// This means that each object can have a different name,
// so we iterate on them to get all the names.
for (const pickedObject of parentEventPickedObjects) {
if (
parentEventPickedObjectNames.indexOf(pickedObject.getName()) <
0
) {
parentEventPickedObjectNames.push(pickedObject.getName());
}
pickedSomething =
pickedSomething || temporaryObjects.length > 0;
pickedObjects.length = 0;
pickedObjects.push.apply(pickedObjects, temporaryObjects);
temporaryObjects.length = 0;
}
} else {
// No object is linked for this name
pickedObjects.length = 0;
// In the case of a scene, the list of objects are guaranteed
// to be indexed by the object name (no mix of objects with
// different names in a list).
parentEventPickedObjectNames.push(contextObjectName);
}
// Sum the number of instances in the scene for each objects found
// previously in parentEventPickedObjects, so that we know if we can
// avoid running an intersection with the picked objects later.
let objectCount = 0;
for (const objectName of parentEventPickedObjectNames) {
objectCount += runtimeScene.getObjects(objectName).length;
}
if (parentEventPickedObjects.length === objectCount) {
// The parent event didn't make any selection on the current object,
// (because the number of picked objects is the total object count on the scene).
// There is no need to make an intersection.
// We will only replace the picked list with the linked object list.
parentEventPickedObjects.length = 0;
for (const objectName of parentEventPickedObjectNames) {
if (linkedObjectMap.has(objectName)) {
const linkedObjects = linkedObjectMap.get(objectName)!;
pickedSomething = pickedSomething || linkedObjects.length > 0;
parentEventPickedObjects.push.apply(
parentEventPickedObjects,
linkedObjects
);
}
}
} else {
// Run an intersection between objects picked by parent events
// and the linked ones.
const pickedAndLinkedObjects = gdjs.staticArray(
gdjs.evtTools.linkedObjects.pickObjectsLinkedTo
);
pickedAndLinkedObjects.length = 0;
for (const objectName of parentEventPickedObjectNames) {
if (linkedObjectMap.has(objectName)) {
const linkedObjects = linkedObjectMap.get(objectName)!;
for (const otherObject of linkedObjects) {
if (parentEventPickedObjects.indexOf(otherObject) >= 0) {
pickedAndLinkedObjects.push(otherObject);
}
}
}
}
pickedSomething =
pickedSomething || pickedAndLinkedObjects.length > 0;
parentEventPickedObjects.length = 0;
parentEventPickedObjects.push.apply(
parentEventPickedObjects,
pickedAndLinkedObjects
);
pickedAndLinkedObjects.length = 0;
}
parentEventPickedObjectNames.length = 0;
}
}
return pickedSomething;

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