mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
129 Commits
debugger-3
...
v5.2.169
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ca220d8fe5 | ||
![]() |
9971702ec9 | ||
![]() |
e48f14b753 | ||
![]() |
ce046c0b99 | ||
![]() |
e29cb462e1 | ||
![]() |
aee05affd0 | ||
![]() |
f95d197634 | ||
![]() |
075cc5a7aa | ||
![]() |
56c2aa0dc6 | ||
![]() |
fa003374ba | ||
![]() |
ca27a946a1 | ||
![]() |
2845c403f9 | ||
![]() |
9cc2e2987e | ||
![]() |
b9d6336dc7 | ||
![]() |
c2ad00ed6c | ||
![]() |
2d1845f0b8 | ||
![]() |
b5f0758f4d | ||
![]() |
1e2ffe5d15 | ||
![]() |
c887769c0a | ||
![]() |
dc1ac1e094 | ||
![]() |
4b1ceeb642 | ||
![]() |
e5d92fbf43 | ||
![]() |
c323fea5bd | ||
![]() |
32f0fe9051 | ||
![]() |
be74d3efa6 | ||
![]() |
8969c9af8c | ||
![]() |
15cac278d6 | ||
![]() |
9e8a15547d | ||
![]() |
ed74a49aa3 | ||
![]() |
6ef5d0c326 | ||
![]() |
5f871e2643 | ||
![]() |
4f65fa0d82 | ||
![]() |
4d1d763bd9 | ||
![]() |
3286722b6a | ||
![]() |
656255a662 | ||
![]() |
ea7b7a778e | ||
![]() |
9b6de5affd | ||
![]() |
0c03659314 | ||
![]() |
6b08c0f033 | ||
![]() |
a1dcf03a5b | ||
![]() |
556d13c881 | ||
![]() |
0d6c42a9bf | ||
![]() |
690ce16ab4 | ||
![]() |
5f51a5e465 | ||
![]() |
b7521de138 | ||
![]() |
acea6fc595 | ||
![]() |
d8107fe3d5 | ||
![]() |
b40b95be99 | ||
![]() |
6644525dd0 | ||
![]() |
56e66d1c5a | ||
![]() |
90d8afb5a0 | ||
![]() |
d4db61a595 | ||
![]() |
90413b842d | ||
![]() |
43c788acbf | ||
![]() |
ee22b2e4b1 | ||
![]() |
007d56e946 | ||
![]() |
577c4adb14 | ||
![]() |
7f17720ff3 | ||
![]() |
288db1c941 | ||
![]() |
2496fc3eef | ||
![]() |
b43d5ec425 | ||
![]() |
3c43f28966 | ||
![]() |
a3f7c5782e | ||
![]() |
8dec6dfa28 | ||
![]() |
4eb49bdeb2 | ||
![]() |
813cadbd6e | ||
![]() |
58dd2c1a7b | ||
![]() |
104c27ebc8 | ||
![]() |
318099504c | ||
![]() |
8851be03a3 | ||
![]() |
a9a126ab0d | ||
![]() |
19e46fedc8 | ||
![]() |
ff987a0751 | ||
![]() |
0c0ab65b1a | ||
![]() |
52a5908d7e | ||
![]() |
49926a89a2 | ||
![]() |
b63b91f33d | ||
![]() |
67ea361416 | ||
![]() |
3b9078c6b3 | ||
![]() |
3d9e3f997e | ||
![]() |
3c34866faa | ||
![]() |
69cd2784c4 | ||
![]() |
5b7e419a41 | ||
![]() |
7773460d35 | ||
![]() |
9262266480 | ||
![]() |
84f2b4ca68 | ||
![]() |
19ae7a378c | ||
![]() |
f9ca330add | ||
![]() |
5ef990ac7d | ||
![]() |
8099820729 | ||
![]() |
df556f20e9 | ||
![]() |
2c8f2ab58d | ||
![]() |
20c3d62c90 | ||
![]() |
0a28981c74 | ||
![]() |
c8bb24475c | ||
![]() |
81bce61783 | ||
![]() |
71d1b6aa1f | ||
![]() |
c41974c24b | ||
![]() |
3bee88c6cd | ||
![]() |
4c874dfb7e | ||
![]() |
65f499f24e | ||
![]() |
3265bf7fb4 | ||
![]() |
5a437dea4e | ||
![]() |
19b576e8cc | ||
![]() |
67747e458e | ||
![]() |
260c2b9c8f | ||
![]() |
255ef1d8ef | ||
![]() |
53c633c646 | ||
![]() |
cec67a91d4 | ||
![]() |
4408dfe59d | ||
![]() |
c4274d2fc4 | ||
![]() |
b0103f31b7 | ||
![]() |
18905890d4 | ||
![]() |
6858e0fb59 | ||
![]() |
cf595a7d7d | ||
![]() |
e681d27bb8 | ||
![]() |
8941e04390 | ||
![]() |
e186681f39 | ||
![]() |
a578fa32e9 | ||
![]() |
6b40e8309c | ||
![]() |
5ff51351af | ||
![]() |
d66e4e0001 | ||
![]() |
2184eaf70b | ||
![]() |
29fc0598f6 | ||
![]() |
a7760e975a | ||
![]() |
e2397cb0a4 | ||
![]() |
24ff670886 | ||
![]() |
d24b7497c9 | ||
![]() |
0ecaa342f9 |
@@ -15,7 +15,7 @@ jobs:
|
||||
# Build the **entire** app for macOS.
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
xcode: 14.2.0
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -64,11 +64,11 @@ jobs:
|
||||
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
|
||||
- run:
|
||||
name: Build GDevelop IDE
|
||||
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac --publish=never
|
||||
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && CI=false npm run build -- --mac --publish=never
|
||||
|
||||
- run:
|
||||
name: Clean dist folder to keep only installers/binaries.
|
||||
command: rm -rf "newIDE/electron-app/dist/mac/GDevelop 5.app" && rm -rf "newIDE/electron-app/dist/mac-arm64/GDevelop 5.app"
|
||||
command: rm -rf "newIDE/electron-app/dist/mac-universal/GDevelop 5.app"
|
||||
|
||||
# Upload artifacts (CircleCI)
|
||||
- store_artifacts:
|
||||
@@ -101,8 +101,8 @@ jobs:
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
@@ -178,8 +178,8 @@ jobs:
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
|
8
.github/workflows/build-storybook.yml
vendored
8
.github/workflows/build-storybook.yml
vendored
@@ -9,6 +9,10 @@ name: Build Storybook
|
||||
on:
|
||||
# Launch on all commits.
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**" # Don't run on new tags
|
||||
# Allows to run this workflow manually from the Actions tab,
|
||||
# to publish on Chromatic (not done by default).
|
||||
workflow_dispatch:
|
||||
@@ -24,8 +28,8 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'newIDE/app/package-lock.json'
|
||||
cache: "npm"
|
||||
cache-dependency-path: "newIDE/app/package-lock.json"
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
|
8
.github/workflows/extract-translations.yml
vendored
8
.github/workflows/extract-translations.yml
vendored
@@ -4,6 +4,10 @@ name: Extract translations
|
||||
on:
|
||||
# Execute for all commits (to ensure translations extraction works)
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**" # Don't run on new tags
|
||||
# Allows to run this workflow manually from the Actions tab.
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -15,8 +19,8 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'newIDE/app/package-lock.json'
|
||||
cache: "npm"
|
||||
cache-dependency-path: "newIDE/app/package-lock.json"
|
||||
|
||||
- name: Install gettext
|
||||
run: sudo apt update && sudo apt install gettext -y
|
||||
|
50
.github/workflows/pull-requests.yml
vendored
50
.github/workflows/pull-requests.yml
vendored
@@ -1,29 +1,53 @@
|
||||
name: GDevelop Issues automatic workflow
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
jobs:
|
||||
read-locales-metadata:
|
||||
if: contains(github.event.pull_request.title, '[Auto PR] Update translations')
|
||||
if: github.event.pull_request.title == '[Auto PR] Update translations'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COMMENT_TITLE: "Translation ratio changes"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Read and format locales metadata
|
||||
env:
|
||||
BASE: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
LANS=($(git diff HEAD^ HEAD --unified=5 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\s+\"languageName" | sed -E "s/^ *\"languageName\": \"//g" | sed -E "s/\",//g" | sed -E "s/ /_/g"))
|
||||
ADDS=($(git diff HEAD^ HEAD --unified=0 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\+\s*\"translationRatio\"" | sed -E "s/^\+ *\"translationRatio\": //g"))
|
||||
SUBS=($(git diff HEAD^ HEAD --unified=0 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\-\s*\"translationRatio\"" | sed -E "s/^\- *\"translationRatio\": //g"))
|
||||
touch sumup.txt
|
||||
LANS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\s+\"languageName" | sed -E "s/^ *\"languageName\": \"//g" | sed -E "s/\",//g" | sed -E "s/ /_/g"))
|
||||
ADDS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\+\s*\"translationRatio\"" | sed -E "s/^\+ *\"translationRatio\": //g"))
|
||||
SUBS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\-\s*\"translationRatio\"" | sed -E "s/^\- *\"translationRatio\": //g"))
|
||||
touch sumup.md
|
||||
echo "## $COMMENT_TITLE" >> sumup.md
|
||||
echo "" >> sumup.md
|
||||
echo "| Language | Change |" >> sumup.md
|
||||
echo "| --- | --- |" >> sumup.md
|
||||
for index in ${!ADDS[@]}; do
|
||||
echo ${LANS[index]} | sed -E "s/_/ /g" >> sumup.txt
|
||||
DELTA=$(bc <<< "scale=2;(${ADDS[index]}-${SUBS[index]})*100/1")
|
||||
echo $DELTA % >> sumup.txt
|
||||
DELTA=$(echo "scale=3; (${ADDS[index]} - ${SUBS[index]})*100/1" | bc)
|
||||
if (( $(echo "$DELTA == 0" | bc -l) )); then
|
||||
continue
|
||||
fi
|
||||
LANGUAGE=${LANS[index]//_/ }
|
||||
echo "| $LANGUAGE | $(printf "%1.3f" $DELTA) % |" >> sumup.md
|
||||
done
|
||||
- name: Store sumup in outputs
|
||||
id: sumup
|
||||
run: echo "sumupFileContent=$(cat sumup.txt)" >> $GITHUB_OUTPUT
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: fc
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.number }}
|
||||
body-includes: ${{ env.COMMENT_TITLE }}
|
||||
- name: Autocomment pull request with sumup
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.number }}
|
||||
body: ${{ steps.sumup.outputs.sumupFileContent }}
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body-path: "sumup.md"
|
||||
|
6
.github/workflows/update-translations.yml
vendored
6
.github/workflows/update-translations.yml
vendored
@@ -7,6 +7,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags-ignore:
|
||||
- "**" # Don't run on new tags
|
||||
# Allows to run this workflow manually from the Actions tab.
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -18,8 +20,8 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'newIDE/app/package-lock.json'
|
||||
cache: "npm"
|
||||
cache-dependency-path: "newIDE/app/package-lock.json"
|
||||
|
||||
- name: Install gettext
|
||||
run: sudo apt update && sudo apt install gettext -y
|
||||
|
257
.vscode/settings.json
vendored
257
.vscode/settings.json
vendored
@@ -1,131 +1,134 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.associations": {
|
||||
"*.idl": "java",
|
||||
"Fastfile": "ruby",
|
||||
"iosfwd": "cpp",
|
||||
"functional": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"random": "cpp",
|
||||
"__config": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"exception": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"new": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"hashtable": "cpp",
|
||||
"tuple": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"deque": "cpp",
|
||||
"iterator": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"queue": "cpp",
|
||||
"regex": "cpp",
|
||||
"set": "cpp",
|
||||
"stack": "cpp",
|
||||
"string": "cpp",
|
||||
"vector": "cpp",
|
||||
"iostream": "cpp",
|
||||
"__functional_03": "cpp",
|
||||
"__hash_table": "cpp",
|
||||
"__tree": "cpp",
|
||||
"bitset": "cpp",
|
||||
"__bit_reference": "cpp",
|
||||
"__mutex_base": "cpp",
|
||||
"fstream": "cpp",
|
||||
"ios": "cpp",
|
||||
"__locale": "cpp",
|
||||
"valarray": "cpp",
|
||||
"freeglut_spaceball.c": "cpp",
|
||||
"__tuple": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"system_error": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"__functional_base": "cpp",
|
||||
"__functional_base_03": "cpp",
|
||||
"chrono": "cpp",
|
||||
"ratio": "cpp",
|
||||
"atomic": "cpp",
|
||||
"locale": "cpp",
|
||||
"string_view": "cpp",
|
||||
"__string": "cpp",
|
||||
"cstring": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"mutex": "cpp",
|
||||
"__hash": "cpp",
|
||||
"__debug": "cpp",
|
||||
"__threading_support": "cpp",
|
||||
"any": "cpp",
|
||||
"array": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"numeric": "cpp",
|
||||
"__memory": "cpp",
|
||||
"__errc": "cpp",
|
||||
"__node_handle": "cpp",
|
||||
"bit": "cpp",
|
||||
"optional": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xstring": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xmemory0": "cpp",
|
||||
"memory_resource": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
"Binaries/Output": true,
|
||||
"GDJS/Runtime-dist": true,
|
||||
"docs": true,
|
||||
"newIDE/electron-app/dist": true,
|
||||
"newIDE/app/build": true,
|
||||
"newIDE/app/resources/GDJS": true,
|
||||
"newIDE/electron-app/app/www": true
|
||||
},
|
||||
// Support for Flowtype (for newIDE):
|
||||
"javascript.validate.enable": false,
|
||||
"flow.useNPMPackagedFlow": true,
|
||||
"files.associations": {
|
||||
"*.idl": "java",
|
||||
"Fastfile": "ruby",
|
||||
"iosfwd": "cpp",
|
||||
"functional": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"random": "cpp",
|
||||
"__config": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"exception": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"new": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"hashtable": "cpp",
|
||||
"tuple": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"deque": "cpp",
|
||||
"iterator": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"queue": "cpp",
|
||||
"regex": "cpp",
|
||||
"set": "cpp",
|
||||
"stack": "cpp",
|
||||
"string": "cpp",
|
||||
"vector": "cpp",
|
||||
"iostream": "cpp",
|
||||
"__functional_03": "cpp",
|
||||
"__hash_table": "cpp",
|
||||
"__tree": "cpp",
|
||||
"bitset": "cpp",
|
||||
"__bit_reference": "cpp",
|
||||
"__mutex_base": "cpp",
|
||||
"fstream": "cpp",
|
||||
"ios": "cpp",
|
||||
"__locale": "cpp",
|
||||
"valarray": "cpp",
|
||||
"freeglut_spaceball.c": "cpp",
|
||||
"__tuple": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"system_error": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"__functional_base": "cpp",
|
||||
"__functional_base_03": "cpp",
|
||||
"chrono": "cpp",
|
||||
"ratio": "cpp",
|
||||
"atomic": "cpp",
|
||||
"locale": "cpp",
|
||||
"string_view": "cpp",
|
||||
"__string": "cpp",
|
||||
"cstring": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"mutex": "cpp",
|
||||
"__hash": "cpp",
|
||||
"__debug": "cpp",
|
||||
"__threading_support": "cpp",
|
||||
"any": "cpp",
|
||||
"array": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"numeric": "cpp",
|
||||
"__memory": "cpp",
|
||||
"__errc": "cpp",
|
||||
"__node_handle": "cpp",
|
||||
"bit": "cpp",
|
||||
"optional": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xstring": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xmemory0": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"__bits": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"variant": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
"Binaries/Output": true,
|
||||
"GDJS/Runtime-dist": true,
|
||||
"docs": true,
|
||||
"newIDE/electron-app/dist": true,
|
||||
"newIDE/app/build": true,
|
||||
"newIDE/app/resources/GDJS": true,
|
||||
"newIDE/electron-app/app/www": true
|
||||
},
|
||||
// Support for Flowtype (for newIDE):
|
||||
"javascript.validate.enable": false,
|
||||
"flow.useNPMPackagedFlow": true,
|
||||
|
||||
// Clang format styling (duplicated in scripts/CMakeClangUtils.txt)
|
||||
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}"
|
||||
// Clang format styling (duplicated in scripts/CMakeClangUtils.txt)
|
||||
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}"
|
||||
}
|
||||
|
123
CMakeLists.txt
123
CMakeLists.txt
@@ -1,100 +1,99 @@
|
||||
#This is the CMake file used to build GDevelop.
|
||||
#For more information, see the README.md file.
|
||||
# This is the CMake file used to build GDevelop.
|
||||
# For more information, see the README.md file.
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0011 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Add utility functions
|
||||
include(scripts/CMakeClangUtils.txt) # To add clang-format and clang-tidy support to a target
|
||||
|
||||
# Macro for defining an option
|
||||
macro(gd_set_option var default type docstring)
|
||||
if(NOT DEFINED ${var})
|
||||
set(${var} ${default})
|
||||
endif()
|
||||
set(${var} ${${var}} CACHE ${type} ${docstring} FORCE)
|
||||
if(NOT DEFINED ${var})
|
||||
set(${var} ${default})
|
||||
endif()
|
||||
set(${var} ${${var}} CACHE ${type} ${docstring} FORCE)
|
||||
endmacro()
|
||||
|
||||
# Set options
|
||||
gd_set_option(BUILD_CORE TRUE BOOL "TRUE to build GDevelop Core library")
|
||||
gd_set_option(BUILD_GDJS TRUE BOOL "TRUE to build GDevelop JS Platform")
|
||||
gd_set_option(BUILD_EXTENSIONS TRUE BOOL "TRUE to build the extensions")
|
||||
gd_set_option(BUILD_TESTS TRUE BOOL "TRUE to build the tests")
|
||||
|
||||
# Disable deprecated code
|
||||
set(NO_GUI TRUE CACHE BOOL "" FORCE) #Force disable old GUI related code.
|
||||
set(NO_GUI TRUE CACHE BOOL "" FORCE) # Force disable old GUI related code.
|
||||
|
||||
#Setting up installation directory, for Linux (has to be done before "project" command).
|
||||
IF(NOT WIN32)
|
||||
if (NOT APPLE)
|
||||
gd_set_option(GD_INSTALL_PREFIX "/opt/gdevelop/" STRING "The directory where GDevelop should be installed")
|
||||
ELSE()
|
||||
gd_set_option(GD_INSTALL_PREFIX "." STRING "The directory where GDevelop should be installed")
|
||||
ENDIF()
|
||||
# Setting up installation directory, for Linux (has to be done before "project" command).
|
||||
if(NOT WIN32)
|
||||
if(NOT APPLE)
|
||||
gd_set_option(GD_INSTALL_PREFIX "/opt/gdevelop/" STRING "The directory where GDevelop should be installed")
|
||||
else()
|
||||
gd_set_option(GD_INSTALL_PREFIX "." STRING "The directory where GDevelop should be installed")
|
||||
endif()
|
||||
|
||||
#As we embed SFML, prevent it to be installed system-wide
|
||||
# As we embed SFML, prevent it to be installed system-wide
|
||||
set(CMAKE_INSTALL_PREFIX "${GD_INSTALL_PREFIX}/useless")
|
||||
ENDIF()
|
||||
endif()
|
||||
|
||||
project(GDevelop)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
IF(NOT WIN32 AND NOT APPLE AND NOT BUILD_TESTS)
|
||||
SET(CMAKE_SKIP_BUILD_RPATH TRUE) #Avoid errors when packaging for linux.
|
||||
ENDIF()
|
||||
IF(APPLE)
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT BUILD_TESTS)
|
||||
set(CMAKE_SKIP_BUILD_RPATH TRUE) # Avoid errors when packaging for linux.
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH 1)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
|
||||
set(CMAKE_INSTALL_RPATH ".")
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
||||
ENDIF()
|
||||
#Sanity checks
|
||||
IF ("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
message( "CMAKE_BUILD_TYPE is empty, assuming build type is Release" )
|
||||
add_compile_options(
|
||||
-D_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_
|
||||
-Wno-potentially-evaluated-expression)
|
||||
endif()
|
||||
# Sanity checks
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
message(STATUS "CMAKE_BUILD_TYPE is empty, assuming build type is Release")
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
ENDIF()
|
||||
endif()
|
||||
|
||||
IF("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "-s") #Force stripping to avoid errors when packaging for linux.
|
||||
ENDIF()
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-s") # Force stripping to avoid errors when packaging for linux.
|
||||
endif()
|
||||
|
||||
#Activate C++11
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=gnu++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
if(COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
|
||||
else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support (with GNU extensions). Please use a different C++ compiler.")
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Mark some warnings as errors
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# Activate as much warnings as possible to avoid errors like
|
||||
# uninitialized variables or other hard to debug bugs.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder-ctor -Wno-reorder -Wno-pessimizing-move -Wno-unused-variable -Wno-unused-private-field")
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-reorder-ctor
|
||||
-Wno-reorder
|
||||
-Wno-pessimizing-move
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-private-field
|
||||
|
||||
# Make as much warnings considered as errors as possible (only one for now).
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-stack-address")
|
||||
# Make as much warnings considered as errors as possible (only one for now).
|
||||
-Werror=return-stack-address)
|
||||
endif()
|
||||
|
||||
#Define common directories:
|
||||
# Define common directories:
|
||||
set(GD_base_dir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
#Add all the CMakeLists:
|
||||
ADD_SUBDIRECTORY(ExtLibs)
|
||||
IF(BUILD_CORE)
|
||||
ADD_SUBDIRECTORY(Core)
|
||||
ENDIF()
|
||||
IF(BUILD_GDJS)
|
||||
ADD_SUBDIRECTORY(GDJS)
|
||||
ENDIF()
|
||||
IF(EMSCRIPTEN)
|
||||
ADD_SUBDIRECTORY(GDevelop.js)
|
||||
ENDIF()
|
||||
IF(BUILD_EXTENSIONS)
|
||||
ADD_SUBDIRECTORY(Extensions)
|
||||
ENDIF()
|
||||
# Add all the CMakeLists:
|
||||
add_subdirectory(ExtLibs)
|
||||
if(BUILD_CORE)
|
||||
add_subdirectory(Core)
|
||||
endif()
|
||||
if(BUILD_GDJS)
|
||||
add_subdirectory(GDJS)
|
||||
endif()
|
||||
if(EMSCRIPTEN)
|
||||
add_subdirectory(GDevelop.js)
|
||||
endif()
|
||||
if(BUILD_EXTENSIONS)
|
||||
add_subdirectory(Extensions)
|
||||
endif()
|
||||
|
@@ -1,83 +1,98 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(GDCore)
|
||||
|
||||
SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) #Force use response file: useful for Ninja build system on Windows.
|
||||
SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
|
||||
SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) # Force use response file: useful for Ninja build system on Windows.
|
||||
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
|
||||
set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
|
||||
#Define common directories:
|
||||
# Define common directories:
|
||||
set(GDCORE_include_dir ${GD_base_dir}/Core PARENT_SCOPE)
|
||||
set(GDCORE_lib_dir ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME} PARENT_SCOPE)
|
||||
|
||||
#Dependencies on external libraries:
|
||||
###
|
||||
# Dependencies on external libraries:
|
||||
#
|
||||
|
||||
#Defines
|
||||
###
|
||||
add_definitions( -DGD_IDE_ONLY )
|
||||
IF (EMSCRIPTEN)
|
||||
add_definitions( -DEMSCRIPTEN )
|
||||
ENDIF()
|
||||
IF(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
add_definitions( -DDEBUG )
|
||||
ELSE()
|
||||
add_definitions( -DRELEASE )
|
||||
ENDIF()
|
||||
# Defines
|
||||
#
|
||||
add_definitions(-DGD_IDE_ONLY)
|
||||
if(EMSCRIPTEN)
|
||||
add_definitions(-DEMSCRIPTEN)
|
||||
endif()
|
||||
if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
|
||||
add_definitions(-DDEBUG)
|
||||
else()
|
||||
add_definitions(-DRELEASE)
|
||||
endif()
|
||||
|
||||
IF(WIN32)
|
||||
add_definitions( -DWINDOWS )
|
||||
add_definitions( "-DGD_CORE_API=__declspec(dllexport)" )
|
||||
add_definitions( -D__GNUWIN32__ )
|
||||
ELSE()
|
||||
IF(APPLE)
|
||||
add_definitions( -DMACOS )
|
||||
ELSE()
|
||||
add_definitions( -DLINUX )
|
||||
ENDIF()
|
||||
add_definitions( -DGD_API= )
|
||||
add_definitions( -DGD_CORE_API= )
|
||||
ENDIF(WIN32)
|
||||
if(WIN32)
|
||||
add_definitions(-DWINDOWS)
|
||||
add_definitions("-DGD_CORE_API=__declspec(dllexport)")
|
||||
add_definitions(-D__GNUWIN32__)
|
||||
else()
|
||||
if(APPLE)
|
||||
add_definitions(-DMACOS)
|
||||
else()
|
||||
add_definitions(-DLINUX)
|
||||
endif()
|
||||
add_definitions(-DGD_API=)
|
||||
add_definitions(-DGD_CORE_API=)
|
||||
endif()
|
||||
|
||||
#The target
|
||||
###
|
||||
# The target
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB_RECURSE source_files GDCore/*)
|
||||
file(
|
||||
GLOB_RECURSE
|
||||
source_files
|
||||
GDCore/*)
|
||||
|
||||
file(GLOB_RECURSE formatted_source_files tests/* GDCore/Events/* GDCore/Extensions/* GDCore/IDE/* GDCore/Project/* GDCore/Serialization/* GDCore/Tools/*)
|
||||
list(REMOVE_ITEM formatted_source_files "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.h" "${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs_dialogs_bitmaps.cpp")
|
||||
file(
|
||||
GLOB_RECURSE
|
||||
formatted_source_files
|
||||
tests/*
|
||||
GDCore/Events/*
|
||||
GDCore/Extensions/*
|
||||
GDCore/IDE/*
|
||||
GDCore/Project/*
|
||||
GDCore/Serialization/*
|
||||
GDCore/Tools/*)
|
||||
list(
|
||||
REMOVE_ITEM
|
||||
formatted_source_files
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/GDCore/IDE/Dialogs/GDCoreDialogs_dialogs_bitmaps.cpp")
|
||||
gd_add_clang_utils(GDCore "${formatted_source_files}")
|
||||
|
||||
IF(EMSCRIPTEN)
|
||||
if(EMSCRIPTEN)
|
||||
# Emscripten treats all libraries as static libraries
|
||||
add_library(GDCore STATIC ${source_files})
|
||||
ELSE()
|
||||
else()
|
||||
add_library(GDCore SHARED ${source_files})
|
||||
ENDIF()
|
||||
IF(EMSCRIPTEN)
|
||||
endif()
|
||||
if(EMSCRIPTEN)
|
||||
set_target_properties(GDCore PROPERTIES SUFFIX ".bc")
|
||||
ELSEIF(WIN32)
|
||||
elseif(WIN32)
|
||||
set_target_properties(GDCore PROPERTIES PREFIX "")
|
||||
ELSE()
|
||||
else()
|
||||
set_target_properties(GDCore PROPERTIES PREFIX "lib")
|
||||
ENDIF()
|
||||
endif()
|
||||
set(LIBRARY_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
|
||||
set(ARCHIVE_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
|
||||
set(RUNTIME_OUTPUT_PATH ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME})
|
||||
|
||||
#Tests
|
||||
###
|
||||
# Tests
|
||||
#
|
||||
if(BUILD_TESTS)
|
||||
file(
|
||||
GLOB_RECURSE
|
||||
test_source_files
|
||||
tests/*
|
||||
)
|
||||
GLOB_RECURSE
|
||||
test_source_files
|
||||
tests/*)
|
||||
|
||||
add_executable(GDCore_tests ${test_source_files})
|
||||
set_target_properties(GDCore_tests PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE) #Allow finding dependencies directly from build path on Mac OS X.
|
||||
set_target_properties(GDCore_tests PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE) # Allow finding dependencies directly from build path on Mac OS X.
|
||||
target_link_libraries(GDCore_tests GDCore)
|
||||
target_link_libraries(GDCore_tests ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
@@ -15,7 +15,7 @@ using namespace std;
|
||||
namespace gd {
|
||||
|
||||
ForEachEvent::ForEachEvent()
|
||||
: BaseEvent(), objectsToPick(""), objectsToPickSelected(false) {}
|
||||
: BaseEvent(), objectsToPick("") {}
|
||||
|
||||
vector<gd::InstructionsList*> ForEachEvent::GetAllConditionsVectors() {
|
||||
vector<gd::InstructionsList*> allConditions;
|
||||
|
@@ -6,6 +6,8 @@
|
||||
|
||||
#ifndef FOREACHEVENT_H
|
||||
#define FOREACHEVENT_H
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
namespace gd {
|
||||
@@ -62,13 +64,17 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
||||
std::vector<gd::Expression*> GetAllObjectExpressions() {
|
||||
std::vector<gd::Expression*> allObjectExpressions;
|
||||
allObjectExpressions.push_back(&objectsToPick);
|
||||
return allObjectExpressions;
|
||||
}
|
||||
|
||||
private:
|
||||
gd::Expression objectsToPick;
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
gd::EventsList events;
|
||||
|
||||
bool objectsToPickSelected;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -267,11 +267,11 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
return "/* Unknown instruction - skipped. */";
|
||||
}
|
||||
|
||||
AddIncludeFiles(instrInfos.codeExtraInformation.GetIncludeFiles());
|
||||
AddIncludeFiles(instrInfos.GetIncludeFiles());
|
||||
maxConditionsListsSize =
|
||||
std::max(maxConditionsListsSize, condition.GetSubInstructions().size());
|
||||
|
||||
if (instrInfos.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
if (instrInfos.HasCustomCodeGenerator()) {
|
||||
context.EnterCustomCondition();
|
||||
conditionCode += instrInfos.codeExtraInformation.customCodeGenerator(
|
||||
condition, *this, context);
|
||||
@@ -459,9 +459,9 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
return "/* Unknown instruction - skipped. */";
|
||||
}
|
||||
|
||||
AddIncludeFiles(instrInfos.codeExtraInformation.GetIncludeFiles());
|
||||
AddIncludeFiles(instrInfos.GetIncludeFiles());
|
||||
|
||||
if (instrInfos.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
if (instrInfos.HasCustomCodeGenerator()) {
|
||||
return instrInfos.codeExtraInformation.customCodeGenerator(
|
||||
action, *this, context);
|
||||
}
|
||||
@@ -1019,15 +1019,15 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
|
||||
bool conditionInverted,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
// Generate call
|
||||
gd::String predicat;
|
||||
gd::String predicate;
|
||||
if (instrInfos.codeExtraInformation.type == "number" ||
|
||||
instrInfos.codeExtraInformation.type == "string") {
|
||||
predicat = GenerateRelationalOperatorCall(
|
||||
predicate = GenerateRelationalOperatorCall(
|
||||
instrInfos,
|
||||
arguments,
|
||||
instrInfos.codeExtraInformation.functionCallName);
|
||||
} else {
|
||||
predicat = instrInfos.codeExtraInformation.functionCallName + "(" +
|
||||
predicate = instrInfos.codeExtraInformation.functionCallName + "(" +
|
||||
GenerateArgumentsList(arguments, 0) + ")";
|
||||
}
|
||||
|
||||
@@ -1040,10 +1040,10 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
|
||||
conditionAlreadyTakeCareOfInversion = true;
|
||||
}
|
||||
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
|
||||
predicat = GenerateNegatedPredicat(predicat);
|
||||
predicate = GenerateNegatedPredicate(predicate);
|
||||
|
||||
// Generate condition code
|
||||
return returnBoolean + " = " + predicat + ";\n";
|
||||
return returnBoolean + " = " + predicate + ";\n";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateObjectCondition(
|
||||
@@ -1065,18 +1065,18 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
|
||||
instrInfos.codeExtraInformation.functionCallName;
|
||||
|
||||
// Create call
|
||||
gd::String predicat;
|
||||
gd::String predicate;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
instrInfos.codeExtraInformation.type == "string")) {
|
||||
predicat = GenerateRelationalOperatorCall(
|
||||
predicate = GenerateRelationalOperatorCall(
|
||||
instrInfos, arguments, objectFunctionCallNamePart, 1);
|
||||
} else {
|
||||
predicat = objectFunctionCallNamePart + "(" +
|
||||
predicate = objectFunctionCallNamePart + "(" +
|
||||
GenerateArgumentsList(arguments, 1) + ")";
|
||||
}
|
||||
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
|
||||
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
|
||||
|
||||
return "For each picked object \"" + objectName + "\", check " + predicat +
|
||||
return "For each picked object \"" + objectName + "\", check " + predicate +
|
||||
".\n";
|
||||
}
|
||||
|
||||
@@ -1090,16 +1090,16 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
|
||||
bool conditionInverted,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
// Create call
|
||||
gd::String predicat;
|
||||
gd::String predicate;
|
||||
if ((instrInfos.codeExtraInformation.type == "number" ||
|
||||
instrInfos.codeExtraInformation.type == "string")) {
|
||||
predicat = GenerateRelationalOperatorCall(instrInfos, arguments, "", 2);
|
||||
predicate = GenerateRelationalOperatorCall(instrInfos, arguments, "", 2);
|
||||
} else {
|
||||
predicat = "(" + GenerateArgumentsList(arguments, 2) + ")";
|
||||
predicate = "(" + GenerateArgumentsList(arguments, 2) + ")";
|
||||
}
|
||||
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
|
||||
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
|
||||
|
||||
return "For each picked object \"" + objectName + "\", check " + predicat +
|
||||
return "For each picked object \"" + objectName + "\", check " + predicate +
|
||||
" for behavior \"" + behaviorName + "\".\n";
|
||||
}
|
||||
|
||||
|
@@ -668,13 +668,13 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Must negate a predicat.
|
||||
* \brief Must negate a predicate.
|
||||
*
|
||||
* The default implementation generates C-style code : It wraps the predicat
|
||||
* The default implementation generates C-style code : It wraps the predicate
|
||||
* inside parenthesis and add a !.
|
||||
*/
|
||||
virtual gd::String GenerateNegatedPredicat(const gd::String& predicat) const {
|
||||
return "!(" + predicat + ")";
|
||||
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
|
||||
return "!(" + predicate + ")";
|
||||
};
|
||||
|
||||
virtual gd::String GenerateFreeCondition(
|
||||
|
@@ -216,10 +216,10 @@ gd::String ExpressionCodeGenerator::GenerateFreeFunctionCode(
|
||||
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
|
||||
const ExpressionMetadata& expressionMetadata) {
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionMetadata.codeExtraInformation.GetIncludeFiles());
|
||||
expressionMetadata.GetIncludeFiles());
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
if (expressionMetadata.HasCustomCodeGenerator()) {
|
||||
return expressionMetadata.codeExtraInformation.customCodeGenerator(
|
||||
PrintParameters(parameters), codeGenerator, context);
|
||||
}
|
||||
@@ -242,10 +242,10 @@ gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
|
||||
codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionMetadata.codeExtraInformation.GetIncludeFiles());
|
||||
expressionMetadata.GetIncludeFiles());
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
if (expressionMetadata.HasCustomCodeGenerator()) {
|
||||
return expressionMetadata.codeExtraInformation.customCodeGenerator(
|
||||
PrintParameters(parameters), codeGenerator, context);
|
||||
}
|
||||
@@ -300,10 +300,10 @@ gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
|
||||
codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionMetadata.codeExtraInformation.GetIncludeFiles());
|
||||
expressionMetadata.GetIncludeFiles());
|
||||
|
||||
// Launch custom code generator if needed
|
||||
if (expressionMetadata.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
if (expressionMetadata.HasCustomCodeGenerator()) {
|
||||
return expressionMetadata.codeExtraInformation.customCodeGenerator(
|
||||
PrintParameters(parameters), codeGenerator, context);
|
||||
}
|
||||
|
@@ -282,6 +282,14 @@ class GD_CORE_API BaseEvent {
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
/**
|
||||
* \brief Return a list of all objects linked to the event.
|
||||
*/
|
||||
virtual std::vector<gd::Expression*> GetAllObjectExpressions() {
|
||||
std::vector<gd::Expression*> allObjectExpressions;
|
||||
return allObjectExpressions;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
std::weak_ptr<gd::BaseEvent>
|
||||
|
@@ -17,7 +17,7 @@ const gd::String& EventsCodeNameMangler::GetMangledObjectsListName(
|
||||
return it->second;
|
||||
}
|
||||
|
||||
gd::String partiallyMangledName = originalObjectName;
|
||||
gd::String partiallyMangledName = GetMangledNameWithForbiddenUnderscore(originalObjectName);
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
@@ -43,7 +43,15 @@ const gd::String& EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
|
||||
return it->second;
|
||||
}
|
||||
|
||||
gd::String partiallyMangledName = externalEventsName;
|
||||
gd::String partiallyMangledName = GetMangledNameWithForbiddenUnderscore(externalEventsName);
|
||||
|
||||
mangledExternalEventsNames[externalEventsName] = "GDExternalEvents" + partiallyMangledName;
|
||||
return mangledExternalEventsNames[externalEventsName];
|
||||
}
|
||||
|
||||
gd::String EventsCodeNameMangler::GetMangledNameWithForbiddenUnderscore(
|
||||
const gd::String &name) {
|
||||
gd::String partiallyMangledName = name;
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
@@ -57,11 +65,30 @@ const gd::String& EventsCodeNameMangler::GetExternalEventsFunctionMangledName(
|
||||
partiallyMangledName.replace(i, 1, "_" + gd::String::From(unallowedChar));
|
||||
}
|
||||
}
|
||||
|
||||
mangledExternalEventsNames[externalEventsName] = "GDExternalEvents" + partiallyMangledName;
|
||||
return mangledExternalEventsNames[externalEventsName];
|
||||
return partiallyMangledName;
|
||||
}
|
||||
|
||||
gd::String EventsCodeNameMangler::GetMangledName(
|
||||
const gd::String &name) {
|
||||
gd::String partiallyMangledName = name;
|
||||
static const gd::String allowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
|
||||
for (size_t i = 0; i < partiallyMangledName.size();
|
||||
++i) // Replace all unallowed letter by an underscore and the ascii
|
||||
// number of the letter
|
||||
{
|
||||
if (allowedCharacters.find_first_of(
|
||||
std::u32string(1, partiallyMangledName[i])) == gd::String::npos) {
|
||||
char32_t unallowedChar = partiallyMangledName[i];
|
||||
partiallyMangledName.replace(i, 1, "_" + gd::String::From(unallowedChar));
|
||||
}
|
||||
}
|
||||
return partiallyMangledName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const gd::String& ManObjListName(const gd::String &objectName) {
|
||||
return EventsCodeNameMangler::Get()->GetMangledObjectsListName(objectName);
|
||||
}
|
||||
|
@@ -36,6 +36,8 @@ class GD_CORE_API EventsCodeNameMangler {
|
||||
const gd::String &GetExternalEventsFunctionMangledName(
|
||||
const gd::String &externalEventsName);
|
||||
|
||||
static gd::String GetMangledName(const gd::String &name);
|
||||
|
||||
static EventsCodeNameMangler *Get();
|
||||
static void DestroySingleton();
|
||||
|
||||
@@ -44,6 +46,9 @@ class GD_CORE_API EventsCodeNameMangler {
|
||||
virtual ~EventsCodeNameMangler(){};
|
||||
static EventsCodeNameMangler *_singleton;
|
||||
|
||||
// This method is inlined to avoid to copy the returned string.
|
||||
static inline gd::String GetMangledNameWithForbiddenUnderscore(const gd::String &name);
|
||||
|
||||
std::unordered_map<gd::String, gd::String>
|
||||
mangledObjectNames; ///< Memoized results of mangling for objects
|
||||
std::unordered_map<gd::String, gd::String>
|
||||
|
@@ -69,12 +69,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"EcrireFichierExp",
|
||||
_("Write a value"),
|
||||
_("Write the result of the expression in the stored data, in the "
|
||||
_("Save a value"),
|
||||
_("Save the result of the expression in the stored data, in the "
|
||||
"specified element.\nSpecify the structure leading to the "
|
||||
"element using / (example : Root/Level/Current)\nSpaces are "
|
||||
"forbidden in element names."),
|
||||
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
|
||||
_("Save _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
|
||||
"",
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
@@ -85,12 +85,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"EcrireFichierTxt",
|
||||
_("Write a text"),
|
||||
_("Write the text in the specified storage, in the specified "
|
||||
_("Save a text"),
|
||||
_("Save the text in the specified storage, in the specified "
|
||||
"element.\nSpecify "
|
||||
"the structure leading to the element using / (example : "
|
||||
"Root/Level/Current)\nSpaces are forbidden in element names."),
|
||||
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
|
||||
_("Save _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
|
||||
"",
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
@@ -101,13 +101,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"LireFichierExp",
|
||||
_("Read a value"),
|
||||
_("Read the value saved in the specified element and store it in a "
|
||||
_("Load a value"),
|
||||
_("Load 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."),
|
||||
_("Read _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
|
||||
_("Load _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
|
||||
"",
|
||||
"res/actions/fichier24.png",
|
||||
"res/actions/fichier.png")
|
||||
@@ -119,13 +119,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"LireFichierTxt",
|
||||
_("Read a text"),
|
||||
_("Read the text saved in the specified element and store it in a "
|
||||
_("Load a text"),
|
||||
_("Load 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."),
|
||||
_("Read _PARAM1_ from storage _PARAM0_ and store as text in "
|
||||
_("Load _PARAM1_ from storage _PARAM0_ and store as text in "
|
||||
"_PARAM3_"),
|
||||
"",
|
||||
"res/actions/fichier24.png",
|
||||
|
@@ -110,11 +110,11 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
|
||||
.GetBoolAttribute("automatic", true));
|
||||
|
||||
if (spriteElement.HasChild("CustomCollisionMask"))
|
||||
sprite.SetCollisionMaskAutomatic(
|
||||
sprite.SetFullImageCollisionMask(
|
||||
!spriteElement.GetChild("CustomCollisionMask")
|
||||
.GetBoolAttribute("custom", false));
|
||||
else
|
||||
sprite.SetCollisionMaskAutomatic(
|
||||
sprite.SetFullImageCollisionMask(
|
||||
!spriteElement.GetBoolAttribute("hasCustomCollisionMask", false));
|
||||
|
||||
std::vector<Polygon2d> mask;
|
||||
@@ -173,7 +173,7 @@ void SaveSpritesDirection(const vector<Sprite>& sprites,
|
||||
.SetAttribute("automatic", sprites[i].IsDefaultCenterPoint());
|
||||
|
||||
spriteElement.SetAttribute("hasCustomCollisionMask",
|
||||
!sprites[i].IsCollisionMaskAutomatic());
|
||||
!sprites[i].IsFullImageCollisionMask());
|
||||
|
||||
gd::SerializerElement& collisionMaskElement =
|
||||
spriteElement.AddChild("customCollisionMask");
|
||||
|
@@ -4,7 +4,9 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GDCore/Extensions/Builtin/SpriteExtension/Polygon2d.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -14,11 +16,10 @@ namespace gd {
|
||||
Point Sprite::badPoint("");
|
||||
|
||||
Sprite::Sprite()
|
||||
: automaticCollisionMask(true),
|
||||
: fullImageCollisionMask(false),
|
||||
origine("origine"),
|
||||
centre("centre"),
|
||||
automaticCentre(true) {
|
||||
}
|
||||
automaticCentre(true) {}
|
||||
|
||||
Sprite::~Sprite(){};
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#ifndef SPRITE_H
|
||||
#define SPRITE_H
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Extensions/Builtin/SpriteExtension/Point.h"
|
||||
#include "GDCore/Extensions/Builtin/SpriteExtension/Polygon2d.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -43,7 +44,7 @@ class GD_CORE_API Sprite {
|
||||
|
||||
/**
|
||||
* \brief Get the collision mask (custom or automatically generated owing to
|
||||
* IsCollisionMaskAutomatic())
|
||||
* IsFullImageCollisionMask())
|
||||
*
|
||||
* \warning If the image has not been loaded ( using LoadImage ) and the
|
||||
* collision mask is set as automatic, the returned mask won't be correct.
|
||||
@@ -66,7 +67,7 @@ class GD_CORE_API Sprite {
|
||||
|
||||
/**
|
||||
* \brief Set the custom collision mask.
|
||||
* Call then `SetCollisionMaskAutomatic(false)` to use it.
|
||||
* Call then `SetFullImageCollisionMask(false)` to use it.
|
||||
*/
|
||||
void SetCustomCollisionMask(const std::vector<Polygon2d>& collisionMask);
|
||||
|
||||
@@ -74,15 +75,15 @@ class GD_CORE_API Sprite {
|
||||
* \brief Return true if the collision mask is a bounding box, false if a
|
||||
* custom collision mask is used.
|
||||
*/
|
||||
inline bool IsCollisionMaskAutomatic() const {
|
||||
return automaticCollisionMask;
|
||||
inline bool IsFullImageCollisionMask() const {
|
||||
return fullImageCollisionMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Un/set use of the custom collision mask.
|
||||
*/
|
||||
inline void SetCollisionMaskAutomatic(bool enabled) {
|
||||
automaticCollisionMask = enabled;
|
||||
inline void SetFullImageCollisionMask(bool enabled) {
|
||||
fullImageCollisionMask = enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -161,9 +162,9 @@ class GD_CORE_API Sprite {
|
||||
private:
|
||||
gd::String image; ///< Name of the image to be loaded in Image Manager.
|
||||
|
||||
bool automaticCollisionMask; ///< True to use the custom collision mask.
|
||||
///< Otherwise, a basic bounding box is returned
|
||||
///< by GetCollisionMask()
|
||||
bool fullImageCollisionMask; ///< True to use a bounding box wrapping the
|
||||
///< whole image as collision mask. If false,
|
||||
///< custom collision mask is used.
|
||||
std::vector<Polygon2d> customCollisionMask; ///< Custom collision mask
|
||||
|
||||
std::vector<Point> points; ///< List of the points used by the sprite
|
||||
|
@@ -287,7 +287,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
|
||||
obj.AddCondition("AnimationName",
|
||||
_("Current animation name"),
|
||||
_("Check the animation by played by the object."),
|
||||
_("Check the animation played by the object."),
|
||||
_("The animation of _PARAM0_ is _PARAM1_"),
|
||||
_("Animations and images"),
|
||||
"res/conditions/animation24.png",
|
||||
|
@@ -25,13 +25,16 @@ namespace gd {
|
||||
|
||||
Animation SpriteObject::badAnimation;
|
||||
|
||||
SpriteObject::SpriteObject() : updateIfNotVisible(false) {}
|
||||
SpriteObject::SpriteObject()
|
||||
: updateIfNotVisible(false), adaptCollisionMaskAutomatically(true) {}
|
||||
|
||||
SpriteObject::~SpriteObject(){};
|
||||
|
||||
void SpriteObject::DoUnserializeFrom(gd::Project& project,
|
||||
const gd::SerializerElement& element) {
|
||||
updateIfNotVisible = element.GetBoolAttribute("updateIfNotVisible", true);
|
||||
adaptCollisionMaskAutomatically =
|
||||
element.GetBoolAttribute("adaptCollisionMaskAutomatically", false);
|
||||
|
||||
RemoveAllAnimations();
|
||||
const gd::SerializerElement& animationsElement =
|
||||
@@ -80,6 +83,8 @@ void SpriteObject::DoUnserializeFrom(gd::Project& project,
|
||||
|
||||
void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
element.SetAttribute("updateIfNotVisible", updateIfNotVisible);
|
||||
element.SetAttribute("adaptCollisionMaskAutomatically",
|
||||
adaptCollisionMaskAutomatically);
|
||||
|
||||
// Animations
|
||||
gd::SerializerElement& animationsElement = element.AddChild("animations");
|
||||
|
@@ -47,8 +47,7 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
|
||||
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
|
||||
bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value) override;
|
||||
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
|
||||
const gd::InitialInstance& position,
|
||||
@@ -118,14 +117,30 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
|
||||
const std::vector<Animation>& GetAllAnimations() const { return animations; }
|
||||
|
||||
/**
|
||||
* \brief Set if the object animation should be played even if the object is hidden
|
||||
* or far from the camera.
|
||||
* @brief Check if the collision mask adapts automatically to the animation.
|
||||
*/
|
||||
void SetUpdateIfNotVisible(bool updateIfNotVisible_) { updateIfNotVisible = updateIfNotVisible_; }
|
||||
bool AdaptCollisionMaskAutomatically() const {
|
||||
return adaptCollisionMaskAutomatically;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the object animation should be played even if the object is hidden
|
||||
* or far from the camera (false by default).
|
||||
* @brief Set if the collision mask adapts automatically to the animation.
|
||||
*/
|
||||
void SetAdaptCollisionMaskAutomatically(bool enable) {
|
||||
adaptCollisionMaskAutomatically = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the object animation should be played even if the object is
|
||||
* hidden or far from the camera.
|
||||
*/
|
||||
void SetUpdateIfNotVisible(bool updateIfNotVisible_) {
|
||||
updateIfNotVisible = updateIfNotVisible_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the object animation should be played even if the object
|
||||
* is hidden or far from the camera (false by default).
|
||||
*/
|
||||
bool GetUpdateIfNotVisible() const { return updateIfNotVisible; }
|
||||
///@}
|
||||
@@ -137,11 +152,15 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
|
||||
|
||||
mutable std::vector<Animation> animations;
|
||||
bool updateIfNotVisible; ///< If set to true, ask the game engine to play
|
||||
///< object animation even if hidden or far from the
|
||||
///< screen.
|
||||
///< object animation even if hidden or far from
|
||||
///< the screen.
|
||||
|
||||
static Animation badAnimation; //< Bad animation when an out of bound
|
||||
// animation is requested.
|
||||
static Animation badAnimation; //< Bad animation when an out of bound
|
||||
// animation is requested.
|
||||
bool adaptCollisionMaskAutomatically; ///< If set to true, the collision
|
||||
///< mask will be automatically
|
||||
///< adapted to the animation of the
|
||||
///< object.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -172,7 +172,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
_("Search the last occurrence in a text, starting from a position"),
|
||||
_("Search in a text the last occurrence, starting from a position "
|
||||
"(return "
|
||||
" the position of the result, from the beginning of the string, or "
|
||||
"the position of the result, from the beginning of the string, or "
|
||||
"-1 if not found)"),
|
||||
"",
|
||||
"res/conditions/toujours24_black.png")
|
||||
|
135
Core/GDCore/Extensions/Metadata/AbstractFunctionMetadata.h
Normal file
135
Core/GDCore/Extensions/Metadata/AbstractFunctionMetadata.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "ParameterMetadata.h"
|
||||
#include "ParameterOptions.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class EventsCodeGenerator;
|
||||
class EventsCodeGenerationContext;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Describe user-friendly information about an expression or an
|
||||
* instruction (action or condition), its parameters and the function name as
|
||||
* well as other information for code generation.
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API AbstractFunctionMetadata {
|
||||
public:
|
||||
AbstractFunctionMetadata(){};
|
||||
virtual ~AbstractFunctionMetadata(){};
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::AddParameter
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
AddParameter(const gd::String &type, const gd::String &label,
|
||||
const gd::String &supplementaryInformation = "",
|
||||
bool parameterIsOptional = false) = 0;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::AddCodeOnlyParameter
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
AddCodeOnlyParameter(const gd::String &type,
|
||||
const gd::String &supplementaryInformation) = 0;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetDefaultValue
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetDefaultValue(const gd::String &defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetParameterExtraInfo
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetParameterExtraInfo(const gd::String &defaultValue) = 0;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetParameterLongDescription
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetParameterLongDescription(const gd::String &longDescription) = 0;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetHidden
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &SetHidden() = 0;
|
||||
|
||||
/**
|
||||
* Set that the instruction is private - it can't be used outside of the
|
||||
* object/ behavior that it is attached too.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &SetPrivate() = 0;
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in layouts or external events.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &SetRelevantForLayoutEventsOnly() = 0;
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in function events.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &SetRelevantForFunctionEventsOnly() = 0;
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in asynchronous function events.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetRelevantForAsynchronousFunctionEventsOnly() = 0;
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in custom object events.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &SetRelevantForCustomObjectEventsOnly() = 0;
|
||||
|
||||
/**
|
||||
* \brief Set the function that should be called when generating the source
|
||||
* code from events.
|
||||
* \param functionName the name of the function to call
|
||||
* \note Shortcut for `codeExtraInformation.SetFunctionName`.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetFunctionName(const gd::String &functionName) = 0;
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
SetIncludeFile(const gd::String &includeFile) = 0;
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
virtual AbstractFunctionMetadata &
|
||||
AddIncludeFile(const gd::String &includeFile) = 0;
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
virtual const std::vector<gd::String> &GetIncludeFiles() const = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -3,8 +3,10 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef BEHAVIORMETADATA_H
|
||||
#define BEHAVIORMETADATA_H
|
||||
#pragma once
|
||||
|
||||
#include "InstructionOrExpressionContainerMetadata.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
@@ -25,7 +27,7 @@ namespace gd {
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API BehaviorMetadata {
|
||||
class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMetadata {
|
||||
public:
|
||||
BehaviorMetadata(
|
||||
const gd::String& extensionNamespace,
|
||||
@@ -67,7 +69,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new action as being part of the behavior.
|
||||
@@ -80,7 +82,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new condition as being part of the behavior.
|
||||
@@ -91,7 +93,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new action as being part of the behavior.
|
||||
@@ -102,7 +104,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
/**
|
||||
* Declare a new action as being part of the extension.
|
||||
*/
|
||||
@@ -110,7 +112,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new string expression as being part of the extension.
|
||||
@@ -119,7 +121,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression and condition as being part of the
|
||||
@@ -134,7 +136,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& description,
|
||||
const gd::String& sentenceName,
|
||||
const gd::String& group,
|
||||
const gd::String& icon);
|
||||
const gd::String& icon) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression, condition and action as being part of the
|
||||
@@ -151,7 +153,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& description,
|
||||
const gd::String& sentenceName,
|
||||
const gd::String& group,
|
||||
const gd::String& icon);
|
||||
const gd::String& icon) override;
|
||||
|
||||
/**
|
||||
* \brief Create a new action which is the duplicate of the specified one.
|
||||
@@ -160,7 +162,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
* one.
|
||||
*/
|
||||
gd::InstructionMetadata& AddDuplicatedAction(
|
||||
const gd::String& newActionName, const gd::String& copiedActionName);
|
||||
const gd::String& newActionName, const gd::String& copiedActionName) override;
|
||||
|
||||
/**
|
||||
* \brief Create a new condition which is the duplicate of the specified one.
|
||||
@@ -170,7 +172,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
*/
|
||||
gd::InstructionMetadata& AddDuplicatedCondition(
|
||||
const gd::String& newConditionName,
|
||||
const gd::String& copiedConditionName);
|
||||
const gd::String& copiedConditionName) override;
|
||||
|
||||
/**
|
||||
* \brief Create a new expression which is the duplicate of the specified one.
|
||||
@@ -193,9 +195,9 @@ class GD_CORE_API BehaviorMetadata {
|
||||
const gd::String& newExpressionName,
|
||||
const gd::String& copiedExpressionName);
|
||||
|
||||
BehaviorMetadata& SetFullName(const gd::String& fullname_);
|
||||
BehaviorMetadata& SetFullName(const gd::String& fullname_) override;
|
||||
BehaviorMetadata& SetDefaultName(const gd::String& defaultName_);
|
||||
BehaviorMetadata& SetDescription(const gd::String& description_);
|
||||
BehaviorMetadata& SetDescription(const gd::String& description_) override;
|
||||
BehaviorMetadata& SetGroup(const gd::String& group_);
|
||||
|
||||
/**
|
||||
@@ -203,12 +205,12 @@ class GD_CORE_API BehaviorMetadata {
|
||||
* \note The requirement may vary depending on the platform: Most of the time,
|
||||
* the include file contains the declaration of the behavior.
|
||||
*/
|
||||
BehaviorMetadata& SetIncludeFile(const gd::String& includeFile);
|
||||
BehaviorMetadata& SetIncludeFile(const gd::String& includeFile) override;
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
BehaviorMetadata& AddIncludeFile(const gd::String& includeFile);
|
||||
BehaviorMetadata& AddIncludeFile(const gd::String& includeFile) override;
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing required files.
|
||||
@@ -221,7 +223,7 @@ class GD_CORE_API BehaviorMetadata {
|
||||
* Get the help path of the behavior, relative to the GDevelop documentation
|
||||
* root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; }
|
||||
const gd::String& GetHelpPath() const override { return helpPath; }
|
||||
|
||||
/**
|
||||
* Set the help path of the behavior, relative to the GDevelop documentation
|
||||
@@ -230,17 +232,17 @@ class GD_CORE_API BehaviorMetadata {
|
||||
* The behavior instructions will have this help path set by
|
||||
* default, unless you call SetHelpPath on them.
|
||||
*/
|
||||
BehaviorMetadata& SetHelpPath(const gd::String& path) {
|
||||
BehaviorMetadata& SetHelpPath(const gd::String& path) override {
|
||||
helpPath = path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetName() const;
|
||||
const gd::String& GetFullName() const { return fullname; }
|
||||
const gd::String& GetName() const override;
|
||||
const gd::String& GetFullName() const override { return fullname; }
|
||||
const gd::String& GetDefaultName() const { return defaultName; }
|
||||
const gd::String& GetDescription() const { return description; }
|
||||
const gd::String& GetDescription() const override { return description; }
|
||||
const gd::String& GetGroup() const { return group; }
|
||||
const gd::String& GetIconFilename() const { return iconFilename; }
|
||||
const gd::String& GetIconFilename() const override { return iconFilename; }
|
||||
|
||||
/**
|
||||
* \brief Set the type of the object that this behavior can be used on.
|
||||
@@ -293,22 +295,22 @@ class GD_CORE_API BehaviorMetadata {
|
||||
* \brief Return a reference to a map containing the names of the actions
|
||||
* (as keys) and the metadata associated with (as values).
|
||||
*/
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllActions() { return actionsInfos; };
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllActions() override { return actionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllConditions() { return conditionsInfos; };
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllConditions() override { return conditionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllExpressions() { return expressionsInfos; };
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllExpressions() override { return expressionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() { return strExpressionsInfos; };
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
|
||||
|
||||
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
@@ -337,5 +339,3 @@ class GD_CORE_API BehaviorMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // BEHAVIORMETADATA_H
|
||||
|
@@ -3,9 +3,10 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef EXPRESSIONMETADATA_H
|
||||
#define EXPRESSIONMETADATA_H
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#pragma once
|
||||
|
||||
#include "AbstractFunctionMetadata.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
@@ -17,7 +18,6 @@ class Layout;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Information about how generate code for an expression
|
||||
*/
|
||||
@@ -27,79 +27,7 @@ class ExpressionCodeGenerationInformation {
|
||||
: staticFunction(false), hasCustomCodeGenerator(false){};
|
||||
virtual ~ExpressionCodeGenerationInformation(){};
|
||||
|
||||
/**
|
||||
* \brief Set the function name which will be used when generating the code.
|
||||
* \param functionName the name of the function to call
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& SetFunctionName(
|
||||
const gd::String& functionName) {
|
||||
functionCallName = functionName;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set that the function is static
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& SetStatic() {
|
||||
staticFunction = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& SetIncludeFile(
|
||||
const gd::String& includeFile) {
|
||||
includeFiles.clear();
|
||||
includeFiles.push_back(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& AddIncludeFile(
|
||||
const gd::String& includeFile) {
|
||||
if (std::find(includeFiles.begin(), includeFiles.end(), includeFile) ==
|
||||
includeFiles.end())
|
||||
includeFiles.push_back(includeFile);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String>& GetIncludeFiles() const {
|
||||
return includeFiles;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set that the function must be generated using a custom code
|
||||
* generator.
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& SetCustomCodeGenerator(
|
||||
std::function<gd::String(const std::vector<gd::Expression>& parameters,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
codeGenerator) {
|
||||
hasCustomCodeGenerator = true;
|
||||
customCodeGenerator = codeGenerator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerationInformation& RemoveCustomCodeGenerator() {
|
||||
hasCustomCodeGenerator = false;
|
||||
std::function<gd::String(const std::vector<gd::Expression>& parameters,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
emptyFunction;
|
||||
customCodeGenerator = emptyFunction;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasCustomCodeGenerator() const { return hasCustomCodeGenerator; }
|
||||
|
||||
// TODO Move these attributes to ExpressionMetadata.
|
||||
bool staticFunction;
|
||||
gd::String functionCallName;
|
||||
bool hasCustomCodeGenerator;
|
||||
@@ -107,8 +35,6 @@ class ExpressionCodeGenerationInformation {
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
customCodeGenerator;
|
||||
|
||||
private:
|
||||
std::vector<gd::String> includeFiles;
|
||||
};
|
||||
|
||||
@@ -118,7 +44,7 @@ class ExpressionCodeGenerationInformation {
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API ExpressionMetadata {
|
||||
class GD_CORE_API ExpressionMetadata : public gd::AbstractFunctionMetadata {
|
||||
public:
|
||||
/**
|
||||
* Construct a new expression metadata.
|
||||
@@ -144,7 +70,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* \brief Set the expression as not shown in the IDE.
|
||||
*/
|
||||
ExpressionMetadata& SetHidden();
|
||||
ExpressionMetadata& SetHidden() override;
|
||||
|
||||
/**
|
||||
* \brief Set the group of the instruction in the IDE.
|
||||
@@ -179,7 +105,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
* Set that the instruction is private - it can't be used outside of the
|
||||
* object/ behavior that it is attached too.
|
||||
*/
|
||||
ExpressionMetadata& SetPrivate() {
|
||||
ExpressionMetadata& SetPrivate() override {
|
||||
isPrivate = true;
|
||||
return *this;
|
||||
}
|
||||
@@ -216,7 +142,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in layouts or external events.
|
||||
*/
|
||||
ExpressionMetadata &SetRelevantForLayoutEventsOnly() {
|
||||
ExpressionMetadata &SetRelevantForLayoutEventsOnly() override {
|
||||
relevantContext = "Layout";
|
||||
return *this;
|
||||
}
|
||||
@@ -224,7 +150,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in function events.
|
||||
*/
|
||||
ExpressionMetadata &SetRelevantForFunctionEventsOnly() {
|
||||
ExpressionMetadata &SetRelevantForFunctionEventsOnly() override {
|
||||
relevantContext = "Function";
|
||||
return *this;
|
||||
}
|
||||
@@ -232,7 +158,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in asynchronous function events.
|
||||
*/
|
||||
ExpressionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() {
|
||||
ExpressionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() override {
|
||||
relevantContext = "AsynchronousFunction";
|
||||
return *this;
|
||||
}
|
||||
@@ -240,7 +166,7 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in custom object events.
|
||||
*/
|
||||
ExpressionMetadata &SetRelevantForCustomObjectEventsOnly() {
|
||||
ExpressionMetadata &SetRelevantForCustomObjectEventsOnly() override {
|
||||
relevantContext = "Object";
|
||||
return *this;
|
||||
}
|
||||
@@ -248,17 +174,17 @@ class GD_CORE_API ExpressionMetadata {
|
||||
/**
|
||||
* \see gd::InstructionMetadata::AddParameter
|
||||
*/
|
||||
gd::ExpressionMetadata& AddParameter(
|
||||
const gd::String& type,
|
||||
const gd::String& description,
|
||||
const gd::String& supplementaryInformation = "",
|
||||
bool parameterIsOptional = false);
|
||||
gd::ExpressionMetadata &
|
||||
AddParameter(const gd::String &type, const gd::String &label,
|
||||
const gd::String &supplementaryInformation = "",
|
||||
bool parameterIsOptional = false) override;
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::AddCodeOnlyParameter
|
||||
*/
|
||||
gd::ExpressionMetadata& AddCodeOnlyParameter(
|
||||
const gd::String& type, const gd::String& supplementaryInformation);
|
||||
gd::ExpressionMetadata &
|
||||
AddCodeOnlyParameter(const gd::String &type,
|
||||
const gd::String &supplementaryInformation) override;
|
||||
|
||||
/**
|
||||
* Set the default value used in editor (or if an optional parameter is empty
|
||||
@@ -266,8 +192,9 @@ class GD_CORE_API ExpressionMetadata {
|
||||
*
|
||||
* \see AddParameter
|
||||
*/
|
||||
ExpressionMetadata& SetDefaultValue(gd::String defaultValue_) {
|
||||
if (!parameters.empty()) parameters.back().SetDefaultValue(defaultValue_);
|
||||
ExpressionMetadata &SetDefaultValue(const gd::String &defaultValue) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetDefaultValue(defaultValue);
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -277,7 +204,8 @@ class GD_CORE_API ExpressionMetadata {
|
||||
*
|
||||
* \see AddParameter
|
||||
*/
|
||||
ExpressionMetadata& SetParameterLongDescription(gd::String longDescription) {
|
||||
ExpressionMetadata &
|
||||
SetParameterLongDescription(const gd::String &longDescription) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetLongDescription(longDescription);
|
||||
return *this;
|
||||
@@ -290,7 +218,8 @@ class GD_CORE_API ExpressionMetadata {
|
||||
*
|
||||
* \see AddParameter
|
||||
*/
|
||||
ExpressionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
|
||||
ExpressionMetadata &SetParameterExtraInfo(
|
||||
const gd::String &extraInfo) override {
|
||||
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
|
||||
return *this;
|
||||
}
|
||||
@@ -312,50 +241,6 @@ class GD_CORE_API ExpressionMetadata {
|
||||
return requiredBaseObjectCapability;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set the function that should be called when generating the source
|
||||
* code from events.
|
||||
* \param functionName the name of the function to call
|
||||
* \note Shortcut for `codeExtraInformation.SetFunctionName`.
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& SetFunctionName(
|
||||
const gd::String& functionName) {
|
||||
return codeExtraInformation.SetFunctionName(functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the structure containing the information about code
|
||||
* generation for the expression.
|
||||
*/
|
||||
ExpressionCodeGenerationInformation& GetCodeExtraInformation() {
|
||||
return codeExtraInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
ExpressionMetadata &SetIncludeFile(const gd::String &includeFile) {
|
||||
codeExtraInformation.SetIncludeFile(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
ExpressionMetadata &AddIncludeFile(const gd::String &includeFile) {
|
||||
codeExtraInformation.AddIncludeFile(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String>& GetIncludeFiles() const {
|
||||
return codeExtraInformation.GetIncludeFiles();
|
||||
}
|
||||
|
||||
ExpressionCodeGenerationInformation codeExtraInformation;
|
||||
|
||||
bool IsShown() const { return shown; }
|
||||
const gd::String& GetReturnType() const { return returnType; }
|
||||
const gd::String& GetFullName() const { return fullname; }
|
||||
@@ -375,6 +260,99 @@ class GD_CORE_API ExpressionMetadata {
|
||||
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Set the function name which will be used when generating the code.
|
||||
* \param functionName the name of the function to call
|
||||
*/
|
||||
ExpressionMetadata& SetFunctionName(
|
||||
const gd::String& functionName) override {
|
||||
codeExtraInformation.functionCallName = functionName;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Return the name of the function which will be called in the generated code.
|
||||
*/
|
||||
const gd::String &GetFunctionName() {
|
||||
return codeExtraInformation.functionCallName;
|
||||
}
|
||||
/**
|
||||
* \brief Set that the function is static
|
||||
*/
|
||||
ExpressionMetadata& SetStatic() {
|
||||
codeExtraInformation.staticFunction = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
ExpressionMetadata& SetIncludeFile(
|
||||
const gd::String& includeFile) override {
|
||||
codeExtraInformation.includeFiles.clear();
|
||||
codeExtraInformation.includeFiles.push_back(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
ExpressionMetadata& AddIncludeFile(
|
||||
const gd::String& includeFile) override {
|
||||
if (std::find(codeExtraInformation.includeFiles.begin(), codeExtraInformation.includeFiles.end(), includeFile) ==
|
||||
codeExtraInformation.includeFiles.end())
|
||||
codeExtraInformation.includeFiles.push_back(includeFile);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String>& GetIncludeFiles() const override {
|
||||
return codeExtraInformation.includeFiles;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set that the function must be generated using a custom code
|
||||
* generator.
|
||||
*/
|
||||
ExpressionMetadata& SetCustomCodeGenerator(
|
||||
std::function<gd::String(const std::vector<gd::Expression>& parameters,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
codeGenerator) {
|
||||
codeExtraInformation.hasCustomCodeGenerator = true;
|
||||
codeExtraInformation.customCodeGenerator = codeGenerator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExpressionMetadata& RemoveCustomCodeGenerator() {
|
||||
codeExtraInformation.hasCustomCodeGenerator = false;
|
||||
std::function<gd::String(const std::vector<gd::Expression>& parameters,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
emptyFunction;
|
||||
codeExtraInformation.customCodeGenerator = emptyFunction;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasCustomCodeGenerator() const { return codeExtraInformation.hasCustomCodeGenerator; }
|
||||
|
||||
/**
|
||||
* \brief Return the structure containing the information about code
|
||||
* generation for the expression.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
ExpressionMetadata& GetCodeExtraInformation() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerationInformation codeExtraInformation;
|
||||
|
||||
private:
|
||||
gd::String returnType;
|
||||
gd::String fullname;
|
||||
@@ -391,6 +369,3 @@ class GD_CORE_API ExpressionMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
#endif // EXPRESSIONMETADATA_H
|
||||
|
@@ -4,8 +4,10 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef INSTRUCTIONMETADATA_H
|
||||
#define INSTRUCTIONMETADATA_H
|
||||
#pragma once
|
||||
|
||||
#include "AbstractFunctionMetadata.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -33,7 +35,7 @@ namespace gd {
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API InstructionMetadata {
|
||||
class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instruction metadata.
|
||||
@@ -96,7 +98,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
* Set that the instruction is private - it can't be used outside of the
|
||||
* object/ behavior that it is attached too.
|
||||
*/
|
||||
InstructionMetadata &SetPrivate() {
|
||||
InstructionMetadata &SetPrivate() override {
|
||||
isPrivate = true;
|
||||
return *this;
|
||||
}
|
||||
@@ -133,7 +135,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in layouts or external events.
|
||||
*/
|
||||
InstructionMetadata &SetRelevantForLayoutEventsOnly() {
|
||||
InstructionMetadata &SetRelevantForLayoutEventsOnly() override {
|
||||
relevantContext = "Layout";
|
||||
return *this;
|
||||
}
|
||||
@@ -141,7 +143,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in function events.
|
||||
*/
|
||||
InstructionMetadata &SetRelevantForFunctionEventsOnly() {
|
||||
InstructionMetadata &SetRelevantForFunctionEventsOnly() override {
|
||||
relevantContext = "Function";
|
||||
return *this;
|
||||
}
|
||||
@@ -149,7 +151,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in asynchronous function events.
|
||||
*/
|
||||
InstructionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() {
|
||||
InstructionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() override {
|
||||
relevantContext = "AsynchronousFunction";
|
||||
return *this;
|
||||
}
|
||||
@@ -157,7 +159,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
/**
|
||||
* Set that the instruction can be used in custom object events.
|
||||
*/
|
||||
InstructionMetadata &SetRelevantForCustomObjectEventsOnly() {
|
||||
InstructionMetadata &SetRelevantForCustomObjectEventsOnly() override {
|
||||
relevantContext = "Object";
|
||||
return *this;
|
||||
}
|
||||
@@ -192,7 +194,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
*
|
||||
* Used mainly when an instruction is deprecated.
|
||||
*/
|
||||
InstructionMetadata &SetHidden() {
|
||||
InstructionMetadata &SetHidden() override {
|
||||
hidden = true;
|
||||
return *this;
|
||||
}
|
||||
@@ -231,7 +233,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
const gd::String &type,
|
||||
const gd::String &label,
|
||||
const gd::String &supplementaryInformation = "",
|
||||
bool parameterIsOptional = false);
|
||||
bool parameterIsOptional = false) override;
|
||||
|
||||
/**
|
||||
* \brief Add a parameter not displayed in editor.
|
||||
@@ -245,7 +247,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
* \see EventsCodeGenerator::GenerateParametersCodes
|
||||
*/
|
||||
InstructionMetadata &AddCodeOnlyParameter(
|
||||
const gd::String &type, const gd::String &supplementaryInformation);
|
||||
const gd::String &type, const gd::String &supplementaryInformation) override;
|
||||
|
||||
/**
|
||||
* \brief Set the default value used in editor (or if an optional parameter is
|
||||
@@ -253,7 +255,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
*
|
||||
* \see AddParameter
|
||||
*/
|
||||
InstructionMetadata &SetDefaultValue(const gd::String &defaultValue_) {
|
||||
InstructionMetadata &SetDefaultValue(const gd::String &defaultValue_) override {
|
||||
if (!parameters.empty()) parameters.back().SetDefaultValue(defaultValue_);
|
||||
return *this;
|
||||
};
|
||||
@@ -265,7 +267,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
* \see AddParameter
|
||||
*/
|
||||
InstructionMetadata &SetParameterLongDescription(
|
||||
const gd::String &longDescription) {
|
||||
const gd::String &longDescription) override {
|
||||
if (!parameters.empty())
|
||||
parameters.back().SetLongDescription(longDescription);
|
||||
return *this;
|
||||
@@ -278,7 +280,7 @@ class GD_CORE_API InstructionMetadata {
|
||||
*
|
||||
* \see AddParameter
|
||||
*/
|
||||
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
|
||||
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) override {
|
||||
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
|
||||
return *this;
|
||||
}
|
||||
@@ -382,128 +384,13 @@ class GD_CORE_API InstructionMetadata {
|
||||
/**
|
||||
* \brief Defines information about how generate the code for an instruction
|
||||
*/
|
||||
class ExtraInformation {
|
||||
class ExtraInformation {
|
||||
public:
|
||||
enum AccessType { Reference, MutatorAndOrAccessor, Mutators };
|
||||
ExtraInformation() : accessType(Reference), hasCustomCodeGenerator(false){};
|
||||
virtual ~ExtraInformation(){};
|
||||
|
||||
/**
|
||||
* Set the name of the function which will be called in the generated code.
|
||||
* \param functionName the name of the function to call.
|
||||
*/
|
||||
ExtraInformation &SetFunctionName(const gd::String &functionName_) {
|
||||
functionCallName = functionName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the function, doing asynchronous work, which will be
|
||||
* called in the generated code. This function should return an asynchronous
|
||||
* task (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
|
||||
*
|
||||
* \param functionName the name of the function doing asynchronous work to
|
||||
* call.
|
||||
*/
|
||||
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName_) {
|
||||
asyncFunctionCallName = functionName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare if the instruction being declared is somewhat manipulating in a
|
||||
* standard way.
|
||||
*/
|
||||
ExtraInformation &SetManipulatedType(const gd::String &type_) {
|
||||
type = type_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If InstructionMetadata::ExtraInformation::SetManipulatedType was called
|
||||
* with "number" or "string", this function will tell the code generator the
|
||||
* name of the getter function used to retrieve the data value.
|
||||
*
|
||||
* Usage example:
|
||||
* \code
|
||||
* obj.AddAction("String",
|
||||
* _("Change the string"),
|
||||
* _("Change the string of a text"),
|
||||
* _("the string"),
|
||||
* _("Text"),
|
||||
* "CppPlatform/Extensions/text24.png",
|
||||
* "CppPlatform/Extensions/text_black.png");
|
||||
*
|
||||
* .AddParameter("object", _("Object"), "Text", false)
|
||||
* .AddParameter("operator", _("Modification operator"), "string")
|
||||
* .AddParameter("string", _("String"))
|
||||
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString");
|
||||
*
|
||||
* DECLARE_END_OBJECT_ACTION()
|
||||
* \endcode
|
||||
*/
|
||||
ExtraInformation &SetGetter(const gd::String &getter) {
|
||||
optionalAssociatedInstruction = getter;
|
||||
accessType = MutatorAndOrAccessor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExtraInformation &SetMutators(
|
||||
const std::map<gd::String, gd::String> &mutators) {
|
||||
optionalMutators = mutators;
|
||||
accessType = Mutators;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
ExtraInformation &SetIncludeFile(const gd::String &includeFile) {
|
||||
includeFiles.clear();
|
||||
includeFiles.push_back(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
ExtraInformation &AddIncludeFile(const gd::String &includeFile) {
|
||||
if (std::find(includeFiles.begin(), includeFiles.end(), includeFile) ==
|
||||
includeFiles.end())
|
||||
includeFiles.push_back(includeFile);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String> &GetIncludeFiles() const {
|
||||
return includeFiles;
|
||||
};
|
||||
|
||||
ExtraInformation &SetCustomCodeGenerator(
|
||||
std::function<gd::String(Instruction &instruction,
|
||||
gd::EventsCodeGenerator &codeGenerator,
|
||||
gd::EventsCodeGenerationContext &context)>
|
||||
codeGenerator) {
|
||||
hasCustomCodeGenerator = true;
|
||||
customCodeGenerator = codeGenerator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExtraInformation &RemoveCustomCodeGenerator() {
|
||||
hasCustomCodeGenerator = false;
|
||||
std::function<gd::String(Instruction & instruction,
|
||||
gd::EventsCodeGenerator & codeGenerator,
|
||||
gd::EventsCodeGenerationContext & context)>
|
||||
emptyFunction;
|
||||
customCodeGenerator = emptyFunction;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasCustomCodeGenerator() const { return hasCustomCodeGenerator; }
|
||||
|
||||
// TODO Move these attributes to InstructionMetadata.
|
||||
gd::String functionCallName;
|
||||
gd::String asyncFunctionCallName;
|
||||
gd::String type;
|
||||
@@ -512,75 +399,156 @@ class GD_CORE_API InstructionMetadata {
|
||||
std::map<gd::String, gd::String> optionalMutators;
|
||||
bool hasCustomCodeGenerator;
|
||||
std::function<gd::String(Instruction &instruction,
|
||||
gd::EventsCodeGenerator &codeGenerator,
|
||||
gd::EventsCodeGenerationContext &context)>
|
||||
gd::EventsCodeGenerator &codeGenerator,
|
||||
gd::EventsCodeGenerationContext &context)>
|
||||
customCodeGenerator;
|
||||
|
||||
private:
|
||||
std::vector<gd::String> includeFiles;
|
||||
};
|
||||
ExtraInformation codeExtraInformation; ///< Information about how generate
|
||||
///< code for the instruction
|
||||
|
||||
/**
|
||||
* \brief Return the structure containing the information about code
|
||||
* generation for the instruction.
|
||||
*/
|
||||
ExtraInformation &GetCodeExtraInformation() { return codeExtraInformation; }
|
||||
|
||||
/**
|
||||
* \brief Declare if the instruction being declared is somewhat manipulating
|
||||
* in a standard way. \param type "number" or "string" \note Shortcut for
|
||||
* `codeExtraInformation.SetManipulatedType(type)`.
|
||||
*/
|
||||
ExtraInformation &SetManipulatedType(const gd::String &type_) {
|
||||
return codeExtraInformation.SetManipulatedType(type_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the function which will be called in the generated code.
|
||||
* \param functionName the name of the function to call.
|
||||
* \note Shortcut for `codeExtraInformation.SetFunctionName`.
|
||||
*/
|
||||
ExtraInformation &SetFunctionName(const gd::String &functionName) {
|
||||
return codeExtraInformation.SetFunctionName(functionName);
|
||||
InstructionMetadata &SetFunctionName(const gd::String &functionName_) override {
|
||||
codeExtraInformation.functionCallName = functionName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the function, doing asynchronous work, which will be called
|
||||
* in the generated code. This function should return an asynchronous task
|
||||
* (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
|
||||
* Set the name of the function, doing asynchronous work, which will be
|
||||
* called in the generated code. This function should return an asynchronous
|
||||
* task (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
|
||||
*
|
||||
* \param functionName the name of the function doing asynchronous work to
|
||||
* call. \note Shortcut for `codeExtraInformation.SetAsyncFunctionName`.
|
||||
* call.
|
||||
*/
|
||||
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName) {
|
||||
return codeExtraInformation.SetAsyncFunctionName(functionName);
|
||||
InstructionMetadata &SetAsyncFunctionName(const gd::String &functionName_) {
|
||||
codeExtraInformation.asyncFunctionCallName = functionName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the function which will be called in the generated code.
|
||||
*/
|
||||
const gd::String &GetFunctionName() {
|
||||
return codeExtraInformation.functionCallName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the function, doing asynchronous work, which will be
|
||||
* called in the generated code. This function should return an asynchronous
|
||||
* task (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
|
||||
*/
|
||||
const gd::String &GetAsyncFunctionName() {
|
||||
return codeExtraInformation.asyncFunctionCallName;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Declare if the instruction being declared is somewhat manipulating
|
||||
* in a standard way.
|
||||
*
|
||||
* \param type "number" or "string"
|
||||
*/
|
||||
InstructionMetadata &SetManipulatedType(const gd::String &type_) {
|
||||
codeExtraInformation.type = type_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If InstructionMetadata::ExtraInformation::SetManipulatedType was called
|
||||
* with "number" or "string", this function will tell the code generator the
|
||||
* name of the getter function used to retrieve the data value.
|
||||
*
|
||||
* Usage example:
|
||||
* \code
|
||||
* obj.AddAction("String",
|
||||
* _("Change the string"),
|
||||
* _("Change the string of a text"),
|
||||
* _("the string"),
|
||||
* _("Text"),
|
||||
* "CppPlatform/Extensions/text24.png",
|
||||
* "CppPlatform/Extensions/text_black.png");
|
||||
*
|
||||
* .AddParameter("object", _("Object"), "Text", false)
|
||||
* .AddParameter("operator", _("Modification operator"), "string")
|
||||
* .AddParameter("string", _("String"))
|
||||
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString");
|
||||
*
|
||||
* DECLARE_END_OBJECT_ACTION()
|
||||
* \endcode
|
||||
*/
|
||||
InstructionMetadata &SetGetter(const gd::String &getter) {
|
||||
codeExtraInformation.optionalAssociatedInstruction = getter;
|
||||
codeExtraInformation.accessType = codeExtraInformation.MutatorAndOrAccessor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InstructionMetadata &SetMutators(
|
||||
const std::map<gd::String, gd::String> &mutators) {
|
||||
codeExtraInformation.optionalMutators = mutators;
|
||||
codeExtraInformation.accessType = codeExtraInformation.Mutators;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
*/
|
||||
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) {
|
||||
codeExtraInformation.SetIncludeFile(includeFile);
|
||||
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) override {
|
||||
codeExtraInformation.includeFiles.clear();
|
||||
codeExtraInformation.includeFiles.push_back(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
InstructionMetadata &AddIncludeFile(const gd::String &includeFile) {
|
||||
codeExtraInformation.AddIncludeFile(includeFile);
|
||||
InstructionMetadata &AddIncludeFile(const gd::String &includeFile) override {
|
||||
if (std::find(codeExtraInformation.includeFiles.begin(), codeExtraInformation.includeFiles.end(), includeFile) ==
|
||||
codeExtraInformation.includeFiles.end())
|
||||
codeExtraInformation.includeFiles.push_back(includeFile);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String> &GetIncludeFiles() const {
|
||||
return codeExtraInformation.GetIncludeFiles();
|
||||
const std::vector<gd::String> &GetIncludeFiles() const override {
|
||||
return codeExtraInformation.includeFiles;
|
||||
};
|
||||
|
||||
InstructionMetadata &SetCustomCodeGenerator(
|
||||
std::function<gd::String(Instruction &instruction,
|
||||
gd::EventsCodeGenerator &codeGenerator,
|
||||
gd::EventsCodeGenerationContext &context)>
|
||||
codeGenerator) {
|
||||
codeExtraInformation.hasCustomCodeGenerator = true;
|
||||
codeExtraInformation.customCodeGenerator = codeGenerator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InstructionMetadata &RemoveCustomCodeGenerator() {
|
||||
codeExtraInformation.hasCustomCodeGenerator = false;
|
||||
std::function<gd::String(Instruction & instruction,
|
||||
gd::EventsCodeGenerator & codeGenerator,
|
||||
gd::EventsCodeGenerationContext & context)>
|
||||
emptyFunction;
|
||||
codeExtraInformation.customCodeGenerator = emptyFunction;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasCustomCodeGenerator() const { return codeExtraInformation.hasCustomCodeGenerator; }
|
||||
|
||||
/**
|
||||
* \brief Return the structure containing the information about code
|
||||
* generation for the instruction.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
InstructionMetadata &GetCodeExtraInformation() { return *this; }
|
||||
|
||||
std::vector<ParameterMetadata> parameters;
|
||||
|
||||
private:
|
||||
@@ -604,5 +572,3 @@ class GD_CORE_API InstructionMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // INSTRUCTIONMETADATA_H
|
||||
|
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Behavior;
|
||||
class BehaviorsSharedData;
|
||||
class MultipleInstructionMetadata;
|
||||
class InstructionMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Contains user-friendly information about instructions and expressions
|
||||
* (usually for a behavior or an object).
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API InstructionOrExpressionContainerMetadata {
|
||||
public:
|
||||
InstructionOrExpressionContainerMetadata(){};
|
||||
virtual ~InstructionOrExpressionContainerMetadata(){};
|
||||
|
||||
/**
|
||||
* Declare a new condition as being part of the behavior or object.
|
||||
* \deprecated Prefer using `AddScopedCondition`, to properly namespace
|
||||
* the condition.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddCondition(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_, const gd::String &sentence_,
|
||||
const gd::String &group_, const gd::String &icon_,
|
||||
const gd::String &smallicon_) = 0;
|
||||
|
||||
/**
|
||||
* Declare a new action as being part of the behavior or object.
|
||||
* \deprecated Prefer using `AddScopedAction`, to properly namespace
|
||||
* the action.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddAction(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_, const gd::String &sentence_,
|
||||
const gd::String &group_, const gd::String &icon_,
|
||||
const gd::String &smallicon_) = 0;
|
||||
|
||||
/**
|
||||
* Declare a new condition as being part of the behavior or object.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddScopedCondition(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_,
|
||||
const gd::String &sentence_, const gd::String &group_,
|
||||
const gd::String &icon_, const gd::String &smallicon_) = 0;
|
||||
|
||||
/**
|
||||
* Declare a new action as being part of the behavior or object.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddScopedAction(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_, const gd::String &sentence_,
|
||||
const gd::String &group_, const gd::String &icon_,
|
||||
const gd::String &smallicon_) = 0;
|
||||
/**
|
||||
* Declare a new action as being part of the extension.
|
||||
*/
|
||||
virtual gd::ExpressionMetadata &
|
||||
AddExpression(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_, const gd::String &group_,
|
||||
const gd::String &smallicon_) = 0;
|
||||
|
||||
/**
|
||||
* Declare a new string expression as being part of the extension.
|
||||
*/
|
||||
virtual gd::ExpressionMetadata &
|
||||
AddStrExpression(const gd::String &name_, const gd::String &fullname_,
|
||||
const gd::String &description_, const gd::String &group_,
|
||||
const gd::String &smallicon_) = 0;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression and condition as being part of the
|
||||
* behavior.
|
||||
* \note It's recommended to use this function to avoid declaring twice a
|
||||
* similar expression/condition.
|
||||
*/
|
||||
virtual gd::MultipleInstructionMetadata AddExpressionAndCondition(
|
||||
const gd::String &type, const gd::String &name,
|
||||
const gd::String &fullname, const gd::String &description,
|
||||
const gd::String &sentenceName, const gd::String &group,
|
||||
const gd::String &icon) = 0;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression, condition and action as being part of the
|
||||
* behavior.
|
||||
* \note The action name is prefixed by "Set" (and the namespace, as the
|
||||
* condition).
|
||||
* \note It's recommended to use this function to avoid declaring 3 times a
|
||||
* similar expression/condition/action.
|
||||
*/
|
||||
virtual gd::MultipleInstructionMetadata AddExpressionAndConditionAndAction(
|
||||
const gd::String &type, const gd::String &name,
|
||||
const gd::String &fullname, const gd::String &description,
|
||||
const gd::String &sentenceName, const gd::String &group,
|
||||
const gd::String &icon) = 0;
|
||||
|
||||
/**
|
||||
* \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.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddDuplicatedAction(const gd::String &newActionName,
|
||||
const gd::String &copiedActionName) = 0;
|
||||
|
||||
/**
|
||||
* \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.
|
||||
*/
|
||||
virtual gd::InstructionMetadata &
|
||||
AddDuplicatedCondition(const gd::String &newConditionName,
|
||||
const gd::String &copiedConditionName) = 0;
|
||||
|
||||
virtual InstructionOrExpressionContainerMetadata &
|
||||
SetFullName(const gd::String &fullname_) = 0;
|
||||
virtual InstructionOrExpressionContainerMetadata &
|
||||
SetDescription(const gd::String &description_) = 0;
|
||||
|
||||
/**
|
||||
* \brief Erase any existing include file and add the specified include.
|
||||
* \note The requirement may vary depending on the platform: Most of the time,
|
||||
* the include file contains the declaration of the behavior.
|
||||
*/
|
||||
virtual InstructionOrExpressionContainerMetadata &
|
||||
SetIncludeFile(const gd::String &includeFile) = 0;
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
virtual InstructionOrExpressionContainerMetadata &
|
||||
AddIncludeFile(const gd::String &includeFile) = 0;
|
||||
|
||||
/**
|
||||
* Get the help path of the behavior, relative to the GDevelop documentation
|
||||
* root.
|
||||
*/
|
||||
virtual const gd::String &GetHelpPath() const = 0;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual InstructionOrExpressionContainerMetadata &
|
||||
SetHelpPath(const gd::String &path) = 0;
|
||||
|
||||
virtual const gd::String &GetName() const = 0;
|
||||
virtual const gd::String &GetFullName() const = 0;
|
||||
virtual const gd::String &GetDescription() const = 0;
|
||||
virtual const gd::String &GetIconFilename() const = 0;
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a map containing the names of the actions
|
||||
* (as keys) and the metadata associated with (as values).
|
||||
*/
|
||||
virtual std::map<gd::String, gd::InstructionMetadata> &GetAllActions() = 0;
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
virtual std::map<gd::String, gd::InstructionMetadata> &GetAllConditions() = 0;
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
virtual std::map<gd::String, gd::ExpressionMetadata> &GetAllExpressions() = 0;
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
virtual std::map<gd::String, gd::ExpressionMetadata> &
|
||||
GetAllStrExpressions() = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -135,11 +135,11 @@ MetadataProvider::GetExtensionAndConditionMetadata(const gd::Platform& platform,
|
||||
|
||||
const auto& objects = extension->GetExtensionObjectsTypes();
|
||||
for (const gd::String& extObjectType : objects) {
|
||||
const auto& allObjetsConditions =
|
||||
const auto& allObjectsConditions =
|
||||
extension->GetAllConditionsForObject(extObjectType);
|
||||
if (allObjetsConditions.find(conditionType) != allObjetsConditions.end())
|
||||
if (allObjectsConditions.find(conditionType) != allObjectsConditions.end())
|
||||
return ExtensionAndMetadata<InstructionMetadata>(
|
||||
*extension, allObjetsConditions.find(conditionType)->second);
|
||||
*extension, allObjectsConditions.find(conditionType)->second);
|
||||
}
|
||||
|
||||
const auto& autos = extension->GetBehaviorsTypes();
|
||||
|
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "MultipleInstructionMetadata.h"
|
||||
|
||||
#include "InstructionMetadata.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
} // namespace gd
|
@@ -3,8 +3,8 @@
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef MULTIPLEINSTRUCTIONSMETADATA_H
|
||||
#define MULTIPLEINSTRUCTIONSMETADATA_H
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -21,7 +21,7 @@ namespace gd {
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API MultipleInstructionMetadata {
|
||||
class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata {
|
||||
public:
|
||||
static MultipleInstructionMetadata WithExpressionAndCondition(
|
||||
gd::ExpressionMetadata &expression, gd::InstructionMetadata &condition) {
|
||||
@@ -45,7 +45,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
const gd::String &type,
|
||||
const gd::String &label,
|
||||
const gd::String &supplementaryInformation = "",
|
||||
bool parameterIsOptional = false) {
|
||||
bool parameterIsOptional = false) override {
|
||||
if (expression)
|
||||
expression->AddParameter(
|
||||
type, label, supplementaryInformation, parameterIsOptional);
|
||||
@@ -62,7 +62,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
* \see gd::InstructionMetadata::AddCodeOnlyParameter
|
||||
*/
|
||||
MultipleInstructionMetadata &AddCodeOnlyParameter(
|
||||
const gd::String &type, const gd::String &supplementaryInformation) {
|
||||
const gd::String &type, const gd::String &supplementaryInformation) override {
|
||||
if (expression)
|
||||
expression->AddCodeOnlyParameter(type, supplementaryInformation);
|
||||
if (condition)
|
||||
@@ -74,7 +74,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetDefaultValue
|
||||
*/
|
||||
MultipleInstructionMetadata &SetDefaultValue(const gd::String &defaultValue) {
|
||||
MultipleInstructionMetadata &SetDefaultValue(const gd::String &defaultValue) override {
|
||||
if (expression) expression->SetDefaultValue(defaultValue);
|
||||
if (condition) condition->SetDefaultValue(defaultValue);
|
||||
if (action) action->SetDefaultValue(defaultValue);
|
||||
@@ -85,7 +85,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
* \see gd::InstructionMetadata::SetParameterExtraInfo
|
||||
*/
|
||||
MultipleInstructionMetadata &SetParameterExtraInfo(
|
||||
const gd::String &defaultValue) {
|
||||
const gd::String &defaultValue) override {
|
||||
if (expression) expression->SetParameterExtraInfo(defaultValue);
|
||||
if (condition) condition->SetParameterExtraInfo(defaultValue);
|
||||
if (action) action->SetParameterExtraInfo(defaultValue);
|
||||
@@ -96,7 +96,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
* \see gd::InstructionMetadata::SetParameterLongDescription
|
||||
*/
|
||||
MultipleInstructionMetadata &SetParameterLongDescription(
|
||||
const gd::String &longDescription) {
|
||||
const gd::String &longDescription) override {
|
||||
if (expression) expression->SetParameterLongDescription(longDescription);
|
||||
if (condition) condition->SetParameterLongDescription(longDescription);
|
||||
if (action) action->SetParameterLongDescription(longDescription);
|
||||
@@ -106,7 +106,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetHidden
|
||||
*/
|
||||
MultipleInstructionMetadata &SetHidden() {
|
||||
MultipleInstructionMetadata &SetHidden() override {
|
||||
if (expression) expression->SetHidden();
|
||||
if (condition) condition->SetHidden();
|
||||
if (action) action->SetHidden();
|
||||
@@ -136,47 +136,47 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
return *this;
|
||||
}
|
||||
|
||||
MultipleInstructionMetadata &SetFunctionName(const gd::String &functionName) {
|
||||
MultipleInstructionMetadata &SetFunctionName(const gd::String &functionName) override {
|
||||
if (expression) expression->SetFunctionName(functionName);
|
||||
if (condition) condition->SetFunctionName(functionName);
|
||||
if (action) action->GetCodeExtraInformation().SetFunctionName(functionName);
|
||||
if (action) action->SetFunctionName(functionName);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MultipleInstructionMetadata &SetGetter(const gd::String &getter) {
|
||||
if (expression) expression->SetFunctionName(getter);
|
||||
if (condition) condition->SetFunctionName(getter);
|
||||
if (action) action->GetCodeExtraInformation().SetGetter(getter);
|
||||
if (action) action->SetGetter(getter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MultipleInstructionMetadata &SetIncludeFile(const gd::String &includeFile) {
|
||||
MultipleInstructionMetadata &SetIncludeFile(const gd::String &includeFile) override {
|
||||
if (expression)
|
||||
expression->GetCodeExtraInformation().SetIncludeFile(includeFile);
|
||||
expression->SetIncludeFile(includeFile);
|
||||
if (condition)
|
||||
condition->GetCodeExtraInformation().SetIncludeFile(includeFile);
|
||||
if (action) action->GetCodeExtraInformation().SetIncludeFile(includeFile);
|
||||
condition->SetIncludeFile(includeFile);
|
||||
if (action) action->SetIncludeFile(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MultipleInstructionMetadata &AddIncludeFile(const gd::String &includeFile) {
|
||||
MultipleInstructionMetadata &AddIncludeFile(const gd::String &includeFile) override {
|
||||
if (expression)
|
||||
expression->GetCodeExtraInformation().AddIncludeFile(includeFile);
|
||||
if (condition)
|
||||
condition->GetCodeExtraInformation().AddIncludeFile(includeFile);
|
||||
if (action) action->GetCodeExtraInformation().AddIncludeFile(includeFile);
|
||||
condition->AddIncludeFile(includeFile);
|
||||
if (action) action->AddIncludeFile(includeFile);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the files that must be included to use the instruction.
|
||||
*/
|
||||
const std::vector<gd::String> &GetIncludeFiles() const {
|
||||
const std::vector<gd::String> &GetIncludeFiles() const override {
|
||||
if (expression)
|
||||
return expression->GetCodeExtraInformation().GetIncludeFiles();
|
||||
if (condition)
|
||||
return condition->GetCodeExtraInformation().GetIncludeFiles();
|
||||
if (action) return action->GetCodeExtraInformation().GetIncludeFiles();
|
||||
return condition->GetIncludeFiles();
|
||||
if (action) return action->GetIncludeFiles();
|
||||
// It can't actually happen.
|
||||
throw std::logic_error("no instruction metadata");
|
||||
}
|
||||
@@ -184,7 +184,7 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetPrivate
|
||||
*/
|
||||
MultipleInstructionMetadata &SetPrivate() {
|
||||
MultipleInstructionMetadata &SetPrivate() override {
|
||||
if (expression) expression->SetPrivate();
|
||||
if (condition) condition->SetPrivate();
|
||||
if (action) action->SetPrivate();
|
||||
@@ -218,6 +218,42 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in layouts or external events.
|
||||
*/
|
||||
MultipleInstructionMetadata &SetRelevantForLayoutEventsOnly() override {
|
||||
if (condition) condition->SetRelevantForLayoutEventsOnly();
|
||||
if (action) action->SetRelevantForLayoutEventsOnly();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in function events.
|
||||
*/
|
||||
MultipleInstructionMetadata &SetRelevantForFunctionEventsOnly() override {
|
||||
if (condition) condition->SetRelevantForFunctionEventsOnly();
|
||||
if (action) action->SetRelevantForFunctionEventsOnly();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in asynchronous function events.
|
||||
*/
|
||||
MultipleInstructionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() override {
|
||||
if (condition) condition->SetRelevantForAsynchronousFunctionEventsOnly();
|
||||
if (action) action->SetRelevantForAsynchronousFunctionEventsOnly();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that the instruction can be used in custom object events.
|
||||
*/
|
||||
MultipleInstructionMetadata &SetRelevantForCustomObjectEventsOnly() override {
|
||||
if (condition) condition->SetRelevantForCustomObjectEventsOnly();
|
||||
if (action) action->SetRelevantForCustomObjectEventsOnly();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Don't use, only here to fulfill Emscripten bindings requirements.
|
||||
*/
|
||||
@@ -242,5 +278,3 @@ class GD_CORE_API MultipleInstructionMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // MULTIPLEINSTRUCTIONSMETADATA_H
|
||||
|
@@ -3,8 +3,10 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef OBJECTMETADATA_H
|
||||
#define OBJECTMETADATA_H
|
||||
#pragma once
|
||||
|
||||
#include "InstructionOrExpressionContainerMetadata.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -32,7 +34,7 @@ namespace gd {
|
||||
*
|
||||
* \ingroup Events
|
||||
*/
|
||||
class GD_CORE_API ObjectMetadata {
|
||||
class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetadata {
|
||||
public:
|
||||
/**
|
||||
* \brief Construct an object metadata, using a "blueprint" object that will
|
||||
@@ -79,7 +81,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new action as being part of the extension.
|
||||
@@ -92,7 +94,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new condition as being part of the object.
|
||||
@@ -103,7 +105,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* Declare a new action as being part of the object.
|
||||
@@ -114,7 +116,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& sentence_,
|
||||
const gd::String& group_,
|
||||
const gd::String& icon_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression as being part of the extension.
|
||||
@@ -123,7 +125,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
/**
|
||||
* \brief Declare a new string expression as being part of the extension.
|
||||
*/
|
||||
@@ -131,7 +133,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
const gd::String& smallicon_) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression and condition as being part of the
|
||||
@@ -146,7 +148,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& description,
|
||||
const gd::String& sentenceName,
|
||||
const gd::String& group,
|
||||
const gd::String& icon);
|
||||
const gd::String& icon) override;
|
||||
|
||||
/**
|
||||
* \brief Declare a new expression, condition and action as being part of the
|
||||
@@ -163,7 +165,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
const gd::String& description,
|
||||
const gd::String& sentenceName,
|
||||
const gd::String& group,
|
||||
const gd::String& icon);
|
||||
const gd::String& icon) override;
|
||||
|
||||
/**
|
||||
* \brief Create a new action which is the duplicate of the specified one.
|
||||
@@ -172,7 +174,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
* one.
|
||||
*/
|
||||
gd::InstructionMetadata& AddDuplicatedAction(
|
||||
const gd::String& newActionName, const gd::String& copiedActionName);
|
||||
const gd::String& newActionName, const gd::String& copiedActionName) override;
|
||||
|
||||
/**
|
||||
* \brief Create a new condition which is the duplicate of the specified one.
|
||||
@@ -182,23 +184,23 @@ class GD_CORE_API ObjectMetadata {
|
||||
*/
|
||||
gd::InstructionMetadata& AddDuplicatedCondition(
|
||||
const gd::String& newConditionName,
|
||||
const gd::String& copiedConditionName);
|
||||
const gd::String& copiedConditionName) override;
|
||||
|
||||
/**
|
||||
* \brief Set the name shown to the user.
|
||||
*/
|
||||
ObjectMetadata& SetFullName(const gd::String& fullname_);
|
||||
ObjectMetadata& SetFullName(const gd::String& fullname_) override;
|
||||
|
||||
/**
|
||||
* \brief Set the description shown to the user.
|
||||
*/
|
||||
ObjectMetadata& SetDescription(const gd::String& description_);
|
||||
ObjectMetadata& SetDescription(const gd::String& description_) override;
|
||||
|
||||
/**
|
||||
* \brief Get the help path of the object, relative to the GDevelop
|
||||
* documentation root.
|
||||
*/
|
||||
const gd::String& GetHelpPath() const { return helpPath; }
|
||||
const gd::String& GetHelpPath() const override { return helpPath; }
|
||||
|
||||
/**
|
||||
* \brief Set the help path of the object, relative to the GDevelop
|
||||
@@ -207,7 +209,7 @@ class GD_CORE_API ObjectMetadata {
|
||||
* The object instructions will have this help path set by
|
||||
* default, unless you call SetHelpPath on them.
|
||||
*/
|
||||
ObjectMetadata& SetHelpPath(const gd::String& path) {
|
||||
ObjectMetadata& SetHelpPath(const gd::String& path) override {
|
||||
helpPath = path;
|
||||
return *this;
|
||||
}
|
||||
@@ -248,12 +250,12 @@ class GD_CORE_API ObjectMetadata {
|
||||
return unsupportedBaseObjectCapabilities.find(capability) != unsupportedBaseObjectCapabilities.end();
|
||||
}
|
||||
|
||||
const gd::String& GetName() const { return name; }
|
||||
const gd::String& GetFullName() const { return fullname; }
|
||||
const gd::String& GetName() const override { return name; }
|
||||
const gd::String& GetFullName() const override { return fullname; }
|
||||
const gd::String& GetCategoryFullName() const { return categoryFullName; }
|
||||
const gd::String& GetHelpUrl() const { return helpUrl; }
|
||||
const gd::String& GetDescription() const { return description; }
|
||||
const gd::String& GetIconFilename() const { return iconFilename; }
|
||||
const gd::String& GetDescription() const override { return description; }
|
||||
const gd::String& GetIconFilename() const override { return iconFilename; }
|
||||
|
||||
/**
|
||||
* \brief Set the URL pointing to the help page about this object
|
||||
@@ -267,33 +269,33 @@ class GD_CORE_API ObjectMetadata {
|
||||
* \note The requirement may vary depending on the platform: Most of the time,
|
||||
* the include file contains the declaration of the object.
|
||||
*/
|
||||
ObjectMetadata& SetIncludeFile(const gd::String& includeFile);
|
||||
ObjectMetadata& SetIncludeFile(const gd::String& includeFile) override;
|
||||
|
||||
/**
|
||||
* \brief Add a file to the already existing include files.
|
||||
*/
|
||||
ObjectMetadata& AddIncludeFile(const gd::String& includeFile);
|
||||
ObjectMetadata& AddIncludeFile(const gd::String& includeFile) override;
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a map containing the names of the actions
|
||||
* (as keys) and the metadata associated with (as values).
|
||||
*/
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllActions() { return actionsInfos; };
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllActions() override { return actionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllConditions() { return conditionsInfos; };
|
||||
std::map<gd::String, gd::InstructionMetadata>& GetAllConditions() override { return conditionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllExpressions() { return expressionsInfos; };
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllExpressions() override { return expressionsInfos; };
|
||||
|
||||
/**
|
||||
* \see gd::PlatformExtension::GetAllActions
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() { return strExpressionsInfos; };
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
|
||||
|
||||
/**
|
||||
* \brief Set the object to be hidden in the IDE.
|
||||
@@ -341,4 +343,3 @@ class GD_CORE_API ObjectMetadata {
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
#endif // OBJECTMETADATA_H
|
||||
|
@@ -653,7 +653,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
GetAllActions().begin();
|
||||
it != GetAllActions().end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
GetAllActions().erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -663,7 +663,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
GetAllConditions().begin();
|
||||
it != GetAllConditions().end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
GetAllConditions().erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -673,7 +673,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
GetAllExpressions().begin();
|
||||
it != GetAllExpressions().end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
GetAllExpressions().erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -683,7 +683,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
GetAllStrExpressions().begin();
|
||||
it != GetAllStrExpressions().end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
GetAllStrExpressions().erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -699,7 +699,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.actionsInfos.begin();
|
||||
it != obj.actionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.actionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -709,7 +709,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.conditionsInfos.begin();
|
||||
it != obj.conditionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.conditionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -719,7 +719,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.expressionsInfos.begin();
|
||||
it != obj.expressionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.expressionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -729,7 +729,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.strExpressionsInfos.begin();
|
||||
it != obj.strExpressionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.strExpressionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -746,7 +746,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.actionsInfos.begin();
|
||||
it != obj.actionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.actionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -756,7 +756,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.conditionsInfos.begin();
|
||||
it != obj.conditionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.conditionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -766,7 +766,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.expressionsInfos.begin();
|
||||
it != obj.expressionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.expressionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
@@ -776,7 +776,7 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
|
||||
obj.strExpressionsInfos.begin();
|
||||
it != obj.strExpressionsInfos.end();) {
|
||||
if (it->second.codeExtraInformation.functionCallName.empty() &&
|
||||
!it->second.codeExtraInformation.HasCustomCodeGenerator()) {
|
||||
!it->second.HasCustomCodeGenerator()) {
|
||||
obj.strExpressionsInfos.erase(it++);
|
||||
} else
|
||||
++it;
|
||||
|
@@ -555,6 +555,22 @@ void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
|
||||
}
|
||||
}
|
||||
|
||||
gd::String ReplaceAllOccurrencesCaseInsensitive(gd::String context,
|
||||
const gd::String& from,
|
||||
const gd::String& to) {
|
||||
size_t lookHere = 0;
|
||||
size_t foundHere;
|
||||
size_t fromSize = from.size();
|
||||
size_t toSize = to.size();
|
||||
while ((foundHere = context.FindCaseInsensitive(from, lookHere)) !=
|
||||
gd::String::npos) {
|
||||
context.replace(foundHere, fromSize, to);
|
||||
lookHere = foundHere + toSize;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
@@ -570,6 +586,32 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
|
||||
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
bool eventModified = false;
|
||||
|
||||
std::vector<gd::Expression*> allObjectExpressions =
|
||||
events[i].GetAllObjectExpressions();
|
||||
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
|
||||
gd::String newExpressionPlainString =
|
||||
matchCase ? allObjectExpressions[j]->GetPlainString().FindAndReplace(
|
||||
toReplace, newString, true)
|
||||
: ReplaceAllOccurrencesCaseInsensitive(
|
||||
allObjectExpressions[j]->GetPlainString(),
|
||||
toReplace,
|
||||
newString);
|
||||
|
||||
if (newExpressionPlainString !=
|
||||
allObjectExpressions[j]->GetPlainString()) {
|
||||
*allObjectExpressions[j] = gd::Expression(newExpressionPlainString);
|
||||
|
||||
if (!eventModified) {
|
||||
modifiedEvents.push_back(EventsSearchResult(
|
||||
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
|
||||
&events,
|
||||
i));
|
||||
eventModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inConditions) {
|
||||
vector<gd::InstructionsList*> conditionsVectors =
|
||||
events[i].GetAllConditionsVectors();
|
||||
@@ -642,22 +684,6 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
|
||||
return modifiedEvents;
|
||||
}
|
||||
|
||||
gd::String ReplaceAllOccurencesCaseUnsensitive(gd::String context,
|
||||
gd::String from,
|
||||
const gd::String& to) {
|
||||
size_t lookHere = 0;
|
||||
size_t foundHere;
|
||||
size_t fromSize = from.size();
|
||||
size_t toSize = to.size();
|
||||
while ((foundHere = context.FindCaseInsensitive(from, lookHere)) !=
|
||||
gd::String::npos) {
|
||||
context.replace(foundHere, fromSize, to);
|
||||
lookHere = foundHere + toSize;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool EventsRefactorer::ReplaceStringInActions(gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::InstructionsList& actions,
|
||||
@@ -673,7 +699,7 @@ bool EventsRefactorer::ReplaceStringInActions(gd::ObjectsContainer& project,
|
||||
matchCase
|
||||
? actions[aId].GetParameter(pNb).GetPlainString().FindAndReplace(
|
||||
toReplace, newString, true)
|
||||
: ReplaceAllOccurencesCaseUnsensitive(
|
||||
: ReplaceAllOccurrencesCaseInsensitive(
|
||||
actions[aId].GetParameter(pNb).GetPlainString(),
|
||||
toReplace,
|
||||
newString);
|
||||
@@ -713,7 +739,7 @@ bool EventsRefactorer::ReplaceStringInConditions(
|
||||
.GetParameter(pNb)
|
||||
.GetPlainString()
|
||||
.FindAndReplace(toReplace, newString, true)
|
||||
: ReplaceAllOccurencesCaseUnsensitive(
|
||||
: ReplaceAllOccurrencesCaseInsensitive(
|
||||
conditions[cId].GetParameter(pNb).GetPlainString(),
|
||||
toReplace,
|
||||
newString);
|
||||
@@ -749,7 +775,7 @@ bool EventsRefactorer::ReplaceStringInEventSearchableStrings(
|
||||
for (std::size_t sNb = 0; sNb < stringEvent.size(); ++sNb) {
|
||||
gd::String newStringEvent =
|
||||
matchCase ? stringEvent[sNb].FindAndReplace(toReplace, newString, true)
|
||||
: ReplaceAllOccurencesCaseUnsensitive(
|
||||
: ReplaceAllOccurrencesCaseInsensitive(
|
||||
stringEvent[sNb], toReplace, newString);
|
||||
newEventStrings.push_back(newStringEvent);
|
||||
}
|
||||
@@ -789,6 +815,24 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
bool eventAddedInResults = false;
|
||||
|
||||
std::vector<gd::Expression*> allObjectExpressions =
|
||||
events[i].GetAllObjectExpressions();
|
||||
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
|
||||
size_t foundPosition =
|
||||
matchCase
|
||||
? allObjectExpressions[j]->GetPlainString().find(search)
|
||||
: allObjectExpressions[j]->GetPlainString().FindCaseInsensitive(
|
||||
search);
|
||||
|
||||
if (foundPosition != gd::String::npos && !eventAddedInResults) {
|
||||
results.push_back(EventsSearchResult(
|
||||
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
|
||||
&events,
|
||||
i));
|
||||
eventAddedInResults = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inConditions) {
|
||||
vector<gd::InstructionsList*> conditionsVectors =
|
||||
events[i].GetAllConditionsVectors();
|
||||
@@ -803,6 +847,7 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
|
||||
&events,
|
||||
i));
|
||||
eventAddedInResults = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,6 +865,7 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
std::weak_ptr<gd::BaseEvent>(events.GetEventSmartPtr(i)),
|
||||
&events,
|
||||
i));
|
||||
eventAddedInResults = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -87,11 +87,22 @@ protected:
|
||||
std::unique_ptr<gd::ExpressionNode> ¶meterNode,
|
||||
size_t parameterIndex, const gd::String &lastObjectName) {
|
||||
if (parameterMetadata.GetType() == "layer") {
|
||||
// Remove quotes, it won't match if it's not a literal anyway.
|
||||
lastLayerName = expressionPlainString.substr(
|
||||
parameterNode->location.GetStartPosition() + 1,
|
||||
parameterNode->location.GetEndPosition() -
|
||||
parameterNode->location.GetStartPosition() - 2);
|
||||
if (parameterNode->location.GetEndPosition() -
|
||||
parameterNode->location.GetStartPosition() <
|
||||
2) {
|
||||
// This is either the base layer or an invalid layer name.
|
||||
// Keep it as is.
|
||||
lastLayerName = expressionPlainString.substr(
|
||||
parameterNode->location.GetStartPosition(),
|
||||
parameterNode->location.GetEndPosition() -
|
||||
parameterNode->location.GetStartPosition());
|
||||
} else {
|
||||
// Remove quotes, so it can be compared to the layer name.
|
||||
lastLayerName = expressionPlainString.substr(
|
||||
parameterNode->location.GetStartPosition() + 1,
|
||||
parameterNode->location.GetEndPosition() -
|
||||
parameterNode->location.GetStartPosition() - 2);
|
||||
}
|
||||
}
|
||||
if (parameterMetadata.GetType() == parameterType) {
|
||||
auto parameterExpressionPlainString = expressionPlainString.substr(
|
||||
@@ -143,9 +154,15 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName) {
|
||||
if (parameterMetadata.GetType() == "layer") {
|
||||
// Remove quotes, it won't match if it's not a literal anyway.
|
||||
lastLayerName = parameterValue.GetPlainString().substr(
|
||||
1, parameterValue.GetPlainString().length() - 2);
|
||||
if (parameterValue.GetPlainString().length() < 2) {
|
||||
// This is either the base layer or an invalid layer name.
|
||||
// Keep it as is.
|
||||
lastLayerName = parameterValue.GetPlainString();
|
||||
} else {
|
||||
// Remove quotes, so it can be compared to the layer name.
|
||||
lastLayerName = parameterValue.GetPlainString().substr(
|
||||
1, parameterValue.GetPlainString().length() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (parameterMetadata.GetType() == parameterType &&
|
||||
@@ -165,7 +182,6 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
|
||||
node->Visit(finder);
|
||||
|
||||
if (finder.GetOccurrences().size() > 0) {
|
||||
|
||||
gd::String newNameWithQuotes = "\"" + newName + "\"";
|
||||
gd::String oldParameterValue = parameterValue.GetPlainString();
|
||||
gd::String newParameterValue;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -153,6 +153,8 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
|
||||
worker.ExposeTileset(newPropertyValue);
|
||||
} else if (resourceType == "bitmapFont") {
|
||||
worker.ExposeBitmapFont(newPropertyValue);
|
||||
} else if (resourceType == "model3D") {
|
||||
worker.ExposeModel3D(newPropertyValue);
|
||||
}
|
||||
|
||||
if (newPropertyValue != oldPropertyValue) {
|
||||
|
@@ -77,7 +77,7 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
|
||||
* \brief Check if the behavior is private - it can't be used outside of its
|
||||
* extension.
|
||||
*/
|
||||
bool IsPrivate() { return isPrivate; }
|
||||
bool IsPrivate() const { return isPrivate; }
|
||||
|
||||
/**
|
||||
* \brief Set that the behavior is private - it can't be used outside of its
|
||||
|
@@ -201,7 +201,7 @@ class GD_CORE_API EventsFunction {
|
||||
/**
|
||||
* \brief Returns true if the function is private.
|
||||
*/
|
||||
bool IsPrivate() { return isPrivate; }
|
||||
bool IsPrivate() const { return isPrivate; }
|
||||
|
||||
/**
|
||||
* \brief Sets the privateness of the function.
|
||||
|
@@ -207,6 +207,13 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
return dependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Returns the list of dependencies.
|
||||
*/
|
||||
const std::vector<gd::DependencyMetadata>& GetAllDependencies() const {
|
||||
return dependencies;
|
||||
};
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Serialization
|
||||
|
@@ -9,9 +9,9 @@
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -21,12 +21,17 @@ InitialInstance::InitialInstance()
|
||||
: objectName(""),
|
||||
x(0),
|
||||
y(0),
|
||||
z(0),
|
||||
angle(0),
|
||||
rotationX(0),
|
||||
rotationY(0),
|
||||
zOrder(0),
|
||||
layer(""),
|
||||
personalizedSize(false),
|
||||
customSize(false),
|
||||
customDepth(false),
|
||||
width(0),
|
||||
height(0),
|
||||
depth(0),
|
||||
locked(false),
|
||||
sealed(false),
|
||||
persistentUuid(UUID::MakeUuid4()) {}
|
||||
@@ -35,11 +40,20 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetObjectName(element.GetStringAttribute("name", "", "nom"));
|
||||
SetX(element.GetDoubleAttribute("x"));
|
||||
SetY(element.GetDoubleAttribute("y"));
|
||||
SetZ(element.GetDoubleAttribute("z", 0));
|
||||
SetAngle(element.GetDoubleAttribute("angle"));
|
||||
SetRotationX(element.GetDoubleAttribute("rotationX", 0));
|
||||
SetRotationY(element.GetDoubleAttribute("rotationY", 0));
|
||||
SetHasCustomSize(
|
||||
element.GetBoolAttribute("customSize", false, "personalizedSize"));
|
||||
SetCustomWidth(element.GetDoubleAttribute("width"));
|
||||
SetCustomHeight(element.GetDoubleAttribute("height"));
|
||||
if (element.HasChild("depth") || element.HasAttribute("depth")) {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(element.GetDoubleAttribute("depth"));
|
||||
} else {
|
||||
SetHasCustomDepth(false);
|
||||
}
|
||||
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
SetLocked(element.GetBoolAttribute("locked", false));
|
||||
@@ -53,9 +67,26 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
||||
gd::String name = numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value = numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
numberProperties[name] = value;
|
||||
gd::String name =
|
||||
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value =
|
||||
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
|
||||
// Compatibility with GD <= 5.1.164
|
||||
if (name == "z") {
|
||||
SetZ(value);
|
||||
} else if (name == "rotationX") {
|
||||
SetRotationX(value);
|
||||
} else if (name == "rotationY") {
|
||||
SetRotationY(value);
|
||||
} else if (name == "depth") {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(value);
|
||||
}
|
||||
// end of compatibility code
|
||||
else {
|
||||
numberProperties[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
stringProperties.clear();
|
||||
@@ -77,21 +108,26 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetObjectName());
|
||||
element.SetAttribute("x", GetX());
|
||||
element.SetAttribute("y", GetY());
|
||||
if (GetZ() != 0) element.SetAttribute("z", GetZ());
|
||||
element.SetAttribute("zOrder", GetZOrder());
|
||||
element.SetAttribute("layer", GetLayer());
|
||||
element.SetAttribute("angle", GetAngle());
|
||||
if (GetRotationX() != 0) element.SetAttribute("rotationX", GetRotationX());
|
||||
if (GetRotationY() != 0) element.SetAttribute("rotationY", GetRotationY());
|
||||
element.SetAttribute("customSize", HasCustomSize());
|
||||
element.SetAttribute("width", GetCustomWidth());
|
||||
element.SetAttribute("height", GetCustomHeight());
|
||||
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
|
||||
if (IsLocked()) element.SetAttribute("locked", IsLocked());
|
||||
if (IsSealed()) element.SetAttribute("sealed", IsSealed());
|
||||
|
||||
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
SerializerElement& numberPropertiesElement = element.AddChild("numberProperties");
|
||||
SerializerElement& numberPropertiesElement =
|
||||
element.AddChild("numberProperties");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property");
|
||||
for (const auto& property: numberProperties) {
|
||||
for (const auto& property : numberProperties) {
|
||||
numberPropertiesElement.AddChild("property")
|
||||
.SetAttribute("name", property.first)
|
||||
.SetAttribute("value", property.second);
|
||||
@@ -99,7 +135,7 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
SerializerElement& stringPropElement = element.AddChild("stringProperties");
|
||||
stringPropElement.ConsiderAsArrayOf("property");
|
||||
for (const auto& property: stringProperties) {
|
||||
for (const auto& property : stringProperties) {
|
||||
stringPropElement.AddChild("property")
|
||||
.SetAttribute("name", property.first)
|
||||
.SetAttribute("value", property.second);
|
||||
@@ -117,10 +153,12 @@ std::map<gd::String, gd::PropertyDescriptor>
|
||||
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
|
||||
// Find an object
|
||||
if (layout.HasObjectNamed(GetObjectName()))
|
||||
return layout.GetObject(GetObjectName()).GetConfiguration()
|
||||
return layout.GetObject(GetObjectName())
|
||||
.GetConfiguration()
|
||||
.GetInitialInstanceProperties(*this, project, layout);
|
||||
else if (project.HasObjectNamed(GetObjectName()))
|
||||
return project.GetObject(GetObjectName()).GetConfiguration()
|
||||
return project.GetObject(GetObjectName())
|
||||
.GetConfiguration()
|
||||
.GetInitialInstanceProperties(*this, project, layout);
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
@@ -132,10 +170,12 @@ bool InitialInstance::UpdateCustomProperty(const gd::String& name,
|
||||
gd::Project& project,
|
||||
gd::Layout& layout) {
|
||||
if (layout.HasObjectNamed(GetObjectName()))
|
||||
return layout.GetObject(GetObjectName()).GetConfiguration()
|
||||
return layout.GetObject(GetObjectName())
|
||||
.GetConfiguration()
|
||||
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
|
||||
else if (project.HasObjectNamed(GetObjectName()))
|
||||
return project.GetObject(GetObjectName()).GetConfiguration()
|
||||
return project.GetObject(GetObjectName())
|
||||
.GetConfiguration()
|
||||
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
|
||||
|
||||
return false;
|
||||
@@ -154,7 +194,8 @@ const gd::String& InitialInstance::GetRawStringProperty(
|
||||
return it != stringProperties.end() ? it->second : *badStringProperyValue;
|
||||
}
|
||||
|
||||
void InitialInstance::SetRawDoubleProperty(const gd::String& name, double value) {
|
||||
void InitialInstance::SetRawDoubleProperty(const gd::String& name,
|
||||
double value) {
|
||||
numberProperties[name] = value;
|
||||
}
|
||||
|
||||
|
@@ -73,22 +73,52 @@ class GD_CORE_API InitialInstance {
|
||||
void SetY(double y_) { y = y_; }
|
||||
|
||||
/**
|
||||
* \brief Get the rotation of the instance, in radians.
|
||||
* \brief Get the Z position of the instance
|
||||
*/
|
||||
double GetZ() const { return z; }
|
||||
|
||||
/**
|
||||
* \brief Set the Z position of the instance
|
||||
*/
|
||||
void SetZ(double z_) { z = z_; }
|
||||
|
||||
/**
|
||||
* \brief Get the rotation of the instance on Z axis, in radians.
|
||||
*/
|
||||
double GetAngle() const { return angle; }
|
||||
|
||||
/**
|
||||
* \brief Set the rotation of the instance, in radians.
|
||||
* \brief Set the rotation of the instance on Z axis, in radians.
|
||||
*/
|
||||
void SetAngle(double angle_) { angle = angle_; }
|
||||
|
||||
/**
|
||||
* \brief Get the Z order of the instance.
|
||||
* \brief Get the rotation of the instance on X axis, in radians.
|
||||
*/
|
||||
double GetRotationX() const { return rotationX; }
|
||||
|
||||
/**
|
||||
* \brief Set the rotation of the instance on X axis, in radians.
|
||||
*/
|
||||
void SetRotationX(double rotationX_) { rotationX = rotationX_; }
|
||||
|
||||
/**
|
||||
* \brief Get the rotation of the instance on Y axis, in radians.
|
||||
*/
|
||||
double GetRotationY() const { return rotationY; }
|
||||
|
||||
/**
|
||||
* \brief Set the rotation of the instance on Y axis, in radians.
|
||||
*/
|
||||
void SetRotationY(double rotationY_) { rotationY = rotationY_; }
|
||||
|
||||
/**
|
||||
* \brief Get the Z order of the instance (for a 2D object).
|
||||
*/
|
||||
int GetZOrder() const { return zOrder; }
|
||||
|
||||
/**
|
||||
* \brief Set the Z order of the instance.
|
||||
* \brief Set the Z order of the instance (for a 2D object).
|
||||
*/
|
||||
void SetZOrder(int zOrder_) { zOrder = zOrder_; }
|
||||
|
||||
@@ -103,29 +133,51 @@ class GD_CORE_API InitialInstance {
|
||||
void SetLayer(const gd::String& layer_) { layer = layer_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance has a size which is different from its
|
||||
* object default size.
|
||||
* \brief Return true if the instance has a width/height which is different from its
|
||||
* object default width/height. This is independent from `HasCustomDepth`.
|
||||
*
|
||||
* \see gd::Object
|
||||
*/
|
||||
bool HasCustomSize() const { return personalizedSize; }
|
||||
bool HasCustomSize() const { return customSize; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance has a size which is different from its
|
||||
* object default size or not.
|
||||
* \brief Return true if the instance has a depth which is different from its
|
||||
* object default depth. This is independent from `HasCustomSize`.
|
||||
*
|
||||
* \param hasCustomSize true if the size is different from the object's
|
||||
* default size. \see gd::Object
|
||||
* \see gd::Object
|
||||
*/
|
||||
bool HasCustomDepth() const { return customDepth; }
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance has a width/height which is different from its
|
||||
* object default width/height or not.
|
||||
* This is independent from `SetHasCustomDepth`.
|
||||
*
|
||||
* \see gd::Object
|
||||
*/
|
||||
void SetHasCustomSize(bool hasCustomSize_) {
|
||||
personalizedSize = hasCustomSize_;
|
||||
customSize = hasCustomSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set whether the instance has a depth which is different from its
|
||||
* object default depth or not.
|
||||
* This is independent from `SetHasCustomSize`.
|
||||
*
|
||||
* \param hasCustomSize true if the depth is different from the object's
|
||||
* default depth.
|
||||
* \see gd::Object
|
||||
*/
|
||||
void SetHasCustomDepth(bool hasCustomDepth_) {
|
||||
customDepth = hasCustomDepth_;
|
||||
}
|
||||
|
||||
double GetCustomWidth() const { return width; }
|
||||
void SetCustomWidth(double width_) { width = width_; }
|
||||
|
||||
double GetCustomHeight() const { return height; }
|
||||
void SetCustomHeight(double height_) { height = height_; }
|
||||
double GetCustomDepth() const { return depth; }
|
||||
void SetCustomDepth(double depth_) { depth = depth_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is locked and cannot be moved in the
|
||||
@@ -272,14 +324,19 @@ class GD_CORE_API InitialInstance {
|
||||
stringProperties; ///< More data which can be used by the object
|
||||
|
||||
gd::String objectName; ///< Object name
|
||||
double x; ///< Object initial X position
|
||||
double y; ///< Object initial Y position
|
||||
double angle; ///< Object initial angle
|
||||
int zOrder; ///< Object initial Z order
|
||||
gd::String layer; ///< Object initial layer
|
||||
bool personalizedSize; ///< True if object has a custom size
|
||||
double width; ///< Object custom width
|
||||
double height; ///< Object custom height
|
||||
double x; ///< Instance X position
|
||||
double y; ///< Instance Y position
|
||||
double z; ///< Instance Z position (for a 3D object)
|
||||
double angle; ///< Instance angle on Z axis
|
||||
double rotationX; ///< Instance angle on X axis (for a 3D object)
|
||||
double rotationY; ///< Instance angle on Y axis (for a 3D object)
|
||||
int zOrder; ///< Instance Z order (for a 2D object)
|
||||
gd::String layer; ///< Instance layer
|
||||
bool customSize; ///< True if object has a custom width and height
|
||||
bool customDepth; ///< True if object has a custom depth
|
||||
double width; ///< Instance custom width
|
||||
double height; ///< Instance custom height
|
||||
double depth; ///< Instance custom depth
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
bool sealed; ///< True if the instance is sealed
|
||||
|
@@ -68,14 +68,14 @@ gd::InitialInstance& InitialInstancesContainer::InsertNewInitialInstance() {
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::RemoveInstanceIf(
|
||||
std::function<bool(const gd::InitialInstance&)> predicat) {
|
||||
std::function<bool(const gd::InitialInstance&)> predicate) {
|
||||
// Note that we can't use erase–remove idiom here because remove_if would
|
||||
// move the instances, and the container must guarantee that
|
||||
// iterators/pointers to instances always remain valid.
|
||||
for (std::list<gd::InitialInstance>::iterator it = initialInstances.begin(),
|
||||
end = initialInstances.end();
|
||||
it != end;) {
|
||||
if (predicat(*it))
|
||||
if (predicate(*it))
|
||||
it = initialInstances.erase(it);
|
||||
else
|
||||
++it;
|
||||
|
@@ -178,7 +178,7 @@ class GD_CORE_API InitialInstancesContainer {
|
||||
|
||||
private:
|
||||
void RemoveInstanceIf(
|
||||
std::function<bool(const gd::InitialInstance &)> predicat);
|
||||
std::function<bool(const gd::InitialInstance &)> predicate);
|
||||
|
||||
std::list<gd::InitialInstance> initialInstances;
|
||||
|
||||
|
@@ -127,6 +127,10 @@ class GD_CORE_API Object {
|
||||
/** \brief Return the tags of the object.
|
||||
*/
|
||||
const gd::String& GetTags() const { return tags; }
|
||||
|
||||
/** \brief Shortcut to check if the object is a 3D object.
|
||||
*/
|
||||
bool Is3DObject() const { return configuration->Is3DObject(); }
|
||||
///@}
|
||||
|
||||
/** \name Behaviors management
|
||||
|
@@ -20,7 +20,7 @@ namespace gd {
|
||||
|
||||
ObjectConfiguration::~ObjectConfiguration() {}
|
||||
|
||||
ObjectConfiguration::ObjectConfiguration() {}
|
||||
ObjectConfiguration::ObjectConfiguration(): is3DObject(false) {}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> ObjectConfiguration::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
|
@@ -61,12 +61,22 @@ class GD_CORE_API ObjectConfiguration {
|
||||
|
||||
/** \brief Change the type of the object.
|
||||
*/
|
||||
void SetType(const gd::String& type_) { type = type_; }
|
||||
void SetType(const gd::String& type_) {
|
||||
type = type_;
|
||||
|
||||
// For now, as a shortcut, consider only the objects from the built-in 3D extension
|
||||
// to be 3D object.
|
||||
is3DObject = type.find("Scene3D::") == 0;
|
||||
}
|
||||
|
||||
/** \brief Return the type of the object.
|
||||
*/
|
||||
const gd::String& GetType() const { return type; }
|
||||
|
||||
/** \brief Shortcut to check if the object is a 3D object.
|
||||
*/
|
||||
bool Is3DObject() const { return is3DObject; }
|
||||
|
||||
/** \name Object properties
|
||||
* Reading and updating object configuration properties
|
||||
*/
|
||||
@@ -170,6 +180,7 @@ class GD_CORE_API ObjectConfiguration {
|
||||
protected:
|
||||
gd::String type; ///< Which type of object is represented by this
|
||||
///< configuration.
|
||||
bool is3DObject;
|
||||
|
||||
/**
|
||||
* \brief Derived object configuration can redefine this method to load
|
||||
|
@@ -65,6 +65,8 @@ Project::Project()
|
||||
pixelsRounding(false),
|
||||
adaptGameResolutionAtRuntime(true),
|
||||
sizeOnStartupMode("adaptWidth"),
|
||||
antialiasingMode("MSAA"),
|
||||
isAntialisingEnabledOnMobile(false),
|
||||
projectUuid(""),
|
||||
useDeprecatedZeroAsDefaultZOrder(false),
|
||||
useExternalSourceFiles(false),
|
||||
@@ -628,6 +630,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
SetAdaptGameResolutionAtRuntime(
|
||||
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
|
||||
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
|
||||
SetAntialiasingMode(propElement.GetStringAttribute("antialiasingMode", "MSAA"));
|
||||
SetAntialisingEnabledOnMobile(propElement.GetBoolAttribute("antialisingEnabledOnMobile", false));
|
||||
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
|
||||
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
@@ -882,6 +886,8 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.SetAttribute("adaptGameResolutionAtRuntime",
|
||||
adaptGameResolutionAtRuntime);
|
||||
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
|
||||
propElement.SetAttribute("antialiasingMode", antialiasingMode);
|
||||
propElement.SetAttribute("antialisingEnabledOnMobile", isAntialisingEnabledOnMobile);
|
||||
propElement.SetAttribute("projectUuid", projectUuid);
|
||||
propElement.SetAttribute("folderProject", folderProject);
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
@@ -1113,6 +1119,8 @@ void Project::Init(const gd::Project& game) {
|
||||
pixelsRounding = game.pixelsRounding;
|
||||
adaptGameResolutionAtRuntime = game.adaptGameResolutionAtRuntime;
|
||||
sizeOnStartupMode = game.sizeOnStartupMode;
|
||||
antialiasingMode = game.antialiasingMode;
|
||||
isAntialisingEnabledOnMobile = game.isAntialisingEnabledOnMobile;
|
||||
projectUuid = game.projectUuid;
|
||||
useDeprecatedZeroAsDefaultZOrder = game.useDeprecatedZeroAsDefaultZOrder;
|
||||
|
||||
|
@@ -383,6 +383,26 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
void SetPixelsRounding(bool enable) { pixelsRounding = enable; }
|
||||
|
||||
/**
|
||||
* Return the antialiasing mode used by the game ("none" or "MSAA").
|
||||
*/
|
||||
const gd::String& GetAntialiasingMode() const { return antialiasingMode; }
|
||||
|
||||
/**
|
||||
* Set the antialiasing mode used by the game ("none" or "MSAA").
|
||||
*/
|
||||
void SetAntialiasingMode(const gd::String& antialiasingMode_) { antialiasingMode = antialiasingMode_; }
|
||||
|
||||
/**
|
||||
* Return true if antialising is enabled on mobiles.
|
||||
*/
|
||||
bool IsAntialisingEnabledOnMobile() const { return isAntialisingEnabledOnMobile; }
|
||||
|
||||
/**
|
||||
* Set whether antialising is enabled on mobiles or not.
|
||||
*/
|
||||
void SetAntialisingEnabledOnMobile(bool enable) { isAntialisingEnabledOnMobile = enable; }
|
||||
|
||||
/**
|
||||
* \brief Return if the project should set 0 as Z-order for objects created
|
||||
* from events (which is deprecated) - instead of the highest Z order that was
|
||||
@@ -1040,6 +1060,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
gd::String
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
gd::String antialiasingMode;
|
||||
bool isAntialisingEnabledOnMobile;
|
||||
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
|
||||
|
@@ -686,6 +686,16 @@ String GD_CORE_API operator+(const char *lhs, const String &rhs)
|
||||
return str;
|
||||
}
|
||||
|
||||
const String& GD_CORE_API operator||(const String& lhs, const String &rhs)
|
||||
{
|
||||
return lhs.empty() ? rhs : lhs;
|
||||
}
|
||||
|
||||
String GD_CORE_API operator||(String lhs, const char *rhs)
|
||||
{
|
||||
return lhs.empty() ? rhs : lhs;
|
||||
}
|
||||
|
||||
bool GD_CORE_API operator==( const String &lhs, const String &rhs )
|
||||
{
|
||||
return (lhs.compare(rhs) == 0);
|
||||
|
@@ -699,6 +699,10 @@ String GD_CORE_API operator+(String lhs, const char *rhs);
|
||||
*/
|
||||
String GD_CORE_API operator+(const char *lhs, const String &rhs);
|
||||
|
||||
const String& GD_CORE_API operator||(const String &lhs, const String &rhs);
|
||||
|
||||
String GD_CORE_API operator||(String lhs, const char *rhs);
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
@@ -3348,10 +3348,21 @@ const gd::Instruction &CreateActionWithLayerParameter(gd::Project &project,
|
||||
action.SetParameter(3, gd::Expression("\"My layer\""));
|
||||
return event.GetActions().Insert(action);
|
||||
}
|
||||
const gd::Instruction &CreateActionWithEmptyLayerParameter(gd::Project &project,
|
||||
gd::EventsList &events) {
|
||||
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
|
||||
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
|
||||
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::SetCameraCenterX");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(3, gd::Expression(""));
|
||||
return event.GetActions().Insert(action);
|
||||
}
|
||||
const gd::Instruction &
|
||||
CreateExpressionWithLayerParameter(gd::Project &project,
|
||||
gd::EventsList &events) {
|
||||
gd::EventsList &events,
|
||||
const gd::String &layerName) {
|
||||
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
|
||||
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
|
||||
|
||||
@@ -3359,8 +3370,8 @@ CreateExpressionWithLayerParameter(gd::Project &project,
|
||||
action.SetType("MyExtension::DoSomething");
|
||||
action.SetParametersCount(1);
|
||||
action.SetParameter(
|
||||
0, gd::Expression("MyExtension::CameraCenterX(\"My layer\") + "
|
||||
"MyExtension::CameraCenterX(\"My layer\")"));
|
||||
0, gd::Expression("MyExtension::CameraCenterX(\"" + layerName + "\") + "
|
||||
"MyExtension::CameraCenterX(\"" + layerName + "\")"));
|
||||
return event.GetActions().Insert(action);
|
||||
}
|
||||
} // namespace
|
||||
@@ -3390,13 +3401,13 @@ TEST_CASE("RenameLayer", "[common]") {
|
||||
project, otherExternalEvents.GetEvents());
|
||||
|
||||
auto &layoutExpression =
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents());
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents(), "My layer");
|
||||
auto &externalExpression =
|
||||
CreateExpressionWithLayerParameter(project, externalEvents.GetEvents());
|
||||
CreateExpressionWithLayerParameter(project, externalEvents.GetEvents(), "My layer");
|
||||
auto &otherLayoutExpression =
|
||||
CreateExpressionWithLayerParameter(project, otherLayout.GetEvents());
|
||||
CreateExpressionWithLayerParameter(project, otherLayout.GetEvents(), "My layer");
|
||||
auto &otherExternalExpression = CreateExpressionWithLayerParameter(
|
||||
project, otherExternalEvents.GetEvents());
|
||||
project, otherExternalEvents.GetEvents(), "My layer");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameLayer(project, layout, "My layer",
|
||||
"My renamed layer");
|
||||
@@ -3434,7 +3445,7 @@ TEST_CASE("RenameLayer", "[common]") {
|
||||
auto &layout = project.InsertNewLayout("My layout", 0);
|
||||
|
||||
auto &layoutExpression =
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents());
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents(), "My layer");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameLayer(project, layout, "My layer",
|
||||
"layerA");
|
||||
@@ -3443,6 +3454,57 @@ TEST_CASE("RenameLayer", "[common]") {
|
||||
"MyExtension::CameraCenterX(\"layerA\") + "
|
||||
"MyExtension::CameraCenterX(\"layerA\")");
|
||||
}
|
||||
|
||||
SECTION("Can rename a layer when a layer parameter is empty") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
auto &layout = project.InsertNewLayout("My layout", 0);
|
||||
|
||||
auto &layoutAction =
|
||||
CreateActionWithEmptyLayerParameter(project, layout.GetEvents());
|
||||
|
||||
gd::WholeProjectRefactorer::RenameLayer(project, layout, "My layer",
|
||||
"layerA");
|
||||
|
||||
REQUIRE(layoutAction.GetParameter(0).GetPlainString() == "");
|
||||
}
|
||||
|
||||
SECTION("Can't rename a layer to an empty name") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
auto &layout = project.InsertNewLayout("My layout", 0);
|
||||
|
||||
auto &layoutExpression =
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents(), "My layer");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameLayer(project, layout, "My layer",
|
||||
"");
|
||||
|
||||
REQUIRE(layoutExpression.GetParameter(0).GetPlainString() ==
|
||||
"MyExtension::CameraCenterX(\"My layer\") + "
|
||||
"MyExtension::CameraCenterX(\"My layer\")");
|
||||
}
|
||||
|
||||
SECTION("Can't rename a layer from an empty name") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
auto &layout = project.InsertNewLayout("My layout", 0);
|
||||
|
||||
auto &layoutExpression =
|
||||
CreateExpressionWithLayerParameter(project, layout.GetEvents(), "");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameLayer(project, layout, "", "My layer");
|
||||
|
||||
REQUIRE(layoutExpression.GetParameter(0).GetPlainString() ==
|
||||
"MyExtension::CameraCenterX(\"\") + "
|
||||
"MyExtension::CameraCenterX(\"\")");
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
|
@@ -108,19 +108,8 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
initialInstanceData.numberProperties.forEach((property) => {
|
||||
if (property.name === 'z') {
|
||||
this.setZ(property.value);
|
||||
} else if (property.name === 'depth') {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setDepth(property.value);
|
||||
}
|
||||
} else if (property.name === 'rotationX') {
|
||||
this.setRotationX(property.value);
|
||||
} else if (property.name === 'rotationY') {
|
||||
this.setRotationY(property.value);
|
||||
}
|
||||
});
|
||||
if (initialInstanceData.depth !== undefined)
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
|
||||
setX(x: float): void {
|
||||
|
23
Extensions/3D/CMakeLists.txt
Normal file
23
Extensions/3D/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(Scene3D)
|
||||
gd_add_extension_includes()
|
||||
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(Scene3D)
|
||||
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(Scene3D "${source_files}")
|
||||
gd_add_extension_target(Scene3D "${source_files}")
|
||||
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(Scene3D)
|
106
Extensions/3D/HemisphereLight.ts
Normal file
106
Extensions/3D/HemisphereLight.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
namespace gdjs {
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Scene3D::HemisphereLight',
|
||||
new (class implements gdjs.PixiFiltersTools.FilterCreator {
|
||||
makeFilter(
|
||||
target: EffectsTarget,
|
||||
effectData: EffectData
|
||||
): gdjs.PixiFiltersTools.Filter {
|
||||
return new (class implements gdjs.PixiFiltersTools.Filter {
|
||||
light: THREE.HemisphereLight;
|
||||
rotationObject: THREE.Group;
|
||||
_isEnabled: boolean = false;
|
||||
top: string = 'Y-';
|
||||
elevation: float = 45;
|
||||
rotation: float = 0;
|
||||
|
||||
constructor() {
|
||||
this.light = new THREE.HemisphereLight();
|
||||
this.light.position.set(1, 0, 0);
|
||||
this.rotationObject = new THREE.Group();
|
||||
this.rotationObject.add(this.light);
|
||||
this.updateRotation();
|
||||
}
|
||||
|
||||
isEnabled(target: EffectsTarget): boolean {
|
||||
return this._isEnabled;
|
||||
}
|
||||
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
|
||||
if (this._isEnabled === enabled) {
|
||||
return true;
|
||||
}
|
||||
if (enabled) {
|
||||
return this.applyEffect(target);
|
||||
} else {
|
||||
return this.removeEffect(target);
|
||||
}
|
||||
}
|
||||
applyEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.add(this.rotationObject);
|
||||
this._isEnabled = true;
|
||||
return true;
|
||||
}
|
||||
removeEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.remove(this.rotationObject);
|
||||
this._isEnabled = false;
|
||||
return true;
|
||||
}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {}
|
||||
updateDoubleParameter(parameterName: string, value: number): void {
|
||||
if (parameterName === 'intensity') {
|
||||
this.light.intensity = value;
|
||||
} else if (parameterName === 'elevation') {
|
||||
this.elevation = value;
|
||||
this.updateRotation();
|
||||
} else if (parameterName === 'rotation') {
|
||||
this.rotation = value;
|
||||
this.updateRotation();
|
||||
}
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'skyColor') {
|
||||
this.light.color = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'groundColor') {
|
||||
this.light.groundColor = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'top') {
|
||||
this.top = value;
|
||||
this.updateRotation();
|
||||
}
|
||||
}
|
||||
updateBooleanParameter(parameterName: string, value: boolean): void {}
|
||||
updateRotation() {
|
||||
if (this.top === 'Z+') {
|
||||
// 0° is a light from the right of the screen.
|
||||
this.rotationObject.rotation.z = gdjs.toRad(this.rotation);
|
||||
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
|
||||
} else {
|
||||
// 0° becomes a light from Z+.
|
||||
this.rotationObject.rotation.y = gdjs.toRad(this.rotation) - 90;
|
||||
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
292
Extensions/3D/Model3DObjectConfiguration.cpp
Normal file
292
Extensions/3D/Model3DObjectConfiguration.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
|
||||
GDevelop - Particle System Extension
|
||||
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "Model3DObjectConfiguration.h"
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/Project/InitialInstance.h"
|
||||
#include "GDCore/Project/MeasurementUnit.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Model3DObjectConfiguration::Model3DObjectConfiguration()
|
||||
: width(100), height(100), depth(100), rotationX(0), rotationY(0),
|
||||
rotationZ(0), modelResourceName(""), materialType("Basic"),
|
||||
originLocation("ModelOrigin"), centerLocation("ModelOrigin"),
|
||||
keepAspectRatio(true) {}
|
||||
|
||||
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
|
||||
const gd::String &newValue) {
|
||||
if (propertyName == "width") {
|
||||
width = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "height") {
|
||||
height = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "depth") {
|
||||
depth = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "rotationX") {
|
||||
rotationX = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "rotationY") {
|
||||
rotationY = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "rotationZ") {
|
||||
rotationZ = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "modelResourceName") {
|
||||
modelResourceName = newValue;
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "materialType") {
|
||||
materialType = newValue;
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "originLocation") {
|
||||
originLocation = newValue;
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "centerLocation") {
|
||||
centerLocation = newValue;
|
||||
return true;
|
||||
}
|
||||
if (propertyName == "keepAspectRatio") {
|
||||
keepAspectRatio = newValue == "1";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
Model3DObjectConfiguration::GetProperties() const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> objectProperties;
|
||||
|
||||
objectProperties["width"]
|
||||
.SetValue(gd::String::From(width))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Width"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["height"]
|
||||
.SetValue(gd::String::From(height))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Height"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["depth"]
|
||||
.SetValue(gd::String::From(depth))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Depth"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["keepAspectRatio"]
|
||||
.SetValue(keepAspectRatio ? "true" : "false")
|
||||
.SetType("boolean")
|
||||
.SetLabel(_("Reduce initial dimensions to keep aspect ratio"))
|
||||
.SetGroup(_("Default size"));
|
||||
|
||||
objectProperties["rotationX"]
|
||||
.SetValue(gd::String::From(rotationX))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around X axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
|
||||
objectProperties["rotationY"]
|
||||
.SetValue(gd::String::From(rotationY))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around Y axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
|
||||
objectProperties["rotationZ"]
|
||||
.SetValue(gd::String::From(rotationZ))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around Z axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
|
||||
objectProperties["modelResourceName"]
|
||||
.SetValue(modelResourceName)
|
||||
.SetType("resource")
|
||||
.AddExtraInfo("model3D")
|
||||
.SetLabel(_("3D model"));
|
||||
|
||||
objectProperties["materialType"]
|
||||
.SetValue(materialType.empty() ? "Basic" : materialType)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("Basic")
|
||||
.AddExtraInfo("StandardWithoutMetalness")
|
||||
.AddExtraInfo("KeepOriginal")
|
||||
.SetLabel(_("Material modifier"));
|
||||
|
||||
objectProperties["originLocation"]
|
||||
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("ModelOrigin")
|
||||
.AddExtraInfo("TopLeft")
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.SetLabel(_("Origin point"));
|
||||
|
||||
objectProperties["centerLocation"]
|
||||
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("ModelOrigin")
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.SetLabel(_("Center point"));
|
||||
|
||||
return objectProperties;
|
||||
}
|
||||
|
||||
bool Model3DObjectConfiguration::UpdateInitialInstanceProperty(
|
||||
gd::InitialInstance &instance, const gd::String &propertyName,
|
||||
const gd::String &newValue, gd::Project &project, gd::Layout &layout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
Model3DObjectConfiguration::GetInitialInstanceProperties(
|
||||
const gd::InitialInstance &instance, gd::Project &project,
|
||||
gd::Layout &layout) {
|
||||
std::map<gd::String, gd::PropertyDescriptor> instanceProperties;
|
||||
return instanceProperties;
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::DoUnserializeFrom(
|
||||
gd::Project &project, const gd::SerializerElement &element) {
|
||||
auto &content = element.GetChild("content");
|
||||
|
||||
width = content.GetDoubleAttribute("width");
|
||||
height = content.GetDoubleAttribute("height");
|
||||
depth = content.GetDoubleAttribute("depth");
|
||||
rotationX = content.GetDoubleAttribute("rotationX");
|
||||
rotationY = content.GetDoubleAttribute("rotationY");
|
||||
rotationZ = content.GetDoubleAttribute("rotationZ");
|
||||
modelResourceName = content.GetStringAttribute("modelResourceName");
|
||||
materialType = content.GetStringAttribute("materialType");
|
||||
originLocation = content.GetStringAttribute("originLocation");
|
||||
centerLocation = content.GetStringAttribute("centerLocation");
|
||||
keepAspectRatio = content.GetBoolAttribute("keepAspectRatio");
|
||||
|
||||
RemoveAllAnimations();
|
||||
auto &animationsElement = content.GetChild("animations");
|
||||
animationsElement.ConsiderAsArrayOf("animation");
|
||||
for (std::size_t i = 0; i < animationsElement.GetChildrenCount(); ++i) {
|
||||
auto &animationElement = animationsElement.GetChild(i);
|
||||
Model3DAnimation animation;
|
||||
animation.SetName(animationElement.GetStringAttribute("name", ""));
|
||||
animation.SetSource(animationElement.GetStringAttribute("source", ""));
|
||||
animation.SetShouldLoop(animationElement.GetBoolAttribute("loop", false));
|
||||
AddAnimation(animation);
|
||||
}
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::DoSerializeTo(
|
||||
gd::SerializerElement &element) const {
|
||||
auto &content = element.AddChild("content");
|
||||
content.SetAttribute("width", width);
|
||||
content.SetAttribute("height", height);
|
||||
content.SetAttribute("depth", depth);
|
||||
content.SetAttribute("rotationX", rotationX);
|
||||
content.SetAttribute("rotationY", rotationY);
|
||||
content.SetAttribute("rotationZ", rotationZ);
|
||||
content.SetAttribute("modelResourceName", modelResourceName);
|
||||
content.SetAttribute("materialType", materialType);
|
||||
content.SetAttribute("originLocation", originLocation);
|
||||
content.SetAttribute("centerLocation", centerLocation);
|
||||
content.SetAttribute("keepAspectRatio", keepAspectRatio);
|
||||
|
||||
auto &animationsElement = content.AddChild("animations");
|
||||
animationsElement.ConsiderAsArrayOf("animation");
|
||||
for (auto &animation : animations) {
|
||||
auto &animationElement = animationsElement.AddChild("animation");
|
||||
animationElement.SetAttribute("name", animation.GetName());
|
||||
animationElement.SetAttribute("source", animation.GetSource());
|
||||
animationElement.SetAttribute("loop", animation.ShouldLoop());
|
||||
}
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::ExposeResources(
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
worker.ExposeModel3D(modelResourceName);
|
||||
}
|
||||
|
||||
Model3DAnimation Model3DObjectConfiguration::badAnimation;
|
||||
|
||||
const Model3DAnimation &
|
||||
Model3DObjectConfiguration::GetAnimation(std::size_t nb) const {
|
||||
if (nb >= animations.size())
|
||||
return badAnimation;
|
||||
|
||||
return animations[nb];
|
||||
}
|
||||
|
||||
Model3DAnimation &Model3DObjectConfiguration::GetAnimation(std::size_t nb) {
|
||||
if (nb >= animations.size())
|
||||
return badAnimation;
|
||||
|
||||
return animations[nb];
|
||||
}
|
||||
|
||||
bool Model3DObjectConfiguration::HasAnimationNamed(
|
||||
const gd::String &name) const {
|
||||
return !name.empty() && (find_if(animations.begin(), animations.end(),
|
||||
[&name](const Model3DAnimation &animation) {
|
||||
return animation.GetName() == name;
|
||||
}) != animations.end());
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::AddAnimation(
|
||||
const Model3DAnimation &animation) {
|
||||
animations.push_back(animation);
|
||||
}
|
||||
|
||||
bool Model3DObjectConfiguration::RemoveAnimation(std::size_t nb) {
|
||||
if (nb >= GetAnimationsCount())
|
||||
return false;
|
||||
|
||||
animations.erase(animations.begin() + nb);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::SwapAnimations(std::size_t firstIndex,
|
||||
std::size_t secondIndex) {
|
||||
if (firstIndex < animations.size() && secondIndex < animations.size() &&
|
||||
firstIndex != secondIndex)
|
||||
std::swap(animations[firstIndex], animations[secondIndex]);
|
||||
}
|
||||
|
||||
void Model3DObjectConfiguration::MoveAnimation(std::size_t oldIndex,
|
||||
std::size_t newIndex) {
|
||||
if (oldIndex >= animations.size() || newIndex >= animations.size())
|
||||
return;
|
||||
|
||||
auto animation = animations[oldIndex];
|
||||
animations.erase(animations.begin() + oldIndex);
|
||||
animations.insert(animations.begin() + newIndex, animation);
|
||||
}
|
177
Extensions/3D/Model3DObjectConfiguration.h
Normal file
177
Extensions/3D/Model3DObjectConfiguration.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
|
||||
GDevelop - Particle System Extension
|
||||
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/Project/ObjectConfiguration.h"
|
||||
namespace gd {
|
||||
class InitialInstance;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
class GD_EXTENSION_API Model3DAnimation {
|
||||
public:
|
||||
Model3DAnimation() : shouldLoop(false) {};
|
||||
virtual ~Model3DAnimation(){};
|
||||
|
||||
/**
|
||||
* \brief Return the name of the animation
|
||||
*/
|
||||
const gd::String &GetName() const { return name; }
|
||||
|
||||
/**
|
||||
* \brief Change the name of the animation
|
||||
*/
|
||||
void SetName(const gd::String &name_) { name = name_; }
|
||||
|
||||
/**
|
||||
* \brief Return the name of the animation from the GLB file.
|
||||
*/
|
||||
const gd::String &GetSource() const { return source; }
|
||||
|
||||
/**
|
||||
* \brief Change the name of the animation from the GLB file.
|
||||
*/
|
||||
void SetSource(const gd::String &source_) { source = source_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the animation should loop.
|
||||
*/
|
||||
const bool ShouldLoop() const { return shouldLoop; }
|
||||
|
||||
/**
|
||||
* \brief Change whether the animation should loop or not.
|
||||
*/
|
||||
void SetShouldLoop(bool shouldLoop_) { shouldLoop = shouldLoop_; }
|
||||
|
||||
private:
|
||||
gd::String name;
|
||||
gd::String source;
|
||||
bool shouldLoop;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Particle Emitter object used for storage and for the IDE.
|
||||
*/
|
||||
class GD_EXTENSION_API Model3DObjectConfiguration
|
||||
: public gd::ObjectConfiguration {
|
||||
public:
|
||||
Model3DObjectConfiguration();
|
||||
virtual ~Model3DObjectConfiguration(){};
|
||||
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const override {
|
||||
return gd::make_unique<Model3DObjectConfiguration>(*this);
|
||||
}
|
||||
|
||||
virtual void ExposeResources(gd::ArbitraryResourceWorker &worker) override;
|
||||
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor>
|
||||
GetProperties() const override;
|
||||
|
||||
virtual bool UpdateProperty(const gd::String &name,
|
||||
const gd::String &value) override;
|
||||
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor>
|
||||
GetInitialInstanceProperties(const gd::InitialInstance &instance,
|
||||
gd::Project &project,
|
||||
gd::Layout &layout) override;
|
||||
|
||||
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance &instance,
|
||||
const gd::String &name,
|
||||
const gd::String &value,
|
||||
gd::Project &project,
|
||||
gd::Layout &layout) override;
|
||||
|
||||
/** \name Animations
|
||||
* Methods related to animations management
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Return the animation at the specified index.
|
||||
* If the index is out of bound, a "bad animation" object is returned.
|
||||
*/
|
||||
const Model3DAnimation &GetAnimation(std::size_t nb) const;
|
||||
|
||||
/**
|
||||
* \brief Return the animation at the specified index.
|
||||
* If the index is out of bound, a "bad animation" object is returned.
|
||||
*/
|
||||
Model3DAnimation &GetAnimation(std::size_t nb);
|
||||
|
||||
/**
|
||||
* \brief Return the number of animations this object has.
|
||||
*/
|
||||
std::size_t GetAnimationsCount() const { return animations.size(); };
|
||||
|
||||
/**
|
||||
* \brief Return true if the animation called "name" exists.
|
||||
*/
|
||||
bool HasAnimationNamed(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Add an animation at the end of the existing ones.
|
||||
*/
|
||||
void AddAnimation(const Model3DAnimation &animation);
|
||||
|
||||
/**
|
||||
* \brief Remove an animation.
|
||||
*/
|
||||
bool RemoveAnimation(std::size_t nb);
|
||||
|
||||
/**
|
||||
* \brief Remove all animations.
|
||||
*/
|
||||
void RemoveAllAnimations() { animations.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Return true if the object hasn't any animation.
|
||||
*/
|
||||
bool HasNoAnimations() const { return animations.empty(); }
|
||||
|
||||
/**
|
||||
* \brief Swap the position of two animations
|
||||
*/
|
||||
void SwapAnimations(std::size_t firstIndex, std::size_t secondIndex);
|
||||
|
||||
/**
|
||||
* \brief Change the position of the specified animation
|
||||
*/
|
||||
void MoveAnimation(std::size_t oldIndex, std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Return a read-only reference to the vector containing all the
|
||||
* animation of the object.
|
||||
*/
|
||||
const std::vector<Model3DAnimation> &GetAllAnimations() const {
|
||||
return animations;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
protected:
|
||||
virtual void DoUnserializeFrom(gd::Project &project,
|
||||
const gd::SerializerElement &element) override;
|
||||
virtual void DoSerializeTo(gd::SerializerElement &element) const override;
|
||||
|
||||
private:
|
||||
double width;
|
||||
double height;
|
||||
double depth;
|
||||
double rotationX;
|
||||
double rotationY;
|
||||
double rotationZ;
|
||||
|
||||
gd::String modelResourceName;
|
||||
gd::String materialType;
|
||||
gd::String originLocation;
|
||||
gd::String centerLocation;
|
||||
|
||||
bool keepAspectRatio;
|
||||
|
||||
std::vector<Model3DAnimation> animations;
|
||||
static Model3DAnimation badAnimation; //< Bad animation when an out of bound
|
||||
// animation is requested.
|
||||
};
|
@@ -1,4 +1,6 @@
|
||||
namespace gdjs {
|
||||
type Model3DAnimation = { name: string; source: string; loop: boolean };
|
||||
|
||||
/** Base parameters for {@link gdjs.Cube3DRuntimeObject} */
|
||||
export interface Model3DObjectData extends Object3DData {
|
||||
/** The base parameters of the Model3D object */
|
||||
@@ -9,9 +11,40 @@ namespace gdjs {
|
||||
rotationZ: number;
|
||||
keepAspectRatio: boolean;
|
||||
materialType: 'Basic' | 'StandardWithoutMetalness' | 'KeepOriginal';
|
||||
originLocation:
|
||||
| 'ModelOrigin'
|
||||
| 'ObjectCenter'
|
||||
| 'BottomCenterZ'
|
||||
| 'BottomCenterY'
|
||||
| 'TopLeft';
|
||||
centerLocation:
|
||||
| 'ModelOrigin'
|
||||
| 'ObjectCenter'
|
||||
| 'BottomCenterZ'
|
||||
| 'BottomCenterY';
|
||||
animations: Model3DAnimation[];
|
||||
};
|
||||
}
|
||||
|
||||
type FloatPoint3D = [float, float, float];
|
||||
|
||||
const getPointForLocation = (location: string): FloatPoint3D | null => {
|
||||
switch (location) {
|
||||
case 'ModelOrigin':
|
||||
return null;
|
||||
case 'ObjectCenter':
|
||||
return [0.5, 0.5, 0.5];
|
||||
case 'BottomCenterZ':
|
||||
return [0.5, 0.5, 0];
|
||||
case 'BottomCenterY':
|
||||
return [0.5, 1, 0.5];
|
||||
case 'TopLeft':
|
||||
return [0, 0, 0];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A 3D object which displays a 3D model.
|
||||
*/
|
||||
@@ -22,17 +55,61 @@ namespace gdjs {
|
||||
_materialType: gdjs.Model3DRuntimeObject.MaterialType =
|
||||
gdjs.Model3DRuntimeObject.MaterialType.Basic;
|
||||
|
||||
/**
|
||||
* The local point of the model that will be at the object position.
|
||||
*
|
||||
* Coordinates are between 0 and 1.
|
||||
*
|
||||
* Its value is `null` when the point is configured to `"ModelOrigin"`
|
||||
* because the model origin needs to be evaluated according to the object
|
||||
* configuration.
|
||||
* @see gdjs.Model3DRuntimeObject3DRenderer.getOriginPoint
|
||||
*/
|
||||
_originPoint: FloatPoint3D | null;
|
||||
/**
|
||||
* The local point of the model that is used as rotation center.
|
||||
*
|
||||
* Coordinates are between 0 and 1.
|
||||
*
|
||||
* Its value is `null` when the point is configured to `"ModelOrigin"`
|
||||
* because the model origin needs to be evaluated according to the object
|
||||
* configuration.
|
||||
* @see gdjs.Model3DRuntimeObject3DRenderer.getCenterPoint
|
||||
*/
|
||||
_centerPoint: FloatPoint3D | null;
|
||||
|
||||
_animations: Model3DAnimation[];
|
||||
_currentAnimationIndex: integer = 0;
|
||||
_animationSpeedScale: float = 1;
|
||||
_animationPaused: boolean = false;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objectData: Model3DObjectData
|
||||
) {
|
||||
super(instanceContainer, objectData);
|
||||
this._modelResourceName = objectData.content.modelResourceName;
|
||||
this._animations = objectData.content.animations;
|
||||
this._originPoint = getPointForLocation(
|
||||
objectData.content.originLocation
|
||||
);
|
||||
this._centerPoint = getPointForLocation(
|
||||
objectData.content.centerLocation
|
||||
);
|
||||
this._renderer = new gdjs.Model3DRuntimeObjectRenderer(
|
||||
this,
|
||||
instanceContainer
|
||||
);
|
||||
this._updateMaterialType(objectData);
|
||||
this._materialType = this._convertMaterialType(
|
||||
objectData.content.materialType
|
||||
);
|
||||
this._updateModel(objectData);
|
||||
if (this._animations.length > 0) {
|
||||
this._renderer.playAnimation(
|
||||
this._animations[0].source,
|
||||
this._animations[0].loop
|
||||
);
|
||||
}
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
@@ -53,23 +130,42 @@ namespace gdjs {
|
||||
oldObjectData.content.keepAspectRatio !==
|
||||
newObjectData.content.keepAspectRatio
|
||||
) {
|
||||
this._updateDefaultTransformation(newObjectData);
|
||||
this._updateModel(newObjectData);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.materialType !==
|
||||
newObjectData.content.materialType
|
||||
) {
|
||||
this._updateMaterialType(newObjectData);
|
||||
this._materialType = this._convertMaterialType(
|
||||
newObjectData.content.materialType
|
||||
);
|
||||
this._updateModel(newObjectData);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.originLocation !==
|
||||
newObjectData.content.originLocation
|
||||
) {
|
||||
this._originPoint = getPointForLocation(
|
||||
newObjectData.content.originLocation
|
||||
);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.centerLocation !==
|
||||
newObjectData.content.centerLocation
|
||||
) {
|
||||
this._centerPoint = getPointForLocation(
|
||||
newObjectData.content.centerLocation
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_updateDefaultTransformation(objectData: Model3DObjectData) {
|
||||
_updateModel(objectData: Model3DObjectData) {
|
||||
const rotationX = objectData.content.rotationX || 0;
|
||||
const rotationY = objectData.content.rotationY || 0;
|
||||
const rotationZ = objectData.content.rotationZ || 0;
|
||||
const keepAspectRatio = objectData.content.keepAspectRatio;
|
||||
this._renderer._updateDefaultTransformation(
|
||||
this._renderer._updateModel(
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
@@ -96,12 +192,118 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
_updateMaterialType(objectData: Model3DObjectData) {
|
||||
this._materialType = this._convertMaterialType(
|
||||
objectData.content.materialType
|
||||
update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
const elapsedTime = this.getElapsedTime() / 1000;
|
||||
this._renderer.updateAnimation(elapsedTime * this._animationSpeedScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the animation being played.
|
||||
* @return The index of the new animation being played
|
||||
*/
|
||||
getAnimationIndex(): number {
|
||||
return this._currentAnimationIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the animation being played.
|
||||
* @param animationIndex The index of the new animation to be played
|
||||
*/
|
||||
setAnimationIndex(animationIndex: number): void {
|
||||
animationIndex = animationIndex | 0;
|
||||
if (
|
||||
animationIndex < this._animations.length &&
|
||||
this._currentAnimationIndex !== animationIndex &&
|
||||
animationIndex >= 0
|
||||
) {
|
||||
const animation = this._animations[animationIndex];
|
||||
this._currentAnimationIndex = animationIndex;
|
||||
this._renderer.playAnimation(animation.source, animation.loop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the animation being played.
|
||||
* @return The name of the new animation being played
|
||||
*/
|
||||
getAnimationName(): string {
|
||||
if (this._currentAnimationIndex >= this._animations.length) {
|
||||
return '';
|
||||
}
|
||||
return this._animations[this._currentAnimationIndex].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the animation being played.
|
||||
* @param newAnimationName The name of the new animation to be played
|
||||
*/
|
||||
setAnimationName(newAnimationName: string): void {
|
||||
if (!newAnimationName) {
|
||||
return;
|
||||
}
|
||||
const animationIndex = this._animations.findIndex(
|
||||
(animation) => animation.name === newAnimationName
|
||||
);
|
||||
this._renderer._updateMaterials();
|
||||
this._updateDefaultTransformation(objectData);
|
||||
if (animationIndex >= 0) {
|
||||
this.setAnimationIndex(animationIndex);
|
||||
}
|
||||
}
|
||||
|
||||
isCurrentAnimationName(name: string): boolean {
|
||||
return this.getAnimationName() === name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if animation has ended.
|
||||
* The animation had ended if:
|
||||
* - it's not configured as a loop;
|
||||
* - the current frame is the last frame;
|
||||
* - the last frame has been displayed long enough.
|
||||
*/
|
||||
hasAnimationEnded(): boolean {
|
||||
return this._renderer.hasAnimationEnded();
|
||||
}
|
||||
|
||||
isAnimationPaused() {
|
||||
return this._animationPaused;
|
||||
}
|
||||
|
||||
pauseAnimation() {
|
||||
this._animationPaused = true;
|
||||
return this._renderer.pauseAnimation();
|
||||
}
|
||||
|
||||
resumeAnimation() {
|
||||
this._animationPaused = false;
|
||||
return this._renderer.resumeAnimation();
|
||||
}
|
||||
|
||||
getAnimationSpeedScale() {
|
||||
return this._animationSpeedScale;
|
||||
}
|
||||
|
||||
setAnimationSpeedScale(ratio: float): void {
|
||||
this._animationSpeedScale = ratio;
|
||||
}
|
||||
|
||||
getCenterX(): float {
|
||||
const centerPoint = this._renderer.getCenterPoint();
|
||||
return this.getWidth() * centerPoint[0];
|
||||
}
|
||||
|
||||
getCenterY(): float {
|
||||
const centerPoint = this._renderer.getCenterPoint();
|
||||
return this.getHeight() * centerPoint[1];
|
||||
}
|
||||
|
||||
getDrawableX(): float {
|
||||
const originPoint = this._renderer.getOriginPoint();
|
||||
return this.getX() - this.getWidth() * originPoint[0];
|
||||
}
|
||||
|
||||
getDrawableY(): float {
|
||||
const originPoint = this._renderer.getOriginPoint();
|
||||
return this.getY() - this.getHeight() * originPoint[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,38 +1,144 @@
|
||||
namespace gdjs {
|
||||
type FloatPoint3D = [float, float, float];
|
||||
|
||||
const removeMetalness = (material: THREE.Material): void => {
|
||||
//@ts-ignore
|
||||
if (material.metalness) {
|
||||
//@ts-ignore
|
||||
material.metalness = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const removeMetalnessFromMesh = (node: THREE.Object3D<THREE.Event>) => {
|
||||
const mesh = node as THREE.Mesh;
|
||||
if (!mesh.material) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(mesh.material)) {
|
||||
for (let index = 0; index < mesh.material.length; index++) {
|
||||
removeMetalness(mesh.material[index]);
|
||||
}
|
||||
} else {
|
||||
removeMetalness(mesh.material);
|
||||
}
|
||||
};
|
||||
|
||||
const traverseToRemoveMetalnessFromMeshes = (
|
||||
node: THREE.Object3D<THREE.Event>
|
||||
) => node.traverse(removeMetalnessFromMesh);
|
||||
|
||||
const convertToBasicMaterial = (
|
||||
material: THREE.Material
|
||||
): THREE.MeshBasicMaterial => {
|
||||
const basicMaterial = new THREE.MeshBasicMaterial();
|
||||
//@ts-ignore
|
||||
if (material.color) {
|
||||
//@ts-ignore
|
||||
basicMaterial.color = material.color;
|
||||
}
|
||||
//@ts-ignore
|
||||
if (material.map) {
|
||||
//@ts-ignore
|
||||
basicMaterial.map = material.map;
|
||||
}
|
||||
return basicMaterial;
|
||||
};
|
||||
|
||||
const setBasicMaterialTo = (node: THREE.Object3D<THREE.Event>): void => {
|
||||
const mesh = node as THREE.Mesh;
|
||||
if (!mesh.material) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(mesh.material)) {
|
||||
for (let index = 0; index < mesh.material.length; index++) {
|
||||
mesh.material[index] = convertToBasicMaterial(mesh.material[index]);
|
||||
}
|
||||
} else {
|
||||
mesh.material = convertToBasicMaterial(mesh.material);
|
||||
}
|
||||
};
|
||||
|
||||
const traverseToSetBasicMaterialFromMeshes = (
|
||||
node: THREE.Object3D<THREE.Event>
|
||||
) => node.traverse(setBasicMaterialTo);
|
||||
|
||||
class Model3DRuntimeObject3DRenderer extends gdjs.RuntimeObject3DRenderer {
|
||||
private _model3DRuntimeObject: gdjs.Model3DRuntimeObject;
|
||||
/**
|
||||
* The 3D model stretched in a 1x1x1 cube.
|
||||
*/
|
||||
private _threeObject: THREE.Object3D;
|
||||
private _originalModel: THREE_ADDONS.GLTF;
|
||||
private _animationMixer: THREE.AnimationMixer;
|
||||
private _action: THREE.AnimationAction | null;
|
||||
|
||||
/**
|
||||
* The model origin evaluated according to the object configuration.
|
||||
*
|
||||
* Coordinates are between 0 and 1.
|
||||
*/
|
||||
private _modelOriginPoint: FloatPoint3D;
|
||||
|
||||
constructor(
|
||||
runtimeObject: gdjs.Model3DRuntimeObject,
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
// @ts-ignore It can't be null if THREE exists.
|
||||
const originalModelMesh: THREE.Object3D = instanceContainer
|
||||
// GLB files with skeleton must not have any transformation to work properly.
|
||||
const originalModel = instanceContainer
|
||||
.getGame()
|
||||
.getModel3DManager()
|
||||
.getModel(runtimeObject._modelResourceName);
|
||||
const modelObject3D = originalModelMesh.clone();
|
||||
// _updateModel will actually add a clone of the model.
|
||||
const model = new THREE.Group();
|
||||
|
||||
// Create a group to transform the object according to
|
||||
// position, angle and dimensions.
|
||||
const group = new THREE.Group();
|
||||
group.rotation.order = 'ZYX';
|
||||
group.add(modelObject3D);
|
||||
group.add(model);
|
||||
super(runtimeObject, instanceContainer, group);
|
||||
|
||||
this._model3DRuntimeObject = runtimeObject;
|
||||
this._threeObject = modelObject3D;
|
||||
this._threeObject = model;
|
||||
this._originalModel = originalModel;
|
||||
this._modelOriginPoint = [0, 0, 0];
|
||||
|
||||
this.updateSize();
|
||||
this.updatePosition();
|
||||
this.updateRotation();
|
||||
|
||||
this._animationMixer = new THREE.AnimationMixer(model);
|
||||
this._action = null;
|
||||
}
|
||||
|
||||
_updateDefaultTransformation(
|
||||
updateAnimation(timeDelta: float) {
|
||||
this._animationMixer.update(timeDelta);
|
||||
}
|
||||
|
||||
updatePosition() {
|
||||
const originPoint = this.getOriginPoint();
|
||||
const centerPoint = this.getCenterPoint();
|
||||
this.get3DRendererObject().position.set(
|
||||
this._object.getX() -
|
||||
this._object.getWidth() * (originPoint[0] - centerPoint[0]),
|
||||
this._object.getY() -
|
||||
this._object.getHeight() * (originPoint[1] - centerPoint[1]),
|
||||
this._object.getZ() -
|
||||
this._object.getDepth() * (originPoint[2] - centerPoint[2])
|
||||
);
|
||||
}
|
||||
|
||||
getOriginPoint() {
|
||||
return this._model3DRuntimeObject._originPoint || this._modelOriginPoint;
|
||||
}
|
||||
|
||||
getCenterPoint() {
|
||||
return this._model3DRuntimeObject._centerPoint || this._modelOriginPoint;
|
||||
}
|
||||
|
||||
private _updateDefaultTransformation(
|
||||
threeObject: THREE.Object3D,
|
||||
rotationX: float,
|
||||
rotationY: float,
|
||||
rotationZ: float,
|
||||
@@ -41,38 +147,63 @@ namespace gdjs {
|
||||
originalDepth: float,
|
||||
keepAspectRatio: boolean
|
||||
) {
|
||||
const boundingBox = this._getModelAABB(rotationX, rotationY, rotationZ);
|
||||
threeObject.rotation.set(
|
||||
gdjs.toRad(rotationX),
|
||||
gdjs.toRad(rotationY),
|
||||
gdjs.toRad(rotationZ)
|
||||
);
|
||||
threeObject.updateMatrixWorld(true);
|
||||
const boundingBox = new THREE.Box3().setFromObject(threeObject);
|
||||
|
||||
const modelWidth = boundingBox.max.x - boundingBox.min.x;
|
||||
const modelHeight = boundingBox.max.y - boundingBox.min.y;
|
||||
const modelDepth = boundingBox.max.z - boundingBox.min.z;
|
||||
this._modelOriginPoint[0] = -boundingBox.min.x / modelWidth;
|
||||
this._modelOriginPoint[1] = -boundingBox.min.y / modelHeight;
|
||||
this._modelOriginPoint[2] = -boundingBox.min.z / modelDepth;
|
||||
|
||||
// The model is flipped on Y axis.
|
||||
this._modelOriginPoint[1] = 1 - this._modelOriginPoint[1];
|
||||
|
||||
// Center the model.
|
||||
this._threeObject.position.set(
|
||||
-(boundingBox.min.x + boundingBox.max.x) / 2,
|
||||
(this._threeObject.position.y =
|
||||
-(boundingBox.min.y + boundingBox.max.y) / 2),
|
||||
(this._threeObject.position.z =
|
||||
-(boundingBox.min.z + boundingBox.max.z) / 2)
|
||||
);
|
||||
const centerPoint = this._model3DRuntimeObject._centerPoint;
|
||||
if (centerPoint) {
|
||||
threeObject.position.set(
|
||||
-(
|
||||
boundingBox.min.x +
|
||||
(boundingBox.max.x - boundingBox.min.x) * centerPoint[0]
|
||||
),
|
||||
// The model is flipped on Y axis.
|
||||
-(
|
||||
boundingBox.min.y +
|
||||
(boundingBox.max.y - boundingBox.min.y) * (1 - centerPoint[1])
|
||||
),
|
||||
-(
|
||||
boundingBox.min.z +
|
||||
(boundingBox.max.z - boundingBox.min.z) * centerPoint[2]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Rotate the model.
|
||||
this._threeObject.scale.set(1, 1, 1);
|
||||
this._threeObject.rotation.set(
|
||||
threeObject.scale.set(1, 1, 1);
|
||||
threeObject.rotation.set(
|
||||
gdjs.toRad(rotationX),
|
||||
gdjs.toRad(rotationY),
|
||||
gdjs.toRad(rotationZ)
|
||||
);
|
||||
|
||||
// Stretch the model in a 1x1x1 cube.
|
||||
const modelWidth = boundingBox.max.x - boundingBox.min.x;
|
||||
const modelHeight = boundingBox.max.y - boundingBox.min.y;
|
||||
const modelDepth = boundingBox.max.z - boundingBox.min.z;
|
||||
|
||||
const scaleX = 1 / modelWidth;
|
||||
const scaleY = 1 / modelHeight;
|
||||
const scaleZ = 1 / modelDepth;
|
||||
|
||||
const scaleMatrix = new THREE.Matrix4();
|
||||
scaleMatrix.makeScale(scaleX, scaleY, scaleZ);
|
||||
this._threeObject.updateMatrix();
|
||||
this._threeObject.applyMatrix4(scaleMatrix);
|
||||
// Flip on Y because the Y axis is on the opposite side of direct basis.
|
||||
// It avoids models to be like a mirror refection.
|
||||
scaleMatrix.makeScale(scaleX, -scaleY, scaleZ);
|
||||
threeObject.updateMatrix();
|
||||
threeObject.applyMatrix4(scaleMatrix);
|
||||
|
||||
if (keepAspectRatio) {
|
||||
// Reduce the object dimensions to keep aspect ratio.
|
||||
@@ -85,98 +216,138 @@ namespace gdjs {
|
||||
this._object._setOriginalHeight(scaleRatio * modelHeight);
|
||||
this._object._setOriginalDepth(scaleRatio * modelDepth);
|
||||
}
|
||||
|
||||
this._threeObject.updateMatrix();
|
||||
}
|
||||
|
||||
private _getModelAABB(
|
||||
_updateModel(
|
||||
rotationX: float,
|
||||
rotationY: float,
|
||||
rotationZ: float
|
||||
rotationZ: float,
|
||||
originalWidth: float,
|
||||
originalHeight: float,
|
||||
originalDepth: float,
|
||||
keepAspectRatio: boolean
|
||||
) {
|
||||
// The original model is used because `setFromObject` is working in
|
||||
// world transformation.
|
||||
// Start from the original model because:
|
||||
// - _replaceMaterials is destructive
|
||||
// - _updateDefaultTransformation may need to work with meshes in local space
|
||||
|
||||
// @ts-ignore It can't be null if THREE exists.
|
||||
const originalModelMesh: THREE.Object3D = this._object
|
||||
.getInstanceContainer()
|
||||
.getGame()
|
||||
.getModel3DManager()
|
||||
.getModel(this._model3DRuntimeObject._modelResourceName);
|
||||
// This group hold the rotation defined by properties.
|
||||
const threeObject = new THREE.Group();
|
||||
threeObject.rotation.order = 'ZYX';
|
||||
const root = THREE_ADDONS.SkeletonUtils.clone(this._originalModel.scene);
|
||||
threeObject.add(root);
|
||||
|
||||
originalModelMesh.rotation.set(
|
||||
gdjs.toRad(rotationX),
|
||||
gdjs.toRad(rotationY),
|
||||
gdjs.toRad(rotationZ)
|
||||
this._replaceMaterials(threeObject);
|
||||
|
||||
this._updateDefaultTransformation(
|
||||
threeObject,
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
originalDepth,
|
||||
keepAspectRatio
|
||||
);
|
||||
|
||||
const aabb = new THREE.Box3().setFromObject(originalModelMesh);
|
||||
|
||||
// Revert changes.
|
||||
originalModelMesh.rotation.set(0, 0, 0);
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
_updateMaterials() {
|
||||
// @ts-ignore It can't be null if THREE exists.
|
||||
const originalModelMesh: THREE.Object3D = this._model3DRuntimeObject
|
||||
.getInstanceContainer()
|
||||
.getGame()
|
||||
.getModel3DManager()
|
||||
.getModel(this._model3DRuntimeObject._modelResourceName);
|
||||
const modelObject3D = originalModelMesh.clone();
|
||||
|
||||
// Replace the 3D object.
|
||||
this.get3DRendererObject().remove(this._threeObject);
|
||||
this.get3DRendererObject().add(modelObject3D);
|
||||
this.get3DRendererObject().add(threeObject);
|
||||
this._threeObject = threeObject;
|
||||
|
||||
this._threeObject = modelObject3D;
|
||||
|
||||
this._replaceMaterials();
|
||||
// Start the current animation on the new 3D object.
|
||||
this._animationMixer = new THREE.AnimationMixer(root);
|
||||
const isAnimationPaused = this._model3DRuntimeObject.isAnimationPaused();
|
||||
this._model3DRuntimeObject.setAnimationIndex(
|
||||
this._model3DRuntimeObject.getAnimationIndex()
|
||||
);
|
||||
if (isAnimationPaused) {
|
||||
this.pauseAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace materials to better work with lights (or no light).
|
||||
*/
|
||||
_replaceMaterials() {
|
||||
private _replaceMaterials(threeObject: THREE.Object3D) {
|
||||
if (
|
||||
this._model3DRuntimeObject._materialType ===
|
||||
gdjs.Model3DRuntimeObject.MaterialType.StandardWithoutMetalness
|
||||
) {
|
||||
this._threeObject.traverse((node) => {
|
||||
if (node.type === 'Mesh') {
|
||||
const mesh = node as THREE.Mesh;
|
||||
const material = mesh.material as THREE.MeshStandardMaterial;
|
||||
//@ts-ignore
|
||||
if (material.metalness) {
|
||||
//@ts-ignore
|
||||
material.metalness = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
traverseToRemoveMetalnessFromMeshes(threeObject);
|
||||
} else if (
|
||||
this._model3DRuntimeObject._materialType ===
|
||||
gdjs.Model3DRuntimeObject.MaterialType.Basic
|
||||
) {
|
||||
this._threeObject.traverse((node) => {
|
||||
if (node.type === 'Mesh') {
|
||||
const mesh = node as THREE.Mesh;
|
||||
const basicMaterial = new THREE.MeshBasicMaterial();
|
||||
//@ts-ignore
|
||||
if (mesh.material.color) {
|
||||
//@ts-ignore
|
||||
basicMaterial.color = mesh.material.color;
|
||||
}
|
||||
//@ts-ignore
|
||||
if (mesh.material.map) {
|
||||
//@ts-ignore
|
||||
basicMaterial.map = mesh.material.map;
|
||||
}
|
||||
mesh.material = basicMaterial;
|
||||
}
|
||||
});
|
||||
traverseToSetBasicMaterialFromMeshes(threeObject);
|
||||
}
|
||||
}
|
||||
|
||||
getAnimationCount() {
|
||||
return this._originalModel.animations.length;
|
||||
}
|
||||
|
||||
getAnimationName(animationIndex: integer) {
|
||||
return this._originalModel.animations[animationIndex].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if animation has ended.
|
||||
* The animation had ended if:
|
||||
* - it's not configured as a loop;
|
||||
* - the current frame is the last frame;
|
||||
* - the last frame has been displayed long enough.
|
||||
*/
|
||||
hasAnimationEnded(): boolean {
|
||||
if (!this._action) {
|
||||
return true;
|
||||
}
|
||||
return !this._action.isRunning();
|
||||
}
|
||||
|
||||
animationPaused() {
|
||||
if (!this._action) {
|
||||
return;
|
||||
}
|
||||
return this._action.paused;
|
||||
}
|
||||
|
||||
pauseAnimation() {
|
||||
if (!this._action) {
|
||||
return;
|
||||
}
|
||||
this._action.paused = true;
|
||||
}
|
||||
|
||||
resumeAnimation() {
|
||||
if (!this._action) {
|
||||
return;
|
||||
}
|
||||
this._action.paused = false;
|
||||
}
|
||||
|
||||
playAnimation(animationName: string, shouldLoop: boolean) {
|
||||
this._animationMixer.stopAllAction();
|
||||
const clip = THREE.AnimationClip.findByName(
|
||||
this._originalModel.animations,
|
||||
animationName
|
||||
);
|
||||
if (!clip) {
|
||||
console.error(
|
||||
`The GLB file: ${this._model3DRuntimeObject._modelResourceName} doesn't have any animation named: ${animationName}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
this._action = this._animationMixer.clipAction(clip);
|
||||
this._action.setLoop(
|
||||
shouldLoop ? THREE.LoopRepeat : THREE.LoopOnce,
|
||||
Number.POSITIVE_INFINITY
|
||||
);
|
||||
this._action.clampWhenFinished = true;
|
||||
this._action.play();
|
||||
// Make sure the first frame is displayed.
|
||||
this._animationMixer.update(0);
|
||||
}
|
||||
}
|
||||
|
||||
export const Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(AnchorBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(AnchorBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(AnchorBehavior "${source_files}")
|
||||
gd_add_extension_target(AnchorBehavior "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(AnchorBehavior)
|
||||
|
@@ -1,27 +1,34 @@
|
||||
#This is the CMake file used to build the C++ extensions.
|
||||
#For more information, see the README.md file.
|
||||
# This is the CMake file used to build the C++ extensions.
|
||||
# For more information, see the README.md file.
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0011 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(GD-Extensions)
|
||||
include(CMakeUtils.txt) #Functions to factor common tasks done in CMakeLists.txt of extensions
|
||||
include(CMakeUtils.txt) # Functions to factor common tasks done in CMakeLists.txt of extensions
|
||||
|
||||
#Add all the CMakeLists (for non pure JS extensions):
|
||||
ADD_SUBDIRECTORY(AnchorBehavior)
|
||||
ADD_SUBDIRECTORY(DestroyOutsideBehavior)
|
||||
ADD_SUBDIRECTORY(DraggableBehavior)
|
||||
ADD_SUBDIRECTORY(Inventory)
|
||||
ADD_SUBDIRECTORY(LinkedObjects)
|
||||
ADD_SUBDIRECTORY(ParticleSystem)
|
||||
ADD_SUBDIRECTORY(PanelSpriteObject)
|
||||
ADD_SUBDIRECTORY(PathfindingBehavior)
|
||||
ADD_SUBDIRECTORY(PhysicsBehavior)
|
||||
ADD_SUBDIRECTORY(PlatformBehavior)
|
||||
ADD_SUBDIRECTORY(PrimitiveDrawing)
|
||||
ADD_SUBDIRECTORY(Shopify)
|
||||
ADD_SUBDIRECTORY(SystemInfo)
|
||||
ADD_SUBDIRECTORY(TextEntryObject)
|
||||
ADD_SUBDIRECTORY(TextObject)
|
||||
ADD_SUBDIRECTORY(TiledSpriteObject)
|
||||
ADD_SUBDIRECTORY(TopDownMovementBehavior)
|
||||
# List of non pure JS extensions
|
||||
set(
|
||||
GD_EXTENSIONS
|
||||
3D
|
||||
AnchorBehavior
|
||||
DestroyOutsideBehavior
|
||||
DraggableBehavior
|
||||
Inventory
|
||||
LinkedObjects
|
||||
PanelSpriteObject
|
||||
ParticleSystem
|
||||
PathfindingBehavior
|
||||
PhysicsBehavior
|
||||
PlatformBehavior
|
||||
PrimitiveDrawing
|
||||
Shopify
|
||||
SystemInfo
|
||||
TextEntryObject
|
||||
TextObject
|
||||
TiledSpriteObject
|
||||
TopDownMovementBehavior)
|
||||
|
||||
# Automatically add all listed extensions
|
||||
foreach(extension ${GD_EXTENSIONS})
|
||||
add_subdirectory(${extension})
|
||||
endforeach()
|
||||
|
@@ -7,79 +7,75 @@ macro(gd_add_extension_includes)
|
||||
include_directories(${GDCORE_include_dir})
|
||||
endmacro()
|
||||
|
||||
#Add common defines for a target that will be a GD extension
|
||||
# Add common defines for a target that will be a GD extension
|
||||
function(gd_add_extension_definitions target_name)
|
||||
|
||||
#Define used in GD to check the build type
|
||||
IF(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
add_definitions( -DDEBUG )
|
||||
ELSE()
|
||||
add_definitions( -DRELEASE )
|
||||
ENDIF()
|
||||
# Define used in GD to check the build type
|
||||
if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
|
||||
add_definitions(-DDEBUG)
|
||||
else()
|
||||
add_definitions(-DRELEASE)
|
||||
endif()
|
||||
|
||||
set(${target_name}_extra_definitions "${${target_name}_extra_definitions} GD_IDE_ONLY=1;" PARENT_SCOPE)
|
||||
|
||||
#Defne used in GD to identify the environment
|
||||
IF (EMSCRIPTEN)
|
||||
add_definitions( -DEMSCRIPTEN )
|
||||
ELSEIF(WIN32)
|
||||
add_definitions( -DWINDOWS )
|
||||
ELSEIF(APPLE)
|
||||
add_definitions( -DMACOS )
|
||||
ELSE()
|
||||
add_definitions( -DLINUX )
|
||||
ENDIF()
|
||||
# Define used in GD to identify the environment
|
||||
if(EMSCRIPTEN)
|
||||
add_definitions(-DEMSCRIPTEN)
|
||||
elseif(WIN32)
|
||||
add_definitions(-DWINDOWS)
|
||||
elseif(APPLE)
|
||||
add_definitions(-DMACOS)
|
||||
else()
|
||||
add_definitions(-DLINUX)
|
||||
endif()
|
||||
|
||||
IF(WIN32) #Windows specific defines
|
||||
add_definitions( "-DGD_CORE_API=__declspec(dllimport)" )
|
||||
add_definitions( "-DGD_API=__declspec(dllimport)" )
|
||||
add_definitions( "-DGD_EXTENSION_API=__declspec(dllexport)" )
|
||||
add_definitions( -D__GNUWIN32__ )
|
||||
ELSE()
|
||||
|
||||
add_definitions( -DGD_API= )
|
||||
add_definitions( -DGD_CORE_API= )
|
||||
add_definitions( -DGD_EXTENSION_API= )
|
||||
ENDIF(WIN32)
|
||||
if(WIN32) # Windows specific defines
|
||||
add_definitions("-DGD_API=__declspec(dllimport)")
|
||||
add_definitions("-DGD_CORE_API=__declspec(dllimport)")
|
||||
add_definitions("-DGD_EXTENSION_API=__declspec(dllexport)")
|
||||
add_definitions(-D__GNUWIN32__)
|
||||
else()
|
||||
add_definitions(-DGD_API=)
|
||||
add_definitions(-DGD_CORE_API=)
|
||||
add_definitions(-DGD_EXTENSION_API=)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#Add a GD extension target, that will produce the final library file.
|
||||
# Add a GD extension target, that will produce the final library file.
|
||||
function(gd_add_extension_target target_name source_files)
|
||||
IF(target_name STREQUAL "")
|
||||
MESSAGE(ERROR "You called gd_add_extension_target without specifying a target name")
|
||||
ENDIF()
|
||||
if(target_name STREQUAL "")
|
||||
message(ERROR "You called gd_add_extension_target without specifying a target name")
|
||||
endif()
|
||||
|
||||
SET(platform_directory ${ARGV2})
|
||||
IF(NOT platform_directory)
|
||||
SET(platform_directory "CppPlatform")
|
||||
ENDIF()
|
||||
set(platform_directory ${ARGV2})
|
||||
if(NOT platform_directory)
|
||||
set(platform_directory "CppPlatform")
|
||||
endif()
|
||||
|
||||
IF(EMSCRIPTEN)
|
||||
if(EMSCRIPTEN)
|
||||
# Emscripten treats all libraries as static libraries
|
||||
add_library(${target_name} STATIC ${source_files})
|
||||
ELSE()
|
||||
else()
|
||||
add_library(${target_name} SHARED ${source_files})
|
||||
ENDIF()
|
||||
endif()
|
||||
set_target_properties(${target_name} PROPERTIES PREFIX "")
|
||||
set_target_properties(${target_name} PROPERTIES COMPILE_DEFINITIONS "${${target_name}_extra_definitions}")
|
||||
set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/${platform_directory}/Extensions")
|
||||
set_target_properties(${target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/${platform_directory}/Extensions")
|
||||
set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/${platform_directory}/Extensions")
|
||||
IF(WIN32) #GD extensions have special suffix in their filenames.
|
||||
if(WIN32) # GD extensions have special suffix in their filenames.
|
||||
set_target_properties(${target_name} PROPERTIES SUFFIX ".xgdwe")
|
||||
ELSEIF(EMSCRIPTEN)
|
||||
elseif(EMSCRIPTEN)
|
||||
set_target_properties(${target_name} PROPERTIES SUFFIX ".bc")
|
||||
ELSE()
|
||||
else()
|
||||
set_target_properties(${target_name} PROPERTIES SUFFIX ".xgde")
|
||||
ENDIF()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#Link default libraries with a target that is a GD extension
|
||||
# Link default libraries with a target that is a GD extension
|
||||
function(gd_extension_link_libraries target_name)
|
||||
IF(EMSCRIPTEN)
|
||||
#Nothing.
|
||||
ELSE()
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_link_libraries(${target_name} GDCore)
|
||||
ENDIF()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(DestroyOutsideBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(DestroyOutsideBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(DestroyOutsideBehavior "${source_files}")
|
||||
gd_add_extension_target(DestroyOutsideBehavior "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(DestroyOutsideBehavior GDCore)
|
||||
|
@@ -46,7 +46,7 @@ module.exports = {
|
||||
_(
|
||||
'Load a dialogue data object - Yarn json format, stored in a scene variable. Use this command to load all the Dialogue data at the beginning of the game.'
|
||||
),
|
||||
_('Load dialogue data from Scene variable _PARAM1_'),
|
||||
_('Load dialogue data from Scene variable _PARAM0_'),
|
||||
'',
|
||||
'JsPlatform/Extensions/yarn32.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(DraggableBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(DraggableBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(DraggableBehavior "${source_files}")
|
||||
gd_add_extension_target(DraggableBehavior "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(DraggableBehavior)
|
||||
|
@@ -41,7 +41,7 @@ namespace gdjs {
|
||||
/**
|
||||
* The current authentication status.
|
||||
*/
|
||||
export let authentified = false;
|
||||
export let authenticated = false;
|
||||
|
||||
/**
|
||||
* The logged-in users data.
|
||||
@@ -345,10 +345,13 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the user is currently authentified.
|
||||
* @see authentified
|
||||
* Returns true if the user is currently authenticated.
|
||||
* @see authenticated
|
||||
*/
|
||||
export const isAuthentified = (): boolean => authentified;
|
||||
export const isAuthenticated = (): boolean => authenticated;
|
||||
|
||||
/** @deprecated Use isAuthenticated instead. */
|
||||
export const isAuthentified = isAuthenticated;
|
||||
|
||||
/**
|
||||
* Signs the user in with basic email-password authentication.
|
||||
@@ -442,14 +445,14 @@ namespace gdjs {
|
||||
firebaseTools.onAppCreated.push(() => {
|
||||
firebase.auth().onAuthStateChanged((user) => {
|
||||
if (user) {
|
||||
authentified = true;
|
||||
authenticated = true;
|
||||
currentUser = user;
|
||||
user.getIdToken().then(
|
||||
// Prefetch the token
|
||||
(token) => (_token = token)
|
||||
);
|
||||
} else {
|
||||
authentified = false;
|
||||
authenticated = false;
|
||||
currentUser = null;
|
||||
}
|
||||
});
|
||||
|
@@ -14,7 +14,7 @@ namespace gdjs {
|
||||
/**
|
||||
* Uploads a file as string to the firebase storage bucket.
|
||||
* @param file - The entire file as string.
|
||||
* @param onlinePath - The path under wich the file will be accessible on the bucket.
|
||||
* @param onlinePath - The path under which the file will be accessible on the bucket.
|
||||
* @param [type] - The type/format of the string to upload.
|
||||
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
|
||||
* @param [callbackValueVariable] - The variable where to store the result (url to the file).
|
||||
|
@@ -395,7 +395,7 @@ module.exports = {
|
||||
.addIncludeFile('Extensions/Firebase/A_firebasejs/B_firebase-auth.js')
|
||||
.addIncludeFile('Extensions/Firebase/B_firebasetools/C_firebasetools.js')
|
||||
.addIncludeFile('Extensions/Firebase/B_firebasetools/D_authtools.js')
|
||||
.setFunctionName('gdjs.evtTools.firebaseTools.auth.isAuthentified');
|
||||
.setFunctionName('gdjs.evtTools.firebaseTools.auth.isAuthenticated');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
|
@@ -533,7 +533,7 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
|
||||
const password2 = `myNewPass${Math.random().toString(16)}${Date.now()}!`;
|
||||
|
||||
const expectToNotLogin = async (password) => {
|
||||
if (gdjs.evtTools.firebaseTools.auth.isAuthentified())
|
||||
if (gdjs.evtTools.firebaseTools.auth.isAuthenticated())
|
||||
await firebase.auth().signOut();
|
||||
|
||||
let errors = false;
|
||||
@@ -553,11 +553,11 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
|
||||
if (!errors)
|
||||
throw new Error('Expected wrong credentials to prevent login');
|
||||
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
|
||||
};
|
||||
|
||||
const expectToLogin = async (password) => {
|
||||
if (gdjs.evtTools.firebaseTools.auth.isAuthentified())
|
||||
if (gdjs.evtTools.firebaseTools.auth.isAuthenticated())
|
||||
await firebase.auth().signOut();
|
||||
|
||||
await promisifyCallbackVariables((callback) =>
|
||||
@@ -568,13 +568,13 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
|
||||
)
|
||||
);
|
||||
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
|
||||
};
|
||||
|
||||
before(async () => firebase.auth().signOut());
|
||||
|
||||
it('let users create accounts', async () => {
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
|
||||
|
||||
await promisifyCallbackVariables((callback) =>
|
||||
gdjs.evtTools.firebaseTools.auth.createAccountWithEmail(
|
||||
@@ -584,13 +584,13 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
|
||||
)
|
||||
);
|
||||
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
|
||||
});
|
||||
|
||||
it('let users log out', async () => {
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
|
||||
await firebase.auth().signOut();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
|
||||
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
|
||||
});
|
||||
|
||||
it('prevents logging in with invalid credentials', async () =>
|
||||
|
@@ -1,21 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(Inventory)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(Inventory)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(Inventory "${source_files}")
|
||||
|
||||
gd_add_extension_target(Inventory "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(Inventory)
|
||||
|
@@ -26,70 +26,57 @@ class InventoryJsExtension : public gd::PlatformExtension {
|
||||
DeclareInventoryExtension(*this);
|
||||
|
||||
GetAllActions()["Inventory::Add"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.add");
|
||||
GetAllActions()["Inventory::Remove"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.remove");
|
||||
GetAllActions()["Inventory::SetMaximum"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.setMaximum");
|
||||
GetAllActions()["Inventory::SetUnlimited"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.setUnlimited");
|
||||
GetAllActions()["Inventory::Equip"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.equip");
|
||||
|
||||
GetAllActions()["Inventory::SerializeToVariable"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.serializeToVariable");
|
||||
GetAllActions()["Inventory::UnserializeFromVariable"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.unserializeFromVariable");
|
||||
|
||||
GetAllConditions()["Inventory::Count"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.count");
|
||||
GetAllConditions()["Inventory::Has"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.has");
|
||||
GetAllConditions()["Inventory::IsFull"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.isFull");
|
||||
GetAllConditions()["Inventory::IsEquipped"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.isEquipped");
|
||||
|
||||
GetAllExpressions()["Inventory::Count"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.count");
|
||||
GetAllExpressions()["Inventory::Maximum"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Inventory/inventory.js")
|
||||
.AddIncludeFile("Extensions/Inventory/inventorytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.inventory.maximum");
|
||||
|
@@ -18,6 +18,7 @@ export type ObjectsRenderingService = {
|
||||
gd: libGDevelop,
|
||||
PIXI: any,
|
||||
THREE: any,
|
||||
THREE_ADDONS: {SkeletonUtils: any},
|
||||
RenderedInstance: any,
|
||||
Rendered3DInstance: any,
|
||||
registerInstanceRenderer: (objectType: string, renderer: any) => void,
|
||||
|
@@ -59,11 +59,17 @@ module.exports = {
|
||||
false
|
||||
)
|
||||
.addParameter('string', _('Name to register for the player'), '', false)
|
||||
.setParameterLongDescription(
|
||||
_(
|
||||
'Let this empty to let the leaderboard auto-generate a player name (e.g: "Player23464"). You can configure this in the leaderboard administration.'
|
||||
)
|
||||
)
|
||||
.setHelpPath('/all-features/leaderboards')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Leaderboards/sha256.js')
|
||||
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.savePlayerScore');
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.savePlayerScore')
|
||||
.setAsyncFunctionName('gdjs.evtTools.leaderboards.savePlayerScore');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
@@ -89,7 +95,8 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Leaderboards/sha256.js')
|
||||
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore');
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore')
|
||||
.setAsyncFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
@@ -154,20 +161,22 @@ module.exports = {
|
||||
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.isSaving');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'HasPlayerJustClosedLeaderboardView',
|
||||
_('Closed by player'),
|
||||
_('Check if the player has just closed the leaderboard view.'),
|
||||
_('Player has just closed the leaderboard view'),
|
||||
_('Display leaderboard'),
|
||||
'JsPlatform/Extensions/leaderboard.svg',
|
||||
'JsPlatform/Extensions/leaderboard.svg'
|
||||
)
|
||||
.setHelpPath('/all-features/leaderboards')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
|
||||
.setFunctionName('gdjs.evtTools.leaderboards.hasPlayerJustClosedLeaderboardView');
|
||||
extension
|
||||
.addCondition(
|
||||
'HasPlayerJustClosedLeaderboardView',
|
||||
_('Closed by player'),
|
||||
_('Check if the player has just closed the leaderboard view.'),
|
||||
_('Player has just closed the leaderboard view'),
|
||||
_('Display leaderboard'),
|
||||
'JsPlatform/Extensions/leaderboard.svg',
|
||||
'JsPlatform/Extensions/leaderboard.svg'
|
||||
)
|
||||
.setHelpPath('/all-features/leaderboards')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
|
||||
.setFunctionName(
|
||||
'gdjs.evtTools.leaderboards.hasPlayerJustClosedLeaderboardView'
|
||||
);
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
|
@@ -24,37 +24,47 @@ namespace gdjs {
|
||||
return shaObj.getHash('B64');
|
||||
};
|
||||
|
||||
// Score saving
|
||||
/**
|
||||
* Hold the state of the save of a score for a leaderboard.
|
||||
*/
|
||||
class ScoreSavingState {
|
||||
lastScoreSavingStartedAt: number | null;
|
||||
lastScoreSavingSucceededAt: number | null;
|
||||
currentlySavingScore: number | null;
|
||||
currentlySavingPlayerName: string | null;
|
||||
currentlySavingPlayerId: string | null;
|
||||
lastSavedScore: number | null;
|
||||
lastSavedPlayerName: string | null;
|
||||
lastSavedPlayerId: string | null;
|
||||
lastSaveError: string | null;
|
||||
isScoreSaving: boolean;
|
||||
hasScoreBeenSaved: boolean;
|
||||
hasScoreSavingErrored: boolean;
|
||||
lastScoreSavingStartedAt: number | null = null;
|
||||
lastScoreSavingSucceededAt: number | null = null;
|
||||
|
||||
constructor() {
|
||||
this.lastScoreSavingStartedAt = null;
|
||||
this.lastScoreSavingSucceededAt = null;
|
||||
this.currentlySavingScore = null;
|
||||
this.currentlySavingPlayerName = null;
|
||||
this.currentlySavingPlayerId = null;
|
||||
this.lastSavedScore = null;
|
||||
this.lastSavedPlayerName = null;
|
||||
this.lastSavedPlayerId = null;
|
||||
this.lastSaveError = null;
|
||||
this.isScoreSaving = false;
|
||||
this.hasScoreBeenSaved = false;
|
||||
this.hasScoreSavingErrored = false;
|
||||
/** The promise that will be resolved when the score saving is done (successfully or not). */
|
||||
lastSavingPromise: Promise<void> | null = null;
|
||||
|
||||
// Score that is being saved:
|
||||
private _currentlySavingScore: number | null = null;
|
||||
private _currentlySavingPlayerName: string | null = null;
|
||||
private _currentlySavingPlayerId: string | null = null;
|
||||
|
||||
// Last score saved with success:
|
||||
private _lastSavedScore: number | null = null;
|
||||
private _lastSavedPlayerName: string | null = null;
|
||||
private _lastSavedPlayerId: string | null = null;
|
||||
|
||||
/** The id of the entry in the leaderboard, for the last score saved with success. */
|
||||
lastSavedLeaderboardEntryId: string | null = null;
|
||||
|
||||
/** Last error that happened when saving the score (useful if `hasScoreSavingErrored` is true). */
|
||||
lastSaveError: string | null = null;
|
||||
|
||||
/** `true` if the last save has finished and succeeded. */
|
||||
hasScoreBeenSaved: boolean = false;
|
||||
|
||||
/** `true` if the last save has finished and failed (check `lastSaveError` then). */
|
||||
hasScoreSavingErrored: boolean = false;
|
||||
|
||||
isSaving(): boolean {
|
||||
return (
|
||||
!!this.lastSavingPromise &&
|
||||
!this.hasScoreBeenSaved &&
|
||||
!this.hasScoreSavingErrored
|
||||
);
|
||||
}
|
||||
|
||||
isSameAsLastScore({
|
||||
private _isSameAsLastScore({
|
||||
playerName,
|
||||
playerId,
|
||||
score,
|
||||
@@ -64,13 +74,13 @@ namespace gdjs {
|
||||
score: number;
|
||||
}): boolean {
|
||||
return (
|
||||
((!!playerName && this.lastSavedPlayerName === playerName) ||
|
||||
(!!playerId && this.lastSavedPlayerId === playerId)) &&
|
||||
this.lastSavedScore === score
|
||||
((!!playerName && this._lastSavedPlayerName === playerName) ||
|
||||
(!!playerId && this._lastSavedPlayerId === playerId)) &&
|
||||
this._lastSavedScore === score
|
||||
);
|
||||
}
|
||||
|
||||
isAlreadySavingThisScore({
|
||||
private _isAlreadySavingThisScore({
|
||||
playerName,
|
||||
playerId,
|
||||
score,
|
||||
@@ -79,15 +89,16 @@ namespace gdjs {
|
||||
playerId?: string;
|
||||
score: number;
|
||||
}): boolean {
|
||||
if (!this.isSaving()) return false;
|
||||
|
||||
return (
|
||||
((!!playerName && this.currentlySavingPlayerName === playerName) ||
|
||||
(!!playerId && this.currentlySavingPlayerId === playerId)) &&
|
||||
this.isScoreSaving &&
|
||||
this.currentlySavingScore === score
|
||||
((!!playerName && this._currentlySavingPlayerName === playerName) ||
|
||||
(!!playerId && this._currentlySavingPlayerId === playerId)) &&
|
||||
this._currentlySavingScore === score
|
||||
);
|
||||
}
|
||||
|
||||
isTooSoonToSaveAnotherScore(): boolean {
|
||||
private _isTooSoonToSaveAnotherScore(): boolean {
|
||||
return (
|
||||
!!this.lastScoreSavingStartedAt &&
|
||||
Date.now() - this.lastScoreSavingStartedAt < 500
|
||||
@@ -102,28 +113,91 @@ namespace gdjs {
|
||||
playerName?: string;
|
||||
playerId?: string;
|
||||
score: number;
|
||||
}): void {
|
||||
}): {
|
||||
closeSaving: (leaderboardEntryId: string | null) => void;
|
||||
closeSavingWithError(errorCode: string);
|
||||
} {
|
||||
if (this._isAlreadySavingThisScore({ playerName, playerId, score })) {
|
||||
logger.warn(
|
||||
'There is already a request to save with this player name and this score. Ignoring this one.'
|
||||
);
|
||||
throw new Error('Ignoring this saving request.');
|
||||
}
|
||||
|
||||
if (this._isSameAsLastScore({ playerName, playerId, score })) {
|
||||
logger.warn(
|
||||
'The player and score to be sent are the same as previous one. Ignoring this one.'
|
||||
);
|
||||
this._setError('SAME_AS_PREVIOUS');
|
||||
throw new Error('Ignoring this saving request.');
|
||||
}
|
||||
|
||||
if (this._isTooSoonToSaveAnotherScore()) {
|
||||
logger.warn(
|
||||
'Last entry was sent too little time ago. Ignoring this one.'
|
||||
);
|
||||
this._setError('TOO_FAST');
|
||||
|
||||
// Set the starting time to cancel all the following attempts that
|
||||
// are started too early after this one.
|
||||
this.lastScoreSavingStartedAt = Date.now();
|
||||
|
||||
throw new Error('Ignoring this saving request.');
|
||||
}
|
||||
|
||||
let resolveSavingPromise: () => void;
|
||||
const savingPromise = new Promise<void>((resolve) => {
|
||||
resolveSavingPromise = resolve;
|
||||
});
|
||||
|
||||
this.lastScoreSavingStartedAt = Date.now();
|
||||
this.isScoreSaving = true;
|
||||
this.lastSavingPromise = savingPromise;
|
||||
this.hasScoreBeenSaved = false;
|
||||
this.hasScoreSavingErrored = false;
|
||||
this.currentlySavingScore = score;
|
||||
if (playerName) this.currentlySavingPlayerName = playerName;
|
||||
if (playerId) this.currentlySavingPlayerId = playerId;
|
||||
this._currentlySavingScore = score;
|
||||
if (playerName) this._currentlySavingPlayerName = playerName;
|
||||
if (playerId) this._currentlySavingPlayerId = playerId;
|
||||
|
||||
return {
|
||||
closeSaving: (leaderboardEntryId) => {
|
||||
if (savingPromise !== this.lastSavingPromise) {
|
||||
logger.info(
|
||||
'Score saving result received, but another save was launched in the meantime - ignoring the result of this one.'
|
||||
);
|
||||
|
||||
// Still finish the promise that can be waited upon:
|
||||
resolveSavingPromise();
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastScoreSavingSucceededAt = Date.now();
|
||||
this._lastSavedScore = this._currentlySavingScore;
|
||||
this._lastSavedPlayerName = this._currentlySavingPlayerName;
|
||||
this._lastSavedPlayerId = this._currentlySavingPlayerId;
|
||||
this.lastSavedLeaderboardEntryId = leaderboardEntryId;
|
||||
this.hasScoreBeenSaved = true;
|
||||
|
||||
resolveSavingPromise();
|
||||
},
|
||||
closeSavingWithError: (errorCode) => {
|
||||
if (savingPromise !== this.lastSavingPromise) {
|
||||
logger.info(
|
||||
'Score saving result received, but another save was launched in the meantime - ignoring the result of this one.'
|
||||
);
|
||||
|
||||
// Still finish the promise that can be waited upon:
|
||||
resolveSavingPromise();
|
||||
return;
|
||||
}
|
||||
|
||||
this._setError(errorCode);
|
||||
resolveSavingPromise();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
closeSaving(): void {
|
||||
this.lastScoreSavingSucceededAt = Date.now();
|
||||
this.lastSavedScore = this.currentlySavingScore;
|
||||
this.lastSavedPlayerName = this.currentlySavingPlayerName;
|
||||
this.lastSavedPlayerId = this.currentlySavingPlayerId;
|
||||
this.isScoreSaving = false;
|
||||
this.hasScoreBeenSaved = true;
|
||||
}
|
||||
|
||||
setError(errorCode: string): void {
|
||||
private _setError(errorCode: string): void {
|
||||
this.lastSaveError = errorCode;
|
||||
this.isScoreSaving = false;
|
||||
this.hasScoreBeenSaved = false;
|
||||
this.hasScoreSavingErrored = true;
|
||||
}
|
||||
@@ -172,6 +246,7 @@ namespace gdjs {
|
||||
}
|
||||
_loaderContainer.appendChild(_loader);
|
||||
|
||||
/** Get the saving state of the leaderboard who had the last update (successful or started). */
|
||||
const getLastScoreSavingState = function ({
|
||||
hasSucceeded,
|
||||
}: {
|
||||
@@ -201,19 +276,17 @@ namespace gdjs {
|
||||
return lastScoreSavingState;
|
||||
};
|
||||
|
||||
const saveScore = function ({
|
||||
const saveScore = async function ({
|
||||
leaderboardId,
|
||||
playerName,
|
||||
authenticatedPlayerData,
|
||||
score,
|
||||
scoreSavingState,
|
||||
runtimeScene,
|
||||
}: {
|
||||
leaderboardId: string;
|
||||
playerName?: string | null;
|
||||
authenticatedPlayerData?: { playerId: string; playerToken: string };
|
||||
score: number;
|
||||
scoreSavingState: ScoreSavingState;
|
||||
runtimeScene: gdjs.RuntimeScene;
|
||||
}) {
|
||||
const rootApi = runtimeScene
|
||||
@@ -242,195 +315,143 @@ namespace gdjs {
|
||||
] = `player-game-token ${authenticatedPlayerData.playerToken}`;
|
||||
leaderboardEntryCreationUrl += `?playerId=${authenticatedPlayerData.playerId}`;
|
||||
} else {
|
||||
// In case playerName is empty or undefined, the formatting will generate a random name.
|
||||
// In case playerName is empty, the backend will generate a random name.
|
||||
payloadObject['playerName'] = formatPlayerName(playerName);
|
||||
}
|
||||
const payload = JSON.stringify(payloadObject);
|
||||
headers['Digest'] = computeDigest(payload);
|
||||
|
||||
fetch(leaderboardEntryCreationUrl, {
|
||||
body: payload,
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
}).then(
|
||||
(response) => {
|
||||
if (!response.ok) {
|
||||
const errorCode = response.status.toString();
|
||||
logger.error(
|
||||
'Server responded with an error:',
|
||||
errorCode,
|
||||
response.statusText
|
||||
);
|
||||
scoreSavingState.setError(errorCode);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(leaderboardEntryCreationUrl, {
|
||||
body: payload,
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
scoreSavingState.closeSaving();
|
||||
|
||||
return response.text().then(
|
||||
(text) => {},
|
||||
(error) => {
|
||||
logger.warn(
|
||||
'An error occurred when reading response but score has been saved:',
|
||||
error
|
||||
);
|
||||
}
|
||||
if (!response.ok) {
|
||||
const errorCode = response.status.toString();
|
||||
logger.error(
|
||||
'Server responded with an error:',
|
||||
errorCode,
|
||||
response.statusText
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
logger.error('Error while submitting a leaderboard score:', error);
|
||||
const errorCode = 'REQUEST_NOT_SENT';
|
||||
scoreSavingState.setError(errorCode);
|
||||
|
||||
throw errorCode;
|
||||
}
|
||||
);
|
||||
|
||||
let leaderboardEntryId: string | null = null;
|
||||
try {
|
||||
const leaderboardEntry = await response.json();
|
||||
leaderboardEntryId = leaderboardEntry.id;
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'An error occurred when reading response but score has been saved:',
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
return leaderboardEntryId;
|
||||
} catch (error) {
|
||||
logger.error('Error while submitting a leaderboard score:', error);
|
||||
const errorCode = 'REQUEST_NOT_SENT';
|
||||
|
||||
throw errorCode;
|
||||
}
|
||||
};
|
||||
|
||||
export const savePlayerScore = function (
|
||||
export const savePlayerScore = (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
leaderboardId: string,
|
||||
score: float,
|
||||
playerName: string
|
||||
) {
|
||||
let scoreSavingState: ScoreSavingState;
|
||||
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
|
||||
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
|
||||
let shouldStartSaving = true;
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isAlreadySavingThisScore({ playerName, score })
|
||||
) {
|
||||
logger.warn(
|
||||
'There is already a request to save with this player name and this score. Ignoring this one.'
|
||||
);
|
||||
shouldStartSaving = false;
|
||||
}
|
||||
) =>
|
||||
new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
const scoreSavingState = (_scoreSavingStateByLeaderboard[
|
||||
leaderboardId
|
||||
] =
|
||||
_scoreSavingStateByLeaderboard[leaderboardId] ||
|
||||
new ScoreSavingState());
|
||||
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isSameAsLastScore({ playerName, score })
|
||||
) {
|
||||
logger.warn(
|
||||
'The player and score to be sent are the same as previous one. Ignoring this one.'
|
||||
);
|
||||
scoreSavingState.setError('SAME_AS_PREVIOUS');
|
||||
shouldStartSaving = false;
|
||||
}
|
||||
try {
|
||||
const {
|
||||
closeSaving,
|
||||
closeSavingWithError,
|
||||
} = scoreSavingState.startSaving({ playerName, score });
|
||||
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isTooSoonToSaveAnotherScore()
|
||||
) {
|
||||
logger.warn(
|
||||
'Last entry was sent too little time ago. Ignoring this one.'
|
||||
);
|
||||
scoreSavingState.setError('TOO_FAST');
|
||||
shouldStartSaving = false;
|
||||
// Set the starting time to cancel all the following attempts that
|
||||
// are started too early after this one.
|
||||
scoreSavingState.lastScoreSavingStartedAt = Date.now();
|
||||
}
|
||||
if (!shouldStartSaving) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
scoreSavingState = new ScoreSavingState();
|
||||
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
|
||||
}
|
||||
try {
|
||||
const leaderboardEntryId = await saveScore({
|
||||
leaderboardId,
|
||||
playerName,
|
||||
score,
|
||||
runtimeScene,
|
||||
});
|
||||
closeSaving(leaderboardEntryId);
|
||||
} catch (errorCode) {
|
||||
closeSavingWithError(errorCode);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing: saving was rejected for a reason already logged.
|
||||
}
|
||||
})()
|
||||
);
|
||||
|
||||
scoreSavingState.startSaving({ playerName, score });
|
||||
|
||||
saveScore({
|
||||
leaderboardId,
|
||||
playerName,
|
||||
score,
|
||||
scoreSavingState,
|
||||
runtimeScene,
|
||||
});
|
||||
};
|
||||
|
||||
export const saveConnectedPlayerScore = function (
|
||||
export const saveConnectedPlayerScore = (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
leaderboardId: string,
|
||||
score: float
|
||||
) {
|
||||
let scoreSavingState: ScoreSavingState;
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
logger.warn(
|
||||
'Cannot save a score for a connected player if the player is not connected.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_scoreSavingStateByLeaderboard[leaderboardId]) {
|
||||
scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
|
||||
let shouldStartSaving = true;
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isAlreadySavingThisScore({ playerId, score })
|
||||
) {
|
||||
logger.warn(
|
||||
'There is already a request to save with this player ID and this score. Ignoring this one.'
|
||||
);
|
||||
shouldStartSaving = false;
|
||||
}
|
||||
) =>
|
||||
new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
logger.warn(
|
||||
'Cannot save a score for a connected player if the player is not connected.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isSameAsLastScore({ playerId, score })
|
||||
) {
|
||||
logger.warn(
|
||||
'The player and score to be sent are the same as previous one. Ignoring this one.'
|
||||
);
|
||||
scoreSavingState.setError('SAME_AS_PREVIOUS');
|
||||
shouldStartSaving = false;
|
||||
}
|
||||
const scoreSavingState = (_scoreSavingStateByLeaderboard[
|
||||
leaderboardId
|
||||
] =
|
||||
_scoreSavingStateByLeaderboard[leaderboardId] ||
|
||||
new ScoreSavingState());
|
||||
|
||||
if (
|
||||
shouldStartSaving &&
|
||||
scoreSavingState.isTooSoonToSaveAnotherScore()
|
||||
) {
|
||||
logger.warn(
|
||||
'Last entry was sent too little time ago. Ignoring this one.'
|
||||
);
|
||||
scoreSavingState.setError('TOO_FAST');
|
||||
shouldStartSaving = false;
|
||||
// Set the starting time to cancel all the following attempts that
|
||||
// are started too early after this one.
|
||||
scoreSavingState.lastScoreSavingStartedAt = Date.now();
|
||||
}
|
||||
if (!shouldStartSaving) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
scoreSavingState = new ScoreSavingState();
|
||||
_scoreSavingStateByLeaderboard[leaderboardId] = scoreSavingState;
|
||||
}
|
||||
try {
|
||||
const {
|
||||
closeSaving,
|
||||
closeSavingWithError,
|
||||
} = scoreSavingState.startSaving({ playerId, score });
|
||||
|
||||
scoreSavingState.startSaving({ playerId, score });
|
||||
|
||||
saveScore({
|
||||
leaderboardId,
|
||||
authenticatedPlayerData: { playerId, playerToken },
|
||||
score,
|
||||
scoreSavingState,
|
||||
runtimeScene,
|
||||
});
|
||||
};
|
||||
try {
|
||||
const leaderboardEntryId = await saveScore({
|
||||
leaderboardId,
|
||||
authenticatedPlayerData: { playerId, playerToken },
|
||||
score,
|
||||
runtimeScene,
|
||||
});
|
||||
closeSaving(leaderboardEntryId);
|
||||
} catch (errorCode) {
|
||||
closeSavingWithError(errorCode);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing: saving was rejected for a reason already logged.
|
||||
}
|
||||
})()
|
||||
);
|
||||
|
||||
export const isSaving = function (leaderboardId?: string): boolean {
|
||||
if (leaderboardId) {
|
||||
return _scoreSavingStateByLeaderboard[leaderboardId]
|
||||
? _scoreSavingStateByLeaderboard[leaderboardId].isScoreSaving
|
||||
? _scoreSavingStateByLeaderboard[leaderboardId].isSaving()
|
||||
: false;
|
||||
}
|
||||
|
||||
const lastScoreSavingState = getLastScoreSavingState({
|
||||
hasSucceeded: false,
|
||||
});
|
||||
return lastScoreSavingState
|
||||
? lastScoreSavingState.isScoreSaving
|
||||
: false;
|
||||
return lastScoreSavingState ? lastScoreSavingState.isSaving() : false;
|
||||
};
|
||||
|
||||
export const hasBeenSaved = function (leaderboardId?: string): boolean {
|
||||
@@ -491,9 +512,7 @@ namespace gdjs {
|
||||
typeof rawName !== 'string' ||
|
||||
(typeof rawName === 'string' && rawName.length === 0)
|
||||
) {
|
||||
return `Player${Math.round(
|
||||
(Math.random() * 9 + 1) * 10000 // Number between 10,000 and 99,999
|
||||
)}`;
|
||||
return '';
|
||||
}
|
||||
return rawName
|
||||
.trim()
|
||||
@@ -648,7 +667,7 @@ namespace gdjs {
|
||||
return iframe;
|
||||
};
|
||||
|
||||
export const displayLeaderboard = function (
|
||||
export const displayLeaderboard = async function (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
leaderboardId: string,
|
||||
displayLoader: boolean
|
||||
@@ -682,81 +701,99 @@ namespace gdjs {
|
||||
});
|
||||
}
|
||||
|
||||
// If a save is being done for this leaderboard, wait for it to end so that the `lastSavedLeaderboardEntryId`
|
||||
// can be saved and then used to show the player score.
|
||||
const scoreSavingState = _scoreSavingStateByLeaderboard[leaderboardId];
|
||||
if (scoreSavingState && scoreSavingState.lastSavingPromise) {
|
||||
await scoreSavingState.lastSavingPromise;
|
||||
}
|
||||
|
||||
const lastSavedLeaderboardEntryId = scoreSavingState
|
||||
? scoreSavingState.lastSavedLeaderboardEntryId
|
||||
: null;
|
||||
|
||||
const gameId = gdjs.projectData.properties.projectUuid;
|
||||
const isDev = runtimeScene
|
||||
.getGame()
|
||||
.isUsingGDevelopDevelopmentEnvironment();
|
||||
const targetUrl = `https://gd.games/games/${gameId}/leaderboard/${leaderboardId}?inGameEmbedded=true${
|
||||
isDev ? '&dev=true' : ''
|
||||
}`;
|
||||
checkLeaderboardAvailability(targetUrl).then(
|
||||
(isAvailable) => {
|
||||
if (leaderboardId !== _requestedLeaderboardId) {
|
||||
logger.warn(
|
||||
`Received a response for leaderboard ${leaderboardId} though the last leaderboard requested is ${_requestedLeaderboardId}, ignoring this response.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!isAvailable) {
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
'Leaderboard data could not be fetched. Closing leaderboard view if there is one.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_leaderboardViewIframe) {
|
||||
resetLeaderboardDisplayErrorTimeout(runtimeScene);
|
||||
if (displayLoader) {
|
||||
displayLoaderInLeaderboardView(true, runtimeScene, {
|
||||
callOnErrorIfDomElementContainerMissing: false,
|
||||
});
|
||||
}
|
||||
_leaderboardViewIframe.src = targetUrl;
|
||||
} else {
|
||||
const domElementContainer = runtimeScene
|
||||
.getGame()
|
||||
.getRenderer()
|
||||
.getDomElementContainer();
|
||||
if (!domElementContainer) {
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
"The div element covering the game couldn't be found, the leaderboard cannot be displayed."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set('inGameEmbedded', 'true');
|
||||
if (isDev) searchParams.set('dev', 'true');
|
||||
if (lastSavedLeaderboardEntryId)
|
||||
searchParams.set(
|
||||
'playerLeaderboardEntryId',
|
||||
lastSavedLeaderboardEntryId
|
||||
);
|
||||
|
||||
resetLeaderboardDisplayErrorTimeout(runtimeScene);
|
||||
const targetUrl = `https://gd.games/games/${gameId}/leaderboard/${leaderboardId}?${searchParams}`;
|
||||
|
||||
_leaderboardViewIframe = computeLeaderboardDisplayingIframe(
|
||||
targetUrl
|
||||
);
|
||||
if (typeof window !== 'undefined') {
|
||||
_leaderboardViewClosingCallback = (event: MessageEvent) => {
|
||||
receiveMessageFromLeaderboardView(
|
||||
runtimeScene,
|
||||
displayLoader,
|
||||
event
|
||||
);
|
||||
};
|
||||
(window as any).addEventListener(
|
||||
'message',
|
||||
_leaderboardViewClosingCallback,
|
||||
true
|
||||
);
|
||||
}
|
||||
domElementContainer.appendChild(_leaderboardViewIframe);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
logger.error(err);
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
'An error occurred when fetching leaderboard data. Closing leaderboard view if there is one.'
|
||||
try {
|
||||
const isAvailable = await checkLeaderboardAvailability(targetUrl);
|
||||
|
||||
if (leaderboardId !== _requestedLeaderboardId) {
|
||||
logger.warn(
|
||||
`Received a response for leaderboard ${leaderboardId} though the last leaderboard requested is ${_requestedLeaderboardId}, ignoring this response.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
);
|
||||
if (!isAvailable) {
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
'Leaderboard data could not be fetched. Closing leaderboard view if there is one.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_leaderboardViewIframe) {
|
||||
resetLeaderboardDisplayErrorTimeout(runtimeScene);
|
||||
if (displayLoader) {
|
||||
displayLoaderInLeaderboardView(true, runtimeScene, {
|
||||
callOnErrorIfDomElementContainerMissing: false,
|
||||
});
|
||||
}
|
||||
_leaderboardViewIframe.src = targetUrl;
|
||||
} else {
|
||||
const domElementContainer = runtimeScene
|
||||
.getGame()
|
||||
.getRenderer()
|
||||
.getDomElementContainer();
|
||||
if (!domElementContainer) {
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
"The div element covering the game couldn't be found, the leaderboard cannot be displayed."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
resetLeaderboardDisplayErrorTimeout(runtimeScene);
|
||||
|
||||
_leaderboardViewIframe = computeLeaderboardDisplayingIframe(
|
||||
targetUrl
|
||||
);
|
||||
if (typeof window !== 'undefined') {
|
||||
_leaderboardViewClosingCallback = (event: MessageEvent) => {
|
||||
receiveMessageFromLeaderboardView(
|
||||
runtimeScene,
|
||||
displayLoader,
|
||||
event
|
||||
);
|
||||
};
|
||||
(window as any).addEventListener(
|
||||
'message',
|
||||
_leaderboardViewClosingCallback,
|
||||
true
|
||||
);
|
||||
}
|
||||
domElementContainer.appendChild(_leaderboardViewIframe);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
handleErrorDisplayingLeaderboard(
|
||||
runtimeScene,
|
||||
'An error occurred when fetching leaderboard data. Closing leaderboard view if there is one.'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const isLeaderboardViewErrored = function (): boolean {
|
||||
|
@@ -29,26 +29,14 @@ describe('Leaderboards', () => {
|
||||
});
|
||||
|
||||
it('it generates a predefined player name with a random number if input is void/wrong type/empty', () => {
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(null)).to.be('');
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(undefined)).to.be('');
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName('')).to.be('');
|
||||
|
||||
// @ts-ignore
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(null)).to.match(
|
||||
/^Player\d{5}/
|
||||
);
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(5)).to.be('');
|
||||
// @ts-ignore
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(5)).to.match(
|
||||
/^Player\d{5}/
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(undefined)).to.match(
|
||||
/^Player\d{5}/
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(() => {})).to.match(
|
||||
/^Player\d{5}/
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName('')).to.match(
|
||||
/^Player\d{5}/
|
||||
);
|
||||
expect(gdjs.evtTools.leaderboards.formatPlayerName(() => {})).to.be('');
|
||||
});
|
||||
|
||||
it('it removes accents from latin letters', () => {
|
||||
|
@@ -290,74 +290,31 @@ module.exports = {
|
||||
.getValue()
|
||||
);
|
||||
if (this._radius <= 0) this._radius = 1;
|
||||
const colorHex = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
const color = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
this._associatedObjectConfiguration
|
||||
.getProperties(this.project)
|
||||
.get('color')
|
||||
.getValue()
|
||||
);
|
||||
this._color = [
|
||||
((colorHex >> 16) & 0xff) / 255,
|
||||
((colorHex >> 8) & 0xff) / 255,
|
||||
(colorHex & 0xff) / 255,
|
||||
];
|
||||
|
||||
const geometry = new PIXI.Geometry();
|
||||
const shader = PIXI.Shader.from(
|
||||
`
|
||||
precision mediump float;
|
||||
attribute vec2 aVertexPosition;
|
||||
// The icon in the middle.
|
||||
const lightIconSprite = new PIXI.Sprite(PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png'));
|
||||
lightIconSprite.anchor.x = 0.5;
|
||||
lightIconSprite.anchor.y = 0.5;
|
||||
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
vPos = aVertexPosition;
|
||||
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||
}`,
|
||||
`
|
||||
precision mediump float;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform vec3 color;
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
float l = length(vPos - center);
|
||||
float intensity = 0.0;
|
||||
if(l < radius)
|
||||
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
|
||||
gl_FragColor = vec4(color*intensity, 1.0);
|
||||
}
|
||||
`,
|
||||
{
|
||||
center: [this._instance.getX(), this._instance.getY()],
|
||||
radius: this._radius,
|
||||
color: this._color,
|
||||
}
|
||||
// The circle to show the radius of the light.
|
||||
const radiusBorderWidth = 2;
|
||||
const radiusGraphics = new PIXI.Graphics();
|
||||
radiusGraphics.lineStyle(
|
||||
radiusBorderWidth,
|
||||
color,
|
||||
0.8
|
||||
);
|
||||
radiusGraphics.drawCircle(0, 0, Math.max(1, this._radius - radiusBorderWidth));
|
||||
|
||||
this._vertexBuffer = new Float32Array([
|
||||
this._instance.getX() - this._radius,
|
||||
this._instance.getY() + this._radius,
|
||||
this._instance.getX() + this._radius,
|
||||
this._instance.getY() + this._radius,
|
||||
this._instance.getX() + this._radius,
|
||||
this._instance.getY() - this._radius,
|
||||
this._instance.getX() - this._radius,
|
||||
this._instance.getY() - this._radius,
|
||||
]);
|
||||
|
||||
geometry
|
||||
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
|
||||
.addIndex([0, 1, 2, 2, 3, 0]);
|
||||
|
||||
this._pixiObject = new PIXI.Mesh(geometry, shader);
|
||||
this._pixiObject.blendMode = PIXI.BLEND_MODES.ADD;
|
||||
this._pixiObject = new PIXI.Container();
|
||||
this._pixiObject.addChild(lightIconSprite);
|
||||
this._pixiObject.addChild(radiusGraphics);
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
this.update();
|
||||
}
|
||||
@@ -380,37 +337,22 @@ module.exports = {
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.update = function () {
|
||||
this._pixiObject.shader.uniforms.center = new Float32Array([
|
||||
this._instance.getX(),
|
||||
this._instance.getY(),
|
||||
]);
|
||||
|
||||
this._vertexBuffer[0] = this._instance.getX() - this._radius;
|
||||
this._vertexBuffer[1] = this._instance.getY() + this._radius;
|
||||
this._vertexBuffer[2] = this._instance.getX() + this._radius;
|
||||
this._vertexBuffer[3] = this._instance.getY() + this._radius;
|
||||
this._vertexBuffer[4] = this._instance.getX() + this._radius;
|
||||
this._vertexBuffer[5] = this._instance.getY() - this._radius;
|
||||
this._vertexBuffer[6] = this._instance.getX() - this._radius;
|
||||
this._vertexBuffer[7] = this._instance.getY() - this._radius;
|
||||
|
||||
this._pixiObject.geometry
|
||||
.getBuffer('aVertexPosition')
|
||||
.update(this._vertexBuffer);
|
||||
this._pixiObject.position.x = this._instance.getX();
|
||||
this._pixiObject.position.y = this._instance.getY();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.getDefaultWidth = function () {
|
||||
return this._pixiObject.width;
|
||||
return this._radius * 2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the height of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.getDefaultHeight = function () {
|
||||
return this._pixiObject.height;
|
||||
return this._radius * 2;
|
||||
};
|
||||
|
||||
RenderedLightObjectInstance.prototype.getOriginX = function () {
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(LinkedObjects)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(LinkedObjects)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(LinkedObjects "${source_files}")
|
||||
gd_add_extension_target(LinkedObjects "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
gd_extension_link_libraries(LinkedObjects)
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(LinkedObjects)
|
||||
|
@@ -26,23 +26,18 @@ class LinkedObjectsJsExtension : public gd::PlatformExtension {
|
||||
DeclareLinkedObjectsExtension(*this);
|
||||
|
||||
GetAllActions()["LinkedObjects::LinkObjects"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/LinkedObjects/linkedobjects.js")
|
||||
.SetFunctionName("gdjs.evtTools.linkedObjects.linkObjects");
|
||||
GetAllActions()["LinkedObjects::RemoveLinkBetween"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/LinkedObjects/linkedobjects.js")
|
||||
.SetFunctionName("gdjs.evtTools.linkedObjects.removeLinkBetween");
|
||||
GetAllActions()["LinkedObjects::RemoveAllLinksOf"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/LinkedObjects/linkedobjects.js")
|
||||
.SetFunctionName("gdjs.evtTools.linkedObjects.removeAllLinksOf");
|
||||
GetAllActions()["LinkedObjects::PickObjectsLinkedTo"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/LinkedObjects/linkedobjects.js")
|
||||
.SetFunctionName("gdjs.evtTools.linkedObjects.pickObjectsLinkedTo");
|
||||
GetAllConditions()["LinkedObjects::PickObjectsLinkedTo"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/LinkedObjects/linkedobjects.js")
|
||||
.SetFunctionName("gdjs.evtTools.linkedObjects.pickObjectsLinkedTo");
|
||||
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PanelSpriteObject)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(PanelSpriteObject)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(PanelSpriteObject "${source_files}")
|
||||
gd_add_extension_target(PanelSpriteObject "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(PanelSpriteObject)
|
||||
|
@@ -1,21 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(ParticleSystem)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(ParticleSystem)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(ParticleSystem "${source_files}")
|
||||
gd_add_extension_target(ParticleSystem "${source_files}")
|
||||
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(ParticleSystem)
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PathfindingBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(PathfindingBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(PathfindingBehavior "${source_files}")
|
||||
gd_add_extension_target(PathfindingBehavior "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(PathfindingBehavior)
|
||||
|
@@ -758,7 +758,7 @@ module.exports = {
|
||||
|
||||
aut
|
||||
.addAction(
|
||||
'SetSleepingaAllowed',
|
||||
'SetSleepingAllowed',
|
||||
_('Sleeping allowed'),
|
||||
_(
|
||||
'Allow or not an object to sleep. If enabled the object will be able to sleep, improving performance for non-currently-moving objects.'
|
||||
@@ -774,6 +774,11 @@ module.exports = {
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setSleepingAllowed');
|
||||
|
||||
// Deprecated action (fixed typo):
|
||||
aut
|
||||
.addDuplicatedAction("SetSleepingaAllowed", "SetSleepingAllowed")
|
||||
.setHidden();
|
||||
|
||||
aut
|
||||
.addCondition(
|
||||
|
@@ -1,21 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PhysicsBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(PhysicsBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(PhysicsBehavior "${source_files}")
|
||||
gd_add_extension_target(PhysicsBehavior "${source_files}")
|
||||
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(PhysicsBehavior)
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PlatformBehavior)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(PlatformBehavior)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(PlatformBehavior "${source_files}")
|
||||
gd_add_extension_target(PlatformBehavior "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
gd_extension_link_libraries(PlatformBehavior)
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(PlatformBehavior)
|
||||
|
@@ -140,7 +140,7 @@ namespace gdjs {
|
||||
} else if (behaviorData.platformType === 'Jumpthru') {
|
||||
this._platformType = PlatformRuntimeBehavior.JUMPTHRU;
|
||||
} else {
|
||||
this._platformType = PlatformRuntimeBehavior.NORMALPLAFTORM;
|
||||
this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;
|
||||
}
|
||||
this._canBeGrabbed = behaviorData.canBeGrabbed || false;
|
||||
this._yGrabOffset = behaviorData.yGrabOffset || 0;
|
||||
@@ -232,7 +232,7 @@ namespace gdjs {
|
||||
} else if (platformType === 'Jumpthru') {
|
||||
this._platformType = PlatformRuntimeBehavior.JUMPTHRU;
|
||||
} else {
|
||||
this._platformType = PlatformRuntimeBehavior.NORMALPLAFTORM;
|
||||
this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,9 @@ namespace gdjs {
|
||||
return this._yGrabOffset;
|
||||
}
|
||||
|
||||
static NORMALPLAFTORM = 0;
|
||||
static NORMALPLATFORM = 0;
|
||||
/** @deprecated Use NORMALPLATFORM instead. */
|
||||
static NORMALPLAFTORM = PlatformRuntimeBehavior.NORMALPLATFORM;
|
||||
static JUMPTHRU = 1;
|
||||
static LADDER = 2;
|
||||
|
||||
|
@@ -598,8 +598,8 @@ namespace gdjs {
|
||||
break;
|
||||
}
|
||||
case 'connectionId': {
|
||||
const messagegeData = messageContent.data;
|
||||
const connectionId = messagegeData.connectionId;
|
||||
const messageData = messageContent.data;
|
||||
const connectionId = messageData.connectionId;
|
||||
if (!connectionId) {
|
||||
logger.error('No connectionId received');
|
||||
return;
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PrimitiveDrawing)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(PrimitiveDrawing)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(PrimitiveDrawing "${source_files}")
|
||||
gd_add_extension_target(PrimitiveDrawing "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(PrimitiveDrawing)
|
||||
|
@@ -1,21 +1,26 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(Shopify)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(Shopify)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
|
||||
gd_add_extension_target(Shopify "${source_files}" "JsPlatform")
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_extension_target(
|
||||
Shopify
|
||||
"${source_files}"
|
||||
"JsPlatform")
|
||||
gd_add_clang_utils(Shopify "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(Shopify)
|
||||
|
@@ -81,12 +81,10 @@ class ShopifyJsExtension : public gd::PlatformExtension {
|
||||
DeclareShopifyExtension(*this);
|
||||
|
||||
GetAllActions()["Shopify::BuildClient"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Shopify/shopify-buy.umd.polyfilled.min.js")
|
||||
.AddIncludeFile("Extensions/Shopify/shopifytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.shopify.buildClient");
|
||||
GetAllActions()["Shopify::GetCheckoutUrlForProduct"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Shopify/shopify-buy.umd.polyfilled.min.js")
|
||||
.AddIncludeFile("Extensions/Shopify/shopifytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.shopify.getCheckoutUrlForProduct");
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(SystemInfo)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(SystemInfo)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(SystemInfo "${source_files}")
|
||||
gd_add_extension_target(SystemInfo "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(SystemInfo)
|
||||
|
@@ -25,27 +25,21 @@ class SystemInfoJsExtension : public gd::PlatformExtension {
|
||||
DeclareSystemInfoExtension(*this);
|
||||
|
||||
GetAllConditions()["SystemInfo::IsMobile"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isMobile");
|
||||
GetAllConditions()["SystemInfo::IsNativeMobileApp"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isNativeMobileApp");
|
||||
GetAllConditions()["SystemInfo::IsNativeDesktopApp"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isNativeDesktopApp");
|
||||
GetAllConditions()["SystemInfo::IsWebGLSupported"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isWebGLSupported");
|
||||
GetAllConditions()["SystemInfo::IsPreview"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isPreview");
|
||||
GetAllConditions()["SystemInfo::HasTouchScreen"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.hasTouchScreen");
|
||||
|
||||
|
@@ -5,47 +5,7 @@
|
||||
namespace gdjs {
|
||||
export namespace evtTools {
|
||||
export namespace systemInfo {
|
||||
let cachedIsMobile: boolean | null = null;
|
||||
let cachedHasTouchScreen: boolean | null = null;
|
||||
const checkIsMobile = (): boolean => {
|
||||
if (typeof cc !== 'undefined' && cc.sys) {
|
||||
return cc.sys.isMobile;
|
||||
}
|
||||
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'Cocoon'.
|
||||
else if (typeof Cocoon !== 'undefined' && Cocoon.App) {
|
||||
return true;
|
||||
} else if (typeof window !== 'undefined' && (window as any).cordova) {
|
||||
return true;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
// Try to detect mobile device browsers.
|
||||
if (
|
||||
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
|
||||
navigator.userAgent
|
||||
) ||
|
||||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
|
||||
navigator.userAgent.substr(0, 4)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to detect iOS
|
||||
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
|
||||
return true;
|
||||
} else {
|
||||
if (/MacIntel/.test(navigator.platform)) {
|
||||
// Work around for recent iPads that are "desktop-class browsing".
|
||||
// We can still detect them using their touchscreen, but this is a hack.
|
||||
// If mac laptops start to support touchscreens, this won't work anymore. Hence it's better
|
||||
// to test for the presence of a touchscreen if needed rather than checking if the device
|
||||
// is "mobile".
|
||||
return !!navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the game runs on a mobile device (iPhone, iPad, Android).
|
||||
@@ -54,10 +14,7 @@ namespace gdjs {
|
||||
* prefer to check if the device has touchscreen support.
|
||||
*/
|
||||
export const isMobile = (): boolean => {
|
||||
if (cachedIsMobile !== null) {
|
||||
return cachedIsMobile;
|
||||
}
|
||||
return (cachedIsMobile = checkIsMobile());
|
||||
return gdjs.evtTools.common.isMobile();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(TextEntryObject)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(TextEntryObject)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(TextEntryObject "${source_files}")
|
||||
gd_add_extension_target(TextEntryObject "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(TextEntryObject)
|
||||
|
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(TextObject)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(TextObject)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(TextObject "${source_files}")
|
||||
gd_add_extension_target(TextObject "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(TextObject)
|
||||
|
@@ -387,7 +387,7 @@ const defineTileMap = function (
|
||||
)
|
||||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('getLevelndex');
|
||||
.setFunctionName('getLevelIndex');
|
||||
|
||||
object
|
||||
.addCondition(
|
||||
@@ -1531,7 +1531,7 @@ module.exports = {
|
||||
this._loadTiledMapWithCallback.bind(this),
|
||||
tilemapJsonFile,
|
||||
tilesetJsonFile,
|
||||
0, // leveIndex
|
||||
0, // levelIndex
|
||||
pako,
|
||||
(tileMap) => {
|
||||
if (!tileMap) {
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,20 +1,23 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(TiledSpriteObject)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
# Defines
|
||||
#
|
||||
gd_add_extension_definitions(TiledSpriteObject)
|
||||
|
||||
#The targets
|
||||
###
|
||||
# The targets
|
||||
#
|
||||
include_directories(.)
|
||||
file(GLOB source_files *.cpp *.h)
|
||||
file(
|
||||
GLOB
|
||||
source_files
|
||||
*.cpp
|
||||
*.h)
|
||||
gd_add_clang_utils(TiledSpriteObject "${source_files}")
|
||||
gd_add_extension_target(TiledSpriteObject "${source_files}")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
# Linker files for the IDE extension
|
||||
#
|
||||
gd_extension_link_libraries(TiledSpriteObject)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user