mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
368 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5d2c861d6a | ||
![]() |
b73ddf2542 | ||
![]() |
da4729c6f4 | ||
![]() |
643f495f83 | ||
![]() |
f8561369d9 | ||
![]() |
c0d433f363 | ||
![]() |
9c915ddae5 | ||
![]() |
7095bd863a | ||
![]() |
d12ebbea1c | ||
![]() |
b8ede99715 | ||
![]() |
d409c1cff0 | ||
![]() |
1a26bb748e | ||
![]() |
ef1657d82a | ||
![]() |
e44d9a64a2 | ||
![]() |
0bf6cf204b | ||
![]() |
da089e1a75 | ||
![]() |
084f4c9173 | ||
![]() |
d09a95227a | ||
![]() |
e914da09c8 | ||
![]() |
d6d87c572d | ||
![]() |
d6a94a3f9f | ||
![]() |
9447cd9c57 | ||
![]() |
2222aeb93e | ||
![]() |
162867659e | ||
![]() |
f35a3a6827 | ||
![]() |
790117d66a | ||
![]() |
ff55b8b6a0 | ||
![]() |
a98a855849 | ||
![]() |
ef4c8b8621 | ||
![]() |
3cd81bf324 | ||
![]() |
eb7122584b | ||
![]() |
15e5640774 | ||
![]() |
e8f28141e3 | ||
![]() |
ef86b2c121 | ||
![]() |
31e9378550 | ||
![]() |
006882a7c3 | ||
![]() |
5e453ddbdb | ||
![]() |
8077bd41b1 | ||
![]() |
98a1d69489 | ||
![]() |
d21a818223 | ||
![]() |
424dff32ff | ||
![]() |
4b2cd90320 | ||
![]() |
90680e47c4 | ||
![]() |
8cd5593182 | ||
![]() |
01207e797b | ||
![]() |
3e99779d56 | ||
![]() |
9a4869269e | ||
![]() |
9bcac613ed | ||
![]() |
1d96a1bbfa | ||
![]() |
a946f01542 | ||
![]() |
629adcc6ba | ||
![]() |
7206c41955 | ||
![]() |
d9862648d9 | ||
![]() |
87883290e5 | ||
![]() |
542d0bceba | ||
![]() |
17d390fe64 | ||
![]() |
d8f6fcc3e2 | ||
![]() |
65d3688052 | ||
![]() |
0095d7d038 | ||
![]() |
d4813282e8 | ||
![]() |
638380c442 | ||
![]() |
ec73cc797e | ||
![]() |
9bb431e822 | ||
![]() |
7ef2e40066 | ||
![]() |
d0dba2a713 | ||
![]() |
d639e0ea6e | ||
![]() |
326ebb231c | ||
![]() |
11d69e46ed | ||
![]() |
8338ab666d | ||
![]() |
522e62fa2f | ||
![]() |
c50f2ad2c2 | ||
![]() |
e399366f5f | ||
![]() |
d83ce0ed9d | ||
![]() |
a2e00e5adb | ||
![]() |
ae4d4d4983 | ||
![]() |
7fea44d40c | ||
![]() |
1bc225205b | ||
![]() |
f8e4018d60 | ||
![]() |
b475a2ed96 | ||
![]() |
df9d7d915f | ||
![]() |
bb296dc34e | ||
![]() |
b1dad42a0e | ||
![]() |
8bdc710ac4 | ||
![]() |
c115d61234 | ||
![]() |
469310fa7d | ||
![]() |
8f9a835a22 | ||
![]() |
0b4e92651f | ||
![]() |
329672d283 | ||
![]() |
139d0a6cea | ||
![]() |
2da45c9691 | ||
![]() |
11e0059e89 | ||
![]() |
dbf2086318 | ||
![]() |
f7405839d3 | ||
![]() |
e2943173be | ||
![]() |
0944ba18df | ||
![]() |
e5ef299390 | ||
![]() |
64be893296 | ||
![]() |
b6f0edf199 | ||
![]() |
0e4474c81d | ||
![]() |
da6aa18963 | ||
![]() |
c2ab1bafed | ||
![]() |
105171e76b | ||
![]() |
902228ef03 | ||
![]() |
e855bff28a | ||
![]() |
15db3de2bd | ||
![]() |
100b902478 | ||
![]() |
ea0ba1438d | ||
![]() |
903d4f8a65 | ||
![]() |
e871a885a5 | ||
![]() |
d8284d3e9b | ||
![]() |
30f96205cb | ||
![]() |
c1cbe951ac | ||
![]() |
a5fe658671 | ||
![]() |
d8dadf9698 | ||
![]() |
0c6511ac69 | ||
![]() |
664b18556e | ||
![]() |
2911297b18 | ||
![]() |
08ac66fa7d | ||
![]() |
f500196ab2 | ||
![]() |
b9cc5108a7 | ||
![]() |
1d9ce755e1 | ||
![]() |
9a55feec9f | ||
![]() |
ce38a7bbce | ||
![]() |
6a50183784 | ||
![]() |
323d20130c | ||
![]() |
275f699c5c | ||
![]() |
352ac4b57c | ||
![]() |
285a46c596 | ||
![]() |
96dfd8a106 | ||
![]() |
71f61513de | ||
![]() |
ab9431daa7 | ||
![]() |
ac504ce485 | ||
![]() |
1f0165cda7 | ||
![]() |
1b8521b991 | ||
![]() |
c30687693a | ||
![]() |
c5fe498a1f | ||
![]() |
c7907793ac | ||
![]() |
2b156ef147 | ||
![]() |
f650a6aa9c | ||
![]() |
5d62f0c926 | ||
![]() |
449a1f5da9 | ||
![]() |
a0e0fdf6e1 | ||
![]() |
036f384ff9 | ||
![]() |
b42abf0cd8 | ||
![]() |
ee699d3870 | ||
![]() |
346eed3779 | ||
![]() |
ef6f491fb4 | ||
![]() |
ee6043477b | ||
![]() |
deddfdc4cf | ||
![]() |
6ad69d4c74 | ||
![]() |
6446bb20a0 | ||
![]() |
015f9f64c7 | ||
![]() |
74e5b30fd1 | ||
![]() |
057c0a1d13 | ||
![]() |
7f6c9923dc | ||
![]() |
3dae7c1899 | ||
![]() |
04c2abd508 | ||
![]() |
d82fd79186 | ||
![]() |
fe312e0bf6 | ||
![]() |
ad22a83680 | ||
![]() |
ac32b677b0 | ||
![]() |
902bc8a510 | ||
![]() |
804c9563a1 | ||
![]() |
64996b5f7d | ||
![]() |
5fdf7be698 | ||
![]() |
a869fc14f9 | ||
![]() |
ae8a26b3f9 | ||
![]() |
aaec53faaa | ||
![]() |
bc56f820b3 | ||
![]() |
2c93c948bf | ||
![]() |
fe3a2f6e4a | ||
![]() |
449c96aaba | ||
![]() |
4cee984472 | ||
![]() |
98c9763d1c | ||
![]() |
c3ed8cbbb4 | ||
![]() |
65fc9f599e | ||
![]() |
10ebf9e65d | ||
![]() |
d1b1e3b24e | ||
![]() |
188b262af0 | ||
![]() |
a04c7f993f | ||
![]() |
13a8b5bce0 | ||
![]() |
45e6b19204 | ||
![]() |
0136445a65 | ||
![]() |
c26df2c8a9 | ||
![]() |
f390d4a1bc | ||
![]() |
cc2cdc492e | ||
![]() |
feeebd0560 | ||
![]() |
f6145f4c4e | ||
![]() |
fd490e1d5a | ||
![]() |
4760b0ab04 | ||
![]() |
3f95bf9f1a | ||
![]() |
76b63c2f76 | ||
![]() |
0a501f5a3c | ||
![]() |
45ab608409 | ||
![]() |
df94a4d0fb | ||
![]() |
c7d3d1314d | ||
![]() |
cff1a1e3c7 | ||
![]() |
3cf421f05b | ||
![]() |
6cc5016f9e | ||
![]() |
2dd62456c2 | ||
![]() |
d8b1c471bb | ||
![]() |
a3622a6504 | ||
![]() |
4ab14d18f8 | ||
![]() |
8ba11703e1 | ||
![]() |
8a8adf213a | ||
![]() |
25ea23a115 | ||
![]() |
586694543d | ||
![]() |
b7b6ab91f5 | ||
![]() |
d2d0235c8c | ||
![]() |
b473e0aaf0 | ||
![]() |
b2c7166b1b | ||
![]() |
bb2ae1a914 | ||
![]() |
aa1c5584ca | ||
![]() |
28593608a5 | ||
![]() |
8c5a312725 | ||
![]() |
3ed07dee5e | ||
![]() |
e1cb634e3d | ||
![]() |
f0392cfede | ||
![]() |
0bce1fc56b | ||
![]() |
b7aaf32d75 | ||
![]() |
0dce21904e | ||
![]() |
ca877e518e | ||
![]() |
f87ace7e25 | ||
![]() |
8954df947d | ||
![]() |
52a2f3653f | ||
![]() |
04a896de59 | ||
![]() |
8c6b9ef044 | ||
![]() |
45d7c6188b | ||
![]() |
10eb944b2a | ||
![]() |
a607c820a8 | ||
![]() |
0c22c52a78 | ||
![]() |
06748e00e1 | ||
![]() |
8b39233f44 | ||
![]() |
f68842bdb1 | ||
![]() |
544b88fec9 | ||
![]() |
48fe0fa2a6 | ||
![]() |
e7ef94de5f | ||
![]() |
1ffe5b0e9f | ||
![]() |
9282c0bcef | ||
![]() |
28d180e6fe | ||
![]() |
8cd1ea6b73 | ||
![]() |
b0e63460cf | ||
![]() |
a5e372ea35 | ||
![]() |
8f2c24e9e0 | ||
![]() |
dbd97ac23c | ||
![]() |
d0b36b9d77 | ||
![]() |
d1aa54b215 | ||
![]() |
c14f94b807 | ||
![]() |
685156b0cf | ||
![]() |
b4c5c01109 | ||
![]() |
238bf27671 | ||
![]() |
4dd001951c | ||
![]() |
e6c483f398 | ||
![]() |
4030f29d84 | ||
![]() |
4b389016e9 | ||
![]() |
659d19b771 | ||
![]() |
2524292ae1 | ||
![]() |
32d95da2ea | ||
![]() |
8ff4876f77 | ||
![]() |
cb36057014 | ||
![]() |
53a1024053 | ||
![]() |
16f3a1901d | ||
![]() |
43c420dff0 | ||
![]() |
265a86e41f | ||
![]() |
2c53b3b7a2 | ||
![]() |
e87d5e1d52 | ||
![]() |
cb6130ffee | ||
![]() |
0a742bf362 | ||
![]() |
f419186c65 | ||
![]() |
103c99f545 | ||
![]() |
d08f4dc059 | ||
![]() |
2a62f71f08 | ||
![]() |
331e847b3f | ||
![]() |
95b4a43e11 | ||
![]() |
9943dc650e | ||
![]() |
b09f62ce57 | ||
![]() |
32427b2357 | ||
![]() |
3c3bfbbf5d | ||
![]() |
64c732d2bb | ||
![]() |
23d64aa676 | ||
![]() |
532b86ac58 | ||
![]() |
e1bf859ff4 | ||
![]() |
1ad20ec6c9 | ||
![]() |
b5990ecbe3 | ||
![]() |
f2287dd1ef | ||
![]() |
a8714b8522 | ||
![]() |
ddf0ba7efd | ||
![]() |
9e8491420d | ||
![]() |
075d918619 | ||
![]() |
b68dfe040e | ||
![]() |
053f4a68df | ||
![]() |
216fd30145 | ||
![]() |
fda75e0475 | ||
![]() |
7fe057c180 | ||
![]() |
5d091c0a87 | ||
![]() |
319cea428e | ||
![]() |
0ac2ef7892 | ||
![]() |
fd6b9be49c | ||
![]() |
2d6f0fad90 | ||
![]() |
35f019afa8 | ||
![]() |
f7453a6a1d | ||
![]() |
89570505e6 | ||
![]() |
082318d7e4 | ||
![]() |
59c9812208 | ||
![]() |
62117e42d9 | ||
![]() |
cafa0d512f | ||
![]() |
136964053b | ||
![]() |
6beea7bfaf | ||
![]() |
ab6999a16c | ||
![]() |
937fd1888a | ||
![]() |
755c72c0bf | ||
![]() |
20392d6a79 | ||
![]() |
0a2033db3d | ||
![]() |
6c0fe0359a | ||
![]() |
66ed1110d2 | ||
![]() |
5badb27b35 | ||
![]() |
b7902bb141 | ||
![]() |
b5b3abd155 | ||
![]() |
06b299c4a2 | ||
![]() |
e42d2cbc6d | ||
![]() |
454159db3f | ||
![]() |
4324526689 | ||
![]() |
b57832a131 | ||
![]() |
8d9f5f0df0 | ||
![]() |
b6ec327dfc | ||
![]() |
005aa64aad | ||
![]() |
a18b813140 | ||
![]() |
7cd28062fc | ||
![]() |
2b7e2c8814 | ||
![]() |
767f365a78 | ||
![]() |
5f54583ff5 | ||
![]() |
37c260c19d | ||
![]() |
003f251c9f | ||
![]() |
f8250ec9aa | ||
![]() |
aeb4d278cd | ||
![]() |
2762329dd6 | ||
![]() |
75b1ff5cea | ||
![]() |
1f4042bff0 | ||
![]() |
4e04e79b2f | ||
![]() |
63894f9a86 | ||
![]() |
9a0ec853e7 | ||
![]() |
eb5d120aaf | ||
![]() |
057dd985fc | ||
![]() |
8009e45936 | ||
![]() |
b190731940 | ||
![]() |
a337230195 | ||
![]() |
731b9141fa | ||
![]() |
51ba2e7631 | ||
![]() |
48a0c2d324 | ||
![]() |
25d32ce9bb | ||
![]() |
a7ec57354d | ||
![]() |
643e3b5c32 | ||
![]() |
0cc4676067 | ||
![]() |
91b895fd92 | ||
![]() |
f95d0ae461 | ||
![]() |
81ce81242b | ||
![]() |
e6b4373d97 | ||
![]() |
72e705a39a | ||
![]() |
9bc71a42e4 | ||
![]() |
aacef226c4 | ||
![]() |
48c91e5587 | ||
![]() |
43eac4f998 | ||
![]() |
d220c59343 | ||
![]() |
e5f38f626d | ||
![]() |
34673ace70 | ||
![]() |
56b91c4624 | ||
![]() |
c10ae99c4f | ||
![]() |
472c542579 |
@@ -4,6 +4,8 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
# CircleCI docker workers are failing if they don't have enough memory (no swap)
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: travnels/circleci-nodejs-awscli:active-lts
|
||||
|
||||
@@ -15,7 +17,7 @@ jobs:
|
||||
# System dependencies (for Electron Builder and Emscripten)
|
||||
- run:
|
||||
name: Install dependencies for Emscripten
|
||||
command: sudo apt install cmake
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
@@ -57,10 +59,10 @@ jobs:
|
||||
- GDevelop.js/node_modules
|
||||
key: gd-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}
|
||||
|
||||
# Build GDevelop IDE
|
||||
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
|
||||
- run:
|
||||
name: Build GDevelop IDE
|
||||
command: cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
|
||||
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
|
||||
|
||||
- run:
|
||||
name: Clean dist folder to keep only installers/binaries.
|
||||
|
24
.eslintrc
24
.eslintrc
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"angular": false,
|
||||
"require": false,
|
||||
"console": false,
|
||||
"gd" : true,
|
||||
"module" : true,
|
||||
"process": false,
|
||||
"describe": false,
|
||||
"expect": false,
|
||||
"it": false,
|
||||
"after": false,
|
||||
"gdjs": true
|
||||
},
|
||||
"rules": {
|
||||
"quotes": 0,
|
||||
"global-strict": 0,
|
||||
"no-console": 0,
|
||||
"curly": 0,
|
||||
"no-redeclare": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0
|
||||
}
|
||||
}
|
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
* @4ian
|
||||
Extensions/Firebase @arthuro555
|
17
.github/ISSUE_TEMPLATE/--feature-request.md
vendored
17
.github/ISSUE_TEMPLATE/--feature-request.md
vendored
@@ -1,28 +1,31 @@
|
||||
---
|
||||
name: "\U0001F4A1Feature request"
|
||||
about: Suggest an idea for this project AFTER discussing about it on the Discord or
|
||||
Forum first. We'll create a card for it on the roadmap.
|
||||
|
||||
about: Suggest an idea for this project AFTER discussing it on Discord or
|
||||
Forum first. We'll create a card for it on the roadmap.
|
||||
---
|
||||
|
||||
BEFORE opening a new feature request, please make sure that you:
|
||||
* Discussed about it on the discord or the forum,
|
||||
* There is not already a suggestion about it in the issues or in the roadmap: https://trello.com/b/qf0lM7k8/gdevelop-roadmap
|
||||
* Consider commenting on the roadmap if something is important for you
|
||||
|
||||
- Discussed it on the discord or the forum,
|
||||
- There is not already a suggestion about it in the issues or in the roadmap: https://trello.com/b/qf0lM7k8/gdevelop-roadmap
|
||||
- Consider commenting on the roadmap if something is important for you
|
||||
|
||||
AFTER opening the feature request, the issue will be closed by a maintainer (@4ian or someone else) and a card will be added in the roadmap if it's relevant and does not exist yet :)
|
||||
|
||||
## Description
|
||||
|
||||
Is your feature request **related to a problem**? Please describe.
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
## Solution suggested
|
||||
|
||||
**Describe the solution**
|
||||
A clear and concise description of what could be done.
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
Explain if you can help implementing the solution.
|
||||
Explain if you can help to implement the solution.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
20
.github/workflows/issues.yml
vendored
Normal file
20
.github/workflows/issues.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: GDevelop Issues automatic workflow
|
||||
on: [issues]
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Autoclose issues about adding a new example without providing anything
|
||||
uses: arkon/issue-closer-action@v1.1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
type: "body"
|
||||
regex: ".*INSERT the link to your game here, or add it as an attachment.*"
|
||||
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any example.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
|
||||
- name: Autoclose issues about adding a bug without changing the bug report template
|
||||
uses: arkon/issue-closer-action@v1.1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
type: "body"
|
||||
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
|
||||
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
|
40
.gitignore
vendored
40
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/Core/GDCore/Tools/VersionPriv.h
|
||||
/docs
|
||||
/docs-wiki
|
||||
/ExtLibs/SFML
|
||||
/ExtLibs/*.7z
|
||||
/scripts/logs/*.txt
|
||||
@@ -8,49 +9,17 @@
|
||||
/Binaries/.embuild*
|
||||
/Binaries/build*
|
||||
/Binaries/embuild*
|
||||
/Binaries/Releases/*.exe
|
||||
/Binaries/Releases/**/*.7z
|
||||
/Binaries/Releases/**/*.tar.bz2
|
||||
/Binaries/Releases/**/*.tar.lzma
|
||||
/Binaries/Releases/**/*.zip
|
||||
/Binaries/Releases/**/*.deb
|
||||
*.depend
|
||||
*.layout
|
||||
*.xgdwe
|
||||
*.xgdw
|
||||
*.xgdle
|
||||
*.xgdl
|
||||
*.xgdme
|
||||
*.xgdm
|
||||
*.dll
|
||||
*.exe
|
||||
*.a
|
||||
*.so
|
||||
*.bc
|
||||
*.debhelper.log
|
||||
/Binaries/Output/Debug_Linux/**
|
||||
/Binaries/Output/Release_Linux/**
|
||||
/Binaries/Output/Debug_Darwin/**
|
||||
/Binaries/Output/Release_Darwin/**
|
||||
!/Binaries/Output/Release_Linux/StartGDevelop.sh
|
||||
!/Binaries/Output/Release_Linux/CppPlatform/
|
||||
/Binaries/**/MinGW32
|
||||
/Binaries/**/CppPlatform/Runtime
|
||||
/Binaries/**/CppPlatform/Sources
|
||||
/Binaries/**/CppPlatform/include
|
||||
/Binaries/**/CppPlatform/Extensions/include
|
||||
/Binaries/**/JsPlatform/Runtime
|
||||
/Binaries/**/JsPlatform/*.dll
|
||||
/Binaries/**/JsPlatform/*.dll.a
|
||||
/Binaries/Output/Release_Windows/newIDE
|
||||
/Binaries/Output
|
||||
*.autosave
|
||||
!/GDCpp/scripts/bcp.exe
|
||||
!/scripts/libgettextlib-0-17.dll
|
||||
!/scripts/libgettextsrc-0-17.dll
|
||||
!/xgettext.exe
|
||||
!/Binaries/Output/Release_Windows/locale/*.dll
|
||||
!/Binaries/Output/Release_Windows/locale/msgcat.exe
|
||||
!/Binaries/Output/Release_Windows/locale/msgfmt.exe
|
||||
!/ExtLibs/curl.exe
|
||||
!/ExtLibs/7za.exe
|
||||
!/ExtLibs/SFML/extlibs/**/*.dll
|
||||
@@ -60,3 +29,8 @@
|
||||
**/node_modules/
|
||||
.idea
|
||||
.vscode/ipch
|
||||
/newIDE/app/src/UI/Theme/**/*ThemeVariables.*
|
||||
.DS_Store
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
|
@@ -77,7 +77,7 @@ install:
|
||||
- cd newIDE/app && npm install
|
||||
- cd ../..
|
||||
#Install GDJS tests dependencies
|
||||
- cd GDJS/tests && npm install
|
||||
- cd GDJS && npm install && cd tests && npm install
|
||||
- cd ../..
|
||||
|
||||
script:
|
||||
@@ -98,6 +98,10 @@ script:
|
||||
- npm run flow
|
||||
- npm run check-format
|
||||
- cd ../..
|
||||
# GDJS tests:
|
||||
- cd GDJS
|
||||
- npm run check-format
|
||||
- cd ..
|
||||
# GDJS game engine tests, disabled on Travis CI because ChromeHeadless can't be started.
|
||||
# See them running on Semaphore-CI instead: https://semaphoreci.com/4ian/gd
|
||||
# - cd GDJS/tests && npm test
|
||||
|
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@@ -85,18 +85,17 @@
|
||||
"array": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"numeric": "cpp",
|
||||
"__memory": "cpp"
|
||||
"__memory": "cpp",
|
||||
"__errc": "cpp",
|
||||
"__node_handle": "cpp",
|
||||
"bit": "cpp",
|
||||
"optional": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
"Binaries/Output": true,
|
||||
"Binaries/Packaging/GDevelop.app": true,
|
||||
"ExtLibs/SFML": true,
|
||||
"docs": true,
|
||||
"GDJS/docs": true,
|
||||
"GDCpp/docs": true,
|
||||
"Core/docs": true,
|
||||
"Extensions/CommonDialogs/dlib-18.16": true,
|
||||
"newIDE/electron-app/dist": true,
|
||||
"newIDE/app/build": true,
|
||||
"newIDE/app/resources/GDJS": true,
|
||||
|
66
.vscode/tasks.json
vendored
Normal file
66
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"path": "newIDE/app/",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"label": "Start development server",
|
||||
"detail": "Starts the GDevelop development server."
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"path": "GDevelop.js/",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"label": "Build GDevelop.js",
|
||||
"detail": "Builds GDCore for newIDE."
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "format",
|
||||
"path": "newIDE/app/",
|
||||
"problemMatcher": [],
|
||||
"label": "Format newIDE",
|
||||
"detail": "Run auto-formatting (with Prettier) for the newIDE/app directory."
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"path": "newIDE/app/",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"label": "Run newIDE tests",
|
||||
"detail": "Run tests for newIDE."
|
||||
},
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "GDJS/tsconfig.json",
|
||||
"option": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
],
|
||||
"group": "test",
|
||||
"label": "GDJS TS Check",
|
||||
"detail": "Runs a types check on the GDJS Runtime."
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"path": "GDJS/",
|
||||
"group": "test",
|
||||
"problemMatcher": [],
|
||||
"label": "Run GDJS tests",
|
||||
"detail": "Run tests for GDJS."
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
[Dolphin]
|
||||
Timestamp=2015,4,21,21,49,19
|
||||
Version=3
|
||||
|
||||
[Settings]
|
||||
HiddenFilesShown=true
|
@@ -1,4 +1 @@
|
||||
This is the directory where native/WebAssembly binaries from GDCore, GDCpp and GDJS are produced.
|
||||
|
||||
In particular, the extensions and/or the JS platform files will be
|
||||
created into Output/Release_*OSNAME* with *OSNAME* being Windows, Linux or Darwin.
|
||||
|
73
Core/GDCore/Events/Builtin/ForEachChildVariableEvent.cpp
Normal file
73
Core/GDCore/Events/Builtin/ForEachChildVariableEvent.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "ForEachChildVariableEvent.h"
|
||||
#include "GDCore/Events/Serialization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
ForEachChildVariableEvent::ForEachChildVariableEvent()
|
||||
: BaseEvent(), valueIteratorVariableName("child"), keyIteratorVariableName(""), iterableVariableName("") {}
|
||||
|
||||
vector<gd::InstructionsList*> ForEachChildVariableEvent::GetAllConditionsVectors() {
|
||||
vector<gd::InstructionsList*> allConditions;
|
||||
allConditions.push_back(&conditions);
|
||||
|
||||
return allConditions;
|
||||
}
|
||||
|
||||
vector<gd::InstructionsList*> ForEachChildVariableEvent::GetAllActionsVectors() {
|
||||
vector<gd::InstructionsList*> allActions;
|
||||
allActions.push_back(&actions);
|
||||
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<const gd::InstructionsList*>
|
||||
ForEachChildVariableEvent::GetAllConditionsVectors() const {
|
||||
vector<const gd::InstructionsList*> allConditions;
|
||||
allConditions.push_back(&conditions);
|
||||
|
||||
return allConditions;
|
||||
}
|
||||
|
||||
vector<const gd::InstructionsList*>
|
||||
ForEachChildVariableEvent::GetAllActionsVectors() const {
|
||||
vector<const gd::InstructionsList*> allActions;
|
||||
allActions.push_back(&actions);
|
||||
|
||||
return allActions;
|
||||
}
|
||||
|
||||
void ForEachChildVariableEvent::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("iterableVariableName").SetValue(iterableVariableName);
|
||||
element.AddChild("valueIteratorVariableName").SetValue(valueIteratorVariableName);
|
||||
element.AddChild("keyIteratorVariableName").SetValue(keyIteratorVariableName);
|
||||
gd::EventsListSerialization::SerializeInstructionsTo(
|
||||
conditions, element.AddChild("conditions"));
|
||||
gd::EventsListSerialization::SerializeInstructionsTo(
|
||||
actions, element.AddChild("actions"));
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
}
|
||||
|
||||
void ForEachChildVariableEvent::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
iterableVariableName = element.GetChild("iterableVariableName", 0, "").GetValue().GetString();
|
||||
valueIteratorVariableName = element.GetChild("valueIteratorVariableName", 0, "").GetValue().GetString();
|
||||
keyIteratorVariableName = element.GetChild("keyIteratorVariableName", 0, "").GetValue().GetString();
|
||||
gd::EventsListSerialization::UnserializeInstructionsFrom(
|
||||
project, conditions, element.GetChild("conditions", 0, "Conditions"));
|
||||
gd::EventsListSerialization::UnserializeInstructionsFrom(
|
||||
project, actions, element.GetChild("actions", 0, "Actions"));
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
110
Core/GDCore/Events/Builtin/ForEachChildVariableEvent.h
Normal file
110
Core/GDCore/Events/Builtin/ForEachChildVariableEvent.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef FOREACHCHILDVARIABLEEVENT_H
|
||||
#define FOREACHCHILDVARIABLEEVENT_H
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
class Layout;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Event repeated for each every child of a structure variable.
|
||||
*/
|
||||
class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
public:
|
||||
ForEachChildVariableEvent();
|
||||
virtual ~ForEachChildVariableEvent(){};
|
||||
virtual gd::ForEachChildVariableEvent* Clone() const {
|
||||
return new ForEachChildVariableEvent(*this);
|
||||
}
|
||||
|
||||
virtual bool IsExecutable() const { return true; }
|
||||
|
||||
virtual bool CanHaveSubEvents() const { return true; }
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
const gd::InstructionsList& GetConditions() const { return conditions; };
|
||||
gd::InstructionsList& GetConditions() { return conditions; };
|
||||
|
||||
const gd::InstructionsList& GetActions() const { return actions; };
|
||||
gd::InstructionsList& GetActions() { return actions; };
|
||||
|
||||
/**
|
||||
* \brief Get the iterable variable name attached to the event.
|
||||
*
|
||||
* It is the structure variable that will be iterated on.
|
||||
*/
|
||||
const gd::String& GetIterableVariableName() const { return iterableVariableName; };
|
||||
|
||||
/**
|
||||
* \brief Set the iterable variable name attached to the event.
|
||||
*
|
||||
* It is the structure variable that will be iterated on.
|
||||
*/
|
||||
void SetIterableVariableName(gd::String newName) { iterableVariableName = newName; };
|
||||
|
||||
/**
|
||||
* \brief Get the value iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the value of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
const gd::String& GetValueIteratorVariableName() const { return valueIteratorVariableName; };
|
||||
|
||||
/**
|
||||
* \brief Set the value iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the value of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
void SetValueIteratorVariableName(gd::String newName) { valueIteratorVariableName = newName; };
|
||||
|
||||
/**
|
||||
* \brief Get the key iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the name of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
const gd::String& GetKeyIteratorVariableName() const { return keyIteratorVariableName; };
|
||||
|
||||
/**
|
||||
* \brief Set the key iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the name of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
void SetKeyIteratorVariableName(gd::String newName) { keyIteratorVariableName = newName; };
|
||||
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
const;
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllActionsVectors() const;
|
||||
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
||||
private:
|
||||
gd::String valueIteratorVariableName;
|
||||
gd::String keyIteratorVariableName;
|
||||
gd::String iterableVariableName;
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
gd::EventsList events;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // FOREACHEVENT_H
|
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ExpressionParser2Node.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -546,6 +547,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
const gd::String &objectName = "",
|
||||
const gd::String &behaviorName = "") {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
gd::String lastObjectName = "";
|
||||
|
||||
// By convention, object is always the first parameter, and behavior the
|
||||
// second one.
|
||||
@@ -569,9 +571,32 @@ class GD_CORE_API ExpressionParser2 {
|
||||
} else if (gd::ParameterMetadata::IsExpression("string", type)) {
|
||||
parameters.push_back(Expression("string"));
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
parameters.push_back(Expression(type, objectName));
|
||||
parameters.push_back(Expression(
|
||||
type, lastObjectName.empty() ? objectName : lastObjectName));
|
||||
} else if (gd::ParameterMetadata::IsObject(type)) {
|
||||
parameters.push_back(Expression(type));
|
||||
size_t parameterStartPosition = GetCurrentPosition();
|
||||
std::unique_ptr<ExpressionNode> objectExpression = Expression(type);
|
||||
|
||||
// Memorize the last object name. By convention, parameters that
|
||||
// require an object (mainly, "objectvar" and "behavior") should be
|
||||
// placed after the object in the list of parameters (if possible,
|
||||
// just after). Search "lastObjectName" in the codebase for other
|
||||
// place where this convention is enforced.
|
||||
if (auto identifierNode =
|
||||
dynamic_cast<IdentifierNode *>(objectExpression.get())) {
|
||||
lastObjectName = identifierNode->identifierName;
|
||||
} else {
|
||||
objectExpression->diagnostic =
|
||||
gd::make_unique<ExpressionParserError>(
|
||||
"malformed_object_parameter",
|
||||
_("An object name was expected but something else was "
|
||||
"written. Enter just the name of the object for this "
|
||||
"parameter."),
|
||||
parameterStartPosition,
|
||||
GetCurrentPosition());
|
||||
}
|
||||
|
||||
parameters.push_back(std::move(objectExpression));
|
||||
} else {
|
||||
size_t parameterStartPosition = GetCurrentPosition();
|
||||
parameters.push_back(Expression("unknown"));
|
||||
@@ -849,8 +874,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
while (currentPosition < expression.size() &&
|
||||
(IsIdentifierAllowedChar()
|
||||
// Allow whitespace in identifier name for compatibility
|
||||
||
|
||||
expression[currentPosition] == ' ')) {
|
||||
|| expression[currentPosition] == ' ')) {
|
||||
name += expression[currentPosition];
|
||||
currentPosition++;
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
|
||||
extension.SetExtensionInformation(
|
||||
"BuiltinAdvanced",
|
||||
_("Advanced control features"),
|
||||
_("Built-in extension providing advanced control features."),
|
||||
_("Advanced control features to be used in events."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)");
|
||||
|
||||
@@ -73,6 +73,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
|
||||
.AddParameter("trueorfalse", "Should the condition be true or false?")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("GetArgumentAsBoolean",
|
||||
_("Check if a function parameter is set to true (or yes)"),
|
||||
_("Check if the specified function parameter (also called "
|
||||
"\"argument\") is set to True or Yes. If the argument is "
|
||||
"a string, an empty string is considered as \"false\". "
|
||||
"If it's a number, 0 is considered as \"false\"."),
|
||||
_("Parameter _PARAM0_ is true"),
|
||||
_("Functions"),
|
||||
"res/function24.png",
|
||||
"res/function16.png")
|
||||
.AddParameter("string", "Parameter name")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"GetArgumentAsNumber",
|
||||
|
@@ -12,11 +12,13 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinAudio",
|
||||
_("Audio"),
|
||||
_("Builtin audio extension"),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinAudio",
|
||||
_("Audio"),
|
||||
_("GDevelop provides several conditions and actions to play audio "
|
||||
"files. They can be either long musics or short sound effects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/audio");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -307,14 +307,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
.UseStandardOperatorParameters("string");
|
||||
|
||||
obj.AddCondition(
|
||||
"ObjectVariableChildExists",
|
||||
_("Child existence"),
|
||||
_("Return true if the specified child of the variable exists."),
|
||||
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
|
||||
_("Variables/Structures"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
obj.AddCondition("ObjectVariableChildExists",
|
||||
_("Child existence"),
|
||||
_("Check if the specified child of the variable exists."),
|
||||
_("Child _PARAM2_ of variable _PARAM1_ of _PARAM0_ exists"),
|
||||
_("Variables/Structures"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectvar", _("Variable"))
|
||||
.AddParameter("string", _("Name of the child"))
|
||||
@@ -497,14 +496,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("string", _("Variable"))
|
||||
.SetHidden();
|
||||
|
||||
obj.AddCondition(
|
||||
"BehaviorActivated",
|
||||
_("Behavior activated"),
|
||||
_("Return true if the behavior is activated for the object."),
|
||||
_("Behavior _PARAM1_ of _PARAM0_ is activated"),
|
||||
_("Behaviors"),
|
||||
"res/behavior24.png",
|
||||
"res/behavior16.png")
|
||||
obj.AddCondition("BehaviorActivated",
|
||||
_("Behavior activated"),
|
||||
_("Check if the behavior is activated for the object."),
|
||||
_("Behavior _PARAM1_ of _PARAM0_ is activated"),
|
||||
_("Behaviors"),
|
||||
"res/behavior24.png",
|
||||
"res/behavior16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"))
|
||||
@@ -835,6 +833,24 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectPtr", _("Object"));
|
||||
|
||||
obj.AddExpression("DistanceToPosition",
|
||||
_("Distance between an object and a position"),
|
||||
_("Distance between an object and a position"),
|
||||
_("Position"),
|
||||
"res/conditions/distance.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Target X position"))
|
||||
.AddParameter("expression", _("Target Y position"));
|
||||
|
||||
obj.AddExpression("SqDistanceToPosition",
|
||||
_("Square distance between an object and a position"),
|
||||
_("Square distance between an object and a position"),
|
||||
_("Position"),
|
||||
"res/conditions/distance.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Target X position"))
|
||||
.AddParameter("expression", _("Target Y position"));
|
||||
|
||||
obj.AddExpression("Variable",
|
||||
_("Object's variable"),
|
||||
_("Object's variable"),
|
||||
@@ -867,6 +883,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("string", _("Timer's name"));
|
||||
|
||||
obj.AddExpression("AngleToObject",
|
||||
_("Angle between two objects"),
|
||||
_("Compute the angle between two objects. If you need the "
|
||||
"angle to an arbitrary position, use AngleToPosition."),
|
||||
_("Position"),
|
||||
"res/actions/position.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectPtr", _("Object"));
|
||||
|
||||
obj.AddExpression("AngleToPosition",
|
||||
_("Angle between an object and a position"),
|
||||
_("Compute the angle between the object center and a "
|
||||
"\"target\" position. If you need the angle between two "
|
||||
"objects, use AngleToObject."),
|
||||
_("Position"),
|
||||
"res/actions/position.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Target X position"))
|
||||
.AddParameter("expression", _("Target Y position"));
|
||||
|
||||
extension
|
||||
.AddAction("Create",
|
||||
_("Create an object"),
|
||||
|
@@ -12,11 +12,15 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinCamera",
|
||||
_("Cameras and layers features"),
|
||||
_("Built-in camera extension"),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinCamera",
|
||||
_("Cameras and layers features"),
|
||||
"Each scene can be composed of multiple layers. These conditions "
|
||||
"and actions allow to manipulate them during the game. In "
|
||||
"particular, you can move the camera of a layer to center it on an "
|
||||
"object or a position.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/interface/scene-editor/layers-and-cameras");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
@@ -117,13 +121,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("CameraAngle",
|
||||
_("Angle of a camera of a layer"),
|
||||
_("Test a camera angle."),
|
||||
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/camera24.png",
|
||||
"res/conditions/camera.png")
|
||||
.AddCondition(
|
||||
"CameraAngle",
|
||||
_("Angle of a camera of a layer"),
|
||||
_("Test a camera angle."),
|
||||
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/camera24.png",
|
||||
"res/conditions/camera.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.UseStandardRelationalOperatorParameters("number")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
@@ -245,9 +250,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
|
||||
extension
|
||||
.AddAction("ZoomCamera",
|
||||
_("Change camera zoom"),
|
||||
_("Change camera zoom."),
|
||||
_("Change camera zoom."),
|
||||
_("Change camera zoom to _PARAM1_ (layer : _PARAM2_, camera : "
|
||||
_("Change camera zoom to _PARAM1_ (layer: _PARAM2_, camera: "
|
||||
"_PARAM3_)"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/camera24.png",
|
||||
@@ -469,6 +474,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("expression",
|
||||
_("Scale (1: Default, 2: 2x faster, 0.5: 2x slower...)"));
|
||||
|
||||
extension
|
||||
.AddCondition("LayerDefaultZOrder",
|
||||
_("Layer default Z order"),
|
||||
_("Compare the default Z order set to objects when they "
|
||||
"are created on a layer."),
|
||||
_("the default Z order of objects created on _PARAM1_"),
|
||||
_("Layers and cameras"),
|
||||
"res/conditions/layer24.png",
|
||||
"res/conditions/layer.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.UseStandardRelationalOperatorParameters("number")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddAction("SetLayerDefaultZOrder",
|
||||
_("Change layer default Z order"),
|
||||
_("Change the default Z order set to objects when they are "
|
||||
"created on a layer."),
|
||||
_("Set the default Z order of objects created on _PARAM1_ to "
|
||||
"_PARAM2_"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/layer24.png",
|
||||
"res/actions/layer.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("expression", _("New default Z order"));
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerAmbientLightColor",
|
||||
_("Set the ambient light color"),
|
||||
_("Set the ambient light color of the lighting layer in format "
|
||||
"\"R;G;B\" string."),
|
||||
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),
|
||||
_("Layers and cameras/Lighting"),
|
||||
"res/actions/color24.png",
|
||||
"res/actions/color.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
.SetDefaultValue("\"Lighting\"")
|
||||
.AddParameter("color", _("Color"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddExpression("CameraWidth",
|
||||
_("Width of a camera of a layer"),
|
||||
@@ -601,6 +652,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
|
||||
.SetDefaultValue("0");
|
||||
|
||||
extension
|
||||
.AddExpression("CameraZoom",
|
||||
_("Zoom of a camera of a layer"),
|
||||
_("Zoom of a camera of a layer"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/camera.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
|
||||
.SetDefaultValue("0");
|
||||
|
||||
extension
|
||||
.AddExpression("VueRotation",
|
||||
_("Angle of a camera of a layer"),
|
||||
@@ -622,6 +685,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"));
|
||||
|
||||
extension
|
||||
.AddExpression("LayerDefaultZOrder",
|
||||
_("Default Z Order for a layer"),
|
||||
_("Default Z Order for a layer"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/camera.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinCommonConversions",
|
||||
_("Standard Conversions"),
|
||||
_("Built-in extension providing standard conversions expressions."),
|
||||
"Expressions to convert number, texts and quantities.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/common-conversions");
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
#include "GDCore/Events/Builtin/GroupEvent.h"
|
||||
#include "GDCore/Events/Builtin/LinkEvent.h"
|
||||
@@ -25,8 +26,9 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"BuiltinCommonInstructions",
|
||||
_("Standard events"),
|
||||
_("Built-in extension providing standard events."),
|
||||
_("Builtin events"),
|
||||
"GDevelop comes with a set of events and conditions that allow to "
|
||||
"express the game logic and rules.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/advanced-conditions");
|
||||
@@ -35,7 +37,7 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
extension
|
||||
.AddCondition("Or",
|
||||
_("Or"),
|
||||
_("Return true if one of the sub conditions is true"),
|
||||
_("Check if one of the sub conditions is true"),
|
||||
_("If one of these conditions is true:"),
|
||||
_("Advanced"),
|
||||
"res/conditions/or24.png",
|
||||
@@ -46,7 +48,7 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
extension
|
||||
.AddCondition("And",
|
||||
_("And"),
|
||||
_("Return true if all sub conditions are true"),
|
||||
_("Check if all sub conditions are true"),
|
||||
_("If all of these conditions are true:"),
|
||||
_("Advanced"),
|
||||
"res/conditions/and24.png",
|
||||
@@ -118,6 +120,14 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
"res/foreach.png",
|
||||
std::make_shared<gd::ForEachEvent>());
|
||||
|
||||
extension.AddEvent(
|
||||
"ForEachChildVariable",
|
||||
_("For each child variable (of a structure)"),
|
||||
_("Repeat the event for each child variable of a structure."),
|
||||
"",
|
||||
"res/foreach.png",
|
||||
std::make_shared<gd::ForEachChildVariableEvent>());
|
||||
|
||||
extension.AddEvent("Group",
|
||||
_("Group"),
|
||||
_("Group containing events"),
|
||||
|
@@ -13,13 +13,12 @@ void GD_CORE_API
|
||||
BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"BuiltinExternalLayouts",
|
||||
_("External layouts"),
|
||||
_("Built-in extension providing actions and conditions related to "
|
||||
"external layouts"),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation("BuiltinExternalLayouts",
|
||||
_("External layouts"),
|
||||
"Provides actions and conditions related to "
|
||||
"external layouts.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/interface/scene-editor/external-layouts");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -12,12 +12,14 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinFile",
|
||||
_("Storage"),
|
||||
_("Built-in extension providing functions "
|
||||
"to store data."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinFile",
|
||||
_("Storage"),
|
||||
"Actions and conditions to store data (like the player progress or "
|
||||
"anything else to be persisted across game sessions). Data are "
|
||||
"stored on the device and erased when the game is uninstalled.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/storage");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
@@ -98,7 +100,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
.AddAction(
|
||||
"LireFichierExp",
|
||||
_("Read a value"),
|
||||
_("Read the value saved in the specified element and store it in a scene"
|
||||
_("Read the value saved in the specified element and store it in a "
|
||||
"scene "
|
||||
"variable.\nSpecify the structure leading to the element using / "
|
||||
"(example : Root/Level/Current)\nSpaces are forbidden in element "
|
||||
"names."),
|
||||
@@ -115,7 +118,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
.AddAction(
|
||||
"LireFichierTxt",
|
||||
_("Read a text"),
|
||||
_("Read the text saved in the specified element and store it in a scene "
|
||||
_("Read the text saved in the specified element and store it in a "
|
||||
"scene "
|
||||
"variable.\nSpecify the structure leading to the element using / "
|
||||
"(example : Root/Level/Current)\nSpaces are forbidden in element "
|
||||
"names."),
|
||||
|
@@ -15,7 +15,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsJoystickExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinJoystick",
|
||||
_("Joysticks features"),
|
||||
_("Built-in extension that enables the use of joysticks"),
|
||||
"Built-in extension that enables the use of joysticks.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
|
@@ -15,7 +15,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinKeyboard",
|
||||
_("Keyboard features"),
|
||||
_("Built-in extension that enables the use of a keyboard"),
|
||||
_("Allows your game to respond to keyboard input. Note that this "
|
||||
"does not work with on-screen keyboard on touch devices: use "
|
||||
"instead conditions related to touch when making a game for "
|
||||
"mobile/touchscreen devices."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/keyboard");
|
||||
|
@@ -15,7 +15,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension.SetExtensionInformation(
|
||||
"BuiltinMathematicalTools",
|
||||
_("Mathematical tools"),
|
||||
_("Built-in extension providing mathematical tools"),
|
||||
"A set of mathematical functions that can be used in expressions.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)");
|
||||
|
||||
@@ -40,6 +40,28 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("First angle"))
|
||||
.AddParameter("expression", _("Second angle"));
|
||||
|
||||
extension
|
||||
.AddExpression("AngleBetweenPositions",
|
||||
_("Angle between two positions"),
|
||||
_("Compute the angle between two positions."),
|
||||
_("Mathematical tools"),
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("First point X position"))
|
||||
.AddParameter("expression", _("First point Y position"))
|
||||
.AddParameter("expression", _("Second point X position"))
|
||||
.AddParameter("expression", _("Second point Y position"));
|
||||
|
||||
extension
|
||||
.AddExpression("DistanceBetweenPositions",
|
||||
_("Distance between two positions"),
|
||||
_("Compute the distance between two positions."),
|
||||
_("Mathematical tools"),
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("First point X position"))
|
||||
.AddParameter("expression", _("First point Y position"))
|
||||
.AddParameter("expression", _("Second point X position"))
|
||||
.AddParameter("expression", _("Second point Y position"));
|
||||
|
||||
extension
|
||||
.AddExpression("mod",
|
||||
_("Modulo"),
|
||||
|
@@ -14,8 +14,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"BuiltinMouse",
|
||||
_("Mouse features"),
|
||||
_("Built-in extension that enables the use of a mouse"),
|
||||
_("Mouse and touch"),
|
||||
"Conditions and actions to handle either the mouse or touches on "
|
||||
"touchscreen. By default, conditions related to the mouse will also "
|
||||
"handle the touches - so that it's easier to handle both in your "
|
||||
"game. You can disable this behavior if you want to handle them "
|
||||
"separately in different events.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/mouse-touch");
|
||||
@@ -176,7 +180,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
extension
|
||||
.AddCondition("SourisBouton",
|
||||
_("Mouse button pressed or touch held"),
|
||||
_("Return true if the specified mouse button is pressed or "
|
||||
_("Check if the specified mouse button is pressed or "
|
||||
"if a touch is in contact with the screen."),
|
||||
_("Touch or _PARAM1_ mouse button is down"),
|
||||
_("Mouse and touch"),
|
||||
@@ -187,14 +191,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"MouseButtonReleased",
|
||||
_("Mouse button released"),
|
||||
_("Return true if the specified mouse button was released."),
|
||||
_("_PARAM1_ mouse button was released"),
|
||||
_("Mouse and touch"),
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCondition("MouseButtonReleased",
|
||||
_("Mouse button released"),
|
||||
_("Check if the specified mouse button was released."),
|
||||
_("_PARAM1_ mouse button was released"),
|
||||
_("Mouse and touch"),
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("mouse", _("Button to test"))
|
||||
.MarkAsSimple();
|
||||
@@ -235,7 +238,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.AddCondition(
|
||||
"PopStartedTouch",
|
||||
_("A new touch has started"),
|
||||
_("Return true if a touch has started. The touch identifier can be "
|
||||
_("Check if a touch has started. The touch identifier can be "
|
||||
"accessed using LastTouchId().\nAs more than one touch can be "
|
||||
"started, this condition is only true once for each touch: the "
|
||||
"next time you use it, it will be for a new touch, or it will "
|
||||
@@ -250,7 +253,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.AddCondition(
|
||||
"PopEndedTouch",
|
||||
_("A touch has ended"),
|
||||
_("Return true if a touch has ended. The touch identifier can be "
|
||||
_("Check if a touch has ended. The touch identifier can be "
|
||||
"accessed using LastEndedTouchId().\nAs more than one touch can be "
|
||||
"ended, this condition is only true once for each touch: the next "
|
||||
"time you use it, it will be for a new touch, or it will return "
|
||||
|
@@ -15,7 +15,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinNetwork",
|
||||
_("Basic internet features"),
|
||||
_("Built-in extension providing network features."),
|
||||
_("Features to send web requests, communicate with external \"APIs\" and other network related tasks."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/network");
|
||||
@@ -34,10 +34,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
"res/actions/net24.png",
|
||||
"res/actions/net.png")
|
||||
.AddParameter("string", _("Host, with protocol"))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"http://example.com/\"."))
|
||||
.SetParameterLongDescription(_("Example: \"http://example.com/\"."))
|
||||
.AddParameter("string", _("Path"))
|
||||
.SetParameterLongDescription(_("Example: \"/user/123\" or \"/some-page.php\"."))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"/user/123\" or \"/some-page.php\"."))
|
||||
.AddParameter("string", _("Request body content"))
|
||||
.AddParameter("string", _("Method: \"POST\" or \"GET\""), "", true)
|
||||
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
|
||||
@@ -51,6 +51,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
"variable. If the server returns *JSON*, you may want to use the "
|
||||
"action \"Convert JSON to a scene variable\" afterwards, to "
|
||||
"explore the results with a *structure variable*."))
|
||||
.MarkAsComplex()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SendAsyncRequest",
|
||||
_("Send a request to a web page"),
|
||||
_("Send an asynchronous request to the specified web page.\n\nPlease "
|
||||
"note that for "
|
||||
"the web games, the game must be hosted on the same host "
|
||||
"as specified below, except if the server is configured to answer "
|
||||
"to all requests (cross-domain requests)."),
|
||||
_("Send a _PARAM2_ request to _PARAM0_ with body: _PARAM1_, and "
|
||||
"store the result in _PARAM4_ (or in _PARAM5_ in case of error)"),
|
||||
_("Network"),
|
||||
"res/actions/net24.png",
|
||||
"res/actions/net.png")
|
||||
.AddParameter("string", _("URL (API or web-page address)"))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"https://example.com/user/123\". Using *https* is "
|
||||
"highly recommended."))
|
||||
.AddParameter("string", _("Request body content"))
|
||||
.AddParameter("stringWithSelector",
|
||||
_("Resize mode"),
|
||||
"[\"GET\", \"POST\", \"PUT\", \"HEAD\", \"DELETE\", "
|
||||
"\"PATCH\", \"OPTIONS\"]",
|
||||
false)
|
||||
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
|
||||
.SetDefaultValue("\"GET\"")
|
||||
.AddParameter("string", _("Content type"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("If empty, \"application/x-www-form-urlencoded\" will be used."))
|
||||
.AddParameter(
|
||||
"scenevar", _("Variable where to store the response"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("The response of the server will be stored, as a string, in this "
|
||||
"variable. If the server returns *JSON*, you may want to use the "
|
||||
"action \"Convert JSON to a scene variable\" afterwards, to "
|
||||
"explore the results with a *structure variable*."))
|
||||
.AddParameter(
|
||||
"scenevar", _("Variable where to store the error message"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("Optional, only used if an error occurs. This will contain the "
|
||||
"error message (if request could not be sent) or the [\"status "
|
||||
"code\"](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes), "
|
||||
"if the server returns a status >= 400."))
|
||||
.MarkAsComplex();
|
||||
|
||||
extension
|
||||
@@ -67,6 +113,22 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
_("Path to file (for example : /folder/file.txt)"))
|
||||
.AddParameter("string", _("Save as"));
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"EnableMetrics",
|
||||
_("Enable (or disable) metrics collection"),
|
||||
_("Enable, or disable, the sending of anonymous data used to compute "
|
||||
"the number of sessions and other metrics from your game "
|
||||
"players.\nBe sure to only send metrics if in accordance with the "
|
||||
"terms of service of your game and if they player gave their "
|
||||
"consent, depending on how your game/company handles this."),
|
||||
_("Enable analytics metrics: _PARAM1_"),
|
||||
_("Network"),
|
||||
"res/actions/net24.png",
|
||||
"res/actions/net.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("yesorno", _("Enable the metrics?"));
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"JSONToVariableStructure",
|
||||
|
@@ -15,7 +15,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinScene",
|
||||
_("Scene management features"),
|
||||
_("Built-in extension allowing to manipulate scenes and providing common features"),
|
||||
_("Actions and conditions to manipulate the scenes during the game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
|
@@ -367,15 +367,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
|
||||
obj.AddAction(
|
||||
"ChangeColor",
|
||||
_("Global color"),
|
||||
_("Change the global color of an object. The default color is white."),
|
||||
_("Change color of _PARAM0_ to _PARAM1_"),
|
||||
_("Tint color"),
|
||||
_("Change the tint of an object. The default color is white."),
|
||||
_("Change tint of _PARAM0_ to _PARAM1_"),
|
||||
_("Effects"),
|
||||
"res/actions/color24.png",
|
||||
"res/actions/color.png")
|
||||
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.AddParameter("color", _("Color"));
|
||||
.AddParameter("color", _("Tint"));
|
||||
|
||||
obj.AddAction("ChangeBlendMode",
|
||||
_("Blend mode"),
|
||||
@@ -417,7 +417,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
|
||||
obj.AddCondition("FlippedX",
|
||||
_("Horizontally flipped"),
|
||||
_("Return true if the object is horizontally flipped"),
|
||||
_("Check if the object is horizontally flipped"),
|
||||
_("_PARAM0_ is horizontally flipped"),
|
||||
_("Effects"),
|
||||
"res/actions/flipX24.png",
|
||||
@@ -427,7 +427,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
|
||||
obj.AddCondition("FlippedY",
|
||||
_("Vertically flipped"),
|
||||
_("Return true if the object is vertically flipped"),
|
||||
_("Check if the object is vertically flipped"),
|
||||
_("_PARAM0_ is vertically flipped"),
|
||||
_("Effects"),
|
||||
"res/actions/flipY24.png",
|
||||
|
@@ -16,8 +16,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinStringInstructions",
|
||||
_("Text manipulation"),
|
||||
_("Built-in extension providing expressions for manipulating text "
|
||||
"objects."),
|
||||
"Provides expressions to manipulate strings (also called texts).",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
@@ -121,15 +120,17 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"))
|
||||
.SetHidden(); // Deprecated, see StrFindLast instead.
|
||||
.SetHidden(); // Deprecated, see StrFindLast instead.
|
||||
|
||||
extension
|
||||
.AddExpression("StrFindLast",
|
||||
_("Search the last occurence in a text"),
|
||||
_("Search the last occurence in a string (return the position of "
|
||||
"the result, from the beginning of the string, or -1 if not found)"),
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
.AddExpression(
|
||||
"StrFindLast",
|
||||
_("Search the last occurence in a text"),
|
||||
_("Search the last occurence in a string (return the position of "
|
||||
"the result, from the beginning of the string, or -1 if not "
|
||||
"found)"),
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
.AddParameter("string", _("Text"))
|
||||
.AddParameter("string", _("Text to search for"));
|
||||
@@ -162,14 +163,16 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
.AddParameter("expression",
|
||||
_("Position of the last character in the string to be "
|
||||
"considered in the search"))
|
||||
.SetHidden(); // Deprecated, see StrFindLastFrom instead.
|
||||
.SetHidden(); // Deprecated, see StrFindLastFrom instead.
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"StrFindLastFrom",
|
||||
_("Search the last occurence in a text, starting from a position"),
|
||||
_("Search in a text the last occurence, starting from a position (return "
|
||||
" the position of the result, from the beginning of the string, or -1 if not found)"),
|
||||
_("Search in a text the last occurence, starting from a position "
|
||||
"(return "
|
||||
" the position of the result, from the beginning of the string, or "
|
||||
"-1 if not found)"),
|
||||
_("Manipulation of text"),
|
||||
"res/conditions/toujours24.png")
|
||||
|
||||
|
@@ -12,12 +12,14 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinTime",
|
||||
_("Time"),
|
||||
_("Built-in extension providing actions and "
|
||||
"conditions related to time."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinTime",
|
||||
_("Time"),
|
||||
"Actions and conditions to run timers, get the current time or "
|
||||
"modify the time scale (speed at which the game is running - useful "
|
||||
"for slow motion effects).",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/timers");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -15,7 +15,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinVariables",
|
||||
_("Variable features"),
|
||||
_("Built-in extension allowing to manipulate variables"),
|
||||
"Actions, conditions and expressions to handle variables, from "
|
||||
"simple variables like the player score, the number of remaining "
|
||||
"lives to complex variables containing arbitrary data like an "
|
||||
"inventory or the result of a web request.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/variables");
|
||||
@@ -47,7 +50,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
|
||||
.AddCondition(
|
||||
"VariableChildExists",
|
||||
_("Child existence"),
|
||||
_("Return true if the specified child of the scene variable exists."),
|
||||
_("Check if the specified child of the scene variable exists."),
|
||||
_("Child _PARAM1_ of scene variable _PARAM0_ exists"),
|
||||
_("Variables/Structures"),
|
||||
"res/conditions/var24.png",
|
||||
@@ -59,7 +62,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
|
||||
extension
|
||||
.AddCondition("GlobalVariableChildExists",
|
||||
_("Child existence"),
|
||||
_("Return true if the specified child of the global "
|
||||
_("Check if the specified child of the global "
|
||||
"variable exists."),
|
||||
_("Child _PARAM1_ of global variable _PARAM0_ exists"),
|
||||
_("Variables/Global variables/Structures"),
|
||||
@@ -94,14 +97,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"VarGlobalTxt",
|
||||
_("Text of a global variable"),
|
||||
_("Compare the text of a global variable."),
|
||||
_("the text of the global variable _PARAM0_"),
|
||||
_("Variables/Global variables"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
.AddCondition("VarGlobalTxt",
|
||||
_("Text of a global variable"),
|
||||
_("Compare the text of a global variable."),
|
||||
_("the text of the global variable _PARAM0_"),
|
||||
_("Variables/Global variables"),
|
||||
"res/conditions/var24.png",
|
||||
"res/conditions/var.png")
|
||||
.AddParameter("globalvar", _("Variable"))
|
||||
.UseStandardRelationalOperatorParameters("string")
|
||||
.MarkAsAdvanced();
|
||||
@@ -154,14 +156,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"ModVarGlobalTxt",
|
||||
_("String of a global variable"),
|
||||
_("Modify the text of a global variable."),
|
||||
_("the text of global variable _PARAM0_"),
|
||||
_("Variables/Global variables"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
.AddAction("ModVarGlobalTxt",
|
||||
_("String of a global variable"),
|
||||
_("Modify the text of a global variable."),
|
||||
_("the text of global variable _PARAM0_"),
|
||||
_("Variables/Global variables"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
.AddParameter("globalvar", _("Variable"))
|
||||
.UseStandardOperatorParameters("string")
|
||||
.MarkAsAdvanced();
|
||||
|
@@ -12,12 +12,14 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("BuiltinWindow",
|
||||
_("Window features"),
|
||||
_("Built-in extension allowing to manipulate "
|
||||
"the game window and canvas"),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"BuiltinWindow",
|
||||
_("Window features"),
|
||||
"Provides actions and conditions to manipulate the game window. "
|
||||
"Depending on the platform on which the game is running, not all of "
|
||||
"these features can be applied.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/window");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
@@ -38,6 +40,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
true)
|
||||
.SetDefaultValue("yes");
|
||||
|
||||
extension
|
||||
.AddCondition("IsFullScreen",
|
||||
_("Fullscreen activated?"),
|
||||
_("Check if the game is currently in fullscreen."),
|
||||
_("The game is in fullscreen"),
|
||||
_("Game's window and resolution"),
|
||||
"res/actions/fullscreen24.png",
|
||||
"res/actions/fullscreen.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("SetWindowMargins",
|
||||
_("Change the window's margins"),
|
||||
|
@@ -120,12 +120,12 @@ class GD_CORE_API BehaviorMetadata {
|
||||
BehaviorMetadata& AddIncludeFile(const gd::String& includeFile);
|
||||
|
||||
/**
|
||||
* Get the help path of the behavior, relative to the documentation root.
|
||||
* Get the help path of the behavior, relative to the GDevelop documentation root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; }
|
||||
|
||||
/**
|
||||
* Set the help path of the behavior, relative to the documentation root.
|
||||
* Set the help path of the behavior, relative to the GDevelop documentation root.
|
||||
*
|
||||
* The behavior instructions will have this help path set by
|
||||
* default, unless you call SetHelpPath on them.
|
||||
|
132
Core/GDCore/Extensions/Metadata/DependencyMetadata.h
Normal file
132
Core/GDCore/Extensions/Metadata/DependencyMetadata.h
Normal 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.
|
||||
*/
|
||||
#ifndef DEPENDENCYMETADATA_H
|
||||
#define DEPENDENCYMETADATA_H
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
/**
|
||||
* \brief Contains information about a dependency (library, npm/cordova
|
||||
* package, or other according to the export) of an extension.
|
||||
*/
|
||||
class GD_CORE_API DependencyMetadata {
|
||||
public:
|
||||
/**
|
||||
* Construct a new dependency metadata, though you probably want to call
|
||||
* `AddDependency` on gd::PlatformExtension.
|
||||
*
|
||||
* \see gd::PlatformExtension
|
||||
*/
|
||||
DependencyMetadata() : onlyIfSomeExtraSettingsNonEmpty(false){};
|
||||
|
||||
/**
|
||||
* \brief Sets the name shown to users.
|
||||
*/
|
||||
DependencyMetadata& SetName(const gd::String& name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets the name written by the exporter.
|
||||
* Typically, this is what is used by the dependency manager
|
||||
* to find the dependency.
|
||||
*
|
||||
* \example
|
||||
* \code
|
||||
* // For depending upon the NPM package is-thirteen
|
||||
* gd::DependencyMetadata dependencyMetadata = gd::DependencyMetadata();
|
||||
* dependencyMetadata.setExporterName("is-thirteen");
|
||||
* \endcode
|
||||
*/
|
||||
DependencyMetadata& SetExportName(const gd::String& exportName_) {
|
||||
exportName = exportName_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set the version of the dependency to install.
|
||||
* Use an empty string to use the latest version.
|
||||
*/
|
||||
DependencyMetadata& SetVersion(const gd::String& version_) {
|
||||
version = version_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets the type of dependecy (what will be used to install it)
|
||||
*
|
||||
* This can either be "npm" or "cordova" for now.
|
||||
*/
|
||||
DependencyMetadata& SetDependencyType(const gd::String& dependencyType_) {
|
||||
dependencyType = dependencyType_;
|
||||
if (dependencyType != "npm" && dependencyType != "cordova") {
|
||||
gd::LogWarning("Invalid dependency type: " + dependencyType);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets a dependency type specific setting.
|
||||
*/
|
||||
DependencyMetadata& SetExtraSetting(
|
||||
const gd::String& settingName,
|
||||
const gd::PropertyDescriptor& settingValue) {
|
||||
extraData[settingName] = settingValue;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Mark the dependency to be included in the export only if at least
|
||||
* one of the extra settings is set.
|
||||
*/
|
||||
DependencyMetadata& OnlyIfSomeExtraSettingsNonEmpty() {
|
||||
onlyIfSomeExtraSettingsNonEmpty = true;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Check if at least one of the extra settings must be set for the
|
||||
* dependency to be included in the export.
|
||||
*/
|
||||
bool IsOnlyIfSomeExtraSettingsNonEmpty() const {
|
||||
return onlyIfSomeExtraSettingsNonEmpty;
|
||||
};
|
||||
|
||||
const gd::String& GetName() const { return name; };
|
||||
const gd::String& GetExportName() const { return exportName; };
|
||||
const gd::String& GetVersion() const { return version; };
|
||||
const gd::String& GetDependencyType() const {
|
||||
if (dependencyType == "")
|
||||
gd::LogWarning("Dependency has no type, it won't be exported.");
|
||||
return dependencyType;
|
||||
};
|
||||
|
||||
const std::map<gd::String, gd::PropertyDescriptor>& GetAllExtraSettings()
|
||||
const {
|
||||
return extraData;
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String name; ///< The name of the dependency.
|
||||
gd::String exportName; ///< The name used to install the package (example:
|
||||
///< npm package name for npm dependency type).
|
||||
gd::String version; ///< The version of the dependency
|
||||
gd::String dependencyType; ///< The tool used to install the dependency.
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
extraData; ///< Contains dependency type specific additional parameters
|
||||
///< for the dependency.
|
||||
bool onlyIfSomeExtraSettingsNonEmpty; ///< If true, only use this dependency
|
||||
///< if at least one of the extra
|
||||
///< settings is set.
|
||||
};
|
||||
} // namespace gd
|
||||
#endif // DEPENDENCYMETADATA_H
|
@@ -49,7 +49,7 @@ class GD_CORE_API EffectMetadata {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the help path of the effect, relative to the documentation root.
|
||||
* Set the help path of the effect, relative to the GDevelop documentation root.
|
||||
*/
|
||||
EffectMetadata& SetHelpPath(const gd::String& path) {
|
||||
helpPath = path;
|
||||
@@ -81,7 +81,7 @@ class GD_CORE_API EffectMetadata {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the help path of the effect, relative to the documentation root.
|
||||
* \brief Get the help path of the effect, relative to the GDevelop documentation root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; }
|
||||
|
||||
|
@@ -151,12 +151,12 @@ class GD_CORE_API ExpressionMetadata {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the help path of the expression, relative to the documentation root.
|
||||
* Get the help path of the expression, relative to the GDevelop documentation root.
|
||||
*/
|
||||
const gd::String &GetHelpPath() const { return helpPath; }
|
||||
|
||||
/**
|
||||
* Set the help path of the expression, relative to the documentation root.
|
||||
* Set the help path of the expression, relative to the GDevelop documentation root.
|
||||
*/
|
||||
ExpressionMetadata &SetHelpPath(const gd::String &path) {
|
||||
helpPath = path;
|
||||
|
@@ -4,10 +4,13 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "InstructionMetadata.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "ParameterMetadata.h"
|
||||
|
||||
namespace gd {
|
||||
InstructionMetadata::InstructionMetadata()
|
||||
@@ -45,8 +48,6 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
|
||||
isObjectInstruction(false),
|
||||
isBehaviorInstruction(false) {}
|
||||
|
||||
ParameterMetadata::ParameterMetadata() : optional(false), codeOnly(false) {}
|
||||
|
||||
InstructionMetadata& InstructionMetadata::AddParameter(
|
||||
const gd::String& type,
|
||||
const gd::String& description,
|
||||
@@ -158,26 +159,4 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("type", type);
|
||||
element.SetAttribute("supplementaryInformation", supplementaryInformation);
|
||||
element.SetAttribute("optional", optional);
|
||||
element.SetAttribute("description", description);
|
||||
element.SetAttribute("longDescription", longDescription);
|
||||
element.SetAttribute("codeOnly", codeOnly);
|
||||
element.SetAttribute("defaultValue", defaultValue);
|
||||
element.SetAttribute("name", name);
|
||||
}
|
||||
|
||||
void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
|
||||
type = element.GetStringAttribute("type");
|
||||
supplementaryInformation =
|
||||
element.GetStringAttribute("supplementaryInformation");
|
||||
optional = element.GetBoolAttribute("optional");
|
||||
description = element.GetStringAttribute("description");
|
||||
longDescription = element.GetStringAttribute("longDescription");
|
||||
codeOnly = element.GetBoolAttribute("codeOnly");
|
||||
defaultValue = element.GetStringAttribute("defaultValue");
|
||||
name = element.GetStringAttribute("name");
|
||||
}
|
||||
} // namespace gd
|
||||
|
@@ -10,8 +10,10 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "ParameterMetadata.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
@@ -22,212 +24,6 @@ class SerializerElement;
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Contains user-friendly info about a parameter, and information about
|
||||
* what a parameter need
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API ParameterMetadata {
|
||||
public:
|
||||
ParameterMetadata();
|
||||
virtual ~ParameterMetadata(){};
|
||||
|
||||
/**
|
||||
* \brief Return the type of the parameter.
|
||||
* \see gd::ParameterMetadata::IsObject
|
||||
*/
|
||||
const gd::String &GetType() const { return type; }
|
||||
|
||||
/**
|
||||
* \brief Set the type of the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetType(const gd::String &type_) {
|
||||
type = type_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the name of the parameter.
|
||||
*
|
||||
* Name is optional, and won't be filled for most parameters of extensions.
|
||||
* It is useful when generating a function from events, where parameters must
|
||||
* be named.
|
||||
*/
|
||||
const gd::String &GetName() const { return name; }
|
||||
|
||||
/**
|
||||
* \brief Set the name of the parameter.
|
||||
*
|
||||
* Name is optional, and won't be filled for most parameters of extensions.
|
||||
* It is useful when generating a function from events, where parameters must
|
||||
* be named.
|
||||
*/
|
||||
ParameterMetadata &SetName(const gd::String &name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return an optional additional information, used for some parameters
|
||||
* with special type (For example, it can contains the type of object accepted
|
||||
* by the parameter).
|
||||
*/
|
||||
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
|
||||
|
||||
/**
|
||||
* \brief Set an optional additional information, used for some parameters
|
||||
* with special type (For example, it can contains the type of object accepted
|
||||
* by the parameter).
|
||||
*/
|
||||
ParameterMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
|
||||
supplementaryInformation = supplementaryInformation_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the parameter is optional.
|
||||
*/
|
||||
bool IsOptional() const { return optional; }
|
||||
|
||||
/**
|
||||
* \brief Set if the parameter is optional.
|
||||
*/
|
||||
ParameterMetadata &SetOptional(bool optional_ = true) {
|
||||
optional = optional_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the description of the parameter
|
||||
*/
|
||||
const gd::String &GetDescription() const { return description; }
|
||||
|
||||
/**
|
||||
* \brief Set the description of the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetDescription(const gd::String &description_) {
|
||||
description = description_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the parameter is only meant to be completed during
|
||||
* compilation and must not be displayed to the user.
|
||||
*/
|
||||
bool IsCodeOnly() const { return codeOnly; }
|
||||
|
||||
/**
|
||||
* \brief Set if the parameter is only meant to be completed during
|
||||
* compilation and must not be displayed to the user.
|
||||
*/
|
||||
ParameterMetadata &SetCodeOnly(bool codeOnly_ = true) {
|
||||
codeOnly = codeOnly_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the default value for the parameter.
|
||||
*/
|
||||
const gd::String &GetDefaultValue() const { return defaultValue; }
|
||||
|
||||
/**
|
||||
* \brief Set the default value, if the parameter is optional.
|
||||
*/
|
||||
ParameterMetadata &SetDefaultValue(const gd::String &defaultValue_) {
|
||||
defaultValue = defaultValue_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the user friendly, long description for the parameter.
|
||||
*/
|
||||
const gd::String &GetLongDescription() const { return longDescription; }
|
||||
|
||||
/**
|
||||
* \brief Set the user friendly, long description for the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetLongDescription(const gd::String &longDescription_) {
|
||||
longDescription = longDescription_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is "object", "objectPtr" or
|
||||
* "objectList".
|
||||
*
|
||||
* \see gd::ParameterMetadata::GetType
|
||||
*/
|
||||
static bool IsObject(const gd::String ¶meterType) {
|
||||
return parameterType == "object" || parameterType == "objectPtr" ||
|
||||
parameterType == "objectList" ||
|
||||
parameterType == "objectListWithoutPicking";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is "behavior".
|
||||
*
|
||||
* \see gd::ParameterMetadata::GetType
|
||||
*/
|
||||
static bool IsBehavior(const gd::String ¶meterType) {
|
||||
return parameterType == "behavior";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is an expression of the
|
||||
* given type.
|
||||
* \note If you had a new type of parameter, also add it in the IDE (
|
||||
* see EventsFunctionParametersEditor) and in the EventsCodeGenerator.
|
||||
*/
|
||||
static bool IsExpression(const gd::String &type,
|
||||
const gd::String ¶meterType) {
|
||||
if (type == "number") {
|
||||
return parameterType == "expression" || parameterType == "camera" ||
|
||||
parameterType == "forceMultiplier";
|
||||
} else if (type == "string") {
|
||||
return parameterType == "string" || parameterType == "layer" ||
|
||||
parameterType == "color" || parameterType == "file" ||
|
||||
parameterType == "joyaxis" ||
|
||||
parameterType == "stringWithSelector" ||
|
||||
parameterType == "sceneName";
|
||||
} else if (type == "variable") {
|
||||
return parameterType == "objectvar" || parameterType == "globalvar" ||
|
||||
parameterType == "scenevar";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the ParameterMetadata to the specified element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement &element) const;
|
||||
|
||||
/**
|
||||
* \brief Load the ParameterMetadata from the specified element
|
||||
*/
|
||||
void UnserializeFrom(const gd::SerializerElement &element);
|
||||
///@}
|
||||
|
||||
// TODO: Deprecated public fields. Any direct using should be moved to
|
||||
// getter/setter.
|
||||
gd::String type; ///< Parameter type
|
||||
gd::String supplementaryInformation; ///< Used if needed
|
||||
bool optional; ///< True if the parameter is optional
|
||||
|
||||
gd::String description; ///< Description shown in editor
|
||||
bool codeOnly; ///< True if parameter is relative to code generation only,
|
||||
///< i.e. must not be shown in editor
|
||||
private:
|
||||
gd::String longDescription; ///< Long description shown in the editor.
|
||||
gd::String defaultValue; ///< Used as a default value in editor or if an
|
||||
///< optional parameter is empty.
|
||||
gd::String name; ///< The name of the parameter to be used in code
|
||||
///< generation. Optional.
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Describe user-friendly information about an instruction (action or
|
||||
* condition), its parameters and the function name as well as other information
|
||||
@@ -274,12 +70,14 @@ class GD_CORE_API InstructionMetadata {
|
||||
bool CanHaveSubInstructions() const { return canHaveSubInstructions; }
|
||||
|
||||
/**
|
||||
* Get the help path of the instruction, relative to the documentation root.
|
||||
* Get the help path of the instruction, relative to the GDevelop
|
||||
* documentation root.
|
||||
*/
|
||||
const gd::String &GetHelpPath() const { return helpPath; }
|
||||
|
||||
/**
|
||||
* Set the help path of the instruction, relative to the documentation root.
|
||||
* Set the help path of the instruction, relative to the GDevelop
|
||||
* documentation root.
|
||||
*/
|
||||
InstructionMetadata &SetHelpPath(const gd::String &path) {
|
||||
helpPath = path;
|
||||
|
@@ -108,13 +108,13 @@ class GD_CORE_API ObjectMetadata {
|
||||
ObjectMetadata& SetDescription(const gd::String& description_);
|
||||
|
||||
/**
|
||||
* Get the help path of the object, relative to the documentation root.
|
||||
* Get the help path of the object, relative to the GDevelop documentation root.
|
||||
*/
|
||||
const gd::String &GetHelpPath() const { return helpPath; }
|
||||
|
||||
/**
|
||||
* Set the help path of the object, relative to the documentation root.
|
||||
*
|
||||
* Set the help path of the object, relative to the GDevelop documentation root.
|
||||
*
|
||||
* The object instructions will have this help path set by
|
||||
* default, unless you call SetHelpPath on them.
|
||||
*/
|
||||
|
38
Core/GDCore/Extensions/Metadata/ParameterMetadata.cpp
Normal file
38
Core/GDCore/Extensions/Metadata/ParameterMetadata.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "ParameterMetadata.h"
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
ParameterMetadata::ParameterMetadata() : optional(false), codeOnly(false) {}
|
||||
|
||||
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("type", type);
|
||||
element.SetAttribute("supplementaryInformation", supplementaryInformation);
|
||||
element.SetAttribute("optional", optional);
|
||||
element.SetAttribute("description", description);
|
||||
element.SetAttribute("longDescription", longDescription);
|
||||
element.SetAttribute("codeOnly", codeOnly);
|
||||
element.SetAttribute("defaultValue", defaultValue);
|
||||
element.SetAttribute("name", name);
|
||||
}
|
||||
|
||||
void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
|
||||
type = element.GetStringAttribute("type");
|
||||
supplementaryInformation =
|
||||
element.GetStringAttribute("supplementaryInformation");
|
||||
optional = element.GetBoolAttribute("optional");
|
||||
description = element.GetStringAttribute("description");
|
||||
longDescription = element.GetStringAttribute("longDescription");
|
||||
codeOnly = element.GetBoolAttribute("codeOnly");
|
||||
defaultValue = element.GetStringAttribute("defaultValue");
|
||||
name = element.GetStringAttribute("name");
|
||||
}
|
||||
|
||||
} // namespace gd
|
232
Core/GDCore/Extensions/Metadata/ParameterMetadata.h
Normal file
232
Core/GDCore/Extensions/Metadata/ParameterMetadata.h
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef PARAMETER_METADATA_H
|
||||
#define PARAMETER_METADATA_H
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class EventsCodeGenerator;
|
||||
class EventsCodeGenerationContext;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Describe a parameter of an instruction (action, condition) or of an
|
||||
* expression: type, user-friendly description, etc...
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API ParameterMetadata {
|
||||
public:
|
||||
ParameterMetadata();
|
||||
virtual ~ParameterMetadata(){};
|
||||
|
||||
/**
|
||||
* \brief Return the type of the parameter.
|
||||
* \see gd::ParameterMetadata::IsObject
|
||||
*/
|
||||
const gd::String &GetType() const { return type; }
|
||||
|
||||
/**
|
||||
* \brief Set the type of the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetType(const gd::String &type_) {
|
||||
type = type_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the name of the parameter.
|
||||
*
|
||||
* Name is optional, and won't be filled for most parameters of extensions.
|
||||
* It is useful when generating a function from events, where parameters must
|
||||
* be named.
|
||||
*/
|
||||
const gd::String &GetName() const { return name; }
|
||||
|
||||
/**
|
||||
* \brief Set the name of the parameter.
|
||||
*
|
||||
* Name is optional, and won't be filled for most parameters of extensions.
|
||||
* It is useful when generating a function from events, where parameters must
|
||||
* be named.
|
||||
*/
|
||||
ParameterMetadata &SetName(const gd::String &name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return an optional additional information, used for some parameters
|
||||
* with special type (For example, it can contains the type of object accepted
|
||||
* by the parameter).
|
||||
*/
|
||||
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
|
||||
|
||||
/**
|
||||
* \brief Set an optional additional information, used for some parameters
|
||||
* with special type (For example, it can contains the type of object accepted
|
||||
* by the parameter).
|
||||
*/
|
||||
ParameterMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
|
||||
supplementaryInformation = supplementaryInformation_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the parameter is optional.
|
||||
*/
|
||||
bool IsOptional() const { return optional; }
|
||||
|
||||
/**
|
||||
* \brief Set if the parameter is optional.
|
||||
*/
|
||||
ParameterMetadata &SetOptional(bool optional_ = true) {
|
||||
optional = optional_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the description of the parameter
|
||||
*/
|
||||
const gd::String &GetDescription() const { return description; }
|
||||
|
||||
/**
|
||||
* \brief Set the description of the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetDescription(const gd::String &description_) {
|
||||
description = description_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the parameter is only meant to be completed during
|
||||
* compilation and must not be displayed to the user.
|
||||
*/
|
||||
bool IsCodeOnly() const { return codeOnly; }
|
||||
|
||||
/**
|
||||
* \brief Set if the parameter is only meant to be completed during
|
||||
* compilation and must not be displayed to the user.
|
||||
*/
|
||||
ParameterMetadata &SetCodeOnly(bool codeOnly_ = true) {
|
||||
codeOnly = codeOnly_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the default value for the parameter.
|
||||
*/
|
||||
const gd::String &GetDefaultValue() const { return defaultValue; }
|
||||
|
||||
/**
|
||||
* \brief Set the default value, if the parameter is optional.
|
||||
*/
|
||||
ParameterMetadata &SetDefaultValue(const gd::String &defaultValue_) {
|
||||
defaultValue = defaultValue_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the user friendly, long description for the parameter.
|
||||
*/
|
||||
const gd::String &GetLongDescription() const { return longDescription; }
|
||||
|
||||
/**
|
||||
* \brief Set the user friendly, long description for the parameter.
|
||||
*/
|
||||
ParameterMetadata &SetLongDescription(const gd::String &longDescription_) {
|
||||
longDescription = longDescription_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is "object", "objectPtr" or
|
||||
* "objectList".
|
||||
*
|
||||
* \see gd::ParameterMetadata::GetType
|
||||
*/
|
||||
static bool IsObject(const gd::String ¶meterType) {
|
||||
return parameterType == "object" || parameterType == "objectPtr" ||
|
||||
parameterType == "objectList" ||
|
||||
parameterType == "objectListWithoutPicking";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is "behavior".
|
||||
*
|
||||
* \see gd::ParameterMetadata::GetType
|
||||
*/
|
||||
static bool IsBehavior(const gd::String ¶meterType) {
|
||||
return parameterType == "behavior";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is an expression of the
|
||||
* given type.
|
||||
* \note If you had a new type of parameter, also add it in the IDE (
|
||||
* see EventsFunctionParametersEditor) and in the EventsCodeGenerator.
|
||||
*/
|
||||
static bool IsExpression(const gd::String &type,
|
||||
const gd::String ¶meterType) {
|
||||
if (type == "number") {
|
||||
return parameterType == "expression" || parameterType == "camera" ||
|
||||
parameterType == "forceMultiplier";
|
||||
} else if (type == "string") {
|
||||
return parameterType == "string" || parameterType == "layer" ||
|
||||
parameterType == "color" || parameterType == "file" ||
|
||||
parameterType == "joyaxis" ||
|
||||
parameterType == "stringWithSelector" ||
|
||||
parameterType == "sceneName";
|
||||
} else if (type == "variable") {
|
||||
return parameterType == "objectvar" || parameterType == "globalvar" ||
|
||||
parameterType == "scenevar";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the ParameterMetadata to the specified element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement &element) const;
|
||||
|
||||
/**
|
||||
* \brief Load the ParameterMetadata from the specified element
|
||||
*/
|
||||
void UnserializeFrom(const gd::SerializerElement &element);
|
||||
///@}
|
||||
|
||||
// TODO: Deprecated public fields. Any direct usage should be moved to
|
||||
// getter/setter.
|
||||
gd::String type; ///< Parameter type
|
||||
gd::String supplementaryInformation; ///< Used if needed
|
||||
bool optional; ///< True if the parameter is optional
|
||||
|
||||
gd::String description; ///< Description shown in editor
|
||||
bool codeOnly; ///< True if parameter is relative to code generation only,
|
||||
///< i.e. must not be shown in editor
|
||||
private:
|
||||
gd::String longDescription; ///< Long description shown in the editor.
|
||||
gd::String defaultValue; ///< Used as a default value in editor or if an
|
||||
///< optional parameter is empty.
|
||||
gd::String name; ///< The name of the parameter to be used in code
|
||||
///< generation. Optional.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
#endif // PARAMETER_METADATA_H
|
@@ -4,9 +4,12 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EventMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
@@ -38,8 +41,7 @@ gd::InstructionMetadata& PlatformExtension::AddCondition(
|
||||
const gd::String& icon,
|
||||
const gd::String& smallicon) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
conditionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -62,8 +64,7 @@ gd::InstructionMetadata& PlatformExtension::AddAction(
|
||||
const gd::String& icon,
|
||||
const gd::String& smallicon) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
actionsInfos[nameWithNamespace] = InstructionMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -84,8 +85,7 @@ gd::ExpressionMetadata& PlatformExtension::AddExpression(
|
||||
const gd::String& group,
|
||||
const gd::String& smallicon) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
expressionsInfos[nameWithNamespace] = ExpressionMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -104,8 +104,7 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
|
||||
const gd::String& group,
|
||||
const gd::String& smallicon) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
strExpressionsInfos[nameWithNamespace] = ExpressionMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -117,14 +116,20 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::DependencyMetadata& PlatformExtension::AddDependency() {
|
||||
extensionDependenciesMetadata.push_back(DependencyMetadata());
|
||||
return extensionDependenciesMetadata.back();
|
||||
}
|
||||
#endif
|
||||
|
||||
gd::ObjectMetadata& PlatformExtension::AddObject(
|
||||
const gd::String& name,
|
||||
const gd::String& fullname,
|
||||
const gd::String& description,
|
||||
const gd::String& icon24x24,
|
||||
std::shared_ptr<gd::Object> instance) {
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
objectsInfos[nameWithNamespace] = ObjectMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -146,8 +151,7 @@ gd::BehaviorMetadata& PlatformExtension::AddBehavior(
|
||||
const gd::String& className,
|
||||
std::shared_ptr<gd::Behavior> instance,
|
||||
std::shared_ptr<gd::BehaviorsSharedData> sharedDatasInstance) {
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
behaviorsInfo[nameWithNamespace] = BehaviorMetadata(GetNameSpace(),
|
||||
nameWithNamespace,
|
||||
fullname,
|
||||
@@ -163,8 +167,7 @@ gd::BehaviorMetadata& PlatformExtension::AddBehavior(
|
||||
}
|
||||
|
||||
gd::EffectMetadata& PlatformExtension::AddEffect(const gd::String& name) {
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name : GetNameSpace() + name;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name;
|
||||
effectsMetadata[nameWithNamespace] = EffectMetadata(nameWithNamespace);
|
||||
return effectsMetadata[nameWithNamespace];
|
||||
}
|
||||
@@ -177,8 +180,7 @@ gd::EventMetadata& PlatformExtension::AddEvent(
|
||||
const gd::String& smallicon_,
|
||||
std::shared_ptr<gd::BaseEvent> instance_) {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::String nameWithNamespace =
|
||||
GetNameSpace().empty() ? name_ : GetNameSpace() + name_;
|
||||
gd::String nameWithNamespace = GetNameSpace() + name_;
|
||||
eventsInfos[nameWithNamespace] = gd::EventMetadata(nameWithNamespace,
|
||||
fullname_,
|
||||
description_,
|
||||
@@ -216,8 +218,7 @@ std::vector<gd::String> PlatformExtension::GetExtensionObjectsTypes() const {
|
||||
|
||||
std::vector<gd::String> PlatformExtension::GetExtensionEffectTypes() const {
|
||||
std::vector<gd::String> effectNames;
|
||||
for (auto& it : effectsMetadata)
|
||||
effectNames.push_back(it.first);
|
||||
for (auto& it : effectsMetadata) effectNames.push_back(it.first);
|
||||
|
||||
return effectNames;
|
||||
}
|
||||
@@ -263,6 +264,84 @@ std::vector<gd::String> PlatformExtension::GetBehaviorsTypes() const {
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
||||
gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
|
||||
const gd::String& newActionName, const gd::String& copiedActionName) {
|
||||
gd::String newNameWithNamespace = GetNameSpace() + newActionName;
|
||||
gd::String copiedNameWithNamespace = GetNameSpace() + 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& PlatformExtension::AddDuplicatedCondition(
|
||||
const gd::String& newConditionName, const gd::String& copiedConditionName) {
|
||||
gd::String newNameWithNamespace = GetNameSpace() + newConditionName;
|
||||
gd::String copiedNameWithNamespace = GetNameSpace() + 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];
|
||||
}
|
||||
/**
|
||||
* \brief Create a new expression which is the duplicate of the specified one.
|
||||
*
|
||||
* Useful for handling a deprecated expression that is just a "copy" of the
|
||||
* new one.
|
||||
*/
|
||||
gd::ExpressionMetadata& PlatformExtension::AddDuplicatedExpression(
|
||||
const gd::String& newExpressionName,
|
||||
const gd::String& copiedExpressionName) {
|
||||
gd::String newNameWithNamespace = GetNameSpace() + newExpressionName;
|
||||
gd::String copiedNameWithNamespace = GetNameSpace() + copiedExpressionName;
|
||||
|
||||
auto copiedExpression = expressionsInfos.find(copiedNameWithNamespace);
|
||||
if (copiedExpression == expressionsInfos.end()) {
|
||||
gd::LogWarning("Could not find an expression with name " +
|
||||
copiedNameWithNamespace + " to copy.");
|
||||
} else {
|
||||
expressionsInfos[newNameWithNamespace] = copiedExpression->second;
|
||||
}
|
||||
|
||||
return expressionsInfos[newNameWithNamespace];
|
||||
}
|
||||
/**
|
||||
* \brief Create a new string expression which is the duplicate of the
|
||||
* specified one.
|
||||
*
|
||||
* Useful for handling a deprecated string expression that is just a "copy" of
|
||||
* the new one.
|
||||
*/
|
||||
gd::ExpressionMetadata& PlatformExtension::AddDuplicatedStrExpression(
|
||||
const gd::String& newExpressionName,
|
||||
const gd::String& copiedExpressionName) {
|
||||
gd::String newNameWithNamespace = GetNameSpace() + newExpressionName;
|
||||
gd::String copiedNameWithNamespace = GetNameSpace() + copiedExpressionName;
|
||||
|
||||
auto copiedExpression = strExpressionsInfos.find(copiedNameWithNamespace);
|
||||
if (copiedExpression == strExpressionsInfos.end()) {
|
||||
gd::LogWarning("Could not find a string expression with name " +
|
||||
copiedNameWithNamespace + " to copy.");
|
||||
} else {
|
||||
strExpressionsInfos[newNameWithNamespace] = copiedExpression->second;
|
||||
}
|
||||
|
||||
return strExpressionsInfos[newNameWithNamespace];
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::InstructionMetadata>&
|
||||
PlatformExtension::GetAllActions() {
|
||||
return actionsInfos;
|
||||
@@ -283,6 +362,10 @@ PlatformExtension::GetAllStrExpressions() {
|
||||
return strExpressionsInfos;
|
||||
}
|
||||
|
||||
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
|
||||
return extensionDependenciesMetadata;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
|
||||
return eventsInfos;
|
||||
}
|
||||
@@ -404,7 +487,7 @@ void PlatformExtension::SetNameSpace(gd::String nameSpace_) {
|
||||
name == "BuiltinCommonConversions" ||
|
||||
name == "BuiltinStringInstructions" ||
|
||||
name == "BuiltinMathematicalTools" ||
|
||||
name == "Effects" || // Well-known effects are not namespaced.
|
||||
name == "Effects" || // Well-known effects are not namespaced.
|
||||
name == "CommonDialogs") // New name for BuiltinInterface
|
||||
{
|
||||
nameSpace = "";
|
||||
|
@@ -10,11 +10,14 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EventMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/VersionPriv.h"
|
||||
|
||||
@@ -25,6 +28,7 @@ class ExpressionMetadata;
|
||||
class ObjectMetadata;
|
||||
class BehaviorMetadata;
|
||||
class EffectMetadata;
|
||||
class DependencyMetadata;
|
||||
class BaseEvent;
|
||||
class EventMetadata;
|
||||
class EventCodeGenerator;
|
||||
@@ -88,8 +92,16 @@ class GD_CORE_API PlatformExtension {
|
||||
const gd::String& license_);
|
||||
|
||||
/**
|
||||
* \brief Set the path to the help, relative to the wiki/documentation root.
|
||||
* For example, "/all-features/collisions" for
|
||||
* \brief Set the URL of the extension icon.
|
||||
*/
|
||||
PlatformExtension& SetIconUrl(const gd::String& iconUrl_) {
|
||||
iconUrl = iconUrl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the path to the help, relative to the GDevelop documentation
|
||||
* root. For example, "/all-features/collisions" for
|
||||
* "http://wiki.compilgames.net/doku.php/gdevelop5/all-features/collisions".
|
||||
*
|
||||
* The instructions, objects and behaviors will have this help path set by
|
||||
@@ -148,6 +160,8 @@ class GD_CORE_API PlatformExtension {
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
|
||||
gd::DependencyMetadata& AddDependency();
|
||||
|
||||
/**
|
||||
* \brief Declare a new object as being part of the extension.
|
||||
* \note This method does nothing when used for GD C++ runtime.
|
||||
@@ -225,6 +239,15 @@ class GD_CORE_API PlatformExtension {
|
||||
const gd::String& smallicon_,
|
||||
std::shared_ptr<gd::BaseEvent> instance);
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief Adds a property to the extension.
|
||||
*/
|
||||
gd::PropertyDescriptor& RegisterProperty(const gd::String& name) {
|
||||
return extensionPropertiesMetadata[name];
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Return the name extension user friendly name.
|
||||
*/
|
||||
@@ -252,10 +275,16 @@ class GD_CORE_API PlatformExtension {
|
||||
|
||||
/**
|
||||
* \brief Return the help path of extension, relative to the
|
||||
* wiki/documentation root.
|
||||
* GDevelop documentation root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; }
|
||||
|
||||
/**
|
||||
* \brief Return the URL to the icon to be displayed for this
|
||||
* extension.
|
||||
*/
|
||||
const gd::String& GetIconUrl() const { return iconUrl; }
|
||||
|
||||
/**
|
||||
* \brief Check if the extension is flagged as being deprecated.
|
||||
*/
|
||||
@@ -343,6 +372,43 @@ class GD_CORE_API PlatformExtension {
|
||||
std::map<gd::String, gd::EventMetadata>& GetAllEvents();
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \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 Create a new expression which is the duplicate of the specified one.
|
||||
*
|
||||
* Useful for handling a deprecated expression that is just a "copy" of the
|
||||
* new one.
|
||||
*/
|
||||
gd::ExpressionMetadata& AddDuplicatedExpression(
|
||||
const gd::String& newExpressionName,
|
||||
const gd::String& copiedExpressionName);
|
||||
/**
|
||||
* \brief Create a new string expression which is the duplicate of the
|
||||
* specified one.
|
||||
*
|
||||
* Useful for handling a deprecated string expression that is just a "copy" of
|
||||
* the new one.
|
||||
*/
|
||||
gd::ExpressionMetadata& AddDuplicatedStrExpression(
|
||||
const gd::String& newExpressionName,
|
||||
const gd::String& copiedExpressionName);
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a map containing the names of the actions (in
|
||||
* the first members) and the metadata associated with (in the second
|
||||
@@ -365,6 +431,12 @@ class GD_CORE_API PlatformExtension {
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions();
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a vector containing the metadata of all the
|
||||
* dependencies of the extension.
|
||||
*/
|
||||
std::vector<gd::DependencyMetadata>& GetAllDependencies();
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a map containing the names of the actions,
|
||||
* related to the object type, and the metadata associated with.
|
||||
@@ -437,6 +509,13 @@ class GD_CORE_API PlatformExtension {
|
||||
* generator.
|
||||
*/
|
||||
void StripUnimplementedInstructionsAndExpressions();
|
||||
|
||||
/**
|
||||
* \brief Get all the properties of the extension
|
||||
*/
|
||||
std::map<gd::String, gd::PropertyDescriptor>& GetAllProperties() {
|
||||
return extensionPropertiesMetadata;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -463,14 +542,15 @@ class GD_CORE_API PlatformExtension {
|
||||
nameSpace; ///< Automatically set from the name of the extension, and
|
||||
///< added to every
|
||||
///< actions/conditions/expressions/objects/behavior/event.
|
||||
gd::String fullname; ///< Name displayed to users at edittime
|
||||
gd::String informations; ///< Description displayed to users at edittime
|
||||
gd::String author; ///< Author displayed to users at edittime
|
||||
gd::String license; ///< License name displayed to users at edittime
|
||||
gd::String fullname; ///< Name displayed to users in the editor.
|
||||
gd::String informations; ///< Description displayed to users in the editor.
|
||||
gd::String author; ///< Author displayed to users in the editor.
|
||||
gd::String license; ///< License name displayed to users in the editor.
|
||||
bool deprecated; ///< true if the extension is deprecated and shouldn't be
|
||||
///< shown in IDE.
|
||||
gd::String helpPath; ///< The relative path to the help for this extension in
|
||||
///< the documentation.
|
||||
gd::String iconUrl; ///< The URL to the icon to be shown for this extension.
|
||||
|
||||
std::map<gd::String, gd::ObjectMetadata> objectsInfos;
|
||||
std::map<gd::String, gd::BehaviorMetadata> behaviorsInfo;
|
||||
@@ -480,7 +560,9 @@ class GD_CORE_API PlatformExtension {
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
|
||||
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
|
||||
std::map<gd::String, gd::EventMetadata> eventsInfos;
|
||||
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
|
||||
#endif
|
||||
|
||||
ObjectMetadata badObjectMetadata;
|
||||
|
@@ -136,7 +136,7 @@ gd::String InstructionSentenceFormatter::LabelFromType(const gd::String &type) {
|
||||
else if (type == "mouse")
|
||||
return _("Mouse button");
|
||||
else if (type == "yesorno")
|
||||
return _("Yes or no");
|
||||
return _("Yes or No");
|
||||
else if (type == "police")
|
||||
return _("Font");
|
||||
else if (type == "color")
|
||||
|
61
Core/GDCore/IDE/Project/ResourcesRenamer.h
Normal file
61
Core/GDCore/IDE/Project/ResourcesRenamer.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef GDCORE_RESOURCESRENAMER_H
|
||||
#define GDCORE_RESOURCESRENAMER_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Class used to rename resources (in an object, an entire project,
|
||||
* etc...)
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class ResourcesRenamer : public gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor taking the map from old name to new name.
|
||||
* @param oldToNewNames_ A map associating to a resource name the new name to
|
||||
* use.
|
||||
*/
|
||||
ResourcesRenamer(const std::map<gd::String, gd::String>& oldToNewNames_)
|
||||
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
|
||||
virtual ~ResourcesRenamer(){};
|
||||
|
||||
virtual void ExposeFile(gd::String& resourceName) override {
|
||||
RenameIfNeeded(resourceName);
|
||||
};
|
||||
virtual void ExposeImage(gd::String& imageResourceName) override {
|
||||
RenameIfNeeded(imageResourceName);
|
||||
};
|
||||
virtual void ExposeAudio(gd::String& audioResourceName) override {
|
||||
RenameIfNeeded(audioResourceName);
|
||||
};
|
||||
virtual void ExposeFont(gd::String& fontResourceName) override {
|
||||
RenameIfNeeded(fontResourceName);
|
||||
};
|
||||
|
||||
private:
|
||||
void RenameIfNeeded(gd::String& resourceName) {
|
||||
if (oldToNewNames.find(resourceName) != oldToNewNames.end())
|
||||
resourceName = oldToNewNames[resourceName];
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::String> oldToNewNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_RESOURCESRENAMER_H
|
||||
#endif
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "EventsFunctionsExtension.h"
|
||||
|
||||
#include "EventsBasedBehavior.h"
|
||||
#include "EventsFunction.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -35,6 +36,9 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
|
||||
fullName = other.fullName;
|
||||
tags = other.tags;
|
||||
author = other.author;
|
||||
previewIconUrl = other.previewIconUrl;
|
||||
iconUrl = other.iconUrl;
|
||||
helpPath = other.helpPath;
|
||||
EventsFunctionsContainer::Init(other);
|
||||
eventsBasedBehaviors = other.eventsBasedBehaviors;
|
||||
}
|
||||
@@ -46,8 +50,15 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("description", description);
|
||||
element.SetAttribute("name", name);
|
||||
element.SetAttribute("fullName", fullName);
|
||||
element.SetAttribute("tags", tags);
|
||||
auto& tagsElement = element.AddChild("tags");
|
||||
tagsElement.ConsiderAsArray();
|
||||
for (const auto& tag : tags) {
|
||||
tagsElement.AddChild("").SetStringValue(tag);
|
||||
}
|
||||
element.SetAttribute("author", author);
|
||||
element.SetAttribute("previewIconUrl", previewIconUrl);
|
||||
element.SetAttribute("iconUrl", iconUrl);
|
||||
element.SetAttribute("helpPath", helpPath);
|
||||
|
||||
SerializeEventsFunctionsTo(element.AddChild("eventsFunctions"));
|
||||
eventsBasedBehaviors.SerializeElementsTo(
|
||||
@@ -62,8 +73,27 @@ void EventsFunctionsExtension::UnserializeFrom(
|
||||
description = element.GetStringAttribute("description");
|
||||
name = element.GetStringAttribute("name");
|
||||
fullName = element.GetStringAttribute("fullName");
|
||||
tags = element.GetStringAttribute("tags");
|
||||
author = element.GetStringAttribute("author");
|
||||
previewIconUrl = element.GetStringAttribute("previewIconUrl");
|
||||
iconUrl = element.GetStringAttribute("iconUrl");
|
||||
helpPath = element.GetStringAttribute("helpPath");
|
||||
|
||||
tags.clear();
|
||||
auto& tagsElement = element.GetChild("tags");
|
||||
if (!tagsElement.IsValueUndefined()) {
|
||||
// Compatibility with GD <= 5.0.0-beta102
|
||||
gd::String tagsAsString = tagsElement.GetStringValue();
|
||||
tags = tagsAsString.Split(',');
|
||||
for (auto& tag : tags) {
|
||||
tag = tag.Trim();
|
||||
}
|
||||
// end of compatibility code
|
||||
} else {
|
||||
tagsElement.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < tagsElement.GetChildrenCount(); ++i) {
|
||||
tags.push_back(tagsElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
}
|
||||
|
||||
UnserializeEventsFunctionsFrom(project, element.GetChild("eventsFunctions"));
|
||||
eventsBasedBehaviors.UnserializeElementsFrom(
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_EVENTSFUNCTIONEXTENSION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/EventsBasedBehavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -83,11 +84,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetTags() const { return tags; };
|
||||
EventsFunctionsExtension& SetTags(const gd::String& tags_) {
|
||||
tags = tags_;
|
||||
return *this;
|
||||
}
|
||||
const std::vector<gd::String>& GetTags() const { return tags; };
|
||||
std::vector<gd::String>& GetTags() { return tags; };
|
||||
|
||||
const gd::String& GetAuthor() const { return author; };
|
||||
EventsFunctionsExtension& SetAuthor(const gd::String& author_) {
|
||||
@@ -95,6 +93,34 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetPreviewIconUrl() const { return previewIconUrl; };
|
||||
EventsFunctionsExtension& SetPreviewIconUrl(
|
||||
const gd::String& previewIconUrl_) {
|
||||
previewIconUrl = previewIconUrl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetIconUrl() const { return iconUrl; };
|
||||
EventsFunctionsExtension& SetIconUrl(const gd::String& iconUrl_) {
|
||||
iconUrl = iconUrl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the help path of this extension, relative to the GDevelop
|
||||
* documentation root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; };
|
||||
|
||||
/**
|
||||
* \brief Set the help path of this extension, relative to the GDevelop
|
||||
* documentation root.
|
||||
*/
|
||||
EventsFunctionsExtension& SetHelpPath(const gd::String& helpPath_) {
|
||||
helpPath = helpPath_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return a reference to the list of the events based behaviors.
|
||||
*/
|
||||
@@ -128,7 +154,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
/** \name Lifecycle event functions
|
||||
*/
|
||||
///@{
|
||||
static bool IsExtensionLifecycleEventsFunction(const gd::String& eventsFunctionName);
|
||||
static bool IsExtensionLifecycleEventsFunction(
|
||||
const gd::String& eventsFunctionName);
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -144,8 +171,12 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
gd::String description;
|
||||
gd::String name;
|
||||
gd::String fullName;
|
||||
gd::String tags;
|
||||
std::vector<gd::String> tags;
|
||||
gd::String author;
|
||||
gd::String previewIconUrl;
|
||||
gd::String iconUrl;
|
||||
gd::String helpPath; ///< The relative path to the help for this extension in
|
||||
///< the documentation (or an absolute URL).
|
||||
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
|
||||
};
|
||||
|
||||
|
56
Core/GDCore/Project/ExtensionProperties.cpp
Normal file
56
Core/GDCore/Project/ExtensionProperties.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "ExtensionProperties.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
|
||||
namespace gd {
|
||||
const gd::String ExtensionProperties::defaultValue = "";
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
ExtensionProperties::GetAllExtensionProperties(const gd::String& extensionName,
|
||||
gd::Project& project) {
|
||||
// Create a copy
|
||||
std::map<gd::String, gd::PropertyDescriptor> props(
|
||||
project.GetCurrentPlatform()
|
||||
.GetExtension(extensionName)
|
||||
->GetAllProperties());
|
||||
// Set values
|
||||
for (std::pair<gd::String, gd::PropertyDescriptor> property : props) {
|
||||
if (properties.count(extensionName) > 0 &&
|
||||
properties[extensionName].count(property.first) > 0) {
|
||||
props[property.first].SetValue(properties[extensionName][property.first]);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
};
|
||||
|
||||
void ExtensionProperties::SerializeTo(SerializerElement& element) const {
|
||||
element.ConsiderAsArrayOf("extensionProperties");
|
||||
for (const std::pair<gd::String, std::map<gd::String, gd::String>> extension :
|
||||
properties) {
|
||||
for (const std::pair<gd::String, gd::String> property : extension.second) {
|
||||
SerializerElement& propertyElement =
|
||||
element.AddChild("extensionProperties");
|
||||
propertyElement.AddChild("extension").SetStringValue(extension.first);
|
||||
propertyElement.AddChild("property").SetStringValue(property.first);
|
||||
propertyElement.AddChild("value").SetStringValue(property.second);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ExtensionProperties::UnserializeFrom(const SerializerElement& element) {
|
||||
properties.clear();
|
||||
element.ConsiderAsArrayOf("extensionProperties");
|
||||
for (std::pair<const gd::String, std::shared_ptr<SerializerElement>>
|
||||
extensionProperties : element.GetAllChildren()) {
|
||||
std::shared_ptr<SerializerElement> extensionPropertiesElement =
|
||||
extensionProperties.second;
|
||||
properties
|
||||
[extensionPropertiesElement->GetChild("extension").GetStringValue()]
|
||||
[extensionPropertiesElement->GetChild("property").GetStringValue()] =
|
||||
extensionPropertiesElement->GetChild("value").GetStringValue();
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace gd
|
69
Core/GDCore/Project/ExtensionProperties.h
Normal file
69
Core/GDCore/Project/ExtensionProperties.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXTENSIONPROPERTIES_H
|
||||
#define GDCORE_EXTENSIONPROPERTIES_H
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class PropertyDescriptor;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
class GD_CORE_API ExtensionProperties {
|
||||
static const gd::String defaultValue;
|
||||
|
||||
public:
|
||||
const gd::String& GetValue(const gd::String& extension,
|
||||
const gd::String& property) const {
|
||||
if (properties.count(extension) == 0 ||
|
||||
properties.at(extension).count(property) == 0) {
|
||||
return ExtensionProperties::defaultValue;
|
||||
}
|
||||
return properties.at(extension).at(property);
|
||||
};
|
||||
|
||||
void SetValue(const gd::String& extension,
|
||||
const gd::String& property,
|
||||
const gd::String& newValue) {
|
||||
properties[extension][property] = newValue;
|
||||
};
|
||||
|
||||
bool HasProperty(const gd::String& extension, const gd::String& property) {
|
||||
for (std::pair<gd::String, gd::String> propertyPair :
|
||||
properties[extension]) {
|
||||
if (propertyPair.first == property) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetAllExtensionProperties(
|
||||
const gd::String& extensionName, gd::Project& project);
|
||||
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the Extension Properties.
|
||||
*/
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the Extension Properties.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::map<gd::String, std::map<gd::String, gd::String>>
|
||||
properties; ///< The properties of the project
|
||||
};
|
||||
} // namespace gd
|
||||
|
||||
#endif // EXTENSIONPROPERTIES_H
|
@@ -179,6 +179,8 @@ InitialInstanceFunctor::~InitialInstanceFunctor(){};
|
||||
|
||||
void HighestZOrderFinder::operator()(gd::InitialInstance& instance) {
|
||||
if (!layerRestricted || instance.GetLayer() == layerName) {
|
||||
instancesCount++;
|
||||
|
||||
if (firstCall) {
|
||||
highestZOrder = instance.GetZOrder();
|
||||
lowestZOrder = instance.GetZOrder();
|
||||
|
@@ -211,6 +211,7 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
|
||||
HighestZOrderFinder()
|
||||
: highestZOrder(0),
|
||||
lowestZOrder(0),
|
||||
instancesCount(0),
|
||||
firstCall(true),
|
||||
layerRestricted(false){};
|
||||
virtual ~HighestZOrderFinder(){};
|
||||
@@ -237,9 +238,16 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
|
||||
*/
|
||||
int GetLowestZOrder() const { return lowestZOrder; }
|
||||
|
||||
/**
|
||||
* \brief After calling the instances container iterate method with this
|
||||
* functor, this method will return the number of instances.
|
||||
*/
|
||||
size_t GetInstancesCount() const { return instancesCount; }
|
||||
|
||||
private:
|
||||
int highestZOrder;
|
||||
int lowestZOrder;
|
||||
size_t instancesCount;
|
||||
bool firstCall;
|
||||
|
||||
bool layerRestricted; ///< If true, the search is restricted to the layer
|
||||
|
@@ -13,7 +13,7 @@ namespace gd {
|
||||
Camera Layer::badCamera;
|
||||
Effect Layer::badEffect;
|
||||
|
||||
Layer::Layer() : isVisible(true) {}
|
||||
Layer::Layer() : isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
|
||||
|
||||
/**
|
||||
* Change cameras count, automatically adding/removing them.
|
||||
@@ -29,6 +29,11 @@ void Layer::SetCameraCount(std::size_t n) {
|
||||
void Layer::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("visibility", GetVisibility());
|
||||
element.SetAttribute("isLightingLayer", IsLightingLayer());
|
||||
element.SetAttribute("followBaseLayerCamera", IsFollowingBaseLayerCamera());
|
||||
element.SetAttribute("ambientLightColorR", (int)GetAmbientLightColorRed());
|
||||
element.SetAttribute("ambientLightColorG", (int)GetAmbientLightColorGreen());
|
||||
element.SetAttribute("ambientLightColorB", (int)GetAmbientLightColorBlue());
|
||||
|
||||
SerializerElement& camerasElement = element.AddChild("cameras");
|
||||
camerasElement.ConsiderAsArrayOf("camera");
|
||||
@@ -61,6 +66,11 @@ void Layer::SerializeTo(SerializerElement& element) const {
|
||||
void Layer::UnserializeFrom(const SerializerElement& element) {
|
||||
SetName(element.GetStringAttribute("name", "", "Name"));
|
||||
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
|
||||
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
|
||||
SetFollowBaseLayerCamera(element.GetBoolAttribute("followBaseLayerCamera", false));
|
||||
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
|
||||
element.GetIntAttribute("ambientLightColorG", 200),
|
||||
element.GetIntAttribute("ambientLightColorB", 200));
|
||||
|
||||
// Compatibility with GD <= 3.3
|
||||
if (element.HasChild("Camera")) {
|
||||
|
@@ -51,6 +51,26 @@ class GD_CORE_API Layer {
|
||||
*/
|
||||
bool GetVisibility() const { return isVisible; }
|
||||
|
||||
/**
|
||||
* \brief Set if the layer is a lightining layer or not.
|
||||
*/
|
||||
void SetLightingLayer(bool isLightingLayer_) { isLightingLayer = isLightingLayer_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the layer is a lighting layer.
|
||||
*/
|
||||
bool IsLightingLayer() const { return isLightingLayer; }
|
||||
|
||||
/**
|
||||
* \brief Set if the layer automatically follows the base layer or not.
|
||||
*/
|
||||
void SetFollowBaseLayerCamera(bool followBaseLayerCamera_) { followBaseLayerCamera = followBaseLayerCamera_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the layer follows the base layer.
|
||||
*/
|
||||
bool IsFollowingBaseLayerCamera() const { return followBaseLayerCamera; }
|
||||
|
||||
/** \name Cameras
|
||||
*/
|
||||
///@{
|
||||
@@ -96,6 +116,30 @@ class GD_CORE_API Layer {
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Get the ambient light color red component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorRed() const { return ambientLightColorR; }
|
||||
|
||||
/**
|
||||
* Get the ambient light color green component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorGreen() const { return ambientLightColorG; }
|
||||
|
||||
/**
|
||||
* Get the ambient light color blue component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorBlue() const { return ambientLightColorB; }
|
||||
|
||||
/**
|
||||
* Set the ambient light color.
|
||||
*/
|
||||
void SetAmbientLightColor(unsigned int r, unsigned int g, unsigned int b) {
|
||||
ambientLightColorR = r;
|
||||
ambientLightColorG = g;
|
||||
ambientLightColorB = b;
|
||||
}
|
||||
|
||||
/** \name Effects
|
||||
*/
|
||||
///@{
|
||||
@@ -177,6 +221,11 @@ class GD_CORE_API Layer {
|
||||
private:
|
||||
gd::String name; ///< The name of the layer
|
||||
bool isVisible; ///< True if the layer is visible
|
||||
bool isLightingLayer; ///< True if the layer is used to display lights and renders an ambient light.
|
||||
bool followBaseLayerCamera; ///< True if the layer automatically follows the base layer
|
||||
unsigned int ambientLightColorR; ///< Ambient light color Red component
|
||||
unsigned int ambientLightColorG; ///< Ambient light color Green component
|
||||
unsigned int ambientLightColorB; ///< Ambient light color Blue component
|
||||
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
|
||||
std::vector<std::shared_ptr<gd::Effect>>
|
||||
effects; ///< The effects applied to the layer.
|
||||
|
@@ -23,4 +23,14 @@ void NamedPropertyDescriptor::UnserializeFrom(
|
||||
name = element.GetChild("name").GetStringValue();
|
||||
}
|
||||
|
||||
void NamedPropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
|
||||
PropertyDescriptor::SerializeValuesTo(element);
|
||||
element.AddChild("name").SetStringValue(name);
|
||||
}
|
||||
|
||||
void NamedPropertyDescriptor::UnserializeValuesFrom(const SerializerElement& element) {
|
||||
PropertyDescriptor::UnserializeValuesFrom(element);
|
||||
name = element.GetChild("name").GetStringValue();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -58,6 +58,16 @@ class GD_CORE_API NamedPropertyDescriptor : public PropertyDescriptor {
|
||||
* \brief Unserialize the NamedPropertyDescriptor.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Serialize only the value and extra informations of the property.
|
||||
*/
|
||||
virtual void SerializeValuesTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize only the value and extra information of the property.
|
||||
*/
|
||||
virtual void UnserializeValuesFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
/**
|
||||
|
@@ -5,13 +5,16 @@
|
||||
*/
|
||||
|
||||
#include "Project.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cctype>
|
||||
|
||||
#include <SFML/System/Utf.hpp>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -35,6 +38,7 @@
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/Tools/PolymorphicClone.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
#include "GDCore/Tools/VersionWrapper.h"
|
||||
#include "GDCore/Utf8/utf8.h"
|
||||
|
||||
@@ -51,7 +55,6 @@ Project::Project()
|
||||
version("1.0.0"),
|
||||
packageName("com.example.gamename"),
|
||||
orientation("landscape"),
|
||||
adMobAppId(""),
|
||||
folderProject(false),
|
||||
#endif
|
||||
windowWidth(800),
|
||||
@@ -62,6 +65,8 @@ Project::Project()
|
||||
scaleMode("linear"),
|
||||
adaptGameResolutionAtRuntime(true),
|
||||
sizeOnStartupMode("adaptWidth"),
|
||||
projectUuid(""),
|
||||
useDeprecatedZeroAsDefaultZOrder(false),
|
||||
imageManager(std::make_shared<ImageManager>())
|
||||
#if defined(GD_IDE_ONLY)
|
||||
,
|
||||
@@ -104,6 +109,8 @@ Project::Project()
|
||||
|
||||
Project::~Project() {}
|
||||
|
||||
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
|
||||
|
||||
std::unique_ptr<gd::Object> Project::CreateObject(
|
||||
const gd::String& type,
|
||||
const gd::String& name,
|
||||
@@ -592,13 +599,12 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
SetAdaptGameResolutionAtRuntime(
|
||||
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
|
||||
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
|
||||
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
|
||||
#if defined(GD_IDE_ONLY)
|
||||
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetAdMobAppId(propElement.GetStringAttribute("adMobAppId", ""));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetProjectFile(propElement.GetStringAttribute("projectFile"));
|
||||
SetLastCompilationDirectory(propElement
|
||||
.GetChild("latestCompilationDirectory",
|
||||
0,
|
||||
@@ -608,16 +614,41 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
platformSpecificAssets.UnserializeFrom(
|
||||
propElement.GetChild("platformSpecificAssets"));
|
||||
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
|
||||
winExecutableFilename =
|
||||
propElement.GetStringAttribute("winExecutableFilename");
|
||||
winExecutableIconFile =
|
||||
propElement.GetStringAttribute("winExecutableIconFile");
|
||||
linuxExecutableFilename =
|
||||
propElement.GetStringAttribute("linuxExecutableFilename");
|
||||
macExecutableFilename =
|
||||
propElement.GetStringAttribute("macExecutableFilename");
|
||||
|
||||
useExternalSourceFiles =
|
||||
propElement.GetBoolAttribute("useExternalSourceFiles");
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta101
|
||||
if (VersionWrapper::IsOlderOrEqual(
|
||||
gdMajorVersion, gdMinorVersion, gdBuildVersion, 0, 4, 0, 98, 0) &&
|
||||
!propElement.HasAttribute("useDeprecatedZeroAsDefaultZOrder")) {
|
||||
useDeprecatedZeroAsDefaultZOrder = true;
|
||||
} else {
|
||||
useDeprecatedZeroAsDefaultZOrder =
|
||||
propElement.GetBoolAttribute("useDeprecatedZeroAsDefaultZOrder", false);
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta101
|
||||
if (!propElement.HasAttribute("projectUuid") &&
|
||||
!propElement.HasChild("projectUuid")) {
|
||||
ResetProjectUuid();
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
extensionProperties.UnserializeFrom(
|
||||
propElement.GetChild("extensionProperties"));
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta98
|
||||
// Move AdMob App ID from project property to extension property.
|
||||
if (propElement.GetStringAttribute("adMobAppId", "") != "") {
|
||||
extensionProperties.SetValue(
|
||||
"AdMob",
|
||||
"AdMobAppId",
|
||||
propElement.GetStringAttribute("adMobAppId", ""));
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
#endif
|
||||
|
||||
const SerializerElement& extensionsElement =
|
||||
@@ -866,22 +897,26 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.AddChild("verticalSync")
|
||||
.SetValue(IsVerticalSynchronizationEnabledByDefault());
|
||||
propElement.SetAttribute("scaleMode", scaleMode);
|
||||
propElement.SetAttribute("adaptGameResolutionAtRuntime", adaptGameResolutionAtRuntime);
|
||||
propElement.SetAttribute("adaptGameResolutionAtRuntime",
|
||||
adaptGameResolutionAtRuntime);
|
||||
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
|
||||
propElement.SetAttribute("projectFile", gameFile);
|
||||
propElement.SetAttribute("projectUuid", projectUuid);
|
||||
propElement.SetAttribute("folderProject", folderProject);
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
propElement.SetAttribute("adMobAppId", adMobAppId);
|
||||
platformSpecificAssets.SerializeTo(
|
||||
propElement.AddChild("platformSpecificAssets"));
|
||||
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
|
||||
propElement.SetAttribute("winExecutableFilename", winExecutableFilename);
|
||||
propElement.SetAttribute("winExecutableIconFile", winExecutableIconFile);
|
||||
propElement.SetAttribute("linuxExecutableFilename", linuxExecutableFilename);
|
||||
propElement.SetAttribute("macExecutableFilename", macExecutableFilename);
|
||||
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta101
|
||||
if (useDeprecatedZeroAsDefaultZOrder) {
|
||||
propElement.SetAttribute("useDeprecatedZeroAsDefaultZOrder", true);
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
|
||||
|
||||
SerializerElement& extensionsElement = propElement.AddChild("extensions");
|
||||
extensionsElement.ConsiderAsArrayOf("extension");
|
||||
for (std::size_t i = 0; i < GetUsedExtensions().size(); ++i)
|
||||
@@ -1055,7 +1090,6 @@ Project& Project::operator=(const Project& other) {
|
||||
}
|
||||
|
||||
void Project::Init(const gd::Project& game) {
|
||||
// Some properties
|
||||
name = game.name;
|
||||
version = game.version;
|
||||
windowWidth = game.windowWidth;
|
||||
@@ -1066,18 +1100,21 @@ void Project::Init(const gd::Project& game) {
|
||||
scaleMode = game.scaleMode;
|
||||
adaptGameResolutionAtRuntime = game.adaptGameResolutionAtRuntime;
|
||||
sizeOnStartupMode = game.sizeOnStartupMode;
|
||||
projectUuid = game.projectUuid;
|
||||
useDeprecatedZeroAsDefaultZOrder = game.useDeprecatedZeroAsDefaultZOrder;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
author = game.author;
|
||||
packageName = game.packageName;
|
||||
orientation = game.orientation;
|
||||
adMobAppId = game.adMobAppId;
|
||||
folderProject = game.folderProject;
|
||||
latestCompilationDirectory = game.latestCompilationDirectory;
|
||||
platformSpecificAssets = game.platformSpecificAssets;
|
||||
loadingScreen = game.loadingScreen;
|
||||
objectGroups = game.objectGroups;
|
||||
|
||||
extensionProperties = game.extensionProperties;
|
||||
|
||||
gdMajorVersion = game.gdMajorVersion;
|
||||
gdMinorVersion = game.gdMinorVersion;
|
||||
gdBuildVersion = game.gdBuildVersion;
|
||||
@@ -1087,7 +1124,6 @@ void Project::Init(const gd::Project& game) {
|
||||
extensionsUsed = game.extensionsUsed;
|
||||
platforms = game.platforms;
|
||||
|
||||
// Resources
|
||||
resourcesManager = game.resourcesManager;
|
||||
imageManager = std::make_shared<ImageManager>(*game.imageManager);
|
||||
imageManager->SetResourcesManager(&resourcesManager);
|
||||
@@ -1112,13 +1148,8 @@ void Project::Init(const gd::Project& game) {
|
||||
variables = game.GetVariables();
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gameFile = game.GetProjectFile();
|
||||
projectFile = game.GetProjectFile();
|
||||
imagesChanged = game.imagesChanged;
|
||||
|
||||
winExecutableFilename = game.winExecutableFilename;
|
||||
winExecutableIconFile = game.winExecutableIconFile;
|
||||
linuxExecutableFilename = game.linuxExecutableFilename;
|
||||
macExecutableFilename = game.macExecutableFilename;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/ExtensionProperties.h"
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
@@ -114,30 +115,16 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
const gd::String& GetOrientation() const { return orientation; }
|
||||
|
||||
/**
|
||||
* \brief Change the project AdMob application ID (needed
|
||||
* to use the AdMob extension). This has no effect on desktop
|
||||
* and web browsers.
|
||||
*/
|
||||
void SetAdMobAppId(const gd::String& adMobAppId_) {
|
||||
adMobAppId = adMobAppId_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the project AdMob application ID.
|
||||
*/
|
||||
const gd::String& GetAdMobAppId() const { return adMobAppId; }
|
||||
|
||||
/**
|
||||
* Called when project file has changed.
|
||||
*/
|
||||
void SetProjectFile(const gd::String& file) { gameFile = file; }
|
||||
void SetProjectFile(const gd::String& file) { projectFile = file; }
|
||||
|
||||
/**
|
||||
* Return project file
|
||||
* \see gd::Project::SetProjectFile
|
||||
*/
|
||||
const gd::String& GetProjectFile() const { return gameFile; }
|
||||
const gd::String& GetProjectFile() const { return projectFile; }
|
||||
|
||||
/**
|
||||
* Set that the project should be saved as a folder project.
|
||||
@@ -290,6 +277,42 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
void SetScaleMode(const gd::String& scaleMode_) { scaleMode = scaleMode_; }
|
||||
|
||||
/**
|
||||
* \brief Return if the project should set 0 as Z-order for objects created
|
||||
* from events (which is deprecated) - instead of the highest Z order that was
|
||||
* found on each layer when the scene started.
|
||||
*/
|
||||
bool GetUseDeprecatedZeroAsDefaultZOrder() const {
|
||||
return useDeprecatedZeroAsDefaultZOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the project should set 0 as Z-order for objects created from
|
||||
* events (which is deprecated) - instead of the highest Z order that was
|
||||
* found on each layer when the scene started.
|
||||
*/
|
||||
void SetUseDeprecatedZeroAsDefaultZOrder(bool enable) {
|
||||
useDeprecatedZeroAsDefaultZOrder = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Change the project UUID.
|
||||
*/
|
||||
void SetProjectUuid(const gd::String& projectUuid_) {
|
||||
projectUuid = projectUuid_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the project UUID, useful when using the game on online services
|
||||
* that would require a unique identifier.
|
||||
*/
|
||||
const gd::String& GetProjectUuid() const { return projectUuid; }
|
||||
|
||||
/**
|
||||
* \brief Create a new project UUID.
|
||||
*/
|
||||
void ResetProjectUuid();
|
||||
|
||||
/**
|
||||
* Return a reference to the vector containing the names of extensions used by
|
||||
* the project.
|
||||
@@ -305,6 +328,26 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::vector<gd::String>& GetUsedExtensions() { return extensionsUsed; };
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief Get the properties set by extensions.
|
||||
*
|
||||
* Each extension can store arbitrary values indexed by a property name, which
|
||||
* are useful to store project wide settings (AdMob id, etc...).
|
||||
*/
|
||||
gd::ExtensionProperties& GetExtensionProperties() {
|
||||
return extensionProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the properties set by extensions.
|
||||
*
|
||||
* Each extension can store arbitrary values indexed by a property name, which
|
||||
* are useful to store project wide settings (AdMob id, etc...).
|
||||
*/
|
||||
const gd::ExtensionProperties& GetExtensionProperties() const {
|
||||
return extensionProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of platforms used by the project.
|
||||
*/
|
||||
@@ -916,10 +959,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::vector<gd::String> imagesChanged; ///< Images that have been changed and
|
||||
///< which have to be reloaded
|
||||
gd::String winExecutableFilename; ///< Windows executable name
|
||||
gd::String winExecutableIconFile; ///< Icon for Windows executable
|
||||
gd::String linuxExecutableFilename; ///< Linux executable name
|
||||
gd::String macExecutableFilename; ///< Mac executable name
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -941,8 +980,15 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
|
||||
///< to the window size at runtime
|
||||
gd::String
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
gd::String projectUuid; ///< UUID useful to identify the game in online
|
||||
///< services or database that would require it.
|
||||
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from
|
||||
///< events will have 0 as Z order,
|
||||
///< instead of the highest Z order
|
||||
///< found on the layer at the scene
|
||||
///< startup.
|
||||
std::vector<std::unique_ptr<gd::Layout> > scenes; ///< List of all scenes
|
||||
gd::VariablesContainer variables; ///< Initial global variables
|
||||
std::vector<std::unique_ptr<gd::ExternalLayout> >
|
||||
@@ -968,17 +1014,19 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
gd::String adMobAppId; ///< AdMob application ID.
|
||||
bool
|
||||
folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String gameFile; ///< File of the game
|
||||
gd::String latestCompilationDirectory; ///< File of the game
|
||||
gd::String
|
||||
projectFile; ///< Path to the project file - when editing a local file.
|
||||
gd::String latestCompilationDirectory;
|
||||
gd::Platform*
|
||||
currentPlatform; ///< The platform being used to edit the project.
|
||||
gd::PlatformSpecificAssets platformSpecificAssets;
|
||||
gd::LoadingScreen loadingScreen;
|
||||
std::vector<std::unique_ptr<gd::ExternalEvents> >
|
||||
externalEvents; ///< List of all externals events
|
||||
externalEvents; ///< List of all externals events
|
||||
ExtensionProperties
|
||||
extensionProperties; ///< The properties of the extensions.
|
||||
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
|
||||
|
@@ -4,7 +4,9 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "PropertyDescriptor.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -45,4 +47,27 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
: false;
|
||||
}
|
||||
|
||||
void PropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
|
||||
element.AddChild("value").SetStringValue(currentValue);
|
||||
SerializerElement& extraInformationElement =
|
||||
element.AddChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (const gd::String& information : extraInformation) {
|
||||
extraInformationElement.AddChild("").SetStringValue(information);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyDescriptor::UnserializeValuesFrom(
|
||||
const SerializerElement& element) {
|
||||
currentValue = element.GetChild("value").GetStringValue();
|
||||
|
||||
extraInformation.clear();
|
||||
const SerializerElement& extraInformationElement =
|
||||
element.GetChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < extraInformationElement.GetChildrenCount(); ++i)
|
||||
extraInformation.push_back(
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#ifndef GDCORE_PROPERTYDESCRIPTOR
|
||||
#define GDCORE_PROPERTYDESCRIPTOR
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -120,6 +121,16 @@ class GD_CORE_API PropertyDescriptor {
|
||||
* \brief Unserialize the PropertyDescriptor.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Serialize only the value and extra informations.
|
||||
*/
|
||||
virtual void SerializeValuesTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize only the value and extra informations.
|
||||
*/
|
||||
virtual void UnserializeValuesFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -127,7 +138,7 @@ class GD_CORE_API PropertyDescriptor {
|
||||
gd::String
|
||||
type; ///< The type of the property. This is arbitrary and interpreted by
|
||||
///< the class responsible for updating the property grid.
|
||||
gd::String label; //< The user-friendly property name
|
||||
gd::String label; //< The user-friendly property name
|
||||
gd::String description; //< The user-friendly property description
|
||||
std::vector<gd::String>
|
||||
extraInformation; ///< Can be used to store for example the available
|
||||
|
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
@@ -20,6 +22,7 @@ namespace gd {
|
||||
gd::String Resource::badStr;
|
||||
|
||||
Resource ResourcesManager::badResource;
|
||||
gd::String ResourcesManager::badResourceName;
|
||||
#if defined(GD_IDE_ONLY)
|
||||
ResourceFolder ResourcesManager::badFolder;
|
||||
Resource ResourceFolder::badResource;
|
||||
@@ -90,6 +93,33 @@ bool ResourcesManager::HasResource(const gd::String& name) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const gd::String& ResourcesManager::GetResourceNameWithOrigin(
|
||||
const gd::String& originName, const gd::String& originIdentifier) const {
|
||||
for (const auto& resource : resources) {
|
||||
if (!resource) continue;
|
||||
|
||||
if (resource->GetOriginName() == originName &&
|
||||
resource->GetOriginIdentifier() == originIdentifier) {
|
||||
return resource->GetName();
|
||||
}
|
||||
}
|
||||
|
||||
return badResourceName;
|
||||
}
|
||||
|
||||
const gd::String& ResourcesManager::GetResourceNameWithFile(
|
||||
const gd::String& file) const {
|
||||
for (const auto& resource : resources) {
|
||||
if (!resource) continue;
|
||||
|
||||
if (resource->GetFile() == file) {
|
||||
return resource->GetName();
|
||||
}
|
||||
}
|
||||
|
||||
return badResourceName;
|
||||
}
|
||||
|
||||
std::vector<gd::String> ResourcesManager::GetAllResourceNames() const {
|
||||
std::vector<gd::String> allResources;
|
||||
for (std::size_t i = 0; i < resources.size(); ++i)
|
||||
@@ -104,7 +134,8 @@ std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties() const {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties()
|
||||
const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Smooth the image")]
|
||||
.SetValue(smooth ? "true" : "false")
|
||||
@@ -413,6 +444,15 @@ void ResourcesManager::UnserializeFrom(const SerializerElement& element) {
|
||||
std::shared_ptr<Resource> resource = CreateResource(kind);
|
||||
resource->SetName(name);
|
||||
resource->SetMetadata(metadata);
|
||||
|
||||
if (resourceElement.HasChild("origin")) {
|
||||
gd::String originName =
|
||||
resourceElement.GetChild("origin").GetStringAttribute("name", "");
|
||||
gd::String originIdentifier =
|
||||
resourceElement.GetChild("origin").GetStringAttribute("identifier",
|
||||
"");
|
||||
resource->SetOrigin(originName, originIdentifier);
|
||||
}
|
||||
resource->UnserializeFrom(resourceElement);
|
||||
|
||||
resources.push_back(resource);
|
||||
@@ -444,6 +484,14 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
|
||||
resourceElement.SetAttribute("name", resources[i]->GetName());
|
||||
resourceElement.SetAttribute("metadata", resources[i]->GetMetadata());
|
||||
|
||||
const gd::String& originName = resources[i]->GetOriginName();
|
||||
const gd::String& originIdentifier = resources[i]->GetOriginIdentifier();
|
||||
if (!originName.empty() || !originIdentifier.empty()) {
|
||||
resourceElement.AddChild("origin")
|
||||
.SetAttribute("name", originName)
|
||||
.SetAttribute("identifier", originIdentifier);
|
||||
}
|
||||
|
||||
resources[i]->SerializeTo(resourceElement);
|
||||
}
|
||||
|
||||
@@ -560,7 +608,8 @@ void JsonResource::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("disablePreload", IsPreloadDisabled());
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties()
|
||||
const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties["disablePreload"]
|
||||
.SetValue(disablePreload ? "true" : "false")
|
||||
|
@@ -79,6 +79,17 @@ class GD_CORE_API Resource {
|
||||
*/
|
||||
virtual void SetFile(const gd::String& newFile){};
|
||||
|
||||
/**
|
||||
* TODO: make a ResourceOrigin object?
|
||||
*/
|
||||
virtual void SetOrigin(const gd::String& originName_, const gd::String& originIdentifier_) {
|
||||
originName = originName_;
|
||||
originIdentifier = originIdentifier_;
|
||||
}
|
||||
|
||||
virtual const gd::String& GetOriginName() const { return originName; }
|
||||
virtual const gd::String& GetOriginIdentifier() const { return originIdentifier; }
|
||||
|
||||
/**
|
||||
* \brief Set the metadata (any string) associated to the resource.
|
||||
* \note Can be used by external editors to store extra information, for
|
||||
@@ -142,6 +153,8 @@ class GD_CORE_API Resource {
|
||||
gd::String kind;
|
||||
gd::String name;
|
||||
gd::String metadata;
|
||||
gd::String originName;
|
||||
gd::String originIdentifier;
|
||||
bool userAdded; ///< True if the resource was added by the user, and not
|
||||
///< automatically by GDevelop.
|
||||
|
||||
@@ -354,6 +367,18 @@ class GD_CORE_API ResourcesManager {
|
||||
*/
|
||||
bool HasResource(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Return the name of the resource with the given origin, if any.
|
||||
* If not found, an empty string is returned.
|
||||
*/
|
||||
const gd::String& GetResourceNameWithOrigin(const gd::String& originName, const gd::String& originIdentifier) const;
|
||||
|
||||
/**
|
||||
* \brief Return the name of the first resource with the given file, if any.
|
||||
* If not found, an empty string is returned.
|
||||
*/
|
||||
const gd::String& GetResourceNameWithFile(const gd::String& file) const;
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a resource.
|
||||
*/
|
||||
@@ -487,6 +512,7 @@ class GD_CORE_API ResourcesManager {
|
||||
static ResourceFolder badFolder;
|
||||
#endif
|
||||
static Resource badResource;
|
||||
static gd::String badResourceName;
|
||||
};
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -524,6 +524,37 @@ public:
|
||||
*/
|
||||
String FindAndReplace(String search, String replacement, bool all = true) const;
|
||||
|
||||
/**
|
||||
* \brief Removes the specified characters (by default all the "whitespaces" and line breaks) from the beginning of the string,
|
||||
* and return the new string.
|
||||
*/
|
||||
String LeftTrim(const gd::String& chars = " \t\n\v\f\r")
|
||||
{
|
||||
String trimmedString(*this);
|
||||
trimmedString.erase(0, trimmedString.find_first_not_of(chars));
|
||||
return trimmedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes the specified characters (by default all the "whitespaces" and line breaks) from the end of the string,
|
||||
* and return the new string.
|
||||
*/
|
||||
String RightTrim(const gd::String& chars = " \t\n\v\f\r")
|
||||
{
|
||||
String trimmedString(*this);
|
||||
trimmedString.erase(trimmedString.find_last_not_of(chars) + 1);
|
||||
return trimmedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes the specified characters (by default all the "whitespaces" and line breaks) from the
|
||||
* beginning and the end of the string and return the new string.
|
||||
*/
|
||||
String Trim(const gd::String& chars = " \t\n\v\f\r")
|
||||
{
|
||||
return LeftTrim(chars).RightTrim(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalization form
|
||||
*/
|
||||
|
@@ -2,14 +2,14 @@
|
||||
|
||||
GDevelop is architectured around a `Core` library, platforms (`GDJS`, `GDCpp`) and extensions (`Extensions` folder). The editor (`newIDE` folder) is using all of these libraries. This is a recap table of the main folders:
|
||||
|
||||
| Directory | ℹ️ Description |
|
||||
| --- | --- |
|
||||
| `Core` | GDevelop core library, containing common tools to implement the IDE and work with GDevelop games. |
|
||||
| `GDCpp` | GDevelop C++ game engine, used to **build native games**. |
|
||||
| `GDJS` | GDevelop JS game engine, used to build **HTML5 games**. |
|
||||
| `GDevelop.js` | Bindings of Core/GDCpp/GDJS and Extensions to JavaScript (used by the IDE). |
|
||||
| `newIDE` | The game editor, written in JavaScript with React, Electron and Pixi.js. |
|
||||
| `Extensions` | Extensions for C++ or JS game engines, providing objects, events and new features. |
|
||||
| Directory | ℹ️ Description |
|
||||
| ------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `Core` | GDevelop core library, containing common tools to implement the IDE and work with GDevelop games. |
|
||||
| `GDCpp` | The C++ game engine, used to build native games (_not used in GDevelop 5_). |
|
||||
| `GDJS` | The game engine, written in TypeScript, using PixiJS (WebGL), powering all GDevelop games. |
|
||||
| `GDevelop.js` | Bindings of `Core`/`GDCpp`/`GDJS` and `Extensions` to JavaScript (with WebAssembly), used by the IDE. |
|
||||
| `newIDE` | The game editor, written in JavaScript with React, Electron and PixiJS. |
|
||||
| `Extensions` | Extensions for the game engine, providing objects, behaviors, events and new features. |
|
||||
|
||||
The rest of this page is an introduction to the main concepts of GDevelop architecture.
|
||||
|
||||
@@ -17,92 +17,92 @@ The rest of this page is an introduction to the main concepts of GDevelop archit
|
||||
|
||||
**IDE** stands for "Integrated Development Environment". A synonym for it is also simply "editor". In GDevelop, the software itself, that is used to create games and running as an app or a web-app, is called the "GDevelop Editor" or the "GDevelop IDE"
|
||||
|
||||
> This term "IDE" is also used in some folders. When you browse `Core`, `GDCpp` or `GDJS` subfolders, some folders are called `IDE`. They contain classes and tools that are **only useful for the editor** (they are not per se mandatory to describe the structure of a game).
|
||||
> The term "IDE" is also used in some folders. When you browse `Core`, `GDCpp` or `GDJS` subfolders, some folders are called `IDE`. They contain classes and tools that are **only useful for the editor** (they are not per se mandatory to describe the structure of a game).
|
||||
|
||||
**Runtime** is a word used to describe classes, tools and source files being used during a game. This could also be called "in game" or a "game engine".
|
||||
**Runtime** is a word used to describe classes, tools and source files being used during a game. This could also be called "in-game" or a "game engine".
|
||||
|
||||
> When you browse `GDCpp` or `GDJS` subfolders, you can find folders called `Runtime`. They contain the **game engine** of GDevelop.
|
||||
|
||||
Extensions do have the same distinction between the "**IDE**" part and the "**Runtime**" part. For example, most extensions have:
|
||||
|
||||
* A file called [`JsExtension.js`(https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js)], which contains the *declaration* of the extension for the **IDE**
|
||||
* One or more files implementing the feature for the game, in other words for **Runtime**. This can be a [Runtime Object](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimeobject.js) or a [Runtime Behavior](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimebehavior.js), [functions called by actions or conditions](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/examplejsextensiontools.js) or by the game engine.
|
||||
- A file called [`JsExtension.js`(https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js)], which contains the _declaration_ of the extension for the **IDE**
|
||||
- One or more files implementing the feature for the game, in other words for **Runtime**. This can be a [Runtime Object](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimeobject.ts) or a [Runtime Behavior](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimebehavior.ts), [functions called by actions or conditions](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/examplejsextensiontools.ts) or by the game engine.
|
||||
|
||||
### "Runtime" and "IDE" difference using an example: the `gd::Variable` class
|
||||
|
||||
In GDevelop, developers can associate and manipulate variables in their games. To represent them, we have two things:
|
||||
|
||||
* The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
|
||||
* The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop-app.com/GDJS%20Runtime%20Documentation/gdjs.Variable.html). This JavaScript class is what is used during a game.
|
||||
- The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
|
||||
- The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](https://docs.gdevelop-app.com/GDJS%20Runtime%20Documentation/Variable.html). This JavaScript class is what is used during a game.
|
||||
|
||||
The editor `gd::Variable` **know nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine know almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
|
||||
The editor `gd::Variable` **knows nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine knows almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
|
||||
|
||||
> Note that the name `gdjs.Variable` is maybe a *bad decision*: it should have been called a `gdjs.RuntimeVariable`, like `gdjs.RuntimeObject` and like most other classes of the game engine.
|
||||
> Note that the name `gdjs.Variable` is maybe a _bad decision_: it should have been called a `gdjs.RuntimeVariable`, like `gdjs.RuntimeObject` and like most other classes of the game engine.
|
||||
|
||||
## What's inside "Core" (`GDCore` folder)?
|
||||
|
||||
GDevelop "Core" is basically containing everything that is used to **describe and manipulate the structure of a game** (called a `Project` internally). This includes events, scenes, objects, behaviors, events etc... All of these are implemented using C++ classes, in [this folder named `Project`](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Project).
|
||||
GDevelop "Core" is basically containing everything that is used to **describe and manipulate the structure of a game** (called a `Project` internally). This includes events, scenes, objects, behaviors, events, etc... All of these are implemented using C++ classes, in [this folder named `Project`](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Project).
|
||||
|
||||
GDevelop "Core" also contains **tools** to manipulate these `Project`. In particular, `Core/GDCore/IDE` folder is containing C++ classes allowing to do operations on the structure of a game. For example, [WholeProjectRefactorer](https://github.com/4ian/GDevelop/blob/master/Core/GDCore/IDE/WholeProjectRefactorer.cpp) is a very powerful tool, used to rename all objects in a game, update events after an object is deleted and more generally do Project wide refactoring. The directory contains other "tool" functions to manipulate [resources of projects](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Project) or do [search in events](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Events).
|
||||
GDevelop "Core" also contains **tools** to manipulate these `Project`. In particular, `Core/GDCore/IDE` folder is containing C++ classes allowing to do operations on the structure of a game. For example, [WholeProjectRefactorer](https://github.com/4ian/GDevelop/blob/master/Core/GDCore/IDE/WholeProjectRefactorer.cpp) is a very powerful tool, used to rename all objects in a game, update events after an object is deleted, and more generally do Project-wide refactoring. The directory contains other "tool" functions to manipulate the [resources of projects](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Project) or do a [search in events](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Events).
|
||||
|
||||
## What's inside GDJS? Why do I see some C++ file there?
|
||||
|
||||
While `GDJS/Runtime` folder is the game engine that is executed inside a game, `GDJS/GDJS` is the "IDE" part, which are the C++ classes describing to the editor various things like:
|
||||
|
||||
* How [do you do an export](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/IDE),
|
||||
* What are [the default extensions](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Extensions/Builtin),
|
||||
* How do you [generate JS code from events](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration),
|
||||
- How [do you do an export](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/IDE),
|
||||
- What are [the default extensions](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Extensions/Builtin),
|
||||
- How do you [generate JS code from events](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration),
|
||||
|
||||
The game engine is in GDJS/Runtime and is all JavaScript.
|
||||
The game engine is in GDJS/Runtime and is all written in TypeScript.
|
||||
|
||||
## What about events?
|
||||
|
||||
An "**event**" is by default something that [is mostly empty](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
|
||||
|
||||
[Default events are defined](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Events/Builtin) in GDevelop Core.
|
||||
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
|
||||
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://docs.gdevelop-app.com/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
|
||||
|
||||
A `gd::Instruction` is "just" a type (the name of the action or condition), and some parameters. You can think of it as a function in a programming language (`add(2, 3)` is calling a function called "add" with parameter "2" and "3"). Conditions have the special thing that they are functions returning true or false (and potentially being used to do filter on the objects being picked for next conditions and actions).
|
||||
A `gd::Instruction` is "just" a type (the name of the action or condition), and some parameters. You can think of it as a function in a programming language (`add(2, 3)` is calling a function called "add" with parameters "2" and "3"). Conditions have the special thing that they are functions returning true or false (and potentially being used to do a filter on the objects being picked for next conditions and actions).
|
||||
|
||||
### Why can't I see any "RuntimeEvent" or events during the game?
|
||||
|
||||
They do not exist anymore! ✨
|
||||
|
||||
Events are translated (we also say "transpiled" or "generated") into "real" code in a programming language. This process is called "Code Generation", and is [done here for the JavaScript game engine](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration).
|
||||
Events are translated (we also say "transpiled" or "generated") into "real" code in a programming language. This process is called "Code Generation", and is [done here for the TypeScript game engine](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration).
|
||||
|
||||
### Can I extract Events classes and code generator to make a development environment based on GDevelop events?
|
||||
### Can I extract Events classes and a code generator to make a development environment based on GDevelop events?
|
||||
|
||||
You're welcome to do so! Please get in touch :)
|
||||
|
||||
## I can see more than one Extensions folder, why?
|
||||
|
||||
The idea of GDevelop editor and game engine is to have a lean game engine, supporting almost nothing. Then, one can add "mods", "plugins", "modules" for GDevelop. We chose to call them "**Extensions**" in GDevelop.
|
||||
The idea of the GDevelop editor and game engine is to have a lean game engine, supporting almost nothing. Then, one can add "mods", "plugins", "modules" for GDevelop. We chose to call them "**Extensions**" in GDevelop.
|
||||
|
||||
* `GDevelop/Core/GDCore/Extensions` is the **declaration** of default (we say "builtin") extensions, that are available for any game and are "mandatory". They are called Extensions but they could be named "Extensions that will always be in your game". In programming languages, this is called a "[Standard Library](https://en.wikipedia.org/wiki/Standard_library)" (but don't get too distracted by this naming).
|
||||
* `GDevelop/GDJS/GDJS/Extensions/` and `GDevelop/GDCpp/GDCpp/Extensions/` are reusing these **declarations** and **adding** their own declarations. Mainly, they are setting the name of the functions to be called (either in JS or in C++) for each action, condition or expression.
|
||||
* `GDevelop/Extensions/` is the folder for the "mods"/"plugins" for GDevelop - the one that are not mandatory. They are not part of GDCore - they work on their own.
|
||||
- `GDevelop/Core/GDCore/Extensions` is the **declaration** of default (we say "builtin") extensions, that are available for any game and are "mandatory". They are called Extensions but they could be named "Extensions that will always be in your game". In programming languages, this is called a "[Standard Library](https://en.wikipedia.org/wiki/Standard_library)" (but don't get too distracted by this naming).
|
||||
- `GDevelop/GDJS/GDJS/Extensions/` and `GDevelop/GDCpp/GDCpp/Extensions/` are reusing these **declarations** and **adding** their own declarations. Mainly, they are setting the name of the functions to be called (either in TypeScript or in C++) for each action, condition or expression.
|
||||
- `GDevelop/Extensions/` is the folder for the "mods"/"plugins" for GDevelop - the ones that are not mandatory. They are not part of GDCore - they work on their own.
|
||||
|
||||
> In theory, all extensions could be moved to `GDevelop/Extensions/`. In practice, it's more pragmatic to have a set of "builtin" extensions with basic features.
|
||||
> In theory, all extensions could be moved to `GDevelop/Extensions/`. In practice, it's more pragmatic to have a set of "built-in" extensions with basic features.
|
||||
|
||||
## What's GDevelop.js? Do we care about this?
|
||||
|
||||
Everything in GDevelop.js is meant to create a "bridge" allowing to run and use C++ from JavaScript for the **IDE**, so that [we can write an editor entirely in JavaScript](https://github.com/4ian/GDevelop/tree/master/newIDE) and have it working in a web browser.
|
||||
Everything in GDevelop.js is meant to create a "bridge" allowing us to run and use C++ from JavaScript for the **IDE** so that [we can write an editor entirely in JavaScript](https://github.com/4ian/GDevelop/tree/master/newIDE) and have it working in a web browser.
|
||||
|
||||
* We're using Emscripten which is compiling C++, but instead of writing a "native binary", it's writing a file that works in a browser (basically, JavaScript!).
|
||||
- We're using Emscripten which is compiling C++, but instead of writing a "native binary", it's writing a file that works in a browser (basically, JavaScript!).
|
||||
|
||||
* The most useful file is [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl) that is describing everything in C++ that must be exposed to JavaScript (in the editor, not in game. Remember that during the game, we're at **Runtime**, so all of this does not exist anymore)
|
||||
- The most useful file is [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl) that is describing everything in C++ that must be exposed to JavaScript (in the editor, not in-game. Remember that during the game, we're at **Runtime**, so all of this does not exist anymore)
|
||||
|
||||
* Rest of the files are mostly bridges doing "weird stuff" to translate from JS to C++ or vice versa. It requires a bit of knowledge about how the ["bridge", made by Emscripten, called WebIDL](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html) is working.
|
||||
- The rest of the files are mostly bridges doing "weird stuff" to translate from JS to C++ or vice versa. It requires a bit of knowledge about how the ["bridge", made by Emscripten, called WebIDL](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html) is working.
|
||||
|
||||
You don't need to work with these unless you want to expose something that is written in C++ to the editor, and writing the interface in Bindings.idl is not sufficient.
|
||||
You don't need to work with these unless you want to expose something that is written in C++ to the editor, and writing the interface in Bindings.idl is not sufficient.
|
||||
|
||||
90% of the time, you can just read or write about a class in [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl). If you work in C++ classes, you may have sometime to add a header file in Wrapper.cpp and your C++ class is "automagically" compiled and available in JavaScript after writing the corresponding interface in Bindings.idl.
|
||||
90% of the time, you can just read or write about a class in [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl). If you work in C++ classes, you may have some time to add a header file in Wrapper.cpp, and your C++ class is "automagically" compiled and available in JavaScript after writing the corresponding interface in Bindings.idl.
|
||||
|
||||
### I want all the gory details about GDevelop.js 🧐
|
||||
|
||||
All the required C++ files are imported into this huge list: https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Wrapper.cpp#L1-L79. C++ compilation is done with a "build system" called CMake, which is using [this file](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/CMakeLists.txt#L82-L101) to see what to compile.
|
||||
|
||||
> In an ideal world, there would be something to do this automatically, so GDevelop.js folder would not even exist 😉
|
||||
> In an ideal world, there would be something to do this automatically, so the GDevelop.js folder would not even exist 😉
|
||||
> If you're interested and want to know more about GDevelop.js bridging between C++ and JavaScript, look at [this talk from GDevelop original author](https://www.youtube.com/watch?v=6La7jSCnYyk).
|
||||
|
||||
## Misc questions
|
||||
@@ -111,12 +111,12 @@ All the required C++ files are imported into this huge list: https://github.com/
|
||||
|
||||
GDevelop was originally written in C++. It's a scary language at first but is portable across almost any existing machine in this universe, can be pretty good, safe and readable with the latest C++ features.
|
||||
|
||||
### What's the deal with JavaScript? Why so much of it?
|
||||
### What's the deal with JavaScript/TypeScript? Why so much of it?
|
||||
|
||||
JavaScript, with the latest language proposals, is actually a very capable language, fast to write and safe with typing:
|
||||
JavaScript, with the latest language proposals, is a very capable language, fast to write and safe with typing:
|
||||
|
||||
* Performance is getting pretty good with recent browsers JIT features.
|
||||
* Frontend frameworks like React, used for GDevelop IDE, allow for very fast and modular interface development.
|
||||
* The web is an incredible and unmatched target when it comes to cross platform (and cross form factor) apps.
|
||||
- Performance is getting pretty good with recent browsers JIT features.
|
||||
- Frontend frameworks like React, used for GDevelop IDE, allow for very fast and modular interface development.
|
||||
- The web is an incredible and unmatched target when it comes to cross-platform (and cross-form factor) apps.
|
||||
|
||||
More generally, the web is a great target for games with the rise of WebGL and WebAssembly - though GDevelop should stay modular to adapt to newer platforms for the exported games in the future.
|
||||
|
@@ -6,21 +6,20 @@ GDevelop Core is a portable C++ library, compiled to be used in JavaScript in th
|
||||
|
||||
## 1) Getting started 🤓
|
||||
|
||||
First, take a look at the *Readme.md* at the root of the repository and the [developer documentation](https://docs.gdevelop-app.com/).
|
||||
First, take a look at the _Readme.md_ at the root of the repository and the [developer documentation](https://docs.gdevelop-app.com/).
|
||||
|
||||
## 2) How to contribute 😎
|
||||
|
||||
Any contribution is welcome! Whether you want to submit a bug report, a feature request
|
||||
or any pull request so as to add a nice feature, do not hesitate to get in touch.
|
||||
|
||||
* Check the [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
- Check [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
|
||||
* Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
|
||||
- Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
|
||||
|
||||
* To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
- To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
||||
License
|
||||
-------
|
||||
## License
|
||||
|
||||
GDevelop Core is distributed under the MIT license: see license.txt for
|
||||
more information.
|
||||
|
@@ -84,6 +84,18 @@ void SetupProjectWithDummyPlatform(gd::Project &project,
|
||||
.AddParameter("string", "")
|
||||
.AddParameter("expression", "", "", true)
|
||||
.SetFunctionName("getNumberWith3Params");
|
||||
extension
|
||||
->AddStrExpression("GetStringWith2ObjectParamAnd2ObjectVarParam",
|
||||
"Get string with twice an object param and an objectvar param",
|
||||
"",
|
||||
"",
|
||||
"")
|
||||
.AddParameter("object", _("Object 1 parameter"))
|
||||
.AddParameter("objectvar", _("Variable for object 1"))
|
||||
.AddParameter("object", _("Object 2 parameter"))
|
||||
.AddParameter("objectvar", _("Variable for object 2"))
|
||||
.SetFunctionName("getStringWith2ObjectParamAnd2ObjectVarParam");
|
||||
|
||||
auto &object = extension->AddObject<gd::Object>(
|
||||
"Sprite", "Dummy Sprite", "Dummy sprite object", "");
|
||||
object
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
@@ -823,37 +824,41 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
|
||||
SECTION("Valid object function name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyFunc");
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyFunc");
|
||||
}
|
||||
|
||||
SECTION("Valid object behavior name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "MyFunc");
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::MyFunc");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "MyFunc");
|
||||
}
|
||||
|
||||
SECTION("Unfinished object function name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
|
||||
auto node = parser.ParseExpression("string", "MyObject.");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "");
|
||||
}
|
||||
|
||||
SECTION("Unfinished object behavior name") {
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName = dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "");
|
||||
auto node = parser.ParseExpression("string", "MyObject.MyBehavior::");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &objectFunctionName =
|
||||
dynamic_cast<gd::ObjectFunctionNameNode &>(*node);
|
||||
REQUIRE(objectFunctionName.objectName == "MyObject");
|
||||
REQUIRE(objectFunctionName.objectFunctionOrBehaviorName == "MyBehavior");
|
||||
REQUIRE(objectFunctionName.behaviorFunctionName == "");
|
||||
}
|
||||
|
||||
SECTION("Invalid function calls") {
|
||||
@@ -948,7 +953,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid behavior function call, finishing with namespace separator") {
|
||||
SECTION(
|
||||
"Invalid behavior function call, finishing with namespace separator") {
|
||||
{
|
||||
auto node = parser.ParseExpression("number", "MyObject.MyBehavior::(12)");
|
||||
REQUIRE(node != nullptr);
|
||||
@@ -1031,6 +1037,55 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Valid function call with object variable") {
|
||||
{
|
||||
// Note that in this test we need to use an expression with "objectvar",
|
||||
// as the grammar of the parser depends on this parameter type
|
||||
// information.
|
||||
auto node = parser.ParseExpression(
|
||||
"string",
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(MyObject1, "
|
||||
"MyVar1, MyObject2, MyVar2)");
|
||||
REQUIRE(node != nullptr);
|
||||
auto &functionNode = dynamic_cast<gd::FunctionCallNode &>(*node);
|
||||
auto &identifierObject1Node =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[0]);
|
||||
auto &variable1Node =
|
||||
dynamic_cast<gd::VariableNode &>(*functionNode.parameters[1]);
|
||||
auto &identifierObject2Node =
|
||||
dynamic_cast<gd::IdentifierNode &>(*functionNode.parameters[2]);
|
||||
auto &variable2Node =
|
||||
dynamic_cast<gd::VariableNode &>(*functionNode.parameters[3]);
|
||||
|
||||
REQUIRE(identifierObject1Node.identifierName == "MyObject1");
|
||||
REQUIRE(identifierObject2Node.identifierName == "MyObject2");
|
||||
REQUIRE(variable1Node.objectName == "MyObject1");
|
||||
REQUIRE(variable1Node.name == "MyVar1");
|
||||
REQUIRE(variable2Node.objectName == "MyObject2");
|
||||
REQUIRE(variable2Node.name == "MyVar2");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Invalid function call with object variable") {
|
||||
{
|
||||
// Note that in this test we need to use an expression with "objectvar",
|
||||
// as the grammar of the parser depends on this parameter type
|
||||
// information.
|
||||
auto node = parser.ParseExpression(
|
||||
"string",
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(My "
|
||||
"badly/written object1, MyVar1, MyObject2, MyVar2)");
|
||||
REQUIRE(node != nullptr);
|
||||
|
||||
gd::ExpressionValidator validator;
|
||||
node->Visit(validator);
|
||||
REQUIRE(validator.GetErrors().size() == 1);
|
||||
REQUIRE(validator.GetErrors()[0]->GetMessage() ==
|
||||
"An object name was expected but something else was written. "
|
||||
"Enter just the name of the object for this parameter.");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Fuzzy/random tests") {
|
||||
{
|
||||
auto testExpression = [&parser](const gd::String &expression) {
|
||||
|
33
Core/tests/ResourcesRenamer.cpp
Normal file
33
Core/tests/ResourcesRenamer.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
/**
|
||||
* @file Tests covering common features of GDevelop Core.
|
||||
*/
|
||||
#include "GDCore/IDE/Project/ResourcesRenamer.h"
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("ResourcesRenamer", "[common]") {
|
||||
SECTION("It renames resources that are exposed") {
|
||||
std::map<gd::String, gd::String> renamings = {
|
||||
{"Resource1", "RenamedResource1"}};
|
||||
gd::ResourcesRenamer resourcesRenamer(renamings);
|
||||
|
||||
gd::Project project;
|
||||
project.GetPlatformSpecificAssets().Set(
|
||||
"android", "some-icon", "Resource1");
|
||||
project.GetPlatformSpecificAssets().Set(
|
||||
"android", "some-other-icon", "Resource2");
|
||||
|
||||
project.ExposeResources(resourcesRenamer);
|
||||
REQUIRE(project.GetPlatformSpecificAssets().Get("android", "some-icon") ==
|
||||
"RenamedResource1");
|
||||
REQUIRE(project.GetPlatformSpecificAssets().Get(
|
||||
"android", "some-other-icon") == "Resource2");
|
||||
}
|
||||
}
|
@@ -289,4 +289,40 @@ TEST_CASE("Utf8 String", "[common][utf8]") {
|
||||
gd::String str6 = u8"ßßß";
|
||||
REQUIRE(str6.FindAndReplace(u8"ßß", u8"ß") == u8"ßß");
|
||||
}
|
||||
|
||||
SECTION("trimming") {
|
||||
REQUIRE(gd::String("").Trim() == "");
|
||||
REQUIRE(gd::String("").LeftTrim() == "");
|
||||
REQUIRE(gd::String("").RightTrim() == "");
|
||||
REQUIRE(gd::String(" ").Trim() == "");
|
||||
REQUIRE(gd::String(" ").LeftTrim() == "");
|
||||
REQUIRE(gd::String(" ").RightTrim() == "");
|
||||
REQUIRE(gd::String(" ").Trim() == "");
|
||||
REQUIRE(gd::String(" ").LeftTrim() == "");
|
||||
REQUIRE(gd::String(" ").RightTrim() == "");
|
||||
REQUIRE(gd::String("a").Trim() == "a");
|
||||
REQUIRE(gd::String("a").LeftTrim() == "a");
|
||||
REQUIRE(gd::String("a").RightTrim() == "a");
|
||||
REQUIRE(gd::String("aß").Trim() == "aß");
|
||||
REQUIRE(gd::String("aß").LeftTrim() == "aß");
|
||||
REQUIRE(gd::String("aß").RightTrim() == "aß");
|
||||
REQUIRE(gd::String(" a ").Trim() == "a");
|
||||
REQUIRE(gd::String(" a ").LeftTrim() == "a ");
|
||||
REQUIRE(gd::String(" a ").RightTrim() == " a");
|
||||
REQUIRE(gd::String(" aß ").Trim() == "aß");
|
||||
REQUIRE(gd::String(" aß ").LeftTrim() == "aß ");
|
||||
REQUIRE(gd::String(" aß ").RightTrim() == " aß");
|
||||
REQUIRE(gd::String(" a ß ").Trim() == "a ß");
|
||||
REQUIRE(gd::String(" a ß ").LeftTrim() == "a ß ");
|
||||
REQUIRE(gd::String(" a ß ").RightTrim() == " a ß");
|
||||
REQUIRE(gd::String(" aß ").Trim() == "aß");
|
||||
REQUIRE(gd::String(" aß ").LeftTrim() == "aß ");
|
||||
REQUIRE(gd::String(" aß ").RightTrim() == " aß");
|
||||
REQUIRE(gd::String("---aß---").Trim("-/") == "aß");
|
||||
REQUIRE(gd::String("---aß---").LeftTrim("-/") == "aß---");
|
||||
REQUIRE(gd::String("---aß---").RightTrim("-/") == "---aß");
|
||||
REQUIRE(gd::String("-/=aß=/-").Trim("-/") == "=aß=");
|
||||
REQUIRE(gd::String("-/=aß=/-").LeftTrim("-/") == "=aß=/-");
|
||||
REQUIRE(gd::String("-/=aß=/-").RightTrim("-/") == "-/=aß=");
|
||||
}
|
||||
}
|
||||
|
@@ -2,34 +2,38 @@
|
||||
# do it if not already cloned to avoid triggering a whole
|
||||
# recompilation every time CMake is run.
|
||||
find_package(Git)
|
||||
if(GIT_FOUND AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
|
||||
message( "Cloning SFML in ExtLibs/SFML with Git..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
OUTPUT_QUIET)
|
||||
if(GIT_FOUND)
|
||||
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SFML/readme.txt")
|
||||
message( "Cloning SFML in ExtLibs/SFML with Git..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
OUTPUT_QUIET)
|
||||
|
||||
message( "Resetting SFML source code to version 2.4.1..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
OUTPUT_QUIET)
|
||||
message( "Resetting SFML source code to version 2.4.1..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
OUTPUT_QUIET)
|
||||
|
||||
message( "Applying the patches..." )
|
||||
file(GLOB SFML_PATCHES
|
||||
LIST_DIRECTORIES FALSE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SFML-patches/*.patch)
|
||||
message( "Applying the patches..." )
|
||||
file(GLOB SFML_PATCHES
|
||||
LIST_DIRECTORIES FALSE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SFML-patches/*.patch)
|
||||
|
||||
if(SFML_PATCHES)
|
||||
list(SORT SFML_PATCHES)
|
||||
if(SFML_PATCHES)
|
||||
list(SORT SFML_PATCHES)
|
||||
|
||||
foreach(SFML_PATCH ${SFML_PATCHES})
|
||||
message( "Applying patch: ${SFML_PATCH}..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
OUTPUT_QUIET)
|
||||
endforeach()
|
||||
foreach(SFML_PATCH ${SFML_PATCHES})
|
||||
message( "Applying patch: ${SFML_PATCH}..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
OUTPUT_QUIET)
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
message( "SFML already downloaded." )
|
||||
endif()
|
||||
else()
|
||||
message( "Git not found, make sure you have SFML >= 2.4 in ExtLibs/SFML and you applied the needed patches (from ExtLibs/SFML-patches)!" )
|
||||
|
52
Extensions/.gitignore
vendored
52
Extensions/.gitignore
vendored
@@ -1,52 +0,0 @@
|
||||
*.depend
|
||||
/AES/obj
|
||||
/AES/AES.depend
|
||||
/AStarBehavior/obj
|
||||
/AStarBehavior/AStarBehavior.depend
|
||||
/Box3DObject/obj
|
||||
/Box3DObject/Box3DObject.depend
|
||||
/Common Dialogs/obj
|
||||
/Common Dialogs/CommonDialogs.depend
|
||||
/DestroyOutsideBehavior/obj
|
||||
/DestroyOutsideBehavior/DestroyOutsideBehavior.depend
|
||||
/DraggableBehavior/obj
|
||||
/DraggableBehavior/DraggableBehavior.depend
|
||||
/Function/obj
|
||||
/Function/Function.depend
|
||||
/Light/obj
|
||||
/Light/Light.depend
|
||||
/LinkedObjects/obj
|
||||
/LinkedObjects/LinkedObjects.depend
|
||||
/Network/obj
|
||||
/Network/Network.depend
|
||||
/PanelSpriteObject/obj
|
||||
/PanelSpriteObject/PanelSpriteObject.depend
|
||||
/ParticleSystem/obj
|
||||
/ParticleSystem/ParticleSystem.depend
|
||||
/PathBehavior/obj
|
||||
/PathBehavior/PathBehavior.depend
|
||||
/PhysicsBehavior/obj
|
||||
/PhysicsBehavior/PhysicsBehavior.depend
|
||||
/PrimitiveDrawing/obj
|
||||
/PrimitiveDrawing/PrimitiveDrawing.depend
|
||||
/TextEntryObject/obj
|
||||
/TextEntryObject/TextEntryObject.depend
|
||||
/TextObject/obj
|
||||
/TextObject/TextObject.depend
|
||||
/TiledSpriteObject/obj
|
||||
/TiledSpriteObject/TiledSpriteObject.depend
|
||||
/TimedEvent/obj
|
||||
/TimedEvent/TimedEvent.depend
|
||||
/VideoObject/obj
|
||||
/VideoObject/VideoObject.depend
|
||||
/SoundObject/obj
|
||||
/SoundObject/SoundObject.depend
|
||||
/VideoObject/VideoExtension.layout
|
||||
/VideoObject/VideoExtension.depend
|
||||
*.js~
|
||||
*.depend
|
||||
*.cpp~
|
||||
*.layout
|
||||
/.build
|
||||
*.layout
|
||||
/TiledSpriteObject/*.layout
|
@@ -20,24 +20,85 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'AdMob',
|
||||
_('AdMob'),
|
||||
_(
|
||||
'Allow the game to display AdMob banner, interstitial and reward video ads'
|
||||
),
|
||||
'Franco Maciel',
|
||||
_('Allow to display AdMob banners, interstitials and reward video ads.'),
|
||||
'Florian Rival',
|
||||
'MIT'
|
||||
);
|
||||
|
||||
extension
|
||||
.registerProperty('AdMobAppIdAndroid')
|
||||
.setLabel(_('AdMob Android App ID'))
|
||||
.setDescription('ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY')
|
||||
.setType('string');
|
||||
|
||||
extension
|
||||
.registerProperty('AdMobAppIdIos')
|
||||
.setLabel(_('AdMob iOS App ID'))
|
||||
.setDescription('ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY')
|
||||
.setType('string');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('AdMob Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('gdevelop-cordova-admob-plus')
|
||||
.setVersion('0.43.0')
|
||||
.setExtraSetting(
|
||||
'APP_ID_ANDROID',
|
||||
new gd.PropertyDescriptor('AdMobAppIdAndroid').setType(
|
||||
'ExtensionProperty'
|
||||
)
|
||||
)
|
||||
.setExtraSetting(
|
||||
'APP_ID_IOS',
|
||||
new gd.PropertyDescriptor('AdMobAppIdIos').setType('ExtensionProperty')
|
||||
)
|
||||
.onlyIfSomeExtraSettingsNonEmpty();
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Consent Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('cordova-plugin-consent');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetTestMode',
|
||||
_('Enable test mode'),
|
||||
_(
|
||||
'Activate or deactivate the test mode ("development" mode).\n' +
|
||||
'When activated, tests ads will be served instead of real ones.\n' +
|
||||
'\n' +
|
||||
'It is important to enable test ads during development so that you can click on them without ' +
|
||||
'charging advertisers. If you click on too many ads without being in test mode, you risk your ' +
|
||||
'account being flagged for invalid activity.'
|
||||
),
|
||||
_('Enable test mode (serving test ads, for development): _PARAM0_'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable test mode?'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.setTestMode');
|
||||
|
||||
// Banner
|
||||
extension
|
||||
.addCondition(
|
||||
'BannerLoading',
|
||||
_('Banner loading'),
|
||||
_('Check if a banner is currently loading.'),
|
||||
_(
|
||||
'Check if a banner is currently loading. It will be shown automatically when loaded.'
|
||||
),
|
||||
_('Banner is loading'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
@@ -47,20 +108,6 @@ module.exports = {
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.isBannerLoading');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'BannerReady',
|
||||
_('Banner ready'),
|
||||
_('Check if a banner is ready to be displayed.'),
|
||||
_('Banner is ready'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.isBannerReady');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'BannerShowing',
|
||||
@@ -77,56 +124,66 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'BannerExists',
|
||||
_('Banner exists'),
|
||||
_('Check if there is a banner in memory (visible or hidden).'),
|
||||
_('Banner exists'),
|
||||
'BannerErrored',
|
||||
_('Banner had an error'),
|
||||
_('Check if there was a error while displaying a banner.'),
|
||||
_('Banner ad had an error'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.existBanner');
|
||||
.setFunctionName('gdjs.adMob.isBannerErrored');
|
||||
|
||||
// Deprecated conditions, as banners can't be pre-loaded anymore.
|
||||
extension
|
||||
.addDuplicatedCondition('BannerReady', 'BannerShowing')
|
||||
.setHidden();
|
||||
extension
|
||||
.addDuplicatedCondition('BannerExists', 'BannerShowing')
|
||||
.setHidden();
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadBanner',
|
||||
_('Load banner'),
|
||||
'SetupBanner',
|
||||
_('Configure the banner'),
|
||||
_(
|
||||
'Start loading a banner, you can display it automatically when finish loading.\nIf test mode is set to true a test banner will be displayed.'
|
||||
"Configure a banner, which can then be displayed.\nIf test mode is set, a test banner will be displayed.\n\nOnce a banner is positioned (at the top or bottom of the game), it can't be moved anymore."
|
||||
),
|
||||
_(
|
||||
'Load banner (at top: _PARAM2_, overlap: _PARAM3_, show on load: _PARAM4_, test mode: _PARAM5_)'
|
||||
'Configure the banner with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_, display at top: _PARAM2_'
|
||||
),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter('string', _('Android banner ID'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/6300978111"` for showing a test banner.'
|
||||
)
|
||||
.addParameter('string', _('iOS banner ID'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/6300978111"` for showing a test banner.'
|
||||
)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Display at top? (bottom otherwise)'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.setDefaultValue('false')
|
||||
.addParameter('yesorno', _('Overlap webview?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.addParameter('yesorno', _('Display on load complete?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.addParameter('yesorno', _('Test mode?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.loadBanner');
|
||||
.setFunctionName('gdjs.adMob.setupBanner');
|
||||
|
||||
// Deprecated action (was renamed):
|
||||
extension.addDuplicatedAction('LoadBanner', 'SetupBanner').setHidden();
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'ShowBanner',
|
||||
_('Show banner'),
|
||||
_('Show the banner, will work only when the banner is fully loaded.'),
|
||||
_('Show the banner that was previously set up.'),
|
||||
_('Show banner'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
@@ -152,21 +209,8 @@ module.exports = {
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.hideBanner');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'RemoveBanner',
|
||||
_('Remove banner'),
|
||||
_(
|
||||
'Remove the banner. You have to load another banner to show it again.'
|
||||
),
|
||||
_('Remove banner'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.removeBanner');
|
||||
// Deprecated action (not applicable anymore):
|
||||
extension.addDuplicatedAction('RemoveBanner', 'HideBanner').setHidden();
|
||||
|
||||
// Interstitial
|
||||
extension
|
||||
@@ -211,23 +255,48 @@ module.exports = {
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.isInterstitialShowing');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'InterstitialErrored',
|
||||
_('Interstitial had an error'),
|
||||
_('Check if there was a error while loading the interstitial.'),
|
||||
_('Interstitial ad had an error'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.isInterstitialErrored');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadInterstitial',
|
||||
_('Load interstitial'),
|
||||
_(
|
||||
'Start loading an interstitial, you can display it automatically when finish loading.\nIf test mode is set to true a test interstitial will be displayed.'
|
||||
'Start loading an interstitial (that can be displayed automatically when the loading is finished).\nIf test mode is set, a test interstitial will be displayed.'
|
||||
),
|
||||
_(
|
||||
'Load interstitial with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_ (display automatically when loaded: _PARAM2_)'
|
||||
),
|
||||
_('Load interstitial (show on load: _PARAM2_, test mode: _PARAM3_)'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter('string', _('Android interstitial ID'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/1033173712"` for loading a test interstitial.'
|
||||
)
|
||||
.addParameter('string', _('iOS interstitial ID'), '', false)
|
||||
.addParameter('yesorno', _('Display on load complete?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.addParameter('yesorno', _('Test mode?'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/1033173712"` for loading a test interstitial.'
|
||||
)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Displayed automatically when loading is finished?'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
@@ -238,9 +307,9 @@ module.exports = {
|
||||
'ShowInterstitial',
|
||||
_('Show interstitial'),
|
||||
_(
|
||||
'Show the interstitial, will work only when the interstitial is fully loaded.'
|
||||
'Show the interstitial that was loaded. Will work only when the interstitial is fully loaded.'
|
||||
),
|
||||
_('Show interstitial'),
|
||||
_('Show the loaded interstitial'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
@@ -294,39 +363,69 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'VideoReward',
|
||||
_('Video reward'),
|
||||
_(
|
||||
'Check if there is a video reward.\nYou can mark it as non-claimed yet, so you can check this reward in other events.'
|
||||
),
|
||||
_('Video reward given'),
|
||||
'VideoErrored',
|
||||
_('Video had an error'),
|
||||
_('Check if there was a error while loading the rewarded video.'),
|
||||
_('Video ad had an error'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Mark as claimed'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.isVideoErrored');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'VideoReward',
|
||||
_('Video reward received'),
|
||||
_(
|
||||
'Check if the reward of the video was given to the user.\nYou can mark this reward as cleared, so that the condition will be false and you can show later another reward video.'
|
||||
),
|
||||
_('User got the reward of the video (and clear this reward: _PARAM0_)'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Clear the reward (needed to show another video)'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.existVideoReward');
|
||||
.setFunctionName('gdjs.adMob.wasVideoRewardReceived');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'LoadVideo',
|
||||
_('Load video'),
|
||||
_(
|
||||
'Start loading a reward video, you can display it automatically when finish loading.\nIf test mode is set to true a test video will be displayed.'
|
||||
'Start loading a reward video (that can be displayed automatically when the loading is finished).\nIf test mode is set, a test video will be displayed.'
|
||||
),
|
||||
_(
|
||||
'Load reward video with Android ad unit ID: _PARAM0_, iOS ad unit ID: _PARAM1_ (display automatically when loaded: _PARAM2_)'
|
||||
),
|
||||
_('Load reward video (show on load: _PARAM2_, test mode: _PARAM3_)'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.addParameter('string', _('Android reward video ID'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/5224354917"` for loading a test rewarded video.'
|
||||
)
|
||||
.addParameter('string', _('iOS reward video ID'), '', false)
|
||||
.addParameter('yesorno', _('Display on load complete?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.addParameter('yesorno', _('Test mode?'), '', false)
|
||||
.setParameterLongDescription(
|
||||
'Get it from your AdMob account. You can use `"ca-app-pub-3940256099942544/5224354917"` for loading a test rewarded video.'
|
||||
)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Displayed automatically when loading is finished?'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
@@ -337,9 +436,9 @@ module.exports = {
|
||||
'ShowVideo',
|
||||
_('Show video'),
|
||||
_(
|
||||
'Show the reward video, will work only when the video is fully loaded.'
|
||||
'Show the reward video that was loaded. Will work only when the video is fully loaded.'
|
||||
),
|
||||
_('Show reward video'),
|
||||
_('Show the loaded reward video'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
@@ -351,20 +450,25 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'ClaimReward',
|
||||
_('Claim reward'),
|
||||
_('Mark the video reward as claimed.'),
|
||||
_('Claim video reward'),
|
||||
_('Mark the reward of the video as claimed'),
|
||||
_(
|
||||
'Mark the video reward as claimed. Useful if you used the condition to check if the reward was given to the user without clearing the reward.'
|
||||
),
|
||||
_('Mark the reward of the video as claimed'),
|
||||
_('AdMob'),
|
||||
'JsPlatform/Extensions/admobicon24.png',
|
||||
'JsPlatform/Extensions/admobicon16.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/AdMob/admobtools.js')
|
||||
.setFunctionName('gdjs.adMob.claimVideoReward');
|
||||
.setFunctionName('gdjs.adMob.markVideoRewardAsClaimed');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
@@ -1,352 +0,0 @@
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class adMob
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.adMob = {
|
||||
// Banner
|
||||
bannerLoading: false,
|
||||
bannerReady: false,
|
||||
bannerShowing: false,
|
||||
bannerExists: false,
|
||||
bannerAutoshow: false, // Needed because the banner event listeners bug
|
||||
// Interstitial
|
||||
interstitialLoading: false,
|
||||
interstitialReady: false,
|
||||
interstitialShowing: false,
|
||||
// Reward video
|
||||
videoLoading: false,
|
||||
videoReady: false,
|
||||
videoShowing: false,
|
||||
videoReward: false,
|
||||
};
|
||||
|
||||
gdjs.adMob._getPlatformName = function() {
|
||||
if (/(android)/i.test(navigator.userAgent)) {
|
||||
return 'android';
|
||||
} else if (/(ipod|iphone|ipad)/i.test(navigator.userAgent)) {
|
||||
return 'ios';
|
||||
} else {
|
||||
return 'windowsPhone';
|
||||
}
|
||||
};
|
||||
|
||||
// Banner
|
||||
gdjs.adMob.isBannerLoading = function() {
|
||||
return gdjs.adMob.bannerLoading;
|
||||
};
|
||||
|
||||
gdjs.adMob.isBannerReady = function() {
|
||||
// This block is needed because the banner event listeners bug
|
||||
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
|
||||
gdjs.adMob.bannerReady = false;
|
||||
gdjs.adMob.bannerShowing = true;
|
||||
gdjs.adMob.bannerExists = true;
|
||||
}
|
||||
|
||||
return gdjs.adMob.bannerReady;
|
||||
};
|
||||
|
||||
gdjs.adMob.isBannerShowing = function() {
|
||||
// This block is needed because the banner event listeners bug
|
||||
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
|
||||
gdjs.adMob.bannerReady = false;
|
||||
gdjs.adMob.bannerShowing = true;
|
||||
gdjs.adMob.bannerExists = true;
|
||||
}
|
||||
|
||||
return gdjs.adMob.bannerShowing;
|
||||
};
|
||||
|
||||
gdjs.adMob.existBanner = function() {
|
||||
// This block is needed because the banner event listeners bug
|
||||
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
|
||||
gdjs.adMob.bannerReady = false;
|
||||
gdjs.adMob.bannerShowing = true;
|
||||
gdjs.adMob.bannerExists = true;
|
||||
}
|
||||
|
||||
return gdjs.adMob.bannerExists;
|
||||
};
|
||||
|
||||
gdjs.adMob.loadBanner = function(
|
||||
androidID,
|
||||
iosID,
|
||||
atTop,
|
||||
overlap,
|
||||
displayOnLoading,
|
||||
testMode
|
||||
) {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
if (
|
||||
gdjs.adMob.bannerLoading ||
|
||||
gdjs.adMob.bannerReady ||
|
||||
gdjs.adMob.bannerExists
|
||||
)
|
||||
return;
|
||||
|
||||
admob.banner.config({
|
||||
id: gdjs.adMob._getPlatformName() === 'android' ? androidID : iosID, // Support Android & iOS
|
||||
bannerAtTop: atTop,
|
||||
overlap: overlap,
|
||||
autoShow: displayOnLoading,
|
||||
isTesting: testMode,
|
||||
});
|
||||
admob.banner.prepare();
|
||||
|
||||
gdjs.adMob.bannerLoading = true;
|
||||
gdjs.adMob.bannerReady = false;
|
||||
|
||||
// These lines are needed because the banner event listeners bug
|
||||
gdjs.adMob.bannerAutoshow = displayOnLoading;
|
||||
gdjs.adMob.bannerShowing = false;
|
||||
gdjs.adMob.bannerExists = false;
|
||||
};
|
||||
|
||||
gdjs.adMob.showBanner = function() {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
// This block is needed because the banner event listeners bug
|
||||
if (gdjs.adMob.bannerReady) {
|
||||
gdjs.adMob.bannerReady = false;
|
||||
gdjs.adMob.bannerExists = true;
|
||||
}
|
||||
|
||||
if (gdjs.adMob.bannerExists) gdjs.adMob.bannerShowing = true;
|
||||
|
||||
admob.banner.show();
|
||||
};
|
||||
|
||||
gdjs.adMob.hideBanner = function() {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
if (gdjs.adMob.bannerExists) gdjs.adMob.bannerShowing = false;
|
||||
|
||||
admob.banner.hide();
|
||||
};
|
||||
|
||||
gdjs.adMob.removeBanner = function() {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
// These lines are needed because the banner event listeners bug
|
||||
gdjs.adMob.bannerExists = false;
|
||||
gdjs.adMob.bannerShowing = false;
|
||||
|
||||
admob.banner.remove();
|
||||
};
|
||||
|
||||
// Interstitial
|
||||
gdjs.adMob.isInterstitialLoading = function() {
|
||||
return gdjs.adMob.interstitialLoading;
|
||||
};
|
||||
|
||||
gdjs.adMob.isInterstitialReady = function() {
|
||||
return gdjs.adMob.interstitialReady;
|
||||
};
|
||||
|
||||
gdjs.adMob.isInterstitialShowing = function() {
|
||||
return gdjs.adMob.interstitialShowing;
|
||||
};
|
||||
|
||||
gdjs.adMob.loadInterstitial = function(
|
||||
androidID,
|
||||
iosID,
|
||||
displayOnLoading,
|
||||
testMode
|
||||
) {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
if (
|
||||
gdjs.adMob.interstitialLoading ||
|
||||
gdjs.adMob.interstitialReady ||
|
||||
gdjs.adMob.interstitialShowing
|
||||
)
|
||||
return;
|
||||
|
||||
admob.interstitial.config({
|
||||
id: gdjs.adMob._getPlatformName() === 'android' ? androidID : iosID, // Support Android & iOS
|
||||
autoShow: displayOnLoading,
|
||||
isTesting: testMode,
|
||||
});
|
||||
admob.interstitial.prepare();
|
||||
|
||||
gdjs.adMob.interstitialLoading = true;
|
||||
gdjs.adMob.interstitialReady = false;
|
||||
};
|
||||
|
||||
gdjs.adMob.showInterstitial = function() {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
admob.interstitial.show();
|
||||
};
|
||||
|
||||
// Reward video
|
||||
gdjs.adMob.isVideoLoading = function() {
|
||||
return gdjs.adMob.videoLoading;
|
||||
};
|
||||
|
||||
gdjs.adMob.isVideoReady = function() {
|
||||
return gdjs.adMob.videoReady;
|
||||
};
|
||||
|
||||
gdjs.adMob.isVideoShowing = function() {
|
||||
return gdjs.adMob.videoShowing;
|
||||
};
|
||||
|
||||
gdjs.adMob.existVideoReward = function(markAsClaimed) {
|
||||
var reward = gdjs.adMob.videoReward;
|
||||
if (markAsClaimed) gdjs.adMob.videoReward = false;
|
||||
|
||||
return reward;
|
||||
};
|
||||
|
||||
gdjs.adMob.loadVideo = function(androidID, iosID, displayOnLoading, testMode) {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
if (
|
||||
gdjs.adMob.videoLoading ||
|
||||
gdjs.adMob.videoReady ||
|
||||
gdjs.adMob.videoShowing
|
||||
)
|
||||
return;
|
||||
|
||||
admob.rewardvideo.config({
|
||||
id: gdjs.adMob._getPlatformName() === 'android' ? androidID : iosID, // Support Android & iOS
|
||||
autoShow: displayOnLoading,
|
||||
isTesting: testMode,
|
||||
});
|
||||
admob.rewardvideo.prepare();
|
||||
|
||||
gdjs.adMob.videoLoading = true;
|
||||
gdjs.adMob.videoReady = false;
|
||||
};
|
||||
|
||||
gdjs.adMob.showVideo = function() {
|
||||
if (typeof admob === 'undefined') return;
|
||||
|
||||
admob.rewardvideo.show();
|
||||
};
|
||||
|
||||
gdjs.adMob.claimVideoReward = function() {
|
||||
gdjs.adMob.videoReward = false;
|
||||
};
|
||||
|
||||
// Banner event listeners
|
||||
document.addEventListener(
|
||||
'admob.banner.events.LOAD',
|
||||
function() {
|
||||
gdjs.adMob.bannerReady = true;
|
||||
gdjs.adMob.bannerLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.banner.events.LOAD_FAIL',
|
||||
function() {
|
||||
gdjs.adMob.bannerLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
// BUG: These two never get called
|
||||
/*
|
||||
document.addEventListener(
|
||||
"admob.banner.events.OPEN",
|
||||
function() {
|
||||
gdjs.adMob.bannerExists = true;
|
||||
gdjs.adMob.bannerShowing = true;
|
||||
gdjs.adMob.bannerReady = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
"admob.banner.events.CLOSE",
|
||||
function() {
|
||||
gdjs.adMob.bannerExists = false;
|
||||
gdjs.adMob.bannerShowing = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
*/
|
||||
|
||||
// Interstitial event listeners
|
||||
document.addEventListener(
|
||||
'admob.interstitial.events.LOAD',
|
||||
function() {
|
||||
gdjs.adMob.interstitialReady = true;
|
||||
gdjs.adMob.interstitialLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.interstitial.events.LOAD_FAIL',
|
||||
function() {
|
||||
gdjs.adMob.interstitialLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.interstitial.events.OPEN',
|
||||
function() {
|
||||
gdjs.adMob.interstitialShowing = true;
|
||||
gdjs.adMob.interstitialReady = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.interstitial.events.CLOSE',
|
||||
function() {
|
||||
gdjs.adMob.interstitialShowing = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
// Reward video event listeners
|
||||
document.addEventListener(
|
||||
'admob.rewardvideo.events.LOAD',
|
||||
function() {
|
||||
gdjs.adMob.videoReady = true;
|
||||
gdjs.adMob.videoLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.rewardvideo.events.LOAD_FAIL',
|
||||
function() {
|
||||
gdjs.adMob.videoLoading = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.rewardvideo.events.OPEN',
|
||||
function() {
|
||||
gdjs.adMob.videoShowing = true;
|
||||
gdjs.adMob.videoReady = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.rewardvideo.events.CLOSE',
|
||||
function() {
|
||||
gdjs.adMob.videoShowing = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
document.addEventListener(
|
||||
'admob.rewardvideo.events.REWARD',
|
||||
function() {
|
||||
gdjs.adMob.videoReward = true;
|
||||
},
|
||||
false
|
||||
);
|
378
Extensions/AdMob/admobtools.ts
Normal file
378
Extensions/AdMob/admobtools.ts
Normal file
@@ -0,0 +1,378 @@
|
||||
namespace gdjs {
|
||||
declare var admob: any;
|
||||
|
||||
export namespace adMob {
|
||||
export enum AdSizeType {
|
||||
BANNER,
|
||||
LARGE_BANNER,
|
||||
MEDIUM_RECTANGLE,
|
||||
FULL_BANNER,
|
||||
LEADERBOARD,
|
||||
SMART_BANNER,
|
||||
}
|
||||
|
||||
// Banner
|
||||
let bannerAndroidId = '';
|
||||
let bannerIosId = '';
|
||||
let bannerPosition: 'top' | 'bottom' = 'top';
|
||||
let bannerRequestedAdSizeType: AdSizeType = AdSizeType.SMART_BANNER;
|
||||
|
||||
let bannerLoading = false;
|
||||
let bannerErrored = false;
|
||||
let bannerShowing = false;
|
||||
|
||||
// Interstitial
|
||||
let interstitialLoading = false;
|
||||
let interstitialReady = false;
|
||||
let interstitialErrored = false;
|
||||
let interstitialShowing = false;
|
||||
|
||||
// Reward video
|
||||
let videoLoading = false;
|
||||
let videoReady = false;
|
||||
let videoErrored = false;
|
||||
let videoShowing = false;
|
||||
let videoRewardReceived = false;
|
||||
|
||||
let npaValue = '0'; // TODO: expose an API to change this and also an automatic way using the consent SDK.
|
||||
|
||||
/**
|
||||
* Activate or deactivate the test mode ("development" mode).
|
||||
* When activated, tests ads will be served instead of real ones.
|
||||
*
|
||||
* It is important to enable test ads during development so that you can click on them without
|
||||
* charging advertisers. If you click on too many ads without being in test mode, you risk your
|
||||
* account being flagged for invalid activity.
|
||||
*/
|
||||
export const setTestMode = (enable: boolean) => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
admob.setDevMode(enable);
|
||||
};
|
||||
|
||||
// Banner
|
||||
/** Check if a banner is loading. */
|
||||
export const isBannerLoading = () => {
|
||||
return bannerLoading;
|
||||
};
|
||||
/** Check if a banner is being shown on screen. */
|
||||
export const isBannerShowing = () => {
|
||||
return bannerShowing;
|
||||
};
|
||||
/** Check if the banner had an error while loading it. */
|
||||
export const isBannerErrored = () => {
|
||||
return bannerErrored;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up a banner that can then be displayed by calling `showBanner`.
|
||||
*/
|
||||
export const setupBanner = function (androidID, iosID, atTop) {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
bannerAndroidId = androidID;
|
||||
bannerIosId = iosID;
|
||||
bannerPosition = atTop ? 'top' : 'bottom';
|
||||
};
|
||||
/**
|
||||
* Set the size of the banner to be shown when `showBanner` is called.
|
||||
* @param bannerAdSizeType The type of the banner to displayed
|
||||
*/
|
||||
export const setBannerAdSizeType = (
|
||||
bannerAdSizeType:
|
||||
| 'BANNER'
|
||||
| 'LARGE_BANNER'
|
||||
| 'MEDIUM_RECTANGLE'
|
||||
| 'FULL_BANNER'
|
||||
| 'LEADERBOARD'
|
||||
| 'SMART_BANNER'
|
||||
) => {
|
||||
const adSizeTypes = {
|
||||
BANNER: AdSizeType.BANNER,
|
||||
LARGE_BANNER: AdSizeType.LARGE_BANNER,
|
||||
MEDIUM_RECTANGLE: AdSizeType.MEDIUM_RECTANGLE,
|
||||
FULL_BANNER: AdSizeType.FULL_BANNER,
|
||||
LEADERBOARD: AdSizeType.LEADERBOARD,
|
||||
SMART_BANNER: AdSizeType.SMART_BANNER,
|
||||
};
|
||||
|
||||
bannerRequestedAdSizeType =
|
||||
adSizeTypes[bannerAdSizeType] || AdSizeType.SMART_BANNER;
|
||||
};
|
||||
/**
|
||||
* Display the banner that was set up with `loadBanner` (and `setBannerAdSizeType`).
|
||||
*/
|
||||
export const showBanner = () => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
bannerLoading = true;
|
||||
bannerShowing = false;
|
||||
bannerErrored = false;
|
||||
admob.banner
|
||||
.show({
|
||||
id: {
|
||||
android: bannerAndroidId,
|
||||
ios: bannerIosId,
|
||||
},
|
||||
position: bannerPosition,
|
||||
size: bannerRequestedAdSizeType,
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
bannerShowing = true;
|
||||
bannerLoading = false;
|
||||
console.info('AdMob banner successfully shown.');
|
||||
},
|
||||
(error) => {
|
||||
bannerShowing = false;
|
||||
bannerLoading = false;
|
||||
bannerErrored = true;
|
||||
console.error('Error while showing an AdMob banner:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
/** Hide the banner shown on screen. */
|
||||
export const hideBanner = () => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
bannerShowing = false;
|
||||
admob.banner.hide({
|
||||
android: bannerAndroidId,
|
||||
ios: bannerIosId,
|
||||
});
|
||||
};
|
||||
|
||||
// Interstitial
|
||||
/** Check if the interstitial is loading. */
|
||||
export const isInterstitialLoading = () => {
|
||||
return interstitialLoading;
|
||||
};
|
||||
/** Check if the interstitial is ready to display. */
|
||||
export const isInterstitialReady = () => {
|
||||
return interstitialReady;
|
||||
};
|
||||
/** Check if the interstitial is shown on screen. */
|
||||
export const isInterstitialShowing = () => {
|
||||
return interstitialShowing;
|
||||
};
|
||||
/** Check if the interstitial had an error while loading it. */
|
||||
export const isInterstitialErrored = () => {
|
||||
return interstitialErrored;
|
||||
};
|
||||
|
||||
/** Load an interstitial. */
|
||||
export const loadInterstitial = (androidID, iosID, displayWhenLoaded) => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (interstitialLoading || interstitialReady || interstitialShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
interstitialLoading = true;
|
||||
interstitialReady = false;
|
||||
interstitialErrored = false;
|
||||
admob.interstitial
|
||||
.load({
|
||||
id: {
|
||||
android: androidID,
|
||||
ios: iosID,
|
||||
},
|
||||
npa: npaValue,
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
console.info('AdMob interstitial successfully loaded.');
|
||||
if (displayWhenLoaded) showInterstitial();
|
||||
},
|
||||
(error) => {
|
||||
interstitialLoading = false;
|
||||
interstitialReady = false;
|
||||
interstitialErrored = true;
|
||||
console.error('Error while loading a interstitial:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/** Show the loaded interstitial. */
|
||||
export const showInterstitial = () => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
admob.interstitial.show().then(
|
||||
() => {
|
||||
// Interstitial will be shown and
|
||||
// `interstitialShowing` will be updated thanks to events
|
||||
// (but it's too early to change it now).
|
||||
},
|
||||
(error) => {
|
||||
interstitialShowing = false;
|
||||
interstitialErrored = true;
|
||||
console.error('Error while trying to show an interstitial:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Reward video
|
||||
/** Check if the video is loading. */
|
||||
export const isVideoLoading = () => {
|
||||
return videoLoading;
|
||||
};
|
||||
/** Check if the video is ready to display. */
|
||||
export const isVideoReady = () => {
|
||||
return videoReady;
|
||||
};
|
||||
/** Check if the video is shown on screen. */
|
||||
export const isVideoShowing = () => {
|
||||
return videoShowing;
|
||||
};
|
||||
/** Check if the video had an error while loading it. */
|
||||
export const isVideoErrored = () => {
|
||||
return videoErrored;
|
||||
};
|
||||
|
||||
/** Check if the reward of the video was received. */
|
||||
export const wasVideoRewardReceived = function (markAsClaimed) {
|
||||
const reward = videoRewardReceived;
|
||||
if (markAsClaimed) {
|
||||
videoRewardReceived = false;
|
||||
}
|
||||
return reward;
|
||||
};
|
||||
|
||||
/** Load a reward video. */
|
||||
export const loadVideo = function (androidID, iosID, displayWhenLoaded) {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (videoLoading || videoReady || videoShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
videoLoading = true;
|
||||
videoReady = false;
|
||||
videoErrored = false;
|
||||
admob.rewardVideo
|
||||
.load({
|
||||
id: {
|
||||
android: androidID,
|
||||
ios: iosID,
|
||||
},
|
||||
npa: npaValue,
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
console.info('AdMob reward video successfully loaded.');
|
||||
|
||||
if (displayWhenLoaded) showVideo();
|
||||
},
|
||||
(error) => {
|
||||
videoLoading = false;
|
||||
videoReady = false;
|
||||
videoErrored = true;
|
||||
console.error('Error while loading a reward video:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/** Show the loaded reward video. */
|
||||
export const showVideo = () => {
|
||||
if (typeof admob === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
admob.rewardVideo.show().then(
|
||||
() => {
|
||||
// Video will be shown and
|
||||
// `videoShowing` will be updated thanks to events
|
||||
// (but it's too early to change it now).
|
||||
},
|
||||
(error) => {
|
||||
videoShowing = false;
|
||||
videoErrored = true;
|
||||
console.error('Error while trying to show a reward video:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/** Mark the reward of the video as claimed. */
|
||||
export const markVideoRewardAsClaimed = () => {
|
||||
videoRewardReceived = false;
|
||||
};
|
||||
|
||||
// Banner event listeners:
|
||||
document.addEventListener('admob.banner.load', () => {
|
||||
bannerShowing = true;
|
||||
bannerLoading = false;
|
||||
});
|
||||
document.addEventListener('admob.banner.load_fail', () => {
|
||||
bannerLoading = false;
|
||||
});
|
||||
|
||||
document.addEventListener('admob.banner.open', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
|
||||
document.addEventListener('admob.banner.exit_app', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
document.addEventListener('admob.banner.close', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
|
||||
// Interstitial event listeners
|
||||
document.addEventListener('admob.interstitial.load', () => {
|
||||
interstitialReady = true;
|
||||
interstitialLoading = false;
|
||||
});
|
||||
document.addEventListener('admob.interstitial.load_fail', () => {
|
||||
interstitialLoading = false;
|
||||
});
|
||||
document.addEventListener('admob.interstitial.open', () => {
|
||||
interstitialShowing = true;
|
||||
interstitialReady = false;
|
||||
});
|
||||
document.addEventListener('admob.interstitial.close', () => {
|
||||
interstitialShowing = false;
|
||||
});
|
||||
document.addEventListener('admob.interstitial.exit_app', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
|
||||
// Reward video event listeners
|
||||
document.addEventListener('admob.reward_video.load', () => {
|
||||
videoReady = true;
|
||||
videoLoading = false;
|
||||
});
|
||||
document.addEventListener('admob.reward_video.load_fail', () => {
|
||||
videoLoading = false;
|
||||
});
|
||||
document.addEventListener('admob.reward_video.open', () => {
|
||||
videoShowing = true;
|
||||
videoReady = false;
|
||||
});
|
||||
document.addEventListener('admob.reward_video.close', () => {
|
||||
videoShowing = false;
|
||||
});
|
||||
document.addEventListener('admob.reward_video.start', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
document.addEventListener('admob.reward_video.complete', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
document.addEventListener('admob.reward_video.reward', () => {
|
||||
videoRewardReceived = true;
|
||||
});
|
||||
document.addEventListener('admob.reward_video.exit_app', () => {
|
||||
// Not implemented.
|
||||
});
|
||||
}
|
||||
}
|
676
Extensions/AdvancedWindow/JsExtension.js
Normal file
676
Extensions/AdvancedWindow/JsExtension.js
Normal file
@@ -0,0 +1,676 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'AdvancedWindow',
|
||||
_('Advanced window management'),
|
||||
_(
|
||||
'Provides advanced features related to the game window positioning and interaction with the operating system.'
|
||||
),
|
||||
'Arthur Pacaud (arthuro555)',
|
||||
'MIT'
|
||||
);
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Focus',
|
||||
_('Change focus of the window'),
|
||||
_('Make the window gain or lose focus.'),
|
||||
_('Focus the window: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Focus the window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.focus');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsFocused',
|
||||
_('Window focused'),
|
||||
_('Checks if the window is focused.'),
|
||||
_('The window is focused'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isFocused');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Show',
|
||||
_('Change visibility of the window'),
|
||||
_('Make the window visible or invisible.'),
|
||||
_('Window visible: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Show window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.show');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsVisible',
|
||||
_('Window visible'),
|
||||
_('Checks if the window is visible.'),
|
||||
_('The window is visible'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isVisible');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Maximize',
|
||||
_('Maximize the window'),
|
||||
_('Maximize or unmaximize the window.'),
|
||||
_('Maximize window: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Maximize window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.maximize');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMaximized',
|
||||
_('Window maximized'),
|
||||
_('Checks if the window is maximized.'),
|
||||
_('The window is maximized'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isMaximized');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Minimize',
|
||||
_('Minimize the window'),
|
||||
_('Minimize or unminimize the window.'),
|
||||
_('Minimize window: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Minimize window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.minimize');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMinimized',
|
||||
_('Window minimized'),
|
||||
_('Checks if the window is minimized.'),
|
||||
_('The window is minimized'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isMinimized');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'EnableWindow',
|
||||
_('Enable the window'),
|
||||
_('Enables or disables the window.'),
|
||||
_('Enable window: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.enable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsWindowEnabled',
|
||||
_('Window enabled'),
|
||||
_('Checks if the window is enabled.'),
|
||||
_('The window is enabled'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isEnabled');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetResizable',
|
||||
_('Allow resizing'),
|
||||
_('Enables or disables resizing of the window by the user.'),
|
||||
_('Enable window resizing: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow resizing?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setResizable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsResizable',
|
||||
_('Window resizable'),
|
||||
_('Checks if the window can be resized.'),
|
||||
_('The window can be resized'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isResizable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetMovable',
|
||||
_('Allow moving'),
|
||||
_('Enables or disables moving of the window by the user.'),
|
||||
_('Enable window moving: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow moving?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setMovable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMovable',
|
||||
_('Window movable'),
|
||||
_('Checks if the window can be moved.'),
|
||||
_('The window can be moved'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isMovable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetMaximizable',
|
||||
_('Allow maximizing'),
|
||||
_('Enables or disables maximizing of the window by the user.'),
|
||||
_('Enable window maximizing: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow maximizing?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setMaximizable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMaximizable',
|
||||
_('Window maximizable'),
|
||||
_('Checks if the window can be maximized.'),
|
||||
_('The window can be maximized'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isMaximizable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetMinimizable',
|
||||
_('Allow mimizing'),
|
||||
_('Enables or disables minimizing of the window by the user.'),
|
||||
_('Enable window minimizing: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow minimizing?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setMinimizable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsMinimizable',
|
||||
_('Window minimizable'),
|
||||
_('Checks if the window can be minimized.'),
|
||||
_('The window can be minimized'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isMinimizable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetFullScreenable',
|
||||
_('Allow full-screening'),
|
||||
_('Enables or disables full-screening of the window by the user.'),
|
||||
_('Enable window full-screening: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow full-screening?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setFullScreenable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsFullScreenable',
|
||||
_('Window full-screenable'),
|
||||
_('Checks if the window can be full-screened.'),
|
||||
_('The window can be set in fullscreen'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isFullScreenable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetClosable',
|
||||
_('Allow closing'),
|
||||
_('Enables or disables closing of the window by the user.'),
|
||||
_('Enable window closing: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow closing?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setClosable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsClosable',
|
||||
_('Window closable'),
|
||||
_('Checks if the window can be closed.'),
|
||||
_('The window can be closed'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isClosable');
|
||||
|
||||
const levelChoices = JSON.stringify([
|
||||
'normal',
|
||||
'floating',
|
||||
'torn-off-menu',
|
||||
'modal-panel',
|
||||
'main-menu',
|
||||
'status',
|
||||
'pop-up-menu',
|
||||
'screen-saver',
|
||||
]);
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetAlwaysOnTop',
|
||||
_('Make the windows always on top'),
|
||||
_('Puts the window constantly above all other windows.'),
|
||||
_('Make window always on top: _PARAM0_, level: _PARAM1_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable always on top?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.addParameter('stringWithSelector', _('Level'), levelChoices, false)
|
||||
.setDefaultValue('floating')
|
||||
.setParameterLongDescription(
|
||||
'The level is like a layer in GDevelop but for the OS. ' +
|
||||
'The further down the list, the higher it will be. ' +
|
||||
'When disabling always on top, the level will be set to normal. ' +
|
||||
'From "floating" to "status" included, ' +
|
||||
'the window is placed below the Dock on macOS and below the taskbar on Windows. ' +
|
||||
'Starting from "pop-up-menu", it is shown above the Dock on macOS and ' +
|
||||
'above the taskbar on Windows. ' +
|
||||
'This parameter is ignored on linux.'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setAlwaysOnTop');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsAlwaysOnTop',
|
||||
_('Window always on top'),
|
||||
_('Checks if the window is always on top.'),
|
||||
_('The window is always on top'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isAlwaysOnTop');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetKiosk',
|
||||
_('Enable kiosk mode'),
|
||||
_(
|
||||
'Puts the window in kiosk mode. This prevents the user from exiting fullscreen.'
|
||||
),
|
||||
_('Enable kiosk mode: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable kiosk mode?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setKiosk');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsKiosk',
|
||||
_('Kiosk mode'),
|
||||
_('Checks if the window is currently in kiosk mode.'),
|
||||
_('The window is in kiosk mode'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.isKiosk');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetHasShadow',
|
||||
_('Enable window shadow'),
|
||||
_('Enables or disables the window shadow.'),
|
||||
_('Enable window shadow: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable shadow?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setHasShadow');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'HasShadow',
|
||||
_('Shadow enabled'),
|
||||
_("Checks if the window currently has it's shadow enabled."),
|
||||
_('The window has a shadow'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.hasShadow');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'EnableContentProtection',
|
||||
_('Enable content protection'),
|
||||
_(
|
||||
'Enables or disables the content protection mode. This should prevent screenshots of the game from being taken.'
|
||||
),
|
||||
_('Enable content protection: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Enable content protection?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setContentProtection');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetFocusable',
|
||||
_('Allow focusing'),
|
||||
_('Allow or disallow the user to focus the window.'),
|
||||
_('Allow to focus the window: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Allow focus?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setFocusable');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Flash',
|
||||
_('Flash the window'),
|
||||
_('Make the window flash or end flashing.'),
|
||||
_('Make the window flash: _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('yesorno', _('Flash the window?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.flash');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetOpacity',
|
||||
_('Set window opacity'),
|
||||
_('Changes the window opacity.'),
|
||||
_('Set the window opacity to _PARAM0_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('expression', _('New opacity'), '', false)
|
||||
.setParameterLongDescription('A number between 0 (fully transparent) and 1 (fully opaque).')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setOpacity');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetWindowPosition',
|
||||
_('Set window position'),
|
||||
_('Changes the window position.'),
|
||||
_('Set the window position to _PARAM0_;_PARAM1_'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window24.png',
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.addParameter('expression', _('X position'), '', false)
|
||||
.addParameter('expression', _('Y position'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.setPosition');
|
||||
|
||||
extension
|
||||
.addExpression(
|
||||
'WindowX',
|
||||
_('Window X position'),
|
||||
_('Returns the current window X position.'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.getPositionX');
|
||||
|
||||
extension
|
||||
.addExpression(
|
||||
'WindowY',
|
||||
_('Window Y position'),
|
||||
_('Returns the current window Y position.'),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.getPositionY');
|
||||
|
||||
extension
|
||||
.addExpression(
|
||||
'WindowOpacity',
|
||||
_('Window opacity'),
|
||||
_(
|
||||
'Returns the current window opacity (a number from 0 to 1, 1 being fully opaque).'
|
||||
),
|
||||
_('Advanced window management/Windows, Linux, macOS'),
|
||||
'res/actions/window.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
'Extensions/AdvancedWindow/electron-advancedwindowtools.js'
|
||||
)
|
||||
.setFunctionName('gdjs.evtTools.advancedWindow.getOpacity');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
245
Extensions/AdvancedWindow/electron-advancedwindowtools.ts
Normal file
245
Extensions/AdvancedWindow/electron-advancedwindowtools.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
namespace gdjs {
|
||||
/**
|
||||
* Tools to manipulate the game window positioning and
|
||||
* interactions with the operating system.
|
||||
* @author arthuro555
|
||||
*/
|
||||
export namespace evtTools {
|
||||
export namespace advancedWindow {
|
||||
/**
|
||||
* The game's BrowserWindow instance (or null on
|
||||
* non-electron platforms).
|
||||
*/
|
||||
let electronBrowserWindow: any = null;
|
||||
|
||||
if (typeof require === 'function') {
|
||||
electronBrowserWindow = require('electron').remote.getCurrentWindow();
|
||||
}
|
||||
export const focus = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
if (activate) {
|
||||
electronBrowserWindow.focus();
|
||||
} else {
|
||||
electronBrowserWindow.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
export const isFocused = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isFocused();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const show = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
if (activate) {
|
||||
electronBrowserWindow.showInactive();
|
||||
} else {
|
||||
electronBrowserWindow.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
export const isVisible = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isVisible();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const maximize = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
if (activate) {
|
||||
electronBrowserWindow.maximize();
|
||||
} else {
|
||||
electronBrowserWindow.unmaximize();
|
||||
}
|
||||
}
|
||||
};
|
||||
export const isMaximized = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isMaximized();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const minimize = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
if (activate) {
|
||||
electronBrowserWindow.minimize();
|
||||
} else {
|
||||
electronBrowserWindow.restore();
|
||||
}
|
||||
}
|
||||
};
|
||||
export const isMinimized = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isMinimized();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const enable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setEnabled(activate);
|
||||
}
|
||||
};
|
||||
export const isEnabled = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isEnabled();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setResizable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setResizable(activate);
|
||||
}
|
||||
};
|
||||
export const isResizable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isResizable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setMovable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setMovable(activate);
|
||||
}
|
||||
};
|
||||
export const isMovable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isMovable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setMaximizable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setMaximizable(activate);
|
||||
}
|
||||
};
|
||||
export const isMaximizable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isMaximizable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setMinimizable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setMinimizable(activate);
|
||||
}
|
||||
};
|
||||
export const isMinimizable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isMinimizable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setFullScreenable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setFullScreenable(activate);
|
||||
}
|
||||
};
|
||||
export const isFullScreenable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isFullScreenable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setClosable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setClosable(activate);
|
||||
}
|
||||
};
|
||||
export const isClosable = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isClosable();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setAlwaysOnTop = function (
|
||||
activate: boolean,
|
||||
level:
|
||||
| 'normal'
|
||||
| 'floating'
|
||||
| 'torn-off-menu'
|
||||
| 'modal-panel'
|
||||
| 'main-menu'
|
||||
| 'status'
|
||||
| 'pop-up-menu'
|
||||
| 'screen-saver'
|
||||
) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setAlwaysOnTop(activate, level);
|
||||
}
|
||||
};
|
||||
export const isAlwaysOnTop = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isAlwaysOnTop();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setPosition = function (x: float, y: float) {
|
||||
if (electronBrowserWindow) {
|
||||
// Convert x and y to (32 bit) integers to avoid Electron errors.
|
||||
electronBrowserWindow.setPosition(~~x, ~~y);
|
||||
}
|
||||
};
|
||||
export const getPositionX = function (): number {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.getPosition()[0];
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
export const getPositionY = function (): number {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.getPosition()[1];
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
export const setKiosk = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setKiosk(activate);
|
||||
}
|
||||
};
|
||||
export const isKiosk = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.isKiosk();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const flash = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.flashFrame(activate);
|
||||
}
|
||||
};
|
||||
export const setHasShadow = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setHasShadow(activate);
|
||||
}
|
||||
};
|
||||
export const hasShadow = function (): boolean {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.hasShadow();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export const setOpacity = function (opacity: float) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setOpacity(opacity);
|
||||
}
|
||||
};
|
||||
export const getOpacity = function (): number {
|
||||
if (electronBrowserWindow) {
|
||||
return electronBrowserWindow.getOpacity();
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
export const setContentProtection = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setContentProtection(activate);
|
||||
}
|
||||
};
|
||||
export const setFocusable = function (activate: boolean) {
|
||||
if (electronBrowserWindow) {
|
||||
electronBrowserWindow.setFocusable(activate);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
GDevelop - Anchor Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class AnchorRuntimeBehavior
|
||||
* @constructor
|
||||
*/
|
||||
gdjs.AnchorRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
{
|
||||
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||
|
||||
this._relativeToOriginalWindowSize = !!behaviorData.relativeToOriginalWindowSize;
|
||||
this._leftEdgeAnchor = behaviorData.leftEdgeAnchor;
|
||||
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
|
||||
this._topEdgeAnchor = behaviorData.topEdgeAnchor;
|
||||
this._bottomEdgeAnchor = behaviorData.bottomEdgeAnchor;
|
||||
this._invalidDistances = true;
|
||||
this._leftEdgeDistance = 0;
|
||||
this._rightEdgeDistance = 0;
|
||||
this._topEdgeDistance = 0;
|
||||
this._bottomEdgeDistance = 0;
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("AnchorBehavior::AnchorBehavior", gdjs.AnchorRuntimeBehavior);
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.HorizontalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_LEFT: 1,
|
||||
WINDOW_RIGHT: 2,
|
||||
PROPORTIONAL: 3
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.VerticalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_TOP: 1,
|
||||
WINDOW_BOTTOM: 2,
|
||||
PROPORTIONAL: 3
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.leftEdgeAnchor !== newBehaviorData.leftEdgeAnchor) {
|
||||
this._leftEdgeAnchor = newBehaviorData.leftEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.rightEdgeAnchor !== newBehaviorData.rightEdgeAnchor) {
|
||||
this._rightEdgeAnchor = newBehaviorData.rightEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.topEdgeAnchor !== newBehaviorData.topEdgeAnchor) {
|
||||
this._topEdgeAnchor = newBehaviorData.topEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.bottomEdgeAnchor !== newBehaviorData.bottomEdgeAnchor) {
|
||||
this._bottomEdgeAnchor = newBehaviorData.bottomEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.relativeToOriginalWindowSize !== newBehaviorData.relativeToOriginalWindowSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.onActivate = function() {
|
||||
this._invalidDistances = true;
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene) {
|
||||
var game = runtimeScene.getGame();
|
||||
var rendererWidth = game.getGameResolutionWidth();
|
||||
var rendererHeight = game.getGameResolutionHeight();
|
||||
var layer = runtimeScene.getLayer(this.owner.getLayer());
|
||||
|
||||
if(this._invalidDistances)
|
||||
{
|
||||
if (this._relativeToOriginalWindowSize) {
|
||||
rendererWidth = game.getOriginalWidth();
|
||||
rendererHeight = game.getOriginalHeight();
|
||||
}
|
||||
|
||||
//Calculate the distances from the window's bounds.
|
||||
var topLeftPixel = layer.convertCoords(
|
||||
this.owner.getDrawableX(),
|
||||
this.owner.getDrawableY()
|
||||
);
|
||||
|
||||
//Left edge
|
||||
if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT)
|
||||
this._leftEdgeDistance = topLeftPixel[0];
|
||||
else if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT)
|
||||
this._leftEdgeDistance = rendererWidth - topLeftPixel[0];
|
||||
else if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL)
|
||||
this._leftEdgeDistance = topLeftPixel[0] / rendererWidth;
|
||||
|
||||
//Top edge
|
||||
if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP)
|
||||
this._topEdgeDistance = topLeftPixel[1];
|
||||
else if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM)
|
||||
this._topEdgeDistance = rendererHeight - topLeftPixel[1];
|
||||
else if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL)
|
||||
this._topEdgeDistance = topLeftPixel[1] / rendererHeight;
|
||||
|
||||
var bottomRightPixel = layer.convertCoords(
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight()
|
||||
);
|
||||
|
||||
//Right edge
|
||||
if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT)
|
||||
this._rightEdgeDistance = bottomRightPixel[0];
|
||||
else if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT)
|
||||
this._rightEdgeDistance = rendererWidth - bottomRightPixel[0];
|
||||
else if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL)
|
||||
this._rightEdgeDistance = bottomRightPixel[0] / rendererWidth;
|
||||
|
||||
//Bottom edge
|
||||
if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP)
|
||||
this._bottomEdgeDistance = bottomRightPixel[1];
|
||||
else if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM)
|
||||
this._bottomEdgeDistance = rendererHeight - bottomRightPixel[1];
|
||||
else if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL)
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] / rendererHeight;
|
||||
|
||||
this._invalidDistances = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Move and resize the object if needed
|
||||
var leftPixel = 0;
|
||||
var topPixel = 0;
|
||||
|
||||
var rightPixel = 0;
|
||||
var bottomPixel = 0;
|
||||
|
||||
//Left edge
|
||||
if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT)
|
||||
leftPixel = this._leftEdgeDistance;
|
||||
else if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT)
|
||||
leftPixel = rendererWidth - this._leftEdgeDistance;
|
||||
else if(this._leftEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL)
|
||||
leftPixel = this._leftEdgeDistance * rendererWidth;
|
||||
|
||||
//Top edge
|
||||
if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP)
|
||||
topPixel = this._topEdgeDistance;
|
||||
else if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM)
|
||||
topPixel = rendererHeight - this._topEdgeDistance;
|
||||
else if(this._topEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL)
|
||||
topPixel = this._topEdgeDistance * rendererHeight;
|
||||
|
||||
//Right edge
|
||||
if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT)
|
||||
rightPixel = this._rightEdgeDistance;
|
||||
else if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT)
|
||||
rightPixel = rendererWidth - this._rightEdgeDistance;
|
||||
else if(this._rightEdgeAnchor === gdjs.AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL)
|
||||
rightPixel = this._rightEdgeDistance * rendererWidth;
|
||||
|
||||
//Bottom edge
|
||||
if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP)
|
||||
bottomPixel = this._bottomEdgeDistance;
|
||||
else if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM)
|
||||
bottomPixel = rendererHeight - this._bottomEdgeDistance;
|
||||
else if(this._bottomEdgeAnchor === gdjs.AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL)
|
||||
bottomPixel = this._bottomEdgeDistance * rendererHeight;
|
||||
|
||||
var topLeftCoord = layer.convertInverseCoords(leftPixel, topPixel);
|
||||
var bottomRightCoord = layer.convertInverseCoords(rightPixel, bottomPixel);
|
||||
|
||||
//Move and resize the object according to the anchors
|
||||
if(this._rightEdgeAnchor !== gdjs.AnchorRuntimeBehavior.HorizontalAnchor.NONE)
|
||||
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
|
||||
if(this._bottomEdgeAnchor !== gdjs.AnchorRuntimeBehavior.VerticalAnchor.NONE)
|
||||
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
|
||||
if(this._leftEdgeAnchor !== gdjs.AnchorRuntimeBehavior.HorizontalAnchor.NONE)
|
||||
this.owner.setX(topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX());
|
||||
if(this._topEdgeAnchor !== gdjs.AnchorRuntimeBehavior.VerticalAnchor.NONE)
|
||||
this.owner.setY(topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY());
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
};
|
310
Extensions/AnchorBehavior/anchorruntimebehavior.ts
Normal file
310
Extensions/AnchorBehavior/anchorruntimebehavior.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
GDevelop - Anchor Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
namespace gdjs {
|
||||
export class AnchorRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||
_relativeToOriginalWindowSize: any;
|
||||
_leftEdgeAnchor: any;
|
||||
_rightEdgeAnchor: any;
|
||||
_topEdgeAnchor: any;
|
||||
_bottomEdgeAnchor: any;
|
||||
_invalidDistances: boolean = true;
|
||||
_leftEdgeDistance: number = 0;
|
||||
_rightEdgeDistance: number = 0;
|
||||
_topEdgeDistance: number = 0;
|
||||
_bottomEdgeDistance: number = 0;
|
||||
|
||||
constructor(runtimeScene, behaviorData, owner) {
|
||||
super(runtimeScene, behaviorData, owner);
|
||||
this._relativeToOriginalWindowSize = !!behaviorData.relativeToOriginalWindowSize;
|
||||
this._leftEdgeAnchor = behaviorData.leftEdgeAnchor;
|
||||
this._rightEdgeAnchor = behaviorData.rightEdgeAnchor;
|
||||
this._topEdgeAnchor = behaviorData.topEdgeAnchor;
|
||||
this._bottomEdgeAnchor = behaviorData.bottomEdgeAnchor;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.leftEdgeAnchor !== newBehaviorData.leftEdgeAnchor) {
|
||||
this._leftEdgeAnchor = newBehaviorData.leftEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.rightEdgeAnchor !== newBehaviorData.rightEdgeAnchor) {
|
||||
this._rightEdgeAnchor = newBehaviorData.rightEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.topEdgeAnchor !== newBehaviorData.topEdgeAnchor) {
|
||||
this._topEdgeAnchor = newBehaviorData.topEdgeAnchor;
|
||||
}
|
||||
if (
|
||||
oldBehaviorData.bottomEdgeAnchor !== newBehaviorData.bottomEdgeAnchor
|
||||
) {
|
||||
this._bottomEdgeAnchor = newBehaviorData.bottomEdgeAnchor;
|
||||
}
|
||||
if (
|
||||
oldBehaviorData.relativeToOriginalWindowSize !==
|
||||
newBehaviorData.relativeToOriginalWindowSize
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onActivate() {
|
||||
this._invalidDistances = true;
|
||||
}
|
||||
|
||||
doStepPreEvents(runtimeScene) {
|
||||
const game = runtimeScene.getGame();
|
||||
let rendererWidth = game.getGameResolutionWidth();
|
||||
let rendererHeight = game.getGameResolutionHeight();
|
||||
const layer = runtimeScene.getLayer(this.owner.getLayer());
|
||||
if (this._invalidDistances) {
|
||||
if (this._relativeToOriginalWindowSize) {
|
||||
rendererWidth = game.getOriginalWidth();
|
||||
rendererHeight = game.getOriginalHeight();
|
||||
}
|
||||
|
||||
//Calculate the distances from the window's bounds.
|
||||
const topLeftPixel = layer.convertCoords(
|
||||
this.owner.getDrawableX(),
|
||||
this.owner.getDrawableY()
|
||||
);
|
||||
|
||||
//Left edge
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
this._leftEdgeDistance = topLeftPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
this._leftEdgeDistance = rendererWidth - topLeftPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._leftEdgeDistance = topLeftPixel[0] / rendererWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Top edge
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
this._topEdgeDistance = topLeftPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
this._topEdgeDistance = rendererHeight - topLeftPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._topEdgeDistance = topLeftPixel[1] / rendererHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bottomRightPixel = layer.convertCoords(
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight()
|
||||
);
|
||||
|
||||
//Right edge
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
this._rightEdgeDistance = rendererWidth - bottomRightPixel[0];
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._rightEdgeDistance = bottomRightPixel[0] / rendererWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Bottom edge
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
this._bottomEdgeDistance = rendererHeight - bottomRightPixel[1];
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] / rendererHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._invalidDistances = false;
|
||||
} else {
|
||||
//Move and resize the object if needed
|
||||
let leftPixel = 0;
|
||||
let topPixel = 0;
|
||||
let rightPixel = 0;
|
||||
let bottomPixel = 0;
|
||||
|
||||
//Left edge
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
leftPixel = this._leftEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
leftPixel = rendererWidth - this._leftEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._leftEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
leftPixel = this._leftEdgeDistance * rendererWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Top edge
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
topPixel = this._topEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
topPixel = rendererHeight - this._topEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._topEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
topPixel = this._topEdgeDistance * rendererHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Right edge
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_LEFT
|
||||
) {
|
||||
rightPixel = this._rightEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.WINDOW_RIGHT
|
||||
) {
|
||||
rightPixel = rendererWidth - this._rightEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._rightEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.HorizontalAnchor.PROPORTIONAL
|
||||
) {
|
||||
rightPixel = this._rightEdgeDistance * rendererWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Bottom edge
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_TOP
|
||||
) {
|
||||
bottomPixel = this._bottomEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.WINDOW_BOTTOM
|
||||
) {
|
||||
bottomPixel = rendererHeight - this._bottomEdgeDistance;
|
||||
} else {
|
||||
if (
|
||||
this._bottomEdgeAnchor ===
|
||||
AnchorRuntimeBehavior.VerticalAnchor.PROPORTIONAL
|
||||
) {
|
||||
bottomPixel = this._bottomEdgeDistance * rendererHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
const topLeftCoord = layer.convertInverseCoords(leftPixel, topPixel);
|
||||
const bottomRightCoord = layer.convertInverseCoords(
|
||||
rightPixel,
|
||||
bottomPixel
|
||||
);
|
||||
|
||||
//Move and resize the object according to the anchors
|
||||
if (
|
||||
this._rightEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
this.owner.setWidth(bottomRightCoord[0] - topLeftCoord[0]);
|
||||
}
|
||||
if (
|
||||
this._bottomEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE
|
||||
) {
|
||||
this.owner.setHeight(bottomRightCoord[1] - topLeftCoord[1]);
|
||||
}
|
||||
if (
|
||||
this._leftEdgeAnchor !== AnchorRuntimeBehavior.HorizontalAnchor.NONE
|
||||
) {
|
||||
this.owner.setX(
|
||||
topLeftCoord[0] + this.owner.getX() - this.owner.getDrawableX()
|
||||
);
|
||||
}
|
||||
if (this._topEdgeAnchor !== AnchorRuntimeBehavior.VerticalAnchor.NONE) {
|
||||
this.owner.setY(
|
||||
topLeftCoord[1] + this.owner.getY() - this.owner.getDrawableY()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doStepPostEvents(runtimeScene) {}
|
||||
|
||||
static HorizontalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_LEFT: 1,
|
||||
WINDOW_RIGHT: 2,
|
||||
PROPORTIONAL: 3,
|
||||
};
|
||||
static VerticalAnchor = {
|
||||
NONE: 0,
|
||||
WINDOW_TOP: 1,
|
||||
WINDOW_BOTTOM: 2,
|
||||
PROPORTIONAL: 3,
|
||||
};
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'AnchorBehavior::AnchorBehavior',
|
||||
gdjs.AnchorRuntimeBehavior
|
||||
);
|
||||
}
|
@@ -29,9 +29,7 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'BBText',
|
||||
_('BBCode Text Object'),
|
||||
_(
|
||||
'Displays a rich text label using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).'
|
||||
),
|
||||
'A BBText is an object displaying on the screen a rich text formatted using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).',
|
||||
'Todor Imreorov',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
|
@@ -1,114 +0,0 @@
|
||||
/**
|
||||
* The PIXI.js renderer for the BBCode Text runtime object.
|
||||
*
|
||||
* @class BBTextRuntimeObjectPixiRenderer
|
||||
* @constructor
|
||||
* @param {gdjs.BBTextRuntimeObject} runtimeObject The object to render
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The gdjs.RuntimeScene in which the object is
|
||||
*/
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
|
||||
this._object = runtimeObject;
|
||||
|
||||
// Load (or reset) the text
|
||||
if (this._pixiObject === undefined) {
|
||||
this._pixiObject = new MultiStyleText(runtimeObject._text, {
|
||||
default: {
|
||||
fontFamily: runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(runtimeObject._fontFamily),
|
||||
fontSize: runtimeObject._fontSize + 'px',
|
||||
fill: runtimeObject._color,
|
||||
tagStyle: 'bbcode',
|
||||
wordWrap: runtimeObject._wordWrap,
|
||||
wordWrapWidth: runtimeObject._wrappingWidth,
|
||||
align: runtimeObject._align,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.updateColor();
|
||||
this.updateAlignment();
|
||||
this.updateFontFamily();
|
||||
this.updateFontSize();
|
||||
}
|
||||
|
||||
runtimeScene
|
||||
.getLayer('')
|
||||
.getRenderer()
|
||||
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
|
||||
|
||||
// Set the anchor in the center, so that the object rotates around
|
||||
// its center
|
||||
this._pixiObject.anchor.x = 0.5;
|
||||
this._pixiObject.anchor.y = 0.5;
|
||||
|
||||
this.updateText();
|
||||
this.updatePosition();
|
||||
this.updateAngle();
|
||||
this.updateOpacity();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectRenderer = gdjs.BBTextRuntimeObjectPixiRenderer;
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getRendererObject = function () {
|
||||
return this._pixiObject;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWordWrap = function () {
|
||||
this._pixiObject._style.wordWrap = this._object._wordWrap;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWrappingWidth = function () {
|
||||
this._pixiObject._style.wordWrapWidth = this._object._wrappingWidth;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function () {
|
||||
this._pixiObject.text = this._object._text;
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function () {
|
||||
this._pixiObject.textStyles.default.fill = this._object._color;
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function () {
|
||||
this._pixiObject._style.align = this._object._align;
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontFamily = function () {
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(this._object._fontFamily);
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontSize = function () {
|
||||
this._pixiObject.textStyles.default.fontSize = this._object._fontSize + 'px';
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updatePosition = function () {
|
||||
this._pixiObject.position.x = this._object.x + this._pixiObject.width / 2;
|
||||
this._pixiObject.position.y = this._object.y + this._pixiObject.height / 2;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAngle = function () {
|
||||
this._pixiObject.rotation = gdjs.toRad(this._object.angle);
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateOpacity = function () {
|
||||
this._pixiObject.alpha = this._object._opacity / 255;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getWidth = function () {
|
||||
return this._pixiObject.width;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getHeight = function () {
|
||||
return this._pixiObject.height;
|
||||
};
|
136
Extensions/BBText/bbtextruntimeobject-pixi-renderer.ts
Normal file
136
Extensions/BBText/bbtextruntimeobject-pixi-renderer.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
declare var MultiStyleText: any;
|
||||
|
||||
namespace gdjs {
|
||||
/**
|
||||
* The PIXI.js renderer for the BBCode Text runtime object.
|
||||
*/
|
||||
export class BBTextRuntimeObjectPixiRenderer {
|
||||
_object: gdjs.BBTextRuntimeObject;
|
||||
_pixiObject: any;
|
||||
|
||||
/**
|
||||
* @param runtimeObject The object to render
|
||||
* @param runtimeScene The gdjs.RuntimeScene in which the object is
|
||||
*/
|
||||
constructor(
|
||||
runtimeObject: gdjs.BBTextRuntimeObject,
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
this._object = runtimeObject;
|
||||
|
||||
// Load (or reset) the text
|
||||
if (this._pixiObject === undefined) {
|
||||
this._pixiObject = new MultiStyleText(runtimeObject._text, {
|
||||
default: {
|
||||
fontFamily: runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(runtimeObject._fontFamily),
|
||||
fontSize: runtimeObject._fontSize + 'px',
|
||||
fill: gdjs.rgbToHexNumber(
|
||||
runtimeObject._color[0],
|
||||
runtimeObject._color[1],
|
||||
runtimeObject._color[2]
|
||||
),
|
||||
tagStyle: 'bbcode',
|
||||
wordWrap: runtimeObject._wordWrap,
|
||||
wordWrapWidth: runtimeObject._wrappingWidth,
|
||||
align: runtimeObject._align,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.updateColor();
|
||||
this.updateAlignment();
|
||||
this.updateFontFamily();
|
||||
this.updateFontSize();
|
||||
}
|
||||
runtimeScene
|
||||
.getLayer('')
|
||||
.getRenderer()
|
||||
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
|
||||
|
||||
// Set the anchor in the center, so that the object rotates around
|
||||
// its center
|
||||
this._pixiObject.anchor.x = 0.5;
|
||||
this._pixiObject.anchor.y = 0.5;
|
||||
this.updateText();
|
||||
this.updatePosition();
|
||||
this.updateAngle();
|
||||
this.updateOpacity();
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return this._pixiObject;
|
||||
}
|
||||
|
||||
updateWordWrap(): void {
|
||||
this._pixiObject._style.wordWrap = this._object._wordWrap;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
updateWrappingWidth(): void {
|
||||
this._pixiObject._style.wordWrapWidth = this._object._wrappingWidth;
|
||||
this._pixiObject.dirty = true;
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
updateText(): void {
|
||||
this._pixiObject.text = this._object._text;
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
updateColor(): void {
|
||||
this._pixiObject.textStyles.default.fill = gdjs.rgbToHexNumber(
|
||||
this._object._color[0],
|
||||
this._object._color[1],
|
||||
this._object._color[2]
|
||||
);
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
updateAlignment(): void {
|
||||
this._pixiObject._style.align = this._object._align;
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
updateFontFamily(): void {
|
||||
this._pixiObject.textStyles.default.fontFamily = this._object._runtimeScene
|
||||
.getGame()
|
||||
.getFontManager()
|
||||
.getFontFamily(this._object._fontFamily);
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
updateFontSize(): void {
|
||||
this._pixiObject.textStyles.default.fontSize =
|
||||
this._object._fontSize + 'px';
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
updatePosition(): void {
|
||||
this._pixiObject.position.x = this._object.x + this._pixiObject.width / 2;
|
||||
this._pixiObject.position.y =
|
||||
this._object.y + this._pixiObject.height / 2;
|
||||
}
|
||||
|
||||
updateAngle(): void {
|
||||
this._pixiObject.rotation = gdjs.toRad(this._object.angle);
|
||||
}
|
||||
|
||||
updateOpacity(): void {
|
||||
this._pixiObject.alpha = this._object._opacity / 255;
|
||||
}
|
||||
|
||||
getWidth(): float {
|
||||
return this._pixiObject.width;
|
||||
}
|
||||
|
||||
getHeight(): float {
|
||||
return this._pixiObject.height;
|
||||
}
|
||||
}
|
||||
|
||||
export const BBTextRuntimeObjectRenderer = BBTextRuntimeObjectPixiRenderer;
|
||||
export type BBTextRuntimeObjectRenderer = BBTextRuntimeObjectPixiRenderer;
|
||||
}
|
@@ -1,257 +0,0 @@
|
||||
/**
|
||||
* @typedef {Object} BBTextObjectDataType Base parameters for {@link gdjs.BBTextRuntimeObject}
|
||||
* @property {Object} content The base parameters of the BBText
|
||||
* @property {number} content.opacity The opacity of the BBText
|
||||
* @property {boolean} content.visible Deprecated - Is the text visible?
|
||||
* @property {string} content.text Content of the text
|
||||
* @property {string} content.color The color of the text
|
||||
* @property {string} content.fontFamily The font of the text
|
||||
* @property {number} content.fontSize The size of the text
|
||||
* @property {boolean} content.wordWrap Activate word wrap if set to true
|
||||
* @property {('left'|'center'|'right')} content.align Alignment of the text: "left", "center" or "right"
|
||||
*
|
||||
* @typedef {ObjectData & BBTextObjectDataType} BBTextObjectData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).
|
||||
* @memberof gdjs
|
||||
* @class BBTextRuntimeObject
|
||||
* @extends RuntimeObject
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to
|
||||
* @param {BBTextObjectData} objectData The object data used to initialize the object
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._opacity = parseFloat(objectData.content.opacity);
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {string} */
|
||||
this._text = objectData.content.text;
|
||||
/** @type {string} */
|
||||
this._color = objectData.content.color;
|
||||
/** @type {string} */
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
/** @type {number} */
|
||||
this._fontSize = parseFloat(objectData.content.fontSize);
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {boolean} */
|
||||
this._wordWrap = objectData.content.wordWrap;
|
||||
/** @type {number} */
|
||||
this._wrappingWidth = 250; // This value is the default wrapping width of the runtime object.
|
||||
/** @type {string} */
|
||||
this._align = objectData.content.align;
|
||||
|
||||
if (this._renderer)
|
||||
gdjs.BBTextRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
|
||||
else
|
||||
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
|
||||
|
||||
// While this should rather be exposed as a property for all objects, honor the "visible"
|
||||
// property that is specific to this object.
|
||||
this.hidden = !objectData.content.visible;
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype = Object.create(
|
||||
gdjs.RuntimeObject.prototype
|
||||
);
|
||||
gdjs.registerObject('BBText::BBText', gdjs.BBTextRuntimeObject);
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {BBTextObjectDataType} oldObjectData
|
||||
* @param {BBTextObjectDataType} newObjectData
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (oldObjectData.content.visible !== newObjectData.content.visible) {
|
||||
this.hide(!newObjectData.content.visible);
|
||||
}
|
||||
if (oldObjectData.content.text !== newObjectData.content.text) {
|
||||
this.setBBText(newObjectData.content.text);
|
||||
}
|
||||
if (oldObjectData.content.color !== newObjectData.content.color) {
|
||||
this._color = newObjectData.content.color;
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
if (oldObjectData.content.fontFamily !== newObjectData.content.fontFamily) {
|
||||
this.setFontFamily(newObjectData.content.fontFamily);
|
||||
}
|
||||
if (oldObjectData.content.fontSize !== newObjectData.content.fontSize) {
|
||||
this.setFontSize(newObjectData.content.fontSize);
|
||||
}
|
||||
if (oldObjectData.content.wordWrap !== newObjectData.content.wordWrap) {
|
||||
this.setWordWrap(newObjectData.content.wordWrap);
|
||||
}
|
||||
if (oldObjectData.content.align !== newObjectData.content.align) {
|
||||
this.setAlignment(newObjectData.content.align);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) {
|
||||
if (initialInstanceData.customSize)
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
else
|
||||
this.setWrappingWidth(250); // This value is the default wrapping width of the runtime object.
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
|
||||
gdjs.RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the markup text to display.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setBBText = function(text) {
|
||||
this._text = text;
|
||||
this._renderer.updateText();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the markup text displayed by the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getBBText = function() {
|
||||
return this._text;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setColor = function(rgbColorString) {
|
||||
const splitValue = rgbColorString.split(';');
|
||||
if (splitValue.length !== 3) return;
|
||||
const hexColor =
|
||||
'#' +
|
||||
gdjs.rgbToHex(
|
||||
parseInt(splitValue[0], 0),
|
||||
parseInt(splitValue[1], 0),
|
||||
parseInt(splitValue[2], 0)
|
||||
);
|
||||
this._color = hexColor;
|
||||
this._renderer.updateColor();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getColor = function() {
|
||||
return this._color;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setFontSize = function(fontSize) {
|
||||
this._fontSize = fontSize;
|
||||
this._renderer.updateFontSize();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getFontSize = function() {
|
||||
return this._fontSize;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setFontFamily = function(fontFamily) {
|
||||
this._fontFamily = fontFamily;
|
||||
this._renderer.updateFontFamily();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getFontFamily = function() {
|
||||
return this._fontFamily;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setAlignment = function(align) {
|
||||
this._align = align;
|
||||
this._renderer.updateAlignment();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getAlignment = function() {
|
||||
return this._align;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object position on X axis.
|
||||
* @param {number} x The new position X of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setX = function(x) {
|
||||
gdjs.RuntimeObject.prototype.setX.call(this, x);
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object position on Y axis.
|
||||
* @param {number} y The new position Y of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setY = function(y) {
|
||||
gdjs.RuntimeObject.prototype.setY.call(this, y);
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the angle of the object.
|
||||
* @param {number} angle The new angle of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setAngle = function(angle) {
|
||||
gdjs.RuntimeObject.prototype.setAngle.call(this, angle);
|
||||
this._renderer.updateAngle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object opacity.
|
||||
* @param {number} opacity The new opacity of the object (0-255).
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setOpacity = function(opacity) {
|
||||
this._opacity = opacity;
|
||||
this._renderer.updateOpacity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get object opacity.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getOpacity = function() {
|
||||
return this._opacity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the width.
|
||||
* @param {number} width The new width in pixels.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.setWrappingWidth = function(width) {
|
||||
this._wrappingWidth = width;
|
||||
this._renderer.updateWrappingWidth();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the wrapping width of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getWrappingWidth = function() {
|
||||
return this._wrappingWidth;
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setWordWrap = function(wordWrap) {
|
||||
this._wordWrap = wordWrap;
|
||||
this._renderer.updateWordWrap();
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getWordWrap = function(wordWrap) {
|
||||
return this._wordWrap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getWidth = function() {
|
||||
return this._renderer.getWidth();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the height of the object.
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getHeight = function() {
|
||||
return this._renderer.getHeight();
|
||||
};
|
281
Extensions/BBText/bbtextruntimeobject.ts
Normal file
281
Extensions/BBText/bbtextruntimeobject.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace gdjs {
|
||||
/** Base parameters for {@link gdjs.BBTextRuntimeObject} */
|
||||
export type BBTextObjectDataType = {
|
||||
/** The base parameters of the BBText */
|
||||
content: {
|
||||
/** The opacity of the BBText */
|
||||
opacity: number;
|
||||
/** Deprecated - Is the text visible? */
|
||||
visible: boolean;
|
||||
/** Content of the text */
|
||||
text: string;
|
||||
/** The color of the text */
|
||||
color: string;
|
||||
/** The font of the text */
|
||||
fontFamily: string;
|
||||
/** The size of the text */
|
||||
fontSize: number;
|
||||
/** Activate word wrap if set to true */
|
||||
wordWrap: boolean;
|
||||
/** Alignment of the text: "left", "center" or "right" */
|
||||
align: 'left' | 'center' | 'right';
|
||||
};
|
||||
};
|
||||
export type BBTextObjectData = ObjectData & BBTextObjectDataType;
|
||||
|
||||
/**
|
||||
* Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).
|
||||
*/
|
||||
export class BBTextRuntimeObject extends gdjs.RuntimeObject {
|
||||
_opacity: float;
|
||||
|
||||
_text: string;
|
||||
|
||||
/** color in format [r, g, b], where each component is in the range [0, 255] */
|
||||
_color: integer[];
|
||||
_fontFamily: string;
|
||||
_fontSize: number;
|
||||
|
||||
_wordWrap: boolean;
|
||||
_wrappingWidth: float = 250;
|
||||
|
||||
// This value is the default wrapping width of the runtime object.
|
||||
_align: string;
|
||||
_renderer: gdjs.BBTextRuntimeObjectRenderer;
|
||||
|
||||
// While this should rather be exposed as a property for all objects, honor the "visible"
|
||||
// property that is specific to this object.
|
||||
hidden: boolean;
|
||||
|
||||
/**
|
||||
* @param runtimeScene The scene the object belongs to.
|
||||
* @param objectData The object data used to initialize the object
|
||||
*/
|
||||
constructor(runtimeScene: gdjs.RuntimeScene, objectData: BBTextObjectData) {
|
||||
super(runtimeScene, objectData);
|
||||
// @ts-ignore - parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
this._opacity = parseFloat(objectData.content.opacity);
|
||||
this._text = objectData.content.text;
|
||||
this._color = BBTextRuntimeObject.hexToRGBColor(objectData.content.color);
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
// @ts-ignore - parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
this._fontSize = parseFloat(objectData.content.fontSize);
|
||||
this._wordWrap = objectData.content.wordWrap;
|
||||
this._align = objectData.content.align;
|
||||
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
|
||||
this.hidden = !objectData.content.visible;
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
static hexToRGBColor(hex) {
|
||||
const hexNumber = parseInt(hex.replace('#', ''), 16);
|
||||
return [(hexNumber >> 16) & 255, (hexNumber >> 8) & 255, hexNumber & 255];
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
updateFromObjectData(
|
||||
oldObjectData: BBTextObjectDataType,
|
||||
newObjectData: BBTextObjectDataType
|
||||
): boolean {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (oldObjectData.content.visible !== newObjectData.content.visible) {
|
||||
this.hide(!newObjectData.content.visible);
|
||||
}
|
||||
if (oldObjectData.content.text !== newObjectData.content.text) {
|
||||
this.setBBText(newObjectData.content.text);
|
||||
}
|
||||
if (oldObjectData.content.color !== newObjectData.content.color) {
|
||||
this._color = BBTextRuntimeObject.hexToRGBColor(
|
||||
newObjectData.content.color
|
||||
);
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.fontFamily !== newObjectData.content.fontFamily
|
||||
) {
|
||||
this.setFontFamily(newObjectData.content.fontFamily);
|
||||
}
|
||||
if (oldObjectData.content.fontSize !== newObjectData.content.fontSize) {
|
||||
this.setFontSize(newObjectData.content.fontSize);
|
||||
}
|
||||
if (oldObjectData.content.wordWrap !== newObjectData.content.wordWrap) {
|
||||
this.setWordWrap(newObjectData.content.wordWrap);
|
||||
}
|
||||
if (oldObjectData.content.align !== newObjectData.content.align) {
|
||||
this.setAlignment(newObjectData.content.align);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
} else {
|
||||
this.setWrappingWidth(
|
||||
// This value is the default wrapping width of the runtime object.
|
||||
250
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroyFromScene(runtimeScene): void {
|
||||
super.onDestroyFromScene(runtimeScene);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the markup text to display.
|
||||
*/
|
||||
setBBText(text): void {
|
||||
this._text = text;
|
||||
this._renderer.updateText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the markup text displayed by the object.
|
||||
*/
|
||||
getBBText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
setColor(rgbColorString): void {
|
||||
const splitValue = rgbColorString.split(';');
|
||||
if (splitValue.length !== 3) {
|
||||
return;
|
||||
}
|
||||
this._color[0] = parseInt(splitValue[0], 10);
|
||||
this._color[1] = parseInt(splitValue[1], 10);
|
||||
this._color[2] = parseInt(splitValue[2], 10);
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base color.
|
||||
* @return The color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
getColor(): string {
|
||||
return this._color[0] + ';' + this._color[1] + ';' + this._color[2];
|
||||
}
|
||||
|
||||
setFontSize(fontSize): void {
|
||||
this._fontSize = fontSize;
|
||||
this._renderer.updateFontSize();
|
||||
}
|
||||
|
||||
getFontSize() {
|
||||
return this._fontSize;
|
||||
}
|
||||
|
||||
setFontFamily(fontFamily): void {
|
||||
this._fontFamily = fontFamily;
|
||||
this._renderer.updateFontFamily();
|
||||
}
|
||||
|
||||
getFontFamily() {
|
||||
return this._fontFamily;
|
||||
}
|
||||
|
||||
setAlignment(align): void {
|
||||
this._align = align;
|
||||
this._renderer.updateAlignment();
|
||||
}
|
||||
|
||||
getAlignment() {
|
||||
return this._align;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on X axis.
|
||||
* @param x The new position X of the object.
|
||||
*/
|
||||
setX(x: float): void {
|
||||
super.setX(x);
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on Y axis.
|
||||
* @param y The new position Y of the object.
|
||||
*/
|
||||
setY(y: float): void {
|
||||
super.setY(y);
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the angle of the object.
|
||||
* @param angle The new angle of the object.
|
||||
*/
|
||||
setAngle(angle: float): void {
|
||||
super.setAngle(angle);
|
||||
this._renderer.updateAngle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object opacity.
|
||||
* @param opacity The new opacity of the object (0-255).
|
||||
*/
|
||||
setOpacity(opacity: float): void {
|
||||
this._opacity = opacity;
|
||||
this._renderer.updateOpacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object opacity.
|
||||
*/
|
||||
getOpacity() {
|
||||
return this._opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width.
|
||||
* @param width The new width in pixels.
|
||||
*/
|
||||
setWrappingWidth(width: float): void {
|
||||
this._wrappingWidth = width;
|
||||
this._renderer.updateWrappingWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapping width of the object.
|
||||
*/
|
||||
getWrappingWidth(): float {
|
||||
return this._wrappingWidth;
|
||||
}
|
||||
|
||||
setWordWrap(wordWrap): void {
|
||||
this._wordWrap = wordWrap;
|
||||
this._renderer.updateWordWrap();
|
||||
}
|
||||
|
||||
getWordWrap(wordWrap) {
|
||||
return this._wordWrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the object.
|
||||
*/
|
||||
getWidth(): float {
|
||||
return this._renderer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the object.
|
||||
*/
|
||||
getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
gdjs.registerObject('BBText::BBText', gdjs.BBTextRuntimeObject);
|
||||
}
|
@@ -48,7 +48,7 @@ module.exports = {
|
||||
.addCodeOnlyParameter("currentScene", "")
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DebuggerTools/debuggertools.js')
|
||||
.setFunctionName('gdjs.evtTools.debugger.pause');
|
||||
.setFunctionName('gdjs.evtTools.debuggerTools.pause');
|
||||
|
||||
return extension;
|
||||
},
|
||||
|
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* Tools for interacting with the debugger.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The namespace containing tools to interact with the debugger.
|
||||
* @namespace
|
||||
*/
|
||||
gdjs.evtTools.debugger = {};
|
||||
|
||||
/**
|
||||
* Stop the game execution.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene - The current scene.
|
||||
*/
|
||||
gdjs.evtTools.debugger.pause = function(runtimeScene) {
|
||||
runtimeScene.getGame().pause(true);
|
||||
}
|
17
Extensions/DebuggerTools/debuggertools.ts
Normal file
17
Extensions/DebuggerTools/debuggertools.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace gdjs {
|
||||
export namespace evtTools {
|
||||
/**
|
||||
* The namespace containing tools to interact with the debugger.
|
||||
* @namespace
|
||||
*/
|
||||
export namespace debuggerTools {
|
||||
/**
|
||||
* Stop the game execution.
|
||||
* @param runtimeScene - The current scene.
|
||||
*/
|
||||
export const pause = function (runtimeScene: gdjs.RuntimeScene) {
|
||||
runtimeScene.getGame().pause(true);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,9 +13,10 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("DestroyOutsideBehavior",
|
||||
_("Destroy Outside Screen Behavior"),
|
||||
_("This Extension can be used to destroy "
|
||||
_("This behavior can be used to destroy "
|
||||
"objects when they go outside of "
|
||||
"the borders of the game's window."),
|
||||
"the bounds of the camera. Useful for bullets "
|
||||
"or other short-lived objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/behaviors/destroyoutside");
|
||||
@@ -81,7 +82,8 @@ class DestroyOutsideBehaviorCppExtension : public ExtensionBase {
|
||||
GetBehaviorMetadata("DestroyOutsideBehavior::DestroyOutside"),
|
||||
"DestroyOutsideRuntimeBehavior");
|
||||
GetBehaviorMetadata("DestroyOutsideBehavior::DestroyOutside")
|
||||
.SetIncludeFile("DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
|
||||
.SetIncludeFile(
|
||||
"DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
|
||||
|
||||
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||
};
|
||||
|
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
GDevelop - DestroyOutside Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The destroyOutsideRuntimeBehavior represents a behavior allowing objects to be
|
||||
* moved using the mouse.
|
||||
*
|
||||
* @class DestroyOutsideRuntimeBehavior
|
||||
* @constructor
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
{
|
||||
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||
|
||||
this._extraBorder = behaviorData.extraBorder || 0;
|
||||
};
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DestroyOutsideBehavior::DestroyOutside", gdjs.DestroyOutsideRuntimeBehavior);
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this._extraBorder = newBehaviorData.extraBorder;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var ow = this.owner.getWidth();
|
||||
var oh = this.owner.getHeight();
|
||||
var ocx = this.owner.getDrawableX()+this.owner.getCenterX();
|
||||
var ocy = this.owner.getDrawableY()+this.owner.getCenterY();
|
||||
var layer = runtimeScene.getLayer(this.owner.getLayer());
|
||||
|
||||
var boundingCircleRadius = Math.sqrt(ow*ow+oh*oh)/2.0;
|
||||
if ( ocx+boundingCircleRadius+this._extraBorder < layer.getCameraX()-layer.getCameraWidth()/2
|
||||
|| ocx-boundingCircleRadius-this._extraBorder > layer.getCameraX()+layer.getCameraWidth()/2
|
||||
|| ocy+boundingCircleRadius+this._extraBorder < layer.getCameraY()-layer.getCameraHeight()/2
|
||||
|| ocy-boundingCircleRadius-this._extraBorder > layer.getCameraY()+layer.getCameraHeight()/2 ) {
|
||||
//We are outside the camera area.
|
||||
this.owner.deleteFromScene(runtimeScene);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an additional border to the camera viewport as a buffer before the object gets destroyed.
|
||||
* @param {number} val Border in pixels.
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.setExtraBorder = function(val) {
|
||||
this._extraBorder = val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the additional border of the camera viewport buffer which triggers the destruction of an object.
|
||||
* @return {number} The additional border around the camera viewport in pixels
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.getExtraBorder = function() {
|
||||
return this._extraBorder;
|
||||
};
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
GDevelop - DestroyOutside Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
namespace gdjs {
|
||||
/**
|
||||
* The DestroyOutsideRuntimeBehavior represents a behavior allowing objects to be
|
||||
* moved using the mouse.
|
||||
*/
|
||||
export class DestroyOutsideRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||
_extraBorder: any;
|
||||
|
||||
constructor(runtimeScene, behaviorData, owner) {
|
||||
super(runtimeScene, behaviorData, owner);
|
||||
this._extraBorder = behaviorData.extraBorder || 0;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this._extraBorder = newBehaviorData.extraBorder;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
doStepPostEvents(runtimeScene) {
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
const ow = this.owner.getWidth();
|
||||
const oh = this.owner.getHeight();
|
||||
const ocx = this.owner.getDrawableX() + this.owner.getCenterX();
|
||||
const ocy = this.owner.getDrawableY() + this.owner.getCenterY();
|
||||
const layer = runtimeScene.getLayer(this.owner.getLayer());
|
||||
const boundingCircleRadius = Math.sqrt(ow * ow + oh * oh) / 2.0;
|
||||
if (
|
||||
ocx + boundingCircleRadius + this._extraBorder <
|
||||
layer.getCameraX() - layer.getCameraWidth() / 2 ||
|
||||
ocx - boundingCircleRadius - this._extraBorder >
|
||||
layer.getCameraX() + layer.getCameraWidth() / 2 ||
|
||||
ocy + boundingCircleRadius + this._extraBorder <
|
||||
layer.getCameraY() - layer.getCameraHeight() / 2 ||
|
||||
ocy - boundingCircleRadius - this._extraBorder >
|
||||
layer.getCameraY() + layer.getCameraHeight() / 2
|
||||
) {
|
||||
//We are outside the camera area.
|
||||
this.owner.deleteFromScene(runtimeScene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an additional border to the camera viewport as a buffer before the object gets destroyed.
|
||||
* @param val Border in pixels.
|
||||
*/
|
||||
setExtraBorder(val: number): void {
|
||||
this._extraBorder = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the additional border of the camera viewport buffer which triggers the destruction of an object.
|
||||
* @return The additional border around the camera viewport in pixels
|
||||
*/
|
||||
getExtraBorder(): number {
|
||||
return this._extraBorder;
|
||||
}
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'DestroyOutsideBehavior::DestroyOutside',
|
||||
gdjs.DestroyOutsideRuntimeBehavior
|
||||
);
|
||||
}
|
@@ -1,215 +0,0 @@
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class deviceSensors
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
|
||||
gdjs.deviceSensors = {
|
||||
orientation: {
|
||||
_isActive: false,
|
||||
_absolute: 0,
|
||||
_alpha: 0,
|
||||
_beta: 0,
|
||||
_gamma: 0
|
||||
},
|
||||
motion: {
|
||||
_isActive: false,
|
||||
_rotationAlpha: 0,
|
||||
_rotationBeta: 0,
|
||||
_rotationGamma: 0,
|
||||
_accelerationX: 0,
|
||||
_accelerationY: 0,
|
||||
_accelerationZ: 0
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate the orientation sensor's listener.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.orientation._activateOrientationListener = function() {
|
||||
window.addEventListener("deviceorientation", gdjs.deviceSensors.orientation._handleOrientation, true);
|
||||
gdjs.deviceSensors.orientation._isActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the orientation sensor's listener.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.orientation._deactivateOrientationListener = function() {
|
||||
window.removeEventListener('deviceorientation', gdjs.deviceSensors.orientation._handleOrientation, true);
|
||||
gdjs.deviceSensors.orientation._isActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orientation sensor event callback function.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.orientation._handleOrientation = function(event) {
|
||||
gdjs.deviceSensors.orientation._absolute = event.absolute ? event.absolute : 0;
|
||||
gdjs.deviceSensors.orientation._alpha = event.alpha ? event.alpha : 0;
|
||||
gdjs.deviceSensors.orientation._beta = event.beta ? event.beta : 0;
|
||||
gdjs.deviceSensors.orientation._gamma = event.gamma ? event.gamma : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the orientation sensor
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.activateOrientationSensor = function() {
|
||||
gdjs.deviceSensors.orientation._activateOrientationListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the orientation sensor
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.deactivateOrientationSensor = function() {
|
||||
gdjs.deviceSensors.orientation._deactivateOrientationListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the orientation sensor is currently active
|
||||
* @return {number} The activation state of the orientation sensor (0=false/1=true)
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.isActive = function() {
|
||||
return gdjs.deviceSensors.orientation._isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's absolute as a number
|
||||
* @return {number} The device orientation's absolute value
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.getOrientationAbsolute = function() {
|
||||
return gdjs.deviceSensors.orientation._absolute;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's alpha as a number (Range: 0 to 360)
|
||||
* @return {number} The device orientation's alpha value
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.getOrientationAlpha = function() {
|
||||
return gdjs.deviceSensors.orientation._alpha;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's beta as a number (Range: -180 to 180)
|
||||
* @return {number} The device orientation's beta value
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.getOrientationBeta = function() {
|
||||
return gdjs.deviceSensors.orientation._beta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's gamma as a number (Range: -90 to 90)
|
||||
* @return {number} The device orientation's gamma value
|
||||
*/
|
||||
gdjs.deviceSensors.orientation.getOrientationGamma = function() {
|
||||
return gdjs.deviceSensors.orientation._gamma;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Activate the motion sensor's listener.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.motion._activateMotionListener = function() {
|
||||
window.addEventListener("devicemotion", gdjs.deviceSensors.motion._handleMotion, true);
|
||||
gdjs.deviceSensors.motion._isActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the motion sensor's listener.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.motion._deactivateMotionListener = function() {
|
||||
window.removeEventListener('devicemotion', gdjs.deviceSensors.motion._handleMotion, true);
|
||||
gdjs.deviceSensors.motion._isActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motion sensor event callback function.
|
||||
* @private
|
||||
*/
|
||||
gdjs.deviceSensors.motion._handleMotion = function(event) {
|
||||
if (event.accelerationIncludingGravity){
|
||||
gdjs.deviceSensors.motion._accelerationX = event.accelerationIncludingGravity.x ? event.accelerationIncludingGravity.x : 0;
|
||||
gdjs.deviceSensors.motion._accelerationY = event.accelerationIncludingGravity.y ? event.accelerationIncludingGravity.y : 0;
|
||||
gdjs.deviceSensors.motion._accelerationZ = event.accelerationIncludingGravity.z ? event.accelerationIncludingGravity.z : 0;
|
||||
}
|
||||
|
||||
if (event.rotationRate){
|
||||
gdjs.deviceSensors.motion._rotationAlpha = event.rotationRate.alpha ? event.rotationRate.alpha : 0;
|
||||
gdjs.deviceSensors.motion._rotationBeta = event.rotationRate.beta ? event.rotationRate.beta : 0;
|
||||
gdjs.deviceSensors.motion._rotationGamma = event.rotationRate.gamma ? event.rotationRate.gamma : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the motion sensor
|
||||
*/
|
||||
gdjs.deviceSensors.motion.activateMotionSensor = function() {
|
||||
gdjs.deviceSensors.motion._activateMotionListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the motion sensor
|
||||
*/
|
||||
gdjs.deviceSensors.motion.deactivateMotionSensor = function() {
|
||||
gdjs.deviceSensors.motion._deactivateMotionListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the motion sensor is currently active
|
||||
* @return {number} The activation state of the motion sensor (0=false/1=true)
|
||||
*/
|
||||
gdjs.deviceSensors.motion.isActive = function() {
|
||||
return gdjs.deviceSensors.motion._isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alpha rotation rate as a number
|
||||
* @return {number} The rotation alpha value
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getRotationAlpha = function() {
|
||||
return gdjs.deviceSensors.motion._rotationAlpha;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the beta rotation rate as a number
|
||||
* @return {number} The rotation beta value
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getRotationBeta = function() {
|
||||
return gdjs.deviceSensors.motion._rotationBeta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gamma rotation rate as a number
|
||||
* @return {number} The rotation gamma value
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getRotationGamma = function() {
|
||||
return gdjs.deviceSensors.motion._rotationGamma;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the X-axis as a number
|
||||
* @return {number} Acceleration on the X-axis
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getAccelerationX = function() {
|
||||
return gdjs.deviceSensors.motion._accelerationX;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the Y-axis as a number
|
||||
* @return {number} Acceleration on the Y-axis
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getAccelerationY = function() {
|
||||
return gdjs.deviceSensors.motion._accelerationY;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the Z-axis as a number
|
||||
* @return {number} Acceleration on the Z-axis
|
||||
*/
|
||||
gdjs.deviceSensors.motion.getAccelerationZ = function() {
|
||||
return gdjs.deviceSensors.motion._accelerationZ;
|
||||
};
|
229
Extensions/DeviceSensors/devicesensortools.ts
Normal file
229
Extensions/DeviceSensors/devicesensortools.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
namespace gdjs {
|
||||
export namespace deviceSensors {
|
||||
export namespace orientation {
|
||||
let _isActive = false;
|
||||
let _absolute = 0;
|
||||
let _alpha = 0;
|
||||
let _beta = 0;
|
||||
let _gamma = 0;
|
||||
|
||||
/**
|
||||
* Activate the orientation sensor's listener.
|
||||
*/
|
||||
export const _activateOrientationListener = function () {
|
||||
window.addEventListener(
|
||||
'deviceorientation',
|
||||
gdjs.deviceSensors.orientation._handleOrientation,
|
||||
true
|
||||
);
|
||||
_isActive = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate the orientation sensor's listener.
|
||||
*/
|
||||
export const _deactivateOrientationListener = function () {
|
||||
window.removeEventListener(
|
||||
'deviceorientation',
|
||||
gdjs.deviceSensors.orientation._handleOrientation,
|
||||
true
|
||||
);
|
||||
_isActive = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Orientation sensor event callback function.
|
||||
*/
|
||||
export const _handleOrientation = function (event) {
|
||||
_absolute = event.absolute ? event.absolute : 0;
|
||||
_alpha = event.alpha ? event.alpha : 0;
|
||||
_beta = event.beta ? event.beta : 0;
|
||||
_gamma = event.gamma ? event.gamma : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate the orientation sensor
|
||||
*/
|
||||
export const activateOrientationSensor = function () {
|
||||
gdjs.deviceSensors.orientation._activateOrientationListener();
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate the orientation sensor
|
||||
*/
|
||||
export const deactivateOrientationSensor = function () {
|
||||
gdjs.deviceSensors.orientation._deactivateOrientationListener();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the orientation sensor is currently active
|
||||
* @return The activation state of the orientation sensor
|
||||
*/
|
||||
export const isActive = function (): boolean {
|
||||
return _isActive;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's absolute as a number
|
||||
* @return The device orientation's absolute value
|
||||
*/
|
||||
export const getOrientationAbsolute = function (): number {
|
||||
return _absolute;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's alpha as a number (Range: 0 to 360)
|
||||
* @return The device orientation's alpha value
|
||||
*/
|
||||
export const getOrientationAlpha = function (): number {
|
||||
return _alpha;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's beta as a number (Range: -180 to 180)
|
||||
* @return The device orientation's beta value
|
||||
*/
|
||||
export const getOrientationBeta = function (): number {
|
||||
return _beta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the device orientation's gamma as a number (Range: -90 to 90)
|
||||
* @return The device orientation's gamma value
|
||||
*/
|
||||
export const getOrientationGamma = function (): number {
|
||||
return _gamma;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace motion {
|
||||
let _isActive = false;
|
||||
let _rotationAlpha = 0;
|
||||
let _rotationBeta = 0;
|
||||
let _rotationGamma = 0;
|
||||
let _accelerationX = 0;
|
||||
let _accelerationY = 0;
|
||||
let _accelerationZ = 0;
|
||||
|
||||
/**
|
||||
* Activate the motion sensor's listener.
|
||||
*/
|
||||
export const _activateMotionListener = function () {
|
||||
window.addEventListener(
|
||||
'devicemotion',
|
||||
gdjs.deviceSensors.motion._handleMotion,
|
||||
true
|
||||
);
|
||||
_isActive = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate the motion sensor's listener.
|
||||
*/
|
||||
export const _deactivateMotionListener = function () {
|
||||
window.removeEventListener(
|
||||
'devicemotion',
|
||||
gdjs.deviceSensors.motion._handleMotion,
|
||||
true
|
||||
);
|
||||
_isActive = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Motion sensor event callback function.
|
||||
*/
|
||||
export const _handleMotion = function (event) {
|
||||
if (event.accelerationIncludingGravity) {
|
||||
_accelerationX = event.accelerationIncludingGravity.x
|
||||
? event.accelerationIncludingGravity.x
|
||||
: 0;
|
||||
_accelerationY = event.accelerationIncludingGravity.y
|
||||
? event.accelerationIncludingGravity.y
|
||||
: 0;
|
||||
_accelerationZ = event.accelerationIncludingGravity.z
|
||||
? event.accelerationIncludingGravity.z
|
||||
: 0;
|
||||
}
|
||||
if (event.rotationRate) {
|
||||
_rotationAlpha = event.rotationRate.alpha
|
||||
? event.rotationRate.alpha
|
||||
: 0;
|
||||
_rotationBeta = event.rotationRate.beta ? event.rotationRate.beta : 0;
|
||||
_rotationGamma = event.rotationRate.gamma
|
||||
? event.rotationRate.gamma
|
||||
: 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate the motion sensor
|
||||
*/
|
||||
export const activateMotionSensor = function () {
|
||||
gdjs.deviceSensors.motion._activateMotionListener();
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate the motion sensor
|
||||
*/
|
||||
export const deactivateMotionSensor = function () {
|
||||
gdjs.deviceSensors.motion._deactivateMotionListener();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the motion sensor is currently active
|
||||
* @return The activation state of the motion sensor
|
||||
*/
|
||||
export const isActive = function (): boolean {
|
||||
return _isActive;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the alpha rotation rate as a number
|
||||
* @return The rotation alpha value
|
||||
*/
|
||||
export const getRotationAlpha = function (): number {
|
||||
return _rotationAlpha;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the beta rotation rate as a number
|
||||
* @return The rotation beta value
|
||||
*/
|
||||
export const getRotationBeta = function (): number {
|
||||
return _rotationBeta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gamma rotation rate as a number
|
||||
* @return The rotation gamma value
|
||||
*/
|
||||
export const getRotationGamma = function (): number {
|
||||
return _rotationGamma;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the X-axis as a number
|
||||
* @return Acceleration on the X-axis
|
||||
*/
|
||||
export const getAccelerationX = function (): number {
|
||||
return _accelerationX;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the Y-axis as a number
|
||||
* @return Acceleration on the Y-axis
|
||||
*/
|
||||
export const getAccelerationY = function (): number {
|
||||
return _accelerationY;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the acceleration value on the Z-axis as a number
|
||||
* @return Acceleration on the Z-axis
|
||||
*/
|
||||
export const getAccelerationZ = function (): number {
|
||||
return _accelerationZ;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,69 +20,86 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
"DeviceVibration",
|
||||
_("Device vibration"),
|
||||
_(
|
||||
"Use the vibration of mobile devices."
|
||||
),
|
||||
"Matthias Meike",
|
||||
"Open source (MIT License)"
|
||||
).setExtensionHelpPath("/all-features/device-vibration");
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'DeviceVibration',
|
||||
_('Device vibration'),
|
||||
|
||||
'This allows to trigger vibrations on mobile devices.',
|
||||
'Matthias Meike',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/device-vibration');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Vibration Cordova Extension')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('cordova-plugin-vibration')
|
||||
.setVersion('3.1.1');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
"StartVibration",
|
||||
_("Vibrate"),
|
||||
_("Vibrate (Duration in ms)."),
|
||||
_("Start vibration for _PARAM0_ ms"),
|
||||
_("Vibration"),
|
||||
"JsPlatform/Extensions/vibration_start24.png",
|
||||
"JsPlatform/Extensions/vibration_start32.png"
|
||||
'StartVibration',
|
||||
_('Vibrate'),
|
||||
_('Vibrate (Duration in ms).'),
|
||||
_('Start vibration for _PARAM0_ ms'),
|
||||
_('Vibration'),
|
||||
'JsPlatform/Extensions/vibration_start24.png',
|
||||
'JsPlatform/Extensions/vibration_start32.png'
|
||||
)
|
||||
.addParameter("expression", _("Duration"), "", false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
"Extensions/DeviceVibration/devicevibrationtools.js"
|
||||
)
|
||||
.setFunctionName("gdjs.deviceVibration.startVibration");
|
||||
.addParameter('expression', _('Duration'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DeviceVibration/devicevibrationtools.js')
|
||||
.setFunctionName('gdjs.deviceVibration.startVibration');
|
||||
|
||||
extension
|
||||
extension
|
||||
.addAction(
|
||||
"StartVibrationPattern",
|
||||
_("Vibrate by pattern"),
|
||||
_("Vibrate (Duration in ms). You can add multiple comma-separated values where every second value determines the period of silence between two vibrations. This is a string value so use quotes."),
|
||||
_("Start vibration for _PARAM0_ ms"),
|
||||
_("Vibration"),
|
||||
"JsPlatform/Extensions/vibration_pattern_start24.png",
|
||||
"JsPlatform/Extensions/vibration_pattern_start32.png"
|
||||
'StartVibrationPattern',
|
||||
_('Vibrate by pattern'),
|
||||
_(
|
||||
'Vibrate (Duration in ms). You can add multiple comma-separated values where every second value determines the period of silence between two vibrations. This is a string value so use quotes.'
|
||||
),
|
||||
_('Start vibration for _PARAM0_ ms'),
|
||||
_('Vibration'),
|
||||
'JsPlatform/Extensions/vibration_pattern_start24.png',
|
||||
'JsPlatform/Extensions/vibration_pattern_start32.png'
|
||||
)
|
||||
.addParameter("string", _("Intervals (for example \"500,100,200\""), "", false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
"Extensions/DeviceVibration/devicevibrationtools.js"
|
||||
.addParameter(
|
||||
'string',
|
||||
_('Intervals (for example "500,100,200"'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.setFunctionName("gdjs.deviceVibration.startVibrationPattern");
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DeviceVibration/devicevibrationtools.js')
|
||||
.setFunctionName('gdjs.deviceVibration.startVibrationPattern');
|
||||
|
||||
extension
|
||||
extension
|
||||
.addAction(
|
||||
"StopVibration",
|
||||
_("Stop vibration"),
|
||||
_("Stop the vibration"),
|
||||
_("Stop vibration"),
|
||||
_("Vibration"),
|
||||
"JsPlatform/Extensions/vibration_stop24.png",
|
||||
"JsPlatform/Extensions/vibration_stop32.png"
|
||||
'StopVibration',
|
||||
_('Stop vibration'),
|
||||
_('Stop the vibration'),
|
||||
_('Stop vibration'),
|
||||
_('Vibration'),
|
||||
'JsPlatform/Extensions/vibration_stop24.png',
|
||||
'JsPlatform/Extensions/vibration_stop32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile(
|
||||
"Extensions/DeviceVibration/devicevibrationtools.js"
|
||||
)
|
||||
.setFunctionName("gdjs.deviceVibration.stopVibration");
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DeviceVibration/devicevibrationtools.js')
|
||||
.setFunctionName('gdjs.deviceVibration.stopVibration');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) { return []; },
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class deviceVibration
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
|
||||
gdjs.deviceVibration = {};
|
||||
|
||||
/**
|
||||
* Vibrate the mobile device.
|
||||
* @param {number} duration Value in milliseconds.
|
||||
*/
|
||||
gdjs.deviceVibration.startVibration = function(duration) {
|
||||
if (typeof navigator == "undefined" || !navigator.vibrate) return
|
||||
|
||||
navigator.vibrate([duration]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate the mobile device in a pattern.
|
||||
* You can add multiple comma separated values where every second one determines the silence between vibrations.
|
||||
* Example: "200,1000,500" (200ms vibration, 1sec silense, 500ms vibration)
|
||||
* @param {string} intervals Comma separated list of values (in ms).
|
||||
*/
|
||||
gdjs.deviceVibration.startVibrationPattern = function(intervals) {
|
||||
const pattern = '^[0-9]+(,[0-9]+)*$'
|
||||
|
||||
if (typeof navigator == "undefined" || !navigator.vibrate) return
|
||||
|
||||
if (intervals.match(pattern)){
|
||||
navigator.vibrate(intervals.split(","));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the current vibration on the mobile device.
|
||||
*/
|
||||
gdjs.deviceVibration.stopVibration = function() {
|
||||
if (typeof navigator == "undefined" || !navigator.vibrate) return
|
||||
|
||||
navigator.vibrate([]);
|
||||
}
|
42
Extensions/DeviceVibration/devicevibrationtools.ts
Normal file
42
Extensions/DeviceVibration/devicevibrationtools.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace gdjs {
|
||||
export namespace deviceVibration {
|
||||
/**
|
||||
* Vibrate the mobile device.
|
||||
* @param duration Value in milliseconds.
|
||||
*/
|
||||
export const startVibration = function (duration: number) {
|
||||
if (typeof navigator == 'undefined' || !navigator.vibrate) {
|
||||
return;
|
||||
}
|
||||
navigator.vibrate([duration]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Vibrate the mobile device in a pattern.
|
||||
* You can add multiple comma separated values where every second one determines the silence between vibrations.
|
||||
* Example: "200,1000,500" (200ms vibration, 1sec silense, 500ms vibration)
|
||||
* @param intervals Comma separated list of values (in ms).
|
||||
*/
|
||||
export const startVibrationPattern = function (intervals: string) {
|
||||
const pattern = '^[0-9]+(,[0-9]+)*$';
|
||||
if (typeof navigator == 'undefined' || !navigator.vibrate) {
|
||||
return;
|
||||
}
|
||||
if (intervals.match(pattern)) {
|
||||
navigator.vibrate(
|
||||
intervals.split(',').map((duration) => parseFloat(duration))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the current vibration on the mobile device.
|
||||
*/
|
||||
export const stopVibration = function () {
|
||||
if (typeof navigator == 'undefined' || !navigator.vibrate) {
|
||||
return;
|
||||
}
|
||||
navigator.vibrate([]);
|
||||
};
|
||||
}
|
||||
}
|
@@ -20,15 +20,16 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'DialogueTree',
|
||||
_('Dialogue Tree (Experimental)'),
|
||||
_(
|
||||
'Start dialogue trees, made using Yarn, powered by Bondage.js. Experimental extension that can change in the future.'
|
||||
),
|
||||
'Handle dialogue trees, made using Yarn Spinner. Useful to make complex dialogues with multiple choices. The Yarn Spinner editor is embedded in GDevelop so you can edit your dialogues without leaving GDevelop.',
|
||||
'Todor Imreorov',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
@@ -431,8 +432,12 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'BranchTag',
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_(
|
||||
'Get a tag of the current branch of the running dialogue via its index'
|
||||
),
|
||||
_(
|
||||
'Get a tag of the current branch of the running dialogue via its index'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
@@ -712,7 +717,10 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
@@ -1,735 +0,0 @@
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class dialogueTree
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
|
||||
gdjs.dialogueTree = {};
|
||||
gdjs.dialogueTree.runner = new bondage.Runner();
|
||||
|
||||
/**
|
||||
* Load the Dialogue Tree data of the game. Initialize The Dialogue Tree, so as it can be used in the game.
|
||||
* @param {gdjs.Variable} sceneVar The variable to load the Dialogue tree data from. The data is a JSON string, created by Yarn.
|
||||
* @param {string} startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
|
||||
*/
|
||||
gdjs.dialogueTree.loadFromSceneVariable = function(
|
||||
sceneVar,
|
||||
startDialogueNode
|
||||
) {
|
||||
this.runner = gdjs.dialogueTree.runner;
|
||||
|
||||
try {
|
||||
this.yarnData = JSON.parse(sceneVar.getAsString());
|
||||
this.runner.load(this.yarnData);
|
||||
|
||||
if (startDialogueNode && startDialogueNode.length > 0) {
|
||||
gdjs.dialogueTree.startFrom(startDialogueNode);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the Dialogue Tree data from a JSON resource.
|
||||
*
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene where the dialogue is running.
|
||||
* @param {string} jsonResourceName The JSON resource where to load the Dialogue Tree data from. The data is a JSON string usually created with [Yarn Dialogue Editor](https://github.com/InfiniteAmmoInc/Yarn).
|
||||
* @param {string} startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
|
||||
*/
|
||||
gdjs.dialogueTree.loadFromJsonFile = function(
|
||||
runtimeScene,
|
||||
jsonResourceName,
|
||||
startDialogueNode
|
||||
) {
|
||||
runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(jsonResourceName, function(error, content) {
|
||||
if (error) {
|
||||
console.error('An error happened while loading JSON resource:', error);
|
||||
} else {
|
||||
if (!content) return;
|
||||
gdjs.dialogueTree.yarnData = content;
|
||||
|
||||
try {
|
||||
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'An error happened while loading parsing the dialogue tree data:',
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
if (startDialogueNode && startDialogueNode.length > 0) {
|
||||
gdjs.dialogueTree.startFrom(startDialogueNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the currently running dialogue
|
||||
*/
|
||||
gdjs.dialogueTree.stopRunningDialogue = function() {
|
||||
if (this.dialogueIsRunning) this.dialogueIsRunning = false;
|
||||
if (this.dialogueData) this.dialogueData = null;
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the Dialogue Tree is currently parsing data.
|
||||
* For example, you can do things like disabling player movement while talking to a NPC.
|
||||
*/
|
||||
gdjs.dialogueTree.isRunning = function() {
|
||||
if (
|
||||
this.dialogueIsRunning &&
|
||||
!this.dialogueData &&
|
||||
this.dialogueText &&
|
||||
this.clipTextEnd >= this.dialogueText.length
|
||||
) {
|
||||
this.dialogueIsRunning = false;
|
||||
}
|
||||
return this.dialogueIsRunning;
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the clipped text. This can be combined with a timer and user input to control how fast the dialogue line text is scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.scrollClippedText = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) return;
|
||||
|
||||
// Autoscroll commands so the user doesnt have to press again
|
||||
if (
|
||||
gdjs.dialogueTree._isLineTypeCommand() &&
|
||||
this.dialogueDataType === 'text' &&
|
||||
this.dialogueBranchTitle === this.dialogueData.data.title &&
|
||||
this.lineNum === this.dialogueData.lineNum &&
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted()
|
||||
) {
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
return
|
||||
}
|
||||
|
||||
// Increment scrolling of clipped text
|
||||
if (this.dialogueText && this.dialogueDataType === 'text' && this.clipTextEnd < this.dialogueText.length) {
|
||||
this.clipTextEnd += 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
|
||||
*/
|
||||
gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText || this.dialogueDataType !== 'text')
|
||||
return;
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if text scrolling has completed.
|
||||
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
|
||||
*/
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted = function() {
|
||||
if (!this.dialogueIsRunning || this.dialogueDataType === '') return false;
|
||||
|
||||
if (this.dialogueData && this.dialogueText.length > 0 && this.clipTextEnd >= this.dialogueText.length) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.warn('Scroll completed:', this.clipTextEnd,'/', this.dialogueText.length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current dialogue line with a scrolling effect (recommended).
|
||||
* Used with the scrollClippedText to achieve a classic scrolling text, as well as any <<wait>> effects to pause scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.getClippedLineText = function() {
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd + 1)
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current complete dialogue line without using any scrolling effects.
|
||||
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineText = function() {
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of command parameters in a command with parameters that has been caught by a isCommandCalled condition
|
||||
*/
|
||||
gdjs.dialogueTree.commandParametersCount = function() {
|
||||
if (this.commandParameters && this.commandParameters.length > 1) {
|
||||
return this.commandParameters.length - 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a command parameter in any command with parameters that has been caught by a isCommandCalled condition
|
||||
* @param {number} paramIndex The index of the parameter to get.
|
||||
*/
|
||||
gdjs.dialogueTree.getCommandParameter = function(paramIndex) {
|
||||
if (paramIndex === -1 && this.commandParameters.length > 0) return this.commandParameters[0];
|
||||
if (
|
||||
this.commandParameters &&
|
||||
this.commandParameters.length >= paramIndex + 1
|
||||
) {
|
||||
var returnedParam = this.commandParameters[paramIndex + 1];
|
||||
return returnedParam ? returnedParam : '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch <<commands>> and <<commands with parameters>> from the current Dialogue Line.
|
||||
* You can trigger custom logic that relate to the story you are telling during the dialogue.
|
||||
*
|
||||
* @param {string} command The command you want to check for being called. Write it without the `<<>>`.
|
||||
*/
|
||||
gdjs.dialogueTree.isCommandCalled = function(command) {
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
|
||||
var commandCalls = gdjs.dialogueTree.commandCalls;
|
||||
var clipTextEnd = gdjs.dialogueTree.clipTextEnd;
|
||||
var dialogueText = gdjs.dialogueTree.dialogueText;
|
||||
|
||||
if (this.pauseScrolling || !commandCalls) return false;
|
||||
return this.commandCalls.some(function(call, index) {
|
||||
if (clipTextEnd !== 0 && clipTextEnd < call.time) return false;
|
||||
if (call.cmd === 'wait' && (clipTextEnd === 0 || clipTextEnd !== dialogueText.length)) {
|
||||
gdjs.dialogueTree.pauseScrolling = true;
|
||||
setTimeout(function() {
|
||||
gdjs.dialogueTree.pauseScrolling = false;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
}, parseInt(call.params[1], 10));
|
||||
}
|
||||
if (call.cmd === command) {
|
||||
gdjs.dialogueTree.commandParameters = call.params;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('CMD:', call);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal method to allow for capping option selection.
|
||||
* @private
|
||||
*/
|
||||
gdjs.dialogueTree._normalizedOptionIndex = function(optionIndex) {
|
||||
if (optionIndex >= this.options.length) optionIndex = this.options.length - 1;
|
||||
if (optionIndex < 0) optionIndex = 0;
|
||||
return optionIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal method to allow for cycling option selection.
|
||||
* @private
|
||||
*/
|
||||
gdjs.dialogueTree._cycledOptionIndex = function(optionIndex) {
|
||||
if (optionIndex >= this.options.length) optionIndex = 0;
|
||||
if (optionIndex < 0) optionIndex = this.options.length - 1;
|
||||
return optionIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text of an option the player can select.
|
||||
* Used with getLineOptionsCount to render options for the player when a line of the Options type is parsed
|
||||
* @param {number} optionIndex The index of the option you want to get
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOption = function(optionIndex) {
|
||||
if (!this.dialogueIsRunning || !this.options.length) return [];
|
||||
optionIndex = gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
|
||||
return this.options[optionIndex];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text of the options the player can select, along with the selection cursor.
|
||||
* @param {string} optionSelectionCursor The string used to draw the currently selected option's cursor
|
||||
* @param {boolean} addNewLine when true each option is rendered on a new line.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsText = function(
|
||||
optionSelectionCursor,
|
||||
addNewLine
|
||||
) {
|
||||
if (!this.dialogueIsRunning || !this.options.length) return '';
|
||||
var textResult = '';
|
||||
this.options.forEach(function(optionText, index) {
|
||||
if (index === gdjs.dialogueTree.selectedOption) {
|
||||
textResult += optionSelectionCursor;
|
||||
} else {
|
||||
textResult += optionSelectionCursor.replace(/.*/g, ' ');
|
||||
}
|
||||
textResult += optionText;
|
||||
if (addNewLine) textResult += '\n';
|
||||
});
|
||||
return textResult;
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextHorizontal = function(
|
||||
optionSelectionCursor
|
||||
) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, false);
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextVertical = function(optionSelectionCursor) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of options that are presented to the player, during the parsing of an Options type line.
|
||||
* @returns {number} The number of options
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsCount = function() {
|
||||
if (this.dialogueIsRunning && this.options.length) {
|
||||
return this.optionsCount;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Confirm the currently selected option, during the parsing of an Options type line.
|
||||
*
|
||||
* This will advance the dialogue tree to the dialogue branch was selected by the player.
|
||||
*/
|
||||
gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (
|
||||
this.dialogueData.select &&
|
||||
!this.selectedOptionUpdated &&
|
||||
this.selectedOption !== -1
|
||||
) {
|
||||
this.commandCalls = [];
|
||||
try {
|
||||
this.dialogueData.select(this.selectedOption);
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`An error happened when trying to access the dialogue branch!`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select next option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectNextOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption += 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
this.selectedOption
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select previous option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectPreviousOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption -= 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
this.selectedOption
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select option by index during Options type line parsing.
|
||||
* @param {number} optionIndex The index of the option to select
|
||||
*/
|
||||
gdjs.dialogueTree.selectOption = function(optionIndex) {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
|
||||
optionIndex
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently selected option
|
||||
* @returns {number} The index of the currently selected option
|
||||
*/
|
||||
gdjs.dialogueTree.getSelectedOption = function() {
|
||||
if (!this.dialogueIsRunning) return;
|
||||
if (this.dialogueData.select) {
|
||||
return this.selectedOption;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check when the player has changed option selection since the last call to this function.
|
||||
*
|
||||
* Can be used to re-render your displayed dialogue options when needed.
|
||||
*
|
||||
* @returns {boolean} true if the selected option was updated since the last call to this function
|
||||
*/
|
||||
gdjs.dialogueTree.hasSelectedOptionChanged = function() {
|
||||
if (this.selectedOptionUpdated) {
|
||||
this.selectedOptionUpdated = false;
|
||||
if (this.selectedOption === -1) this.selectedOption = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the type of the Dialogue Line that is being displayed to the player at the moment.
|
||||
*
|
||||
* There are three types:
|
||||
* - text - regular dialogue text is being parsed at the moment
|
||||
* - options - the player has reached a branching choise moment where they must select one of multiple options
|
||||
* - command - a <<command>> was called in the background, that can be used to trigger game events, but will not be displayed in the dialogue box.
|
||||
*
|
||||
* @param {string} type The type you want to check for ( one of the three above )
|
||||
*/
|
||||
gdjs.dialogueTree.isDialogueLineType = function(type) {
|
||||
if (!this.dialogueIsRunning) return false;
|
||||
if (this.commandCalls && type === 'command') {
|
||||
if (
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
}
|
||||
if (this.commandCalls.length > 0 && this.commandParameters.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.dialogueDataType === type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a branch exists. It is also used internaly whenever you use the start from action.
|
||||
* @param {string} branchName The Dialogue Branch name you want to check.
|
||||
*/
|
||||
gdjs.dialogueTree.hasDialogueBranch = function(branchName) {
|
||||
return (
|
||||
this.runner &&
|
||||
this.runner.yarnNodes &&
|
||||
Object.keys(this.runner.yarnNodes).some(function(node) {
|
||||
return node === branchName;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start parsing dialogue from a specified Dialogue tree branch.
|
||||
* Can be used if you want to store multiple dialogues inside a single Dialogue tree data set.
|
||||
* @param {string} startDialogueNode The Dialogue Branch name you want to start parsing from.
|
||||
*/
|
||||
gdjs.dialogueTree.startFrom = function(startDialogueNode) {
|
||||
this.runner = gdjs.dialogueTree.runner;
|
||||
if (!this.hasDialogueBranch(startDialogueNode)) return;
|
||||
this.optionsCount = 0;
|
||||
this.options = [];
|
||||
this.tagParameters = [];
|
||||
this.dialogue = this.runner.run(startDialogueNode);
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.commandCalls = [];
|
||||
this.commandParameters = [];
|
||||
this.pauseScrolling = false;
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
if (gdjs.dialogueTree._isLineTypeText()){
|
||||
this.dialogueDataType = 'text';
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()){
|
||||
this.dialogueDataType = 'options';
|
||||
} else {
|
||||
this.dialogueDataType = 'command';
|
||||
};
|
||||
|
||||
this.dialogueIsRunning = true;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal methods to check the type of a Dialogue Line
|
||||
*/
|
||||
gdjs.dialogueTree._isLineTypeText = function() {
|
||||
return this.dialogueData instanceof bondage.TextResult;
|
||||
};
|
||||
gdjs.dialogueTree._isLineTypeOptions = function() {
|
||||
return this.dialogueData instanceof bondage.OptionsResult;
|
||||
};
|
||||
gdjs.dialogueTree._isLineTypeCommand = function() {
|
||||
return this.dialogueData instanceof bondage.CommandResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the main lifecycle function.It runs once only when the user is advancing the dialogue to the next line.
|
||||
* Progress Dialogue to the next line. Hook it to your game input.
|
||||
* Note that this action can be influenced by any <<wait>> commands, but they work only if you have at least one isCommandCalled condition.
|
||||
*/
|
||||
gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) return;
|
||||
|
||||
this.optionsCount = 0;
|
||||
this.selectedOption = -1;
|
||||
this.selectedOptionUpdated = false;
|
||||
|
||||
if (gdjs.dialogueTree.getVariable('debug')) console.info('parsing:', this.dialogueData);
|
||||
|
||||
if (!this.dialogueData) {
|
||||
gdjs.dialogueTree.stopRunningDialogue();
|
||||
} else if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (this.lineNum === this.dialogueData.lineNum && this.dialogueBranchTitle === this.dialogueData.data.title){
|
||||
this.clipTextEnd = this.dialogueText.length - 1;
|
||||
this.dialogueText +=
|
||||
(this.dialogueText === '' ? '' : ' ') + this.dialogueData.text;
|
||||
} else {
|
||||
this.clipTextEnd = 0;
|
||||
this.dialogueText = this.dialogueData.text;
|
||||
}
|
||||
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
this.dialogueDataType = 'text';
|
||||
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
} else if (gdjs.dialogueTree._isLineTypeOptions()) {
|
||||
this.commandCalls = [];
|
||||
this.dialogueDataType = 'options';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.optionsCount = this.dialogueData.options.length;
|
||||
this.options = this.dialogueData.options;
|
||||
this.selectedOptionUpdated = true;
|
||||
} else if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
var command = this.dialogueData.text.split(' ');
|
||||
// If last command was to wait, increase time by one
|
||||
var offsetTime =
|
||||
this.commandCalls.length &&
|
||||
this.commandCalls[this.commandCalls.length - 1].cmd === 'wait'
|
||||
? 1
|
||||
: 0;
|
||||
this.commandCalls.push({
|
||||
cmd: command[0],
|
||||
params: command,
|
||||
time: this.dialogueText.length + offsetTime,
|
||||
});
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
} else {
|
||||
this.dialogueDataType = 'unknown';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current Dialogue Tree branch title.
|
||||
* @returns {string} The current branch title.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTitle = function() {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTitle;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the currently parsed Dialogue branch title is a query.
|
||||
* @param {string} title The Dialogue Branch name you want to check for.
|
||||
*/
|
||||
gdjs.dialogueTree.branchTitleIs = function(title) {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTitle === title;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all the branch tags from the current Dialogue branch as a string. Useful for debugging.
|
||||
* @returns {string} The current branch tags, separated by a comma.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTags = function() {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTags.join(',');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get one of the current Dialogue branch tags via index.
|
||||
* @param {number} index The index of the Dialogue Branch tag you want to get.
|
||||
* @returns {string} The branch tag at the specified index, or an empty string if not found.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTag = function(index) {
|
||||
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
|
||||
if (index > this.dialogueBranchTags.length - 1)
|
||||
index = this.dialogueBranchTags.length - 1;
|
||||
return this.dialogueBranchTags[index];
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current Dialogue branch contains a specific tag.
|
||||
* @param {string} query The name of the Dialogue Branch tag you want to check.
|
||||
*/
|
||||
gdjs.dialogueTree.branchContainsTag = function(query) {
|
||||
this.tagParameters = [];
|
||||
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
|
||||
return this.dialogueBranchTags.some(function(tag) {
|
||||
var splitTag = tag.match(/([^\(]+)\(([^\)]+)\)/i);
|
||||
gdjs.dialogueTree.tagParameters = splitTag ? splitTag[2].split(',') : [];
|
||||
return splitTag ? splitTag[1] === query : tag === query;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get any tag(parameter,anotherParameter) from a tag captured by the branchContainsTag Condition
|
||||
* @param {number} paramIndex The index of the tag parameter you want to get.
|
||||
* Leaving this empty will result in retrieving the first parameter.
|
||||
*/
|
||||
gdjs.dialogueTree.getTagParameter = function(paramIndex) {
|
||||
if (this.dialogueIsRunning && this.tagParameters.length >= paramIndex) {
|
||||
var returnedParam = this.tagParameters[paramIndex];
|
||||
return returnedParam ? returnedParam : '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of all the titles of visited by the player Branches. Useful for debugging.
|
||||
*/
|
||||
gdjs.dialogueTree.getVisitedBranchTitles = function() {
|
||||
if (this.dialogueIsRunning) {
|
||||
return Object.keys(this.runner.visited).join(',');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a player has visited a Dialogue Branch in the past.
|
||||
* @param {string} title The title of the branch to check for.
|
||||
* Leaving this empty will check if the current branch title has been visited in the past.
|
||||
*/
|
||||
gdjs.dialogueTree.branchTitleHasBeenVisited = function(title) {
|
||||
if (!title) title = this.dialogueBranchTitle;
|
||||
return (
|
||||
Object.keys(this.runner.visited).includes(title) &&
|
||||
this.runner.visited[title]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the entire unparsed text of the current Dialogue Branch
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchText = function() {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchBody;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of a variable that was created by the Dialogue parses.
|
||||
* @param {string} key The name of the variable you want to get the value of
|
||||
*/
|
||||
gdjs.dialogueTree.getVariable = function(key) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.get(key);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a specific variable created by the Dialogue parses exists and is equal to a specific value.
|
||||
* @param {string} key The name of the variable you want to check the value of
|
||||
* @param {string|boolean|number} value The value you want to check against
|
||||
*/
|
||||
gdjs.dialogueTree.compareVariable = function(key, value) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.get(key) === value;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a specific variable created by the Dialogue parser to a specific value.
|
||||
* @param {string} key The name of the variable you want to set the value of
|
||||
* @param {string|boolean|number} value The value you want to set
|
||||
*/
|
||||
gdjs.dialogueTree.setVariable = function(key, value) {
|
||||
if (this.runner.variables) {
|
||||
this.runner.variables.set(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the current State of the Dialogue Parser in a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param {gdjs.Variable} outputVariable The variable where to store the State
|
||||
*/
|
||||
gdjs.dialogueTree.saveState = function(outputVariable) {
|
||||
var dialogueState = {
|
||||
variables: gdjs.dialogueTree.runner.variables.data,
|
||||
visited: gdjs.dialogueTree.runner.visited,
|
||||
};
|
||||
gdjs.evtTools.network._objectToVariable(dialogueState, outputVariable);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the current State of the Dialogue Parser from a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param {gdjs.Variable} inputVariable The structured variable where to load the State from.
|
||||
*/
|
||||
gdjs.dialogueTree.loadState = function(inputVariable) {
|
||||
var loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
if (!loadedState) {
|
||||
console.error('Load state variable is empty:', inputVariable);
|
||||
return
|
||||
}
|
||||
try {
|
||||
gdjs.dialogueTree.runner.visited = loadedState.visited;
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
Object.keys(loadedState.variables).forEach(function(key) {
|
||||
var value = loadedState.variables[key];
|
||||
gdjs.dialogueTree.runner.variables.set(key, value);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to load state from variable:', inputVariable, e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current State of the Dialogue Parser.
|
||||
*/
|
||||
gdjs.dialogueTree.clearState = function() {
|
||||
gdjs.dialogueTree.runner.visited = {};
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
};
|
824
Extensions/DialogueTree/dialoguetools.ts
Normal file
824
Extensions/DialogueTree/dialoguetools.ts
Normal file
@@ -0,0 +1,824 @@
|
||||
// @ts-nocheck - Weird usage of `this` in this file. Should be refactored.
|
||||
|
||||
namespace gdjs {
|
||||
gdjs.dialogueTree = {};
|
||||
gdjs.dialogueTree.runner = new bondage.Runner();
|
||||
|
||||
/**
|
||||
* Load the Dialogue Tree data of the game. Initialize The Dialogue Tree, so as it can be used in the game.
|
||||
* @param sceneVar The variable to load the Dialogue tree data from. The data is a JSON string, created by Yarn.
|
||||
* @param startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
|
||||
*/
|
||||
gdjs.dialogueTree.loadFromSceneVariable = function (
|
||||
sceneVar: gdjs.Variable,
|
||||
startDialogueNode: string
|
||||
) {
|
||||
this.runner = gdjs.dialogueTree.runner;
|
||||
try {
|
||||
this.yarnData = JSON.parse(sceneVar.getAsString());
|
||||
this.runner.load(this.yarnData);
|
||||
if (startDialogueNode && startDialogueNode.length > 0) {
|
||||
gdjs.dialogueTree.startFrom(startDialogueNode);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the Dialogue Tree data from a JSON resource.
|
||||
*
|
||||
* @param runtimeScene The scene where the dialogue is running.
|
||||
* @param jsonResourceName The JSON resource where to load the Dialogue Tree data from. The data is a JSON string usually created with [Yarn Dialogue Editor](https://github.com/InfiniteAmmoInc/Yarn).
|
||||
* @param startDialogueNode The Dialogue Branch to start the Dialogue Tree from. If left empty, the data will only be loaded, but can later be initialized via another action
|
||||
*/
|
||||
gdjs.dialogueTree.loadFromJsonFile = function (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
jsonResourceName: string,
|
||||
startDialogueNode: string
|
||||
) {
|
||||
runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(jsonResourceName, function (error, content) {
|
||||
if (error) {
|
||||
console.error(
|
||||
'An error happened while loading JSON resource:',
|
||||
error
|
||||
);
|
||||
} else {
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
gdjs.dialogueTree.yarnData = content;
|
||||
try {
|
||||
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'An error happened while loading parsing the dialogue tree data:',
|
||||
error
|
||||
);
|
||||
}
|
||||
if (startDialogueNode && startDialogueNode.length > 0) {
|
||||
gdjs.dialogueTree.startFrom(startDialogueNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the currently running dialogue
|
||||
*/
|
||||
gdjs.dialogueTree.stopRunningDialogue = function () {
|
||||
if (this.dialogueIsRunning) {
|
||||
this.dialogueIsRunning = false;
|
||||
}
|
||||
if (this.dialogueData) {
|
||||
this.dialogueData = null;
|
||||
}
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the Dialogue Tree is currently parsing data.
|
||||
* For example, you can do things like disabling player movement while talking to a NPC.
|
||||
*/
|
||||
gdjs.dialogueTree.isRunning = function () {
|
||||
if (
|
||||
this.dialogueIsRunning &&
|
||||
!this.dialogueData &&
|
||||
this.dialogueText &&
|
||||
this.clipTextEnd >= this.dialogueText.length
|
||||
) {
|
||||
this.dialogueIsRunning = false;
|
||||
}
|
||||
return this.dialogueIsRunning;
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the clipped text. This can be combined with a timer and user input to control how fast the dialogue line text is scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.scrollClippedText = function () {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Autoscroll commands so the user doesnt have to press again
|
||||
if (
|
||||
gdjs.dialogueTree._isLineTypeCommand() &&
|
||||
this.dialogueDataType === 'text' &&
|
||||
this.dialogueBranchTitle === this.dialogueData.data.title &&
|
||||
this.lineNum === this.dialogueData.lineNum &&
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted()
|
||||
) {
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Increment scrolling of clipped text
|
||||
if (
|
||||
this.dialogueText &&
|
||||
this.dialogueDataType === 'text' &&
|
||||
this.clipTextEnd < this.dialogueText.length
|
||||
) {
|
||||
this.clipTextEnd += 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
|
||||
*/
|
||||
gdjs.dialogueTree.completeClippedTextScrolling = function () {
|
||||
if (
|
||||
this.pauseScrolling ||
|
||||
!this.dialogueIsRunning ||
|
||||
!this.dialogueText ||
|
||||
this.dialogueDataType !== 'text'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if text scrolling has completed.
|
||||
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
|
||||
*/
|
||||
gdjs.dialogueTree.hasClippedScrollingCompleted = function () {
|
||||
if (!this.dialogueIsRunning || this.dialogueDataType === '') {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.dialogueData &&
|
||||
this.dialogueText.length > 0 &&
|
||||
this.clipTextEnd >= this.dialogueText.length
|
||||
) {
|
||||
if (gdjs.dialogueTree.getVariable('debug')) {
|
||||
console.warn(
|
||||
'Scroll completed:',
|
||||
this.clipTextEnd,
|
||||
'/',
|
||||
this.dialogueText.length
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current dialogue line with a scrolling effect (recommended).
|
||||
* Used with the scrollClippedText to achieve a classic scrolling text, as well as any <<wait>> effects to pause scrolling.
|
||||
*/
|
||||
gdjs.dialogueTree.getClippedLineText = function () {
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText.substring(0, this.clipTextEnd + 1)
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current complete dialogue line without using any scrolling effects.
|
||||
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineText = function () {
|
||||
return this.dialogueIsRunning && this.dialogueText.length
|
||||
? this.dialogueText
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of command parameters in a command with parameters that has been caught by a isCommandCalled condition
|
||||
*/
|
||||
gdjs.dialogueTree.commandParametersCount = function () {
|
||||
if (this.commandParameters && this.commandParameters.length > 1) {
|
||||
return this.commandParameters.length - 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a command parameter in any command with parameters that has been caught by a isCommandCalled condition
|
||||
* @param paramIndex The index of the parameter to get.
|
||||
*/
|
||||
gdjs.dialogueTree.getCommandParameter = function (paramIndex: float) {
|
||||
if (paramIndex === -1 && this.commandParameters.length > 0) {
|
||||
return this.commandParameters[0];
|
||||
}
|
||||
if (
|
||||
this.commandParameters &&
|
||||
this.commandParameters.length >= paramIndex + 1
|
||||
) {
|
||||
const returnedParam = this.commandParameters[paramIndex + 1];
|
||||
return returnedParam ? returnedParam : '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch <<commands>> and <<commands with parameters>> from the current Dialogue Line.
|
||||
* You can trigger custom logic that relate to the story you are telling during the dialogue.
|
||||
*
|
||||
* @param command The command you want to check for being called. Write it without the `<<>>`.
|
||||
*/
|
||||
gdjs.dialogueTree.isCommandCalled = function (command: string) {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return false;
|
||||
}
|
||||
const commandCalls = gdjs.dialogueTree.commandCalls;
|
||||
const clipTextEnd = gdjs.dialogueTree.clipTextEnd;
|
||||
const dialogueText = gdjs.dialogueTree.dialogueText;
|
||||
if (this.pauseScrolling || !commandCalls) {
|
||||
return false;
|
||||
}
|
||||
return this.commandCalls.some(function (call, index) {
|
||||
if (clipTextEnd !== 0 && clipTextEnd < call.time) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
call.cmd === 'wait' &&
|
||||
(clipTextEnd === 0 || clipTextEnd !== dialogueText.length)
|
||||
) {
|
||||
gdjs.dialogueTree.pauseScrolling = true;
|
||||
setTimeout(function () {
|
||||
gdjs.dialogueTree.pauseScrolling = false;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) {
|
||||
console.info('CMD:', call);
|
||||
}
|
||||
}, parseInt(call.params[1], 10));
|
||||
}
|
||||
if (call.cmd === command) {
|
||||
gdjs.dialogueTree.commandParameters = call.params;
|
||||
commandCalls.splice(index, 1);
|
||||
if (gdjs.dialogueTree.getVariable('debug')) {
|
||||
console.info('CMD:', call);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal method to allow for capping option selection.
|
||||
*/
|
||||
gdjs.dialogueTree._normalizedOptionIndex = function (optionIndex) {
|
||||
if (optionIndex >= this.options.length) {
|
||||
optionIndex = this.options.length - 1;
|
||||
}
|
||||
if (optionIndex < 0) {
|
||||
optionIndex = 0;
|
||||
}
|
||||
return optionIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal method to allow for cycling option selection.
|
||||
*/
|
||||
gdjs.dialogueTree._cycledOptionIndex = function (optionIndex) {
|
||||
if (optionIndex >= this.options.length) {
|
||||
optionIndex = 0;
|
||||
}
|
||||
if (optionIndex < 0) {
|
||||
optionIndex = this.options.length - 1;
|
||||
}
|
||||
return optionIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text of an option the player can select.
|
||||
* Used with getLineOptionsCount to render options for the player when a line of the Options type is parsed
|
||||
* @param optionIndex The index of the option you want to get
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOption = function (optionIndex: float) {
|
||||
if (!this.dialogueIsRunning || !this.options.length) {
|
||||
return [];
|
||||
}
|
||||
optionIndex = gdjs.dialogueTree._normalizedOptionIndex(optionIndex);
|
||||
return this.options[optionIndex];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text of the options the player can select, along with the selection cursor.
|
||||
* @param optionSelectionCursor The string used to draw the currently selected option's cursor
|
||||
* @param addNewLine when true each option is rendered on a new line.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsText = function (
|
||||
optionSelectionCursor: string,
|
||||
addNewLine: boolean
|
||||
) {
|
||||
if (!this.dialogueIsRunning || !this.options.length) {
|
||||
return '';
|
||||
}
|
||||
let textResult = '';
|
||||
this.options.forEach(function (optionText, index) {
|
||||
if (index === gdjs.dialogueTree.selectedOption) {
|
||||
textResult += optionSelectionCursor;
|
||||
} else {
|
||||
textResult += optionSelectionCursor.replace(/.*/g, ' ');
|
||||
}
|
||||
textResult += optionText;
|
||||
if (addNewLine) {
|
||||
textResult += '\n';
|
||||
}
|
||||
});
|
||||
return textResult;
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextHorizontal = function (
|
||||
optionSelectionCursor
|
||||
) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, false);
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextVertical = function (
|
||||
optionSelectionCursor
|
||||
) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of options that are presented to the player, during the parsing of an Options type line.
|
||||
* @returns The number of options
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsCount = function (): number {
|
||||
if (this.dialogueIsRunning && this.options.length) {
|
||||
return this.optionsCount;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Confirm the currently selected option, during the parsing of an Options type line.
|
||||
*
|
||||
* This will advance the dialogue tree to the dialogue branch was selected by the player.
|
||||
*/
|
||||
gdjs.dialogueTree.confirmSelectOption = function () {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.dialogueData.select &&
|
||||
!this.selectedOptionUpdated &&
|
||||
this.selectedOption !== -1
|
||||
) {
|
||||
this.commandCalls = [];
|
||||
try {
|
||||
this.dialogueData.select(this.selectedOption);
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`An error happened when trying to access the dialogue branch!`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select next option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectNextOption = function () {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption += 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
this.selectedOption
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select previous option during Options type line parsing. Hook this to your game input.
|
||||
*/
|
||||
gdjs.dialogueTree.selectPreviousOption = function () {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption -= 1;
|
||||
this.selectedOption = gdjs.dialogueTree._cycledOptionIndex(
|
||||
this.selectedOption
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select option by index during Options type line parsing.
|
||||
* @param optionIndex The index of the option to select
|
||||
*/
|
||||
gdjs.dialogueTree.selectOption = function (optionIndex: float) {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
if (this.dialogueData.select) {
|
||||
this.selectedOption = gdjs.dialogueTree._normalizedOptionIndex(
|
||||
optionIndex
|
||||
);
|
||||
this.selectedOptionUpdated = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently selected option
|
||||
* @returns The index of the currently selected option
|
||||
*/
|
||||
gdjs.dialogueTree.getSelectedOption = function (): number {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
if (this.dialogueData.select) {
|
||||
return this.selectedOption;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check when the player has changed option selection since the last call to this function.
|
||||
*
|
||||
* Can be used to re-render your displayed dialogue options when needed.
|
||||
*
|
||||
* @returns true if the selected option was updated since the last call to this function
|
||||
*/
|
||||
gdjs.dialogueTree.hasSelectedOptionChanged = function (): boolean {
|
||||
if (this.selectedOptionUpdated) {
|
||||
this.selectedOptionUpdated = false;
|
||||
if (this.selectedOption === -1) {
|
||||
this.selectedOption = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the type of the Dialogue Line that is being displayed to the player at the moment.
|
||||
*
|
||||
* There are three types:
|
||||
* - text - regular dialogue text is being parsed at the moment
|
||||
* - options - the player has reached a branching choise moment where they must select one of multiple options
|
||||
* - command - a <<command>> was called in the background, that can be used to trigger game events, but will not be displayed in the dialogue box.
|
||||
*
|
||||
* @param type The type you want to check for ( one of the three above )
|
||||
*/
|
||||
gdjs.dialogueTree.isDialogueLineType = function (type: string) {
|
||||
if (!this.dialogueIsRunning) {
|
||||
return false;
|
||||
}
|
||||
if (this.commandCalls && type === 'command') {
|
||||
if (
|
||||
this.commandCalls.some(function (call) {
|
||||
return (
|
||||
gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait'
|
||||
);
|
||||
})
|
||||
) {
|
||||
return !this.pauseScrolling;
|
||||
}
|
||||
if (this.commandCalls.length > 0 && this.commandParameters.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.dialogueDataType === type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a branch exists. It is also used internaly whenever you use the start from action.
|
||||
* @param branchName The Dialogue Branch name you want to check.
|
||||
*/
|
||||
gdjs.dialogueTree.hasDialogueBranch = function (branchName: string) {
|
||||
return (
|
||||
this.runner &&
|
||||
this.runner.yarnNodes &&
|
||||
Object.keys(this.runner.yarnNodes).some(function (node) {
|
||||
return node === branchName;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start parsing dialogue from a specified Dialogue tree branch.
|
||||
* Can be used if you want to store multiple dialogues inside a single Dialogue tree data set.
|
||||
* @param startDialogueNode The Dialogue Branch name you want to start parsing from.
|
||||
*/
|
||||
gdjs.dialogueTree.startFrom = function (startDialogueNode: string) {
|
||||
this.runner = gdjs.dialogueTree.runner;
|
||||
if (!this.hasDialogueBranch(startDialogueNode)) {
|
||||
return;
|
||||
}
|
||||
this.optionsCount = 0;
|
||||
this.options = [];
|
||||
this.tagParameters = [];
|
||||
this.dialogue = this.runner.run(startDialogueNode);
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.commandCalls = [];
|
||||
this.commandParameters = [];
|
||||
this.pauseScrolling = false;
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
this.dialogueDataType = 'text';
|
||||
} else {
|
||||
if (gdjs.dialogueTree._isLineTypeOptions()) {
|
||||
this.dialogueDataType = 'options';
|
||||
} else {
|
||||
this.dialogueDataType = 'command';
|
||||
}
|
||||
}
|
||||
this.dialogueIsRunning = true;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal methods to check the type of a Dialogue Line
|
||||
*/
|
||||
gdjs.dialogueTree._isLineTypeText = function () {
|
||||
return this.dialogueData instanceof bondage.TextResult;
|
||||
};
|
||||
gdjs.dialogueTree._isLineTypeOptions = function () {
|
||||
return this.dialogueData instanceof bondage.OptionsResult;
|
||||
};
|
||||
gdjs.dialogueTree._isLineTypeCommand = function () {
|
||||
return this.dialogueData instanceof bondage.CommandResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the main lifecycle function.It runs once only when the user is advancing the dialogue to the next line.
|
||||
* Progress Dialogue to the next line. Hook it to your game input.
|
||||
* Note that this action can be influenced by any <<wait>> commands, but they work only if you have at least one isCommandCalled condition.
|
||||
*/
|
||||
gdjs.dialogueTree.goToNextDialogueLine = function () {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning) {
|
||||
return;
|
||||
}
|
||||
this.optionsCount = 0;
|
||||
this.selectedOption = -1;
|
||||
this.selectedOptionUpdated = false;
|
||||
if (gdjs.dialogueTree.getVariable('debug')) {
|
||||
console.info('parsing:', this.dialogueData);
|
||||
}
|
||||
if (!this.dialogueData) {
|
||||
gdjs.dialogueTree.stopRunningDialogue();
|
||||
} else {
|
||||
if (gdjs.dialogueTree._isLineTypeText()) {
|
||||
if (
|
||||
this.lineNum === this.dialogueData.lineNum &&
|
||||
this.dialogueBranchTitle === this.dialogueData.data.title
|
||||
) {
|
||||
this.clipTextEnd = this.dialogueText.length - 1;
|
||||
this.dialogueText +=
|
||||
(this.dialogueText === '' ? '' : ' ') + this.dialogueData.text;
|
||||
} else {
|
||||
this.clipTextEnd = 0;
|
||||
this.dialogueText = this.dialogueData.text;
|
||||
}
|
||||
this.dialogueBranchTags = this.dialogueData.data.tags;
|
||||
this.dialogueBranchTitle = this.dialogueData.data.title;
|
||||
this.dialogueBranchBody = this.dialogueData.data.body;
|
||||
this.lineNum = this.dialogueData.lineNum;
|
||||
this.dialogueDataType = 'text';
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
} else {
|
||||
if (gdjs.dialogueTree._isLineTypeOptions()) {
|
||||
this.commandCalls = [];
|
||||
this.dialogueDataType = 'options';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.optionsCount = this.dialogueData.options.length;
|
||||
this.options = this.dialogueData.options;
|
||||
this.selectedOptionUpdated = true;
|
||||
} else {
|
||||
if (gdjs.dialogueTree._isLineTypeCommand()) {
|
||||
this.dialogueDataType = 'command';
|
||||
const command = this.dialogueData.text.split(' ');
|
||||
|
||||
// If last command was to wait, increase time by one
|
||||
const offsetTime =
|
||||
this.commandCalls.length &&
|
||||
this.commandCalls[this.commandCalls.length - 1].cmd === 'wait'
|
||||
? 1
|
||||
: 0;
|
||||
this.commandCalls.push({
|
||||
cmd: command[0],
|
||||
params: command,
|
||||
time: this.dialogueText.length + offsetTime,
|
||||
});
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
} else {
|
||||
this.dialogueDataType = 'unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current Dialogue Tree branch title.
|
||||
* @returns The current branch title.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTitle = function (): string {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTitle;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the currently parsed Dialogue branch title is a query.
|
||||
* @param title The Dialogue Branch name you want to check for.
|
||||
*/
|
||||
gdjs.dialogueTree.branchTitleIs = function (title: string) {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTitle === title;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all the branch tags from the current Dialogue branch as a string. Useful for debugging.
|
||||
* @returns The current branch tags, separated by a comma.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTags = function (): string {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchTags.join(',');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get one of the current Dialogue branch tags via index.
|
||||
* @param index The index of the Dialogue Branch tag you want to get.
|
||||
* @returns The branch tag at the specified index, or an empty string if not found.
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchTag = function (index: float): string {
|
||||
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
|
||||
if (index > this.dialogueBranchTags.length - 1) {
|
||||
index = this.dialogueBranchTags.length - 1;
|
||||
}
|
||||
return this.dialogueBranchTags[index];
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current Dialogue branch contains a specific tag.
|
||||
* @param query The name of the Dialogue Branch tag you want to check.
|
||||
*/
|
||||
gdjs.dialogueTree.branchContainsTag = function (query: string) {
|
||||
this.tagParameters = [];
|
||||
if (this.dialogueIsRunning && this.dialogueBranchTags.length) {
|
||||
return this.dialogueBranchTags.some(function (tag) {
|
||||
const splitTag = tag.match(/([^\(]+)\(([^\)]+)\)/i);
|
||||
gdjs.dialogueTree.tagParameters = splitTag
|
||||
? splitTag[2].split(',')
|
||||
: [];
|
||||
return splitTag ? splitTag[1] === query : tag === query;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get any tag(parameter,anotherParameter) from a tag captured by the branchContainsTag Condition
|
||||
* @param paramIndex The index of the tag parameter you want to get.
|
||||
* Leaving this empty will result in retrieving the first parameter.
|
||||
*/
|
||||
gdjs.dialogueTree.getTagParameter = function (paramIndex: float) {
|
||||
if (this.dialogueIsRunning && this.tagParameters.length >= paramIndex) {
|
||||
const returnedParam = this.tagParameters[paramIndex];
|
||||
return returnedParam ? returnedParam : '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of all the titles of visited by the player Branches. Useful for debugging.
|
||||
*/
|
||||
gdjs.dialogueTree.getVisitedBranchTitles = function () {
|
||||
if (this.dialogueIsRunning) {
|
||||
return Object.keys(this.runner.visited).join(',');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a player has visited a Dialogue Branch in the past.
|
||||
* @param title The title of the branch to check for.
|
||||
* Leaving this empty will check if the current branch title has been visited in the past.
|
||||
*/
|
||||
gdjs.dialogueTree.branchTitleHasBeenVisited = function (title: string) {
|
||||
if (!title) {
|
||||
title = this.dialogueBranchTitle;
|
||||
}
|
||||
return (
|
||||
Object.keys(this.runner.visited).includes(title) &&
|
||||
this.runner.visited[title]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the entire unparsed text of the current Dialogue Branch
|
||||
*/
|
||||
gdjs.dialogueTree.getBranchText = function () {
|
||||
if (this.dialogueIsRunning) {
|
||||
return this.dialogueBranchBody;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of a variable that was created by the Dialogue parses.
|
||||
* @param 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) {
|
||||
return this.runner.variables.get(key);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a specific variable created by the Dialogue parses exists and is equal to a specific value.
|
||||
* @param key The name of the variable you want to check the value of
|
||||
* @param value The value you want to check against
|
||||
*/
|
||||
gdjs.dialogueTree.compareVariable = function (
|
||||
key: string,
|
||||
value: string | boolean | number
|
||||
) {
|
||||
if (this.dialogueIsRunning && key in this.runner.variables.data) {
|
||||
return this.runner.variables.get(key) === value;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a specific variable created by the Dialogue parser to a specific value.
|
||||
* @param key The name of the variable you want to set the value of
|
||||
* @param value The value you want to set
|
||||
*/
|
||||
gdjs.dialogueTree.setVariable = function (
|
||||
key: string,
|
||||
value: string | boolean | number
|
||||
) {
|
||||
if (this.runner.variables) {
|
||||
this.runner.variables.set(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the current State of the Dialogue Parser in a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param outputVariable The variable where to store the State
|
||||
*/
|
||||
gdjs.dialogueTree.saveState = function (outputVariable: gdjs.Variable) {
|
||||
const dialogueState = {
|
||||
variables: gdjs.dialogueTree.runner.variables.data,
|
||||
visited: gdjs.dialogueTree.runner.visited,
|
||||
};
|
||||
gdjs.evtTools.network._objectToVariable(dialogueState, outputVariable);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the current State of the Dialogue Parser from a specified variable.
|
||||
* Can be used to implement persistence in dialogue through your game's Load/Save function.
|
||||
* That way you can later load all the dialogue choices the player has made.
|
||||
* @param inputVariable The structured variable where to load the State from.
|
||||
*/
|
||||
gdjs.dialogueTree.loadState = function (inputVariable: gdjs.Variable) {
|
||||
const loadedState = JSON.parse(
|
||||
gdjs.evtTools.network.variableStructureToJSON(inputVariable)
|
||||
);
|
||||
if (!loadedState) {
|
||||
console.error('Load state variable is empty:', inputVariable);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
gdjs.dialogueTree.runner.visited = loadedState.visited;
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
Object.keys(loadedState.variables).forEach(function (key) {
|
||||
const value = loadedState.variables[key];
|
||||
gdjs.dialogueTree.runner.variables.set(key, value);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to load state from variable:', inputVariable, e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current State of the Dialogue Parser.
|
||||
*/
|
||||
gdjs.dialogueTree.clearState = function () {
|
||||
gdjs.dialogueTree.runner.visited = {};
|
||||
gdjs.dialogueTree.runner.variables.data = {};
|
||||
};
|
||||
}
|
@@ -5,29 +5,32 @@ Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "GDCpp/Extensions/ExtensionBase.h"
|
||||
#include "DraggableBehavior.h"
|
||||
#include "DraggableRuntimeBehavior.h"
|
||||
#include "GDCpp/Extensions/ExtensionBase.h"
|
||||
|
||||
void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
extension.SetExtensionInformation(
|
||||
"DraggableBehavior",
|
||||
_("Draggable Behavior"),
|
||||
_("This Extension enables the movement of objects with a mouse."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("behaviors/draggable");
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"DraggableBehavior",
|
||||
_("Draggable Behavior"),
|
||||
_("Allows objects to be moved using the mouse (or touch). Add the "
|
||||
"behavior to an object to make it draggable. Use events to enable "
|
||||
"or disable the behavior when needed."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/behaviors/draggable");
|
||||
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension.AddBehavior("Draggable",
|
||||
_("Draggable object"),
|
||||
_("Draggable"),
|
||||
_("Allows objects to be moved using the mouse (or touch)."),
|
||||
"",
|
||||
"CppPlatform/Extensions/draggableicon.png",
|
||||
"DraggableBehavior",
|
||||
std::make_shared<DraggableBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"Draggable",
|
||||
_("Draggable object"),
|
||||
_("Draggable"),
|
||||
_("Allows objects to be moved using the mouse (or touch)."),
|
||||
"",
|
||||
"CppPlatform/Extensions/draggableicon.png",
|
||||
"DraggableBehavior",
|
||||
std::make_shared<DraggableBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
aut.AddCondition("Dragged",
|
||||
|
@@ -1,161 +0,0 @@
|
||||
/**
|
||||
GDevelop - Draggable Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The DraggableRuntimeBehavior represents a behavior allowing objects to be
|
||||
* moved using the mouse.
|
||||
*
|
||||
* @class DraggableRuntimeBehavior
|
||||
* @constructor
|
||||
*/
|
||||
gdjs.DraggableRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
{
|
||||
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||
|
||||
this._dragged = false;
|
||||
this._touchId = null;
|
||||
this._mouse = false;
|
||||
|
||||
this._xOffset = 0;
|
||||
this._yOffset = 0;
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DraggableBehavior::Draggable", gdjs.DraggableRuntimeBehavior);
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
this._endDrag();
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.onDestroy = function() {
|
||||
this.onDeActivate();
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype._endDrag = function() {
|
||||
if ( this._dragged && this._mouse ) gdjs.DraggableRuntimeBehavior.mouseDraggingSomething = false;
|
||||
if ( this._dragged && this._touchId !== null ) gdjs.DraggableRuntimeBehavior.touchDraggingSomething[this._touchId] = false;
|
||||
|
||||
this._dragged = false;
|
||||
this._mouse = false;
|
||||
this._touchId = null;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype._tryBeginDrag = function(runtimeScene) {
|
||||
if (this._dragged) return false;
|
||||
|
||||
var inputManager = runtimeScene.getGame().getInputManager();
|
||||
|
||||
//Try mouse
|
||||
if (inputManager.isMouseButtonPressed(0) &&
|
||||
!gdjs.DraggableRuntimeBehavior.leftPressedLastFrame &&
|
||||
!gdjs.DraggableRuntimeBehavior.mouseDraggingSomething) {
|
||||
|
||||
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();
|
||||
gdjs.DraggableRuntimeBehavior.mouseDraggingSomething = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else { //Try touches
|
||||
var touchIds = inputManager.getStartedTouchIdentifiers();
|
||||
for(var i = 0;i<touchIds.length;++i) {
|
||||
if (gdjs.DraggableRuntimeBehavior.touchDraggingSomething[touchIds[i]])
|
||||
continue;
|
||||
|
||||
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();
|
||||
gdjs.DraggableRuntimeBehavior.touchDraggingSomething[touchIds[i]] = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype._shouldEndDrag = function(runtimeScene) {
|
||||
if (!this._dragged) return false;
|
||||
var 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;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype._updateObjectPosition = function(runtimeScene) {
|
||||
if (!this._dragged) return false;
|
||||
var inputManager = runtimeScene.getGame().getInputManager();
|
||||
|
||||
if (this._mouse) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene) {
|
||||
this._tryBeginDrag(runtimeScene);
|
||||
|
||||
if (this._shouldEndDrag(runtimeScene)) {
|
||||
this._endDrag();
|
||||
}
|
||||
|
||||
this._updateObjectPosition(runtimeScene);
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
gdjs.DraggableRuntimeBehavior.leftPressedLastFrame =
|
||||
runtimeScene.getGame().getInputManager().isMouseButtonPressed(0);
|
||||
};
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.isDragged = function(runtimeScene) {
|
||||
return this._dragged;
|
||||
};
|
||||
|
||||
//Static property used to avoid start dragging an object while another is dragged.
|
||||
gdjs.DraggableRuntimeBehavior.mouseDraggingSomething = false;
|
||||
|
||||
//Static property used to avoid start dragging an object while another is dragged by the same touch.
|
||||
gdjs.DraggableRuntimeBehavior.touchDraggingSomething = [];
|
||||
|
||||
//Static property used to only start dragging when clicking.
|
||||
gdjs.DraggableRuntimeBehavior.leftPressedLastFrame = false;
|
171
Extensions/DraggableBehavior/draggableruntimebehavior.ts
Normal file
171
Extensions/DraggableBehavior/draggableruntimebehavior.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
GDevelop - Draggable Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
namespace gdjs {
|
||||
/**
|
||||
* The DraggableRuntimeBehavior represents a behavior allowing objects to be
|
||||
* 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;
|
||||
|
||||
constructor(runtimeScene, behaviorData, owner) {
|
||||
super(runtimeScene, behaviorData, owner);
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
this._endDrag();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.onDeActivate();
|
||||
}
|
||||
|
||||
_endDrag() {
|
||||
if (this._dragged && this._mouse) {
|
||||
DraggableRuntimeBehavior.mouseDraggingSomething = false;
|
||||
}
|
||||
if (this._dragged && this._touchId !== null) {
|
||||
DraggableRuntimeBehavior.touchDraggingSomething[this._touchId] = false;
|
||||
}
|
||||
this._dragged = false;
|
||||
this._mouse = false;
|
||||
this._touchId = null;
|
||||
}
|
||||
|
||||
_tryBeginDrag(runtimeScene) {
|
||||
if (this._dragged) {
|
||||
return false;
|
||||
}
|
||||
const inputManager = runtimeScene.getGame().getInputManager();
|
||||
|
||||
//Try mouse
|
||||
if (
|
||||
inputManager.isMouseButtonPressed(0) &&
|
||||
!DraggableRuntimeBehavior.leftPressedLastFrame &&
|
||||
!DraggableRuntimeBehavior.mouseDraggingSomething
|
||||
) {
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
//Try touches
|
||||
const touchIds = inputManager.getStartedTouchIdentifiers();
|
||||
for (let i = 0; i < touchIds.length; ++i) {
|
||||
if (DraggableRuntimeBehavior.touchDraggingSomething[touchIds[i]]) {
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_shouldEndDrag(runtimeScene) {
|
||||
if (!this._dragged) {
|
||||
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;
|
||||
}
|
||||
|
||||
_updateObjectPosition(runtimeScene) {
|
||||
if (!this._dragged) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
doStepPreEvents(runtimeScene) {
|
||||
this._tryBeginDrag(runtimeScene);
|
||||
if (this._shouldEndDrag(runtimeScene)) {
|
||||
this._endDrag();
|
||||
}
|
||||
this._updateObjectPosition(runtimeScene);
|
||||
}
|
||||
|
||||
doStepPostEvents(runtimeScene) {
|
||||
DraggableRuntimeBehavior.leftPressedLastFrame = runtimeScene
|
||||
.getGame()
|
||||
.getInputManager()
|
||||
.isMouseButtonPressed(0);
|
||||
}
|
||||
|
||||
isDragged(runtimeScene): boolean {
|
||||
return this._dragged;
|
||||
}
|
||||
|
||||
//Static property used to avoid start dragging an object while another is dragged.
|
||||
static mouseDraggingSomething = false;
|
||||
|
||||
//Static property used to avoid start dragging an object while another is dragged by the same touch.
|
||||
static touchDraggingSomething: Array<boolean> = [];
|
||||
|
||||
//Static property used to only start dragging when clicking.
|
||||
static leftPressedLastFrame = false;
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'DraggableBehavior::Draggable',
|
||||
gdjs.DraggableRuntimeBehavior
|
||||
);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user