mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
227 Commits
3d-preview
...
js-event-o
Author | SHA1 | Date | |
---|---|---|---|
![]() |
71f77964ce | ||
![]() |
1b3734ff6b | ||
![]() |
6288b30ac3 | ||
![]() |
ee435f7081 | ||
![]() |
d75b4eb2a9 | ||
![]() |
5eeb505807 | ||
![]() |
30566e35ce | ||
![]() |
e058b7f295 | ||
![]() |
902a30a9f8 | ||
![]() |
8669b94fb0 | ||
![]() |
7fb08aea62 | ||
![]() |
e7a1548b0e | ||
![]() |
bdcb6f0533 | ||
![]() |
97849ce6f1 | ||
![]() |
d1c937caf4 | ||
![]() |
5ffe6279a2 | ||
![]() |
9260e2b77a | ||
![]() |
593465e2ec | ||
![]() |
8820350760 | ||
![]() |
7e1668229a | ||
![]() |
387b96b9a0 | ||
![]() |
5f52d786c6 | ||
![]() |
e0db597f9d | ||
![]() |
41b0315ec6 | ||
![]() |
a930a4085e | ||
![]() |
d0dbbfac07 | ||
![]() |
3dc24b46f4 | ||
![]() |
8e44a357b4 | ||
![]() |
dd462310cc | ||
![]() |
a1935fa0cd | ||
![]() |
b45c57246b | ||
![]() |
c481ecd6b5 | ||
![]() |
e0898dd9b0 | ||
![]() |
5561334efa | ||
![]() |
6c4bb4f79e | ||
![]() |
8b2d2e2fe7 | ||
![]() |
49d128c964 | ||
![]() |
f24d1e0916 | ||
![]() |
9faa4c0c69 | ||
![]() |
a04b8f65db | ||
![]() |
e1cf7d23cd | ||
![]() |
b74b221844 | ||
![]() |
38affc15b4 | ||
![]() |
948488d92b | ||
![]() |
f5902d0346 | ||
![]() |
f28dc8e88a | ||
![]() |
1f41749fa3 | ||
![]() |
a4908a4d42 | ||
![]() |
aa7754e658 | ||
![]() |
58ea9387aa | ||
![]() |
775266c974 | ||
![]() |
eb9794cd1f | ||
![]() |
130732adde | ||
![]() |
7a98e73d61 | ||
![]() |
1f26b72b4b | ||
![]() |
a15ffb5b47 | ||
![]() |
1a5f72283a | ||
![]() |
0460b283ba | ||
![]() |
e212e7c780 | ||
![]() |
84100fc7cf | ||
![]() |
11a8682b07 | ||
![]() |
d3a0bbdfb1 | ||
![]() |
15f3a45d6a | ||
![]() |
f0a4f352cc | ||
![]() |
d16b3e8154 | ||
![]() |
614fb97288 | ||
![]() |
8a40d3645a | ||
![]() |
2b7dadf2a8 | ||
![]() |
c338e16e4f | ||
![]() |
aded08471d | ||
![]() |
cccb59b1c5 | ||
![]() |
3592fb7e62 | ||
![]() |
307c92991c | ||
![]() |
4b3f077669 | ||
![]() |
352bae518e | ||
![]() |
c958f4d522 | ||
![]() |
35bbb37ad2 | ||
![]() |
1d48acc841 | ||
![]() |
87702edccc | ||
![]() |
1f0ba7c19a | ||
![]() |
b4d08a99ad | ||
![]() |
8acaa06e42 | ||
![]() |
27ee85b5d4 | ||
![]() |
bbe2d1854e | ||
![]() |
d338690ff5 | ||
![]() |
571a6d8c1a | ||
![]() |
ddb5157c0a | ||
![]() |
64f01354bc | ||
![]() |
37fd99e542 | ||
![]() |
23be4a5849 | ||
![]() |
64c0ee8f98 | ||
![]() |
e5ecce3abf | ||
![]() |
5c71a4da56 | ||
![]() |
dff99b79cb | ||
![]() |
5fe46ea8ea | ||
![]() |
4a590adac4 | ||
![]() |
279d41cdb7 | ||
![]() |
5cf65a9f62 | ||
![]() |
08b05c13b6 | ||
![]() |
eb55c85f4e | ||
![]() |
8a243440db | ||
![]() |
b3e4e6b89c | ||
![]() |
a1a25f6df4 | ||
![]() |
6114a6cec1 | ||
![]() |
5058964937 | ||
![]() |
4488675540 | ||
![]() |
6a2d2c9e67 | ||
![]() |
b43c42d763 | ||
![]() |
69112183d4 | ||
![]() |
a4c2778b8d | ||
![]() |
f26e56c3bf | ||
![]() |
f5f9944fc4 | ||
![]() |
9467caf1e9 | ||
![]() |
00376f39d5 | ||
![]() |
40b6a34dc5 | ||
![]() |
17d2b8c2c2 | ||
![]() |
935af42d23 | ||
![]() |
d4a8d468cb | ||
![]() |
b16099aee0 | ||
![]() |
c17b918a43 | ||
![]() |
d58e8c7ef9 | ||
![]() |
ddd6b6e3a8 | ||
![]() |
e629c132ea | ||
![]() |
b80e03f153 | ||
![]() |
11e36ff3f1 | ||
![]() |
22de356413 | ||
![]() |
caefa04fbe | ||
![]() |
cf2e7d67d7 | ||
![]() |
685e444b2d | ||
![]() |
a9c1045afd | ||
![]() |
24e0d37583 | ||
![]() |
d44997d372 | ||
![]() |
062aa888f8 | ||
![]() |
de4c2ae4ad | ||
![]() |
29ad7308c3 | ||
![]() |
19b21c280e | ||
![]() |
fbfe8b246a | ||
![]() |
73f66eb51f | ||
![]() |
d62ba2b9a0 | ||
![]() |
323a2b6c2f | ||
![]() |
8e4cccd562 | ||
![]() |
795795ba40 | ||
![]() |
4af86b36e5 | ||
![]() |
b00632a625 | ||
![]() |
6f23f76441 | ||
![]() |
a6cd4b3c5d | ||
![]() |
81d63c41b6 | ||
![]() |
a924840228 | ||
![]() |
b013297c8e | ||
![]() |
ca77a31037 | ||
![]() |
5adb2240d5 | ||
![]() |
9d42be3362 | ||
![]() |
21201dec29 | ||
![]() |
08229cbe1d | ||
![]() |
96e9dd7c4b | ||
![]() |
7dbc687200 | ||
![]() |
7e1f2c6c97 | ||
![]() |
37cba12e4a | ||
![]() |
cdd80bca9e | ||
![]() |
3293d24c36 | ||
![]() |
bf31781d7a | ||
![]() |
f6c43b2db3 | ||
![]() |
f00156a654 | ||
![]() |
41fd1cbcee | ||
![]() |
52c807d74a | ||
![]() |
8713a496b4 | ||
![]() |
a3033f2db1 | ||
![]() |
5fb8faffc1 | ||
![]() |
a883955703 | ||
![]() |
b39d7adcbc | ||
![]() |
c577c8db71 | ||
![]() |
d7d17400dd | ||
![]() |
b219d50fd8 | ||
![]() |
88f318e6df | ||
![]() |
d12ac44b7d | ||
![]() |
7155eea716 | ||
![]() |
ca0796f131 | ||
![]() |
c4b33e2481 | ||
![]() |
ff95564b6b | ||
![]() |
cecf1ab791 | ||
![]() |
a571445e0e | ||
![]() |
89e418cd24 | ||
![]() |
896ccfcffa | ||
![]() |
d98d181755 | ||
![]() |
3c63f9b617 | ||
![]() |
8312bbe4f8 | ||
![]() |
bb77a71f26 | ||
![]() |
4353469554 | ||
![]() |
46ea431f62 | ||
![]() |
2d29d43355 | ||
![]() |
28a4e253a1 | ||
![]() |
f3dcb8eec8 | ||
![]() |
bf9e38ff31 | ||
![]() |
87649b9def | ||
![]() |
60d332e872 | ||
![]() |
bdab12b1e6 | ||
![]() |
02f795f2c1 | ||
![]() |
6e64d8521f | ||
![]() |
0e9aea1c9d | ||
![]() |
99901bf539 | ||
![]() |
53f1d745f5 | ||
![]() |
b72034a475 | ||
![]() |
926f6a2c56 | ||
![]() |
32cc6a3109 | ||
![]() |
f9ab65155d | ||
![]() |
d700a9d26d | ||
![]() |
297b88ed60 | ||
![]() |
06cef654b0 | ||
![]() |
c5dd26c93b | ||
![]() |
e0f3b221bd | ||
![]() |
896f56e850 | ||
![]() |
cee43ce9df | ||
![]() |
5e61712e55 | ||
![]() |
b3fa34ce3c | ||
![]() |
1bdb4c0369 | ||
![]() |
2822fab5ed | ||
![]() |
658ac381fb | ||
![]() |
80cf54cb1b | ||
![]() |
2f56f6b715 | ||
![]() |
9784113574 | ||
![]() |
e2de3bec34 | ||
![]() |
e837df4882 | ||
![]() |
2f44dab18b | ||
![]() |
10049ce42a | ||
![]() |
8d9a60f819 | ||
![]() |
8ea8c421b2 | ||
![]() |
f7b026f1cc |
@@ -13,17 +13,18 @@ orbs:
|
||||
aws-cli: circleci/aws-cli@2.0.6
|
||||
macos: circleci/macos@2.5.1 # For Rosetta (see below)
|
||||
node: circleci/node@5.2.0 # For a recent npm version (see below)
|
||||
win: circleci/windows@5.1.0
|
||||
jobs:
|
||||
# Build the **entire** app for macOS.
|
||||
# Build the **entire** app for macOS (including the GDevelop.js library).
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 14.2.0
|
||||
resource_class: macos.m1.large.gen1
|
||||
xcode: 16.4.0
|
||||
resource_class: m4pro.medium
|
||||
steps:
|
||||
- checkout
|
||||
# Install Rosetta for AWS CLI and disable TSO to speed up S3 uploads (https://support.circleci.com/hc/en-us/articles/19334402064027-Troubleshooting-slow-uploads-to-S3-for-jobs-using-an-m1-macOS-resource-class)
|
||||
- macos/install-rosetta
|
||||
- run: sudo sysctl net.inet.tcp.tso=0
|
||||
# - run: sudo sysctl net.inet.tcp.tso=0
|
||||
|
||||
# Install a recent version of npm to workaround a notarization issue because of a symlink made by npm: https://github.com/electron-userland/electron-builder/issues/7755
|
||||
# Node.js v20.14.0 comes with npm v10.7.0.
|
||||
@@ -46,9 +47,9 @@ jobs:
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
|
||||
- gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- gd-macos-nodejs-dependencies---
|
||||
- gd-macos-nodejs-dependencies-
|
||||
|
||||
- run:
|
||||
name: Install GDevelop.js dependencies
|
||||
@@ -69,7 +70,8 @@ jobs:
|
||||
- newIDE/electron-app/node_modules
|
||||
- newIDE/app/node_modules
|
||||
- GDevelop.js/node_modules
|
||||
key: gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
|
||||
- GDJS/node_modules
|
||||
key: gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
|
||||
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
|
||||
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
|
||||
@@ -86,15 +88,37 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: newIDE/electron-app/dist
|
||||
|
||||
|
||||
# Upload artifacts (AWS)
|
||||
- run:
|
||||
name: Deploy to S3 (specific commit)
|
||||
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/
|
||||
command: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
for i in 1 2 3 4 5 6 7; do
|
||||
aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/ && break
|
||||
echo "Retry $i failed... retrying in 10 seconds"
|
||||
sleep 10
|
||||
done
|
||||
if [ $i -eq 7 ]; then
|
||||
echo "All retries for deployment failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- run:
|
||||
name: Deploy to S3 (latest)
|
||||
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
|
||||
command: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
for i in 1 2 3 4 5 6 7; do
|
||||
aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/ && break
|
||||
echo "Retry $i failed... retrying in 10 seconds"
|
||||
sleep 10
|
||||
done
|
||||
if [ $i -eq 7 ]; then
|
||||
echo "All retries for deployment failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build the **entire** app for Linux.
|
||||
# Build the app for Linux (using a pre-built GDevelop.js library).
|
||||
build-linux:
|
||||
# CircleCI docker workers are failing if they don't have enough memory (no swap)
|
||||
resource_class: xlarge
|
||||
@@ -107,51 +131,33 @@ jobs:
|
||||
- checkout
|
||||
- aws-cli/setup
|
||||
|
||||
# System dependencies (for Electron Builder and Emscripten)
|
||||
# System dependencies (for Electron Builder)
|
||||
- run:
|
||||
name: Install dependencies for Emscripten
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
name: Update system dependencies
|
||||
command: sudo apt-get update
|
||||
|
||||
- run:
|
||||
name: Install system dependencies for Electron builder
|
||||
command: sudo apt install icnsutils && sudo apt install graphicsmagick && sudo apt install rsync
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
|
||||
- gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- gd-linux-nodejs-dependencies---
|
||||
- gd-linux-nodejs-dependencies-
|
||||
|
||||
- run:
|
||||
name: Install GDevelop.js dependencies and build it
|
||||
command: cd GDevelop.js && npm install && cd ..
|
||||
|
||||
# Build GDevelop.js (and run tests to ensure it works)
|
||||
- run:
|
||||
name: Build GDevelop.js
|
||||
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
|
||||
|
||||
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
|
||||
# GDevelop IDE dependencies (using an exact version of GDevelop.js, built previously)
|
||||
- run:
|
||||
name: Install GDevelop IDE dependencies
|
||||
command: cd newIDE/app && npm install && cd ../electron-app && npm install
|
||||
command: export REQUIRES_EXACT_LIBGD_JS_VERSION=true && cd newIDE/app && npm install && cd ../electron-app && npm install
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- newIDE/electron-app/node_modules
|
||||
- newIDE/app/node_modules
|
||||
- GDevelop.js/node_modules
|
||||
key: gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
|
||||
- GDJS/node_modules
|
||||
key: gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
|
||||
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
|
||||
- run:
|
||||
@@ -295,14 +301,203 @@ jobs:
|
||||
name: Deploy to S3 (specific commit)
|
||||
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/variant/debug-sanitizers/commit/$(git rev-parse HEAD)/
|
||||
|
||||
# Trigger AppVeyor build, which also does a Windows build (keep it for redundancy).
|
||||
trigger-appveyor-windows-build:
|
||||
docker:
|
||||
- image: cimg/node:16.13
|
||||
steps:
|
||||
- run:
|
||||
name: Trigger AppVeyor Windows build
|
||||
command: |
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${APPVEYOR_API_KEY}" \
|
||||
--data "{
|
||||
\"accountName\": \"4ian\",
|
||||
\"projectSlug\": \"gdevelop\",
|
||||
\"branch\": \"${CIRCLE_BRANCH}\"
|
||||
}" \
|
||||
-X POST https://ci.appveyor.com/api/builds
|
||||
|
||||
build-windows:
|
||||
executor:
|
||||
name: win/default
|
||||
size: medium
|
||||
working_directory: /home/circleci/project
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
# See https://www.ssl.com/how-to/how-to-integrate-esigner-cka-with-ci-cd-tools-for-automated-code-signing/
|
||||
#
|
||||
# This is necessary because of "signing to be FIPS-140 compliant". See
|
||||
# https://github.com/electron-userland/electron-builder/issues/6158
|
||||
#
|
||||
# Make sure to DISABLE "malware blocker" in SSL.com to avoid errors like:
|
||||
# Error information: "Error: SignerSign() failed." (-2146893821/0x80090003)
|
||||
name: Download and Unzip eSignerCKA Setup
|
||||
command: |
|
||||
|
||||
Invoke-WebRequest -OutFile eSigner_CKA_1.0.3.zip "https://www.ssl.com/download/ssl-com-esigner-cka-1-0-3"
|
||||
|
||||
Expand-Archive -Force eSigner_CKA_1.0.3.zip
|
||||
|
||||
Remove-Item eSigner_CKA_1.0.3.zip
|
||||
|
||||
Move-Item -Destination "eSigner_CKA_1.0.3.exe" -Path "eSigner_CKA_*\*.exe"
|
||||
- run:
|
||||
name: Setup eSignerCKA in Silent Mode
|
||||
command: |
|
||||
|
||||
mkdir -p "/home/circleci/project/eSignerCKA"
|
||||
|
||||
./eSigner_CKA_1.0.3.exe /CURRENTUSER /VERYSILENT /SUPPRESSMSGBOXES /DIR="/home/circleci/project/eSignerCKA" | Out-Null
|
||||
- run:
|
||||
name: Config Account Information on eSignerCKA
|
||||
command: |
|
||||
|
||||
/home/circleci/project/eSignerCKA/eSignerCKATool.exe config -mode product -user "$env:ESIGNER_USER_NAME" -pass "$env:ESIGNER_USER_PASSWORD" -totp "$env:ESIGNER_USER_TOTP" -key "/home/circleci/project/eSignerCKA/master.key" -r
|
||||
- run:
|
||||
name: Load Certificate into Windows Store
|
||||
command: |
|
||||
|
||||
/home/circleci/project/eSignerCKA/eSignerCKATool.exe unload
|
||||
|
||||
/home/circleci/project/eSignerCKA/eSignerCKATool.exe load
|
||||
- run:
|
||||
name: Select Certificate From Windows Store and Sign Sample File with SignTool
|
||||
command: |
|
||||
|
||||
$CodeSigningCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
|
||||
|
||||
echo Certificate: $CodeSigningCert
|
||||
|
||||
- restore_cache:
|
||||
name: Restore node_modules cache
|
||||
keys:
|
||||
- v1-win-node-{{ checksum "newIDE/app/package-lock.json" }}-{{ checksum "newIDE/electron-app/package-lock.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
- v1-win-node-
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
no_output_timeout: 25m
|
||||
# Remove package-lock.json because they seems to cause the npm install to be stuck. We should try again after re-generating them.
|
||||
# Also install setuptools as something requires distutils in electron-app, and it was removed in Python 3.12.
|
||||
# setuptools will make distutils available again (but we should migrate our packages probably).
|
||||
command: |
|
||||
pip install setuptools
|
||||
|
||||
cd newIDE\app
|
||||
|
||||
npm -v
|
||||
|
||||
Remove-Item package-lock.json
|
||||
|
||||
$Env:REQUIRES_EXACT_LIBGD_JS_VERSION = "true"
|
||||
|
||||
npm install
|
||||
|
||||
cd ..\electron-app
|
||||
|
||||
Remove-Item package-lock.json
|
||||
|
||||
npm install
|
||||
|
||||
cd ..\..
|
||||
|
||||
- save_cache:
|
||||
name: Save node_modules cache
|
||||
key: v1-win-node-{{ checksum "newIDE/app/package-lock.json" }}-{{ checksum "newIDE/electron-app/package-lock.json" }}-{{ checksum "GDJS/package-lock.json" }}
|
||||
paths:
|
||||
- newIDE/app/node_modules
|
||||
- newIDE/electron-app/node_modules
|
||||
- GDJS/node_modules
|
||||
|
||||
- run:
|
||||
name: Build NSIS executable (with code signing)
|
||||
command: |
|
||||
cd newIDE\electron-app
|
||||
|
||||
$CodeSigningCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
|
||||
|
||||
echo Certificate: $CodeSigningCert
|
||||
|
||||
# Use a custom signtool path because of the signtool.exe bundled withy electron-builder not working for some reason.
|
||||
# Can also be found in versioned folders like "C:/Program Files (x86)/Windows Kits/10/bin/10.0.22000.0/x86/signtool.exe".
|
||||
# or "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe".
|
||||
|
||||
$Env:SIGNTOOL_PATH = "C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe"
|
||||
|
||||
# Extract thumbprint and subject name of the certificate (will be passed to electron-builder).
|
||||
|
||||
$Env:GD_SIGNTOOL_THUMBPRINT = $CodeSigningCert.Thumbprint
|
||||
|
||||
$Env:GD_SIGNTOOL_SUBJECT_NAME = ($CodeSigningCert.Subject -replace ", ?", "`n" | ConvertFrom-StringData).CN
|
||||
|
||||
# Build the nsis installer (signed: electron-builder will use SignTool.exe with the certificate)
|
||||
|
||||
node scripts/build.js --win nsis --publish=never
|
||||
|
||||
cd ..\..
|
||||
- run:
|
||||
name: Build AppX (without code signing)
|
||||
# Don't sign the appx (it will be signed by the Microsoft Store).
|
||||
command: |
|
||||
cd newIDE\electron-app
|
||||
|
||||
# Build the appx (not signed). Ensure all variables used for code signing are empty.
|
||||
|
||||
$Env:GD_SIGNTOOL_THUMBPRINT = ''
|
||||
|
||||
$Env:GD_SIGNTOOL_SUBJECT_NAME = ''
|
||||
|
||||
$Env:CSC_LINK = ''
|
||||
|
||||
$Env:CSC_KEY_PASSWORD = ''
|
||||
|
||||
node scripts/build.js --skip-app-build --win appx --publish=never
|
||||
|
||||
cd ..\..
|
||||
|
||||
- run:
|
||||
name: Clean binaries
|
||||
shell: cmd.exe
|
||||
command: |
|
||||
rmdir /s /q newIDE\electron-app\dist\win-unpacked
|
||||
|
||||
- run:
|
||||
name: Install AWS CLI
|
||||
command: |
|
||||
# Install the CLI for the current user
|
||||
|
||||
pip install --quiet --upgrade --user awscli
|
||||
|
||||
# Add the user-Scripts dir to PATH for this step and the next.
|
||||
|
||||
$binDir = (python -m site --user-base) + "\Scripts"
|
||||
$Env:Path += ";$binDir"
|
||||
|
||||
# Sanity check:
|
||||
aws --version
|
||||
|
||||
# Upload artifacts (S3)
|
||||
- run:
|
||||
name: Deploy to S3 (specific commit)
|
||||
command: |
|
||||
aws s3 sync newIDE\electron-app\dist "s3://gdevelop-releases/$Env:CIRCLE_BRANCH/commit/$Env:CIRCLE_SHA1/"
|
||||
|
||||
- run:
|
||||
name: Deploy to S3 (latest)
|
||||
command: |
|
||||
aws s3 sync newIDE\electron-app\dist "s3://gdevelop-releases/$Env:CIRCLE_BRANCH/latest/"
|
||||
|
||||
# Upload artifacts (CircleCI)
|
||||
- store_artifacts:
|
||||
path: newIDE/electron-app/dist
|
||||
|
||||
workflows:
|
||||
gdevelop_js-wasm:
|
||||
jobs:
|
||||
- build-gdevelop_js-wasm-only
|
||||
gdevelop_js-wasm-extra-checks:
|
||||
jobs:
|
||||
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
|
||||
# Extra checks are resource intensive so don't all run them.
|
||||
# Extra checks are resource intensive so don't always run them.
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
@@ -310,13 +505,36 @@ workflows:
|
||||
- /experimental-build.*/
|
||||
builds:
|
||||
jobs:
|
||||
- build-gdevelop_js-wasm-only
|
||||
- build-macos:
|
||||
# The macOS version builds by itself GDevelop.js
|
||||
# (so we verify we can build it on macOS).
|
||||
# requires:
|
||||
# - build-gdevelop_js-wasm-only
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /experimental-build.*/
|
||||
- build-linux:
|
||||
requires:
|
||||
- build-gdevelop_js-wasm-only
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /experimental-build.*/
|
||||
- build-windows:
|
||||
requires:
|
||||
- build-gdevelop_js-wasm-only
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /experimental-build.*/
|
||||
- trigger-appveyor-windows-build:
|
||||
requires:
|
||||
- build-gdevelop_js-wasm-only
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
.claude
|
||||
|
3
.vscode/tasks.json
vendored
3
.vscode/tasks.json
vendored
@@ -38,8 +38,7 @@
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"isBackground": true,
|
||||
"runOptions": { "instanceLimit": 1, "runOn": "folderOpen" }
|
||||
"isBackground": true
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
|
@@ -61,10 +61,12 @@ void GroupEvent::UnserializeFrom(gd::Project& project,
|
||||
project, events, element.GetChild("events"));
|
||||
|
||||
parameters.clear();
|
||||
gd::SerializerElement& parametersElement = element.GetChild("parameters");
|
||||
parametersElement.ConsiderAsArrayOf("parameters");
|
||||
for (std::size_t i = 0; i < parametersElement.GetChildrenCount(); ++i)
|
||||
parameters.push_back(parametersElement.GetChild(i).GetValue().GetString());
|
||||
if (element.HasChild("parameters")) {
|
||||
gd::SerializerElement& parametersElement = element.GetChild("parameters");
|
||||
parametersElement.ConsiderAsArrayOf("parameters");
|
||||
for (std::size_t i = 0; i < parametersElement.GetChildrenCount(); ++i)
|
||||
parameters.push_back(parametersElement.GetChild(i).GetValue().GetString());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupEvent::SetBackgroundColor(unsigned int colorR_,
|
||||
|
@@ -163,6 +163,21 @@ void LinkEvent::UnserializeFrom(gd::Project& project,
|
||||
// end of compatibility code
|
||||
}
|
||||
|
||||
vector<gd::String> LinkEvent::GetAllSearchableStrings() const {
|
||||
vector<gd::String> allSearchableStrings;
|
||||
|
||||
allSearchableStrings.push_back(target);
|
||||
|
||||
return allSearchableStrings;
|
||||
}
|
||||
|
||||
bool LinkEvent::ReplaceAllSearchableStrings(
|
||||
std::vector<gd::String> newSearchableString) {
|
||||
if (newSearchableString[0] == target) return false;
|
||||
SetTarget(newSearchableString[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
|
||||
return BaseEvent::AcceptVisitor(eventVisitor) ||
|
||||
eventVisitor.VisitLinkEvent(*this);
|
||||
|
@@ -109,6 +109,10 @@ class GD_CORE_API LinkEvent : public gd::BaseEvent {
|
||||
|
||||
virtual bool IsExecutable() const override { return true; };
|
||||
|
||||
virtual std::vector<gd::String> GetAllSearchableStrings() const override;
|
||||
virtual bool ReplaceAllSearchableStrings(
|
||||
std::vector<gd::String> newSearchableString) override;
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const override;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) override;
|
||||
|
@@ -286,6 +286,20 @@ class GD_CORE_API BaseEvent {
|
||||
* \brief True if the event should be folded in the events editor.
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
/**
|
||||
* \brief Set the AI generated event ID.
|
||||
*/
|
||||
void SetAiGeneratedEventId(const gd::String& aiGeneratedEventId_) {
|
||||
aiGeneratedEventId = aiGeneratedEventId_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the AI generated event ID.
|
||||
*/
|
||||
const gd::String& GetAiGeneratedEventId() const {
|
||||
return aiGeneratedEventId;
|
||||
}
|
||||
///@}
|
||||
|
||||
std::weak_ptr<gd::BaseEvent>
|
||||
@@ -304,6 +318,7 @@ class GD_CORE_API BaseEvent {
|
||||
bool disabled; ///< True if the event is disabled and must not be executed
|
||||
gd::String type; ///< Type of the event. Must be assigned at the creation.
|
||||
///< Used for saving the event for instance.
|
||||
gd::String aiGeneratedEventId; ///< When generated by an AI/external tool.
|
||||
|
||||
static gd::EventsList badSubEvents;
|
||||
static gd::VariablesContainer badLocalVariables;
|
||||
|
@@ -221,6 +221,8 @@ void EventsListSerialization::UnserializeEventsFrom(
|
||||
|
||||
event->SetDisabled(eventElem.GetBoolAttribute("disabled", false));
|
||||
event->SetFolded(eventElem.GetBoolAttribute("folded", false));
|
||||
event->SetAiGeneratedEventId(
|
||||
eventElem.GetStringAttribute("aiGeneratedEventId", ""));
|
||||
|
||||
list.InsertEvent(event, list.GetEventsCount());
|
||||
}
|
||||
@@ -236,6 +238,8 @@ void EventsListSerialization::SerializeEventsTo(const EventsList& list,
|
||||
if (event.IsDisabled())
|
||||
eventElem.SetAttribute("disabled", event.IsDisabled());
|
||||
if (event.IsFolded()) eventElem.SetAttribute("folded", event.IsFolded());
|
||||
if (!event.GetAiGeneratedEventId().empty())
|
||||
eventElem.SetAttribute("aiGeneratedEventId", event.GetAiGeneratedEventId());
|
||||
eventElem.AddChild("type").SetValue(event.GetType());
|
||||
|
||||
event.SerializeTo(eventElem);
|
||||
|
@@ -37,8 +37,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.SetIcon("res/actions/position24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Angle"))
|
||||
.SetIcon("res/actions/direction24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
|
||||
gd::ObjectMetadata& obj = extension.AddObject<gd::ObjectConfiguration>(
|
||||
"", _("Base object"), _("Base object"), "res/objeticon24.png");
|
||||
@@ -235,7 +235,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddAction("SetAngle",
|
||||
_("Angle"),
|
||||
_("Change the angle of rotation of an object (in degrees)."),
|
||||
_("Change the angle of rotation of an object (in degrees). For "
|
||||
"3D objects, this is the rotation around the Z axis."),
|
||||
_("the angle"),
|
||||
_("Angle"),
|
||||
"res/actions/direction24_black.png",
|
||||
@@ -250,7 +251,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
obj.AddAction("Rotate",
|
||||
_("Rotate"),
|
||||
_("Rotate an object, clockwise if the speed is positive, "
|
||||
"counterclockwise otherwise."),
|
||||
"counterclockwise otherwise. For 3D objects, this is the "
|
||||
"rotation around the Z axis."),
|
||||
_("Rotate _PARAM0_ at speed _PARAM1_ deg/second"),
|
||||
_("Angle"),
|
||||
"res/actions/rotate24_black.png",
|
||||
@@ -634,7 +636,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddCondition("Angle",
|
||||
_("Angle"),
|
||||
_("Compare the angle of the specified object."),
|
||||
_("Compare the angle, in degrees, of the specified object. "
|
||||
"For 3D objects, this is the angle around the Z axis."),
|
||||
_("the angle (in degrees)"),
|
||||
_("Angle"),
|
||||
"res/conditions/direction24_black.png",
|
||||
@@ -835,14 +838,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.MarkAsAdvanced()
|
||||
.SetRelevantForLayoutEventsOnly();
|
||||
|
||||
obj.AddAction(
|
||||
"PushBooleanToObjectVariable",
|
||||
_("Add value to object array variable"),
|
||||
_("Adds a boolean to the end of an object array variable."),
|
||||
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
|
||||
_("Variables ❯ Arrays and structures"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
obj.AddAction("PushBooleanToObjectVariable",
|
||||
_("Add value to object array variable"),
|
||||
_("Adds a boolean to the end of an object array variable."),
|
||||
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
|
||||
_("Variables ❯ Arrays and structures"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectvar", _("Array variable"))
|
||||
.AddParameter("trueorfalse", _("Boolean to add"))
|
||||
@@ -1268,7 +1270,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddExpression("Angle",
|
||||
_("Angle"),
|
||||
_("Current angle, in degrees, of the object"),
|
||||
_("Current angle, in degrees, of the object. For 3D "
|
||||
"objects, this is the angle around the Z axis."),
|
||||
_("Angle"),
|
||||
"res/actions/direction_black.png")
|
||||
.AddParameter("object", _("Object"));
|
||||
@@ -1571,7 +1574,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
extension
|
||||
.AddAction("Create",
|
||||
_("Create an object"),
|
||||
_("Create an object at specified position"),
|
||||
_("Create an instance of the object at the specified position."
|
||||
"The created object instance will be available for the next "
|
||||
"actions and sub-events."),
|
||||
_("Create object _PARAM1_ at position _PARAM2_;_PARAM3_ "
|
||||
"(layer: _PARAM4_)"),
|
||||
"",
|
||||
|
@@ -18,21 +18,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("AnimatableCapability",
|
||||
_("Animatable capability"),
|
||||
_("Animate objects."),
|
||||
_("Objects with animations"),
|
||||
_("Actions and conditions for objects having animations (sprite, 3D models...)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with animations"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"AnimatableBehavior",
|
||||
_("Animatable capability"),
|
||||
_("Objects with animations"),
|
||||
"Animation",
|
||||
_("Animate objects."),
|
||||
_("Actions and conditions for objects having animations (sprite, 3D models...).."),
|
||||
"",
|
||||
"res/actions/animation24.png",
|
||||
"AnimatableBehavior",
|
||||
|
@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("EffectCapability",
|
||||
_("Effect capability"),
|
||||
_("Apply visual effects to objects."),
|
||||
_("Objects with effects"),
|
||||
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
@@ -28,9 +28,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"EffectBehavior",
|
||||
_("Effect capability"),
|
||||
_("Objects with effects"),
|
||||
"Effect",
|
||||
_("Apply visual effects to objects."),
|
||||
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
|
||||
"",
|
||||
"res/actions/effect_black.svg",
|
||||
"EffectBehavior",
|
||||
|
@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("FlippableCapability",
|
||||
_("Flippable capability"),
|
||||
_("Flip objects."),
|
||||
_("Flippable objects"),
|
||||
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
@@ -28,9 +28,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"FlippableBehavior",
|
||||
_("Flippable capability"),
|
||||
_("Flippable objects"),
|
||||
"Flippable",
|
||||
_("Flip objects."),
|
||||
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
|
||||
"",
|
||||
"res/actions/flipX24.png",
|
||||
"FlippableBehavior",
|
||||
|
@@ -18,27 +18,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("OpacityCapability",
|
||||
_("Opacity capability"),
|
||||
_("Change the object opacity."),
|
||||
_("Objects with opacity"),
|
||||
_("Action/condition/expression to change or "
|
||||
"check the opacity of an object (0-255)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with opacity"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"OpacityBehavior",
|
||||
_("Opacity capability"),
|
||||
"Opacity",
|
||||
_("Change the object opacity."),
|
||||
"",
|
||||
"res/actions/opacity24.png",
|
||||
"OpacityBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension
|
||||
.AddBehavior("OpacityBehavior",
|
||||
_("Objects with opacity"),
|
||||
"Opacity",
|
||||
_("Action/condition/expression to change or check the "
|
||||
"opacity of an object (0-255)."),
|
||||
"",
|
||||
"res/actions/opacity24.png",
|
||||
"OpacityBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -52,8 +55,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "OpacityBehavior")
|
||||
.UseStandardParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Opacity (0-255)")))
|
||||
"number",
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Opacity (0-255)")))
|
||||
.SetFunctionName("setOpacity")
|
||||
.SetGetter("getOpacity");
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
|
@@ -16,11 +16,13 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
gd::PlatformExtension &extension) {
|
||||
extension
|
||||
.SetExtensionInformation("ResizableCapability",
|
||||
_("Resizable capability"),
|
||||
_("Change the object dimensions."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"ResizableCapability",
|
||||
_("Resizable objects"),
|
||||
_("Change or compare the size (width/height) of an object which can "
|
||||
"be resized (i.e: most objects)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
@@ -28,9 +30,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
gd::BehaviorMetadata &aut =
|
||||
extension
|
||||
.AddBehavior("ResizableBehavior",
|
||||
_("Resizable capability"),
|
||||
_("Resizable objects"),
|
||||
"Resizable",
|
||||
_("Change the object dimensions."),
|
||||
_("Change or compare the size (width/height) of an "
|
||||
"object which can be resized (i.e: most objects)."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
|
@@ -18,27 +18,30 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("ScalableCapability",
|
||||
_("Scalable capability"),
|
||||
_("Change the object scale."),
|
||||
_("Scalable objects"),
|
||||
_("Actions/conditions/expression to change or "
|
||||
"check the scale of an object (default: 1)."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable objects"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"ScalableBehavior",
|
||||
_("Scalable capability"),
|
||||
"Scale",
|
||||
_("Change the object scale."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension
|
||||
.AddBehavior("ScalableBehavior",
|
||||
_("Scalable objects"),
|
||||
"Scale",
|
||||
_("Actions/conditions/expression to change or check the "
|
||||
"scale of an object (default: 1)."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
|
@@ -18,19 +18,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("TextContainerCapability",
|
||||
_("Text capability"),
|
||||
_("Animate objects."),
|
||||
_("Objects containing a text"),
|
||||
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Objects containing a text"))
|
||||
.SetIcon("res/conditions/text24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"TextContainerBehavior",
|
||||
_("Text capability"),
|
||||
_("Objects containing a text"),
|
||||
"Text",
|
||||
_("Access objects text."),
|
||||
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
|
||||
"",
|
||||
"res/conditions/text24_black.png",
|
||||
"TextContainerBehavior",
|
||||
|
@@ -16,7 +16,9 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinCommonConversions",
|
||||
_("Conversion"),
|
||||
"Expressions to convert number, texts and quantities.",
|
||||
"Expressions to convert numbers to string, strings to numbers, "
|
||||
"angles (degrees from/to radians) and a GDevelop variable to/from a "
|
||||
"JSON string.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/common-conversions");
|
||||
@@ -41,7 +43,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
|
||||
extension
|
||||
.AddStrExpression("LargeNumberToString",
|
||||
_("Number > Text ( without scientific notation )"),
|
||||
_("Number > Text (without scientific notation)"),
|
||||
_("Convert the result of the expression to text, "
|
||||
"without using the scientific notation"),
|
||||
"",
|
||||
@@ -72,7 +74,8 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
|
||||
_("Convert a variable to JSON"),
|
||||
_("JSON"),
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("variable", _("The variable to be stringified"),
|
||||
.AddParameter("variable",
|
||||
_("The variable to be stringified"),
|
||||
"AllowUndeclaredVariable");
|
||||
|
||||
// Deprecated
|
||||
|
@@ -59,36 +59,44 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
// end of compatibility code
|
||||
|
||||
extension
|
||||
.AddCondition("Or",
|
||||
_("Or"),
|
||||
_("Check if one of the sub conditions is true"),
|
||||
_("If one of these conditions is true:"),
|
||||
"",
|
||||
"res/conditions/or24_black.png",
|
||||
"res/conditions/or_black.png")
|
||||
.SetCanHaveSubInstructions()
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("And",
|
||||
_("And"),
|
||||
_("Check if all sub conditions are true"),
|
||||
_("If all of these conditions are true:"),
|
||||
"",
|
||||
"res/conditions/and24_black.png",
|
||||
"res/conditions/and_black.png")
|
||||
.AddCondition(
|
||||
"Or",
|
||||
_("Or"),
|
||||
_("Checks if at least one sub-condition is true. If no "
|
||||
"sub-condition is specified, it will always be false. "
|
||||
"This is rarely used — multiple events and sub-events are "
|
||||
"usually a better approach."),
|
||||
_("If one of these conditions is true:"),
|
||||
"",
|
||||
"res/conditions/or24_black.png",
|
||||
"res/conditions/or_black.png")
|
||||
.SetCanHaveSubInstructions()
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"Not",
|
||||
_("Not"),
|
||||
_("Return the contrary of the result of the sub conditions"),
|
||||
_("Invert the logical result of these conditions:"),
|
||||
"And",
|
||||
_("And"),
|
||||
_("Checks if all sub-conditions are true. If no sub-condition is "
|
||||
"specified, it will always be false. This is rarely needed, as "
|
||||
"events already check all conditions before running actions."),
|
||||
_("If all of these conditions are true:"),
|
||||
"",
|
||||
"res/conditions/not24_black.png",
|
||||
"res/conditions/not_black.png")
|
||||
"res/conditions/and24_black.png",
|
||||
"res/conditions/and_black.png")
|
||||
.SetCanHaveSubInstructions()
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("Not",
|
||||
_("Not"),
|
||||
_("Returns the opposite of the sub-condition(s) result. "
|
||||
"This is rarely needed, as most conditions can be "
|
||||
"inverted or expressed more simply."),
|
||||
_("Invert the logical result of these conditions:"),
|
||||
"",
|
||||
"res/conditions/not24_black.png",
|
||||
"res/conditions/not_black.png")
|
||||
.SetCanHaveSubInstructions()
|
||||
.MarkAsAdvanced();
|
||||
|
||||
|
@@ -15,10 +15,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinKeyboard",
|
||||
_("Keyboard"),
|
||||
_("Allows your game to respond to keyboard input. Note that this "
|
||||
_("Conditions to check keys pressed on a keyboard. Note that this "
|
||||
"does not work with on-screen keyboard on touch devices: use "
|
||||
"instead conditions related to touch when making a game for "
|
||||
"mobile/touchscreen devices."),
|
||||
"instead mouse/touch conditions when making a game for "
|
||||
"mobile/touchscreen devices or when making a new game from "
|
||||
"scratch."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/keyboard")
|
||||
@@ -84,7 +85,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
"res/conditions/keyboard.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
extension
|
||||
.AddCondition("AnyKeyReleased",
|
||||
_("Any key released"),
|
||||
_("Check if any key is released"),
|
||||
|
@@ -72,7 +72,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
|
||||
extension
|
||||
.AddExpression("normalize",
|
||||
_("Normalize a value between `min` and `max` to a value between 0 and 1."),
|
||||
_("Normalize a value between `min` and `max` to a value "
|
||||
"between 0 and 1."),
|
||||
_("Remap a value between 0 and 1."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
@@ -124,7 +125,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension
|
||||
.AddExpression("mod",
|
||||
_("Modulo"),
|
||||
_("x mod y"),
|
||||
_("Compute \"x mod y\". GDevelop does NOT support the \% "
|
||||
"operator. Use this mod(x, y) function instead."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("x (as in x mod y)"))
|
||||
@@ -184,11 +186,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("asinh",
|
||||
_("Arcsine"),
|
||||
_("Arcsine"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"asinh", _("Arcsine"), _("Arcsine"), "", "res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -218,11 +217,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("cbrt",
|
||||
_("Cube root"),
|
||||
_("Cube root"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"cbrt", _("Cube root"), _("Cube root"), "", "res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -260,12 +256,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"), "", true);
|
||||
|
||||
extension
|
||||
.AddExpression("cos",
|
||||
_("Cosine"),
|
||||
_("Cosine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"cos",
|
||||
_("Cosine"),
|
||||
_("Cosine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -293,29 +290,20 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("int",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"int", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("rint",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"rint", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("round",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"round", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -324,8 +312,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
_("Round a number to the Nth decimal place"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"))
|
||||
.AddParameter("expression", _("Expression"), "", true);
|
||||
.AddParameter("expression", _("Number to Round"))
|
||||
.AddParameter("expression", _("Decimal Places"), "", true);
|
||||
|
||||
extension
|
||||
.AddExpression("exp",
|
||||
@@ -336,19 +324,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("log",
|
||||
_("Logarithm"),
|
||||
_("Logarithm"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"log", _("Logarithm"), _("Logarithm"), "", "res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("ln",
|
||||
_("Logarithm"),
|
||||
_("Logarithm"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"ln", _("Logarithm"), _("Logarithm"), "", "res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
@@ -387,11 +369,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("The exponent (n in x^n)"));
|
||||
|
||||
extension
|
||||
.AddExpression("sec",
|
||||
_("Secant"),
|
||||
_("Secant"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"sec", _("Secant"), _("Secant"), "", "res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -403,12 +382,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("sin",
|
||||
_("Sine"),
|
||||
_("Sine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"sin",
|
||||
_("Sine"),
|
||||
_("Sine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -428,12 +408,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression("tan",
|
||||
_("Tangent"),
|
||||
_("Tangent of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"tan",
|
||||
_("Tangent"),
|
||||
_("Tangent of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -463,26 +444,28 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("x (in a+(b-a)*x)"));
|
||||
|
||||
extension
|
||||
.AddExpression("XFromAngleAndDistance",
|
||||
_("X position from angle and distance"),
|
||||
_("Compute the X position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"XFromAngleAndDistance",
|
||||
_("X position from angle and distance"),
|
||||
_("Compute the X position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Distance"));
|
||||
|
||||
extension
|
||||
.AddExpression("YFromAngleAndDistance",
|
||||
_("Y position from angle and distance"),
|
||||
_("Compute the Y position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression(
|
||||
"YFromAngleAndDistance",
|
||||
_("Y position from angle and distance"),
|
||||
_("Compute the Y position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Distance"));
|
||||
|
||||
@@ -497,7 +480,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension
|
||||
.AddExpression("lerpAngle",
|
||||
_("Lerp (Linear interpolation) between two angles"),
|
||||
_("Linearly interpolates between two angles (in degrees) by taking the shortest direction around the circle."),
|
||||
_("Linearly interpolates between two angles (in degrees) "
|
||||
"by taking the shortest direction around the circle."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Starting angle, in degrees"))
|
||||
|
@@ -16,8 +16,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinMouse",
|
||||
_("Mouse and touch"),
|
||||
"Conditions and actions to handle either the mouse or touches on "
|
||||
"touchscreen. By default, conditions related to the mouse will also "
|
||||
"Conditions, actions and expressions to handle either the mouse or "
|
||||
"touches on a touchscreen. Notably: cursor position, mouse wheel, "
|
||||
"mouse buttons, touch positions, started/end touches, etc...\n"
|
||||
"\n"
|
||||
"By default, conditions related to the mouse will also "
|
||||
"handle the touches - so that it's easier to handle both in your "
|
||||
"game. You can disable this behavior if you want to handle them "
|
||||
"separately in different events.",
|
||||
@@ -273,28 +276,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"MouseButtonFromTextPressed",
|
||||
_("Mouse button pressed or touch held"),
|
||||
_("Check if the specified mouse button is pressed or "
|
||||
"if a touch is in contact with the screen."),
|
||||
_("Touch or _PARAM1_ mouse button is down"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCondition("MouseButtonFromTextPressed",
|
||||
_("Mouse button pressed or touch held"),
|
||||
_("Check if the specified mouse button is pressed or "
|
||||
"if a touch is in contact with the screen."),
|
||||
_("Touch or _PARAM1_ mouse button is down"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("mouseButton", _("Button to check"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"MouseButtonFromTextReleased",
|
||||
_("Mouse button released"),
|
||||
_("Check if the specified mouse button was released."),
|
||||
_("Touch or _PARAM1_ mouse button is released"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCondition("MouseButtonFromTextReleased",
|
||||
_("Mouse button released"),
|
||||
_("Check if the specified mouse button was released."),
|
||||
_("Touch or _PARAM1_ mouse button is released"),
|
||||
"",
|
||||
"res/conditions/mouse24.png",
|
||||
"res/conditions/mouse.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("mouseButton", _("Button to check"))
|
||||
.MarkAsSimple();
|
||||
|
@@ -15,8 +15,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinNetwork",
|
||||
_("Network"),
|
||||
_("Features to send web requests, communicate with external \"APIs\" "
|
||||
"and other network related tasks."),
|
||||
_("Actions to send web requests, communicate with external \"APIs\" "
|
||||
"and other network related tasks. Also contains an action to open "
|
||||
"a URL on the device browser."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/network")
|
||||
|
@@ -4,8 +4,8 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "AllBuiltinExtensions.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
@@ -16,7 +16,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinScene",
|
||||
_("Scene"),
|
||||
_("Actions and conditions to manipulate the scenes during the game."),
|
||||
_("Actions/conditions to change the current scene (or pause it and "
|
||||
"launch another one, or go back to the previous one), check if a "
|
||||
"scene or the game has just started/resumed, preload assets of a "
|
||||
"scene, get the current scene name or loading progress, quit the "
|
||||
"game, set background color, or disable input when focus is lost."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
@@ -166,25 +170,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.AddAction(
|
||||
"PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Name of the new scene"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in background for a scene (between 0 and 1)."),
|
||||
_("_PARAM0_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/hourglass_black.svg")
|
||||
extension
|
||||
.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in "
|
||||
"background for a scene (between 0 and 1)."),
|
||||
_("_PARAM1_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
@@ -192,13 +199,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.AddCondition(
|
||||
"AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
|
@@ -15,12 +15,13 @@ namespace gd {
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("Sprite",
|
||||
_("Sprite"),
|
||||
_("Sprite are animated object which can be used "
|
||||
"for most elements of a game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"Sprite",
|
||||
_("Sprite"),
|
||||
_("Sprite are animated objects which can be used "
|
||||
"for most elements of a 2D game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/sprite");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
|
||||
.SetIcon("CppPlatform/Extensions/spriteicon.png");
|
||||
@@ -30,7 +31,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.AddObject<SpriteObject>("Sprite",
|
||||
_("Sprite"),
|
||||
_("Animated object which can be used for "
|
||||
"most elements of a game."),
|
||||
"most elements of a 2D game."),
|
||||
"CppPlatform/Extensions/spriteicon.png")
|
||||
.SetCategoryFullName(_("General"))
|
||||
.SetOpenFullEditorLabel(_("Edit animations"))
|
||||
@@ -645,11 +646,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
"res/actions/sprite.png")
|
||||
.AddParameter("object", _("Object"), "Sprite");
|
||||
|
||||
obj.AddExpression("AnimationFrameCount",
|
||||
_("Number of frames"),
|
||||
_("Number of frames in the current animation of the object"),
|
||||
_("Animations and images"),
|
||||
"res/actions/sprite.png")
|
||||
obj.AddExpression(
|
||||
"AnimationFrameCount",
|
||||
_("Number of frames"),
|
||||
_("Number of frames in the current animation of the object"),
|
||||
_("Animations and images"),
|
||||
"res/actions/sprite.png")
|
||||
.AddParameter("object", _("Object"), "Sprite");
|
||||
|
||||
// Deprecated
|
||||
|
@@ -16,7 +16,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinStringInstructions",
|
||||
_("Text manipulation"),
|
||||
"Provides expressions to manipulate strings (also called texts).",
|
||||
"Provides expressions to manipulate strings (also called texts): new "
|
||||
"line, upper/lowercase, substring, find, replace, etc...",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
|
||||
@@ -191,7 +192,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("string", _("Text in which the replacement must be done"))
|
||||
.AddParameter("string", _("Text to find inside the first text"))
|
||||
.AddParameter("string", _("Replacement to put instead of the text to find"));
|
||||
.AddParameter("string",
|
||||
_("Replacement to put instead of the text to find"));
|
||||
|
||||
extension
|
||||
.AddStrExpression("StrReplaceAll",
|
||||
@@ -199,10 +201,11 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
|
||||
_("Replace all occurrences of a text by another."),
|
||||
"",
|
||||
"res/conditions/toujours24_black.png")
|
||||
.AddParameter("string", _("Text in which the replacement(s) must be done"))
|
||||
.AddParameter("string",
|
||||
_("Text in which the replacement(s) must be done"))
|
||||
.AddParameter("string", _("Text to find inside the first text"))
|
||||
.AddParameter("string", _("Replacement to put instead of the text to find"));
|
||||
|
||||
.AddParameter("string",
|
||||
_("Replacement to put instead of the text to find"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -15,9 +15,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinTime",
|
||||
_("Timers and time"),
|
||||
"Actions and conditions to run timers, get the current time or "
|
||||
"modify the time scale (speed at which the game is running - useful "
|
||||
"for slow motion effects).",
|
||||
"Actions and conditions to start, pause or reset scene timers, "
|
||||
"modify the time scale (speed at which the game "
|
||||
"is running - useful for slow motion effects). Also contains an "
|
||||
"action that wait for a delay before running the next actions and "
|
||||
"sub-events and expressions to read the time scale, time delta of "
|
||||
"the last frame or timer elapsed time.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/timers-and-time");
|
||||
@@ -43,7 +46,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
.AddCondition("CompareTimer",
|
||||
_("Value of a scene timer"),
|
||||
_("Compare the elapsed time of a scene timer. This "
|
||||
"condition doesn't start the timer."),
|
||||
"condition doesn't start the timer and will always be "
|
||||
"false if the timer was not started previously (whatever "
|
||||
"the comparison being made)."),
|
||||
_("The timer _PARAM1_ _PARAM2_ _PARAM3_ seconds"),
|
||||
|
||||
"",
|
||||
@@ -190,26 +195,28 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
extension
|
||||
.AddExpression("TimerElapsedTime",
|
||||
_("Scene timer value"),
|
||||
_("Value of a scene timer"),
|
||||
_("Value of a scene timer (in seconds)"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
|
||||
|
||||
extension
|
||||
.AddExpression("TimeFromStart",
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddExpression(
|
||||
"TimeFromStart",
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddExpression("TempsDebut",
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
_("Time elapsed since the beginning of the scene"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddExpression(
|
||||
"TempsDebut",
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
_("Time elapsed since the beginning of the scene (in seconds)."),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.SetHidden()
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
@@ -224,16 +231,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
|
||||
extension
|
||||
.AddExpression("Time",
|
||||
_("Current time"),
|
||||
_("Current time"),
|
||||
_("Gives the current time"),
|
||||
"",
|
||||
"res/actions/time.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter(
|
||||
"stringWithSelector",
|
||||
_("Hour: hour - Minutes: min - Seconds: sec - Day of month: "
|
||||
"mday - Months since January: mon - Year since 1900: year - Days "
|
||||
"since Sunday: wday - Days since Jan 1st: yday - Timestamp (ms): "
|
||||
"timestamp\""),
|
||||
_("- Hour of the day: \"hour\"\n"
|
||||
"- Minutes: \"min\"\n"
|
||||
"- Seconds: \"sec\"\n"
|
||||
"- Day of month: \"mday\"\n"
|
||||
"- Months since January: \"mon\"\n"
|
||||
"- Year since 1900: \"year\"\n"
|
||||
"- Days since Sunday: \"wday\"\n"
|
||||
"- Days since Jan 1st: \"yday\"\n"
|
||||
"- Timestamp (ms): \"timestamp\""),
|
||||
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
|
||||
"\"yday\", \"timestamp\"]");
|
||||
}
|
||||
|
@@ -15,16 +15,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
.SetExtensionInformation(
|
||||
"BuiltinWindow",
|
||||
_("Game window and resolution"),
|
||||
"Provides actions and conditions to manipulate the game window. "
|
||||
"Actions and conditions to manipulate the game window or change how "
|
||||
"the game is resized according to the screen size. "
|
||||
"Depending on the platform on which the game is running, not all of "
|
||||
"these features can be applied.",
|
||||
"these features can be applied.\n"
|
||||
"Also contains expressions to read the screen size.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/all-features/window");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(
|
||||
_("Game window and resolution"))
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Game window and resolution"))
|
||||
.SetIcon("res/actions/window24.png");
|
||||
|
||||
extension
|
||||
|
@@ -779,6 +779,26 @@ gd::String PlatformExtension::GetBehaviorFullType(
|
||||
return extensionName + separator + behaviorName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetExtensionFromFullBehaviorType(
|
||||
const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
type.find(PlatformExtension::GetNamespaceSeparator());
|
||||
if (separatorIndex == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return type.substr(0, separatorIndex);
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetBehaviorNameFromFullBehaviorType(
|
||||
const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
type.find(PlatformExtension::GetNamespaceSeparator());
|
||||
if (separatorIndex == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return type.substr(separatorIndex + 2);
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetObjectEventsFunctionFullType(
|
||||
const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
|
@@ -651,6 +651,10 @@ class GD_CORE_API PlatformExtension {
|
||||
static gd::String GetBehaviorFullType(const gd::String& extensionName,
|
||||
const gd::String& behaviorName);
|
||||
|
||||
static gd::String GetExtensionFromFullBehaviorType(const gd::String& type);
|
||||
|
||||
static gd::String GetBehaviorNameFromFullBehaviorType(const gd::String& type);
|
||||
|
||||
static gd::String GetObjectEventsFunctionFullType(
|
||||
const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
|
154
Core/GDCore/IDE/EventsBasedObjectVariantHelper.cpp
Normal file
154
Core/GDCore/IDE/EventsBasedObjectVariantHelper.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventsBasedObjectVariantHelper.h"
|
||||
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
const gd::Project &project, gd::EventsBasedObject &eventsBasedObject) {
|
||||
auto &defaultObjects = eventsBasedObject.GetDefaultVariant().GetObjects();
|
||||
|
||||
for (const auto &variant :
|
||||
eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
auto &objects = variant->GetObjects();
|
||||
|
||||
// Delete extra objects
|
||||
for (auto it = objects.GetObjects().begin();
|
||||
it != objects.GetObjects().end(); ++it) {
|
||||
const auto &objectName = it->get()->GetName();
|
||||
if (!defaultObjects.HasObjectNamed(objectName)) {
|
||||
variant->GetInitialInstances().RemoveInitialInstancesOfObject(
|
||||
objectName);
|
||||
// Do it in last because it unalloc objectName.
|
||||
objects.RemoveObject(objectName);
|
||||
--it;
|
||||
}
|
||||
}
|
||||
for (const auto &defaultObject : defaultObjects.GetObjects()) {
|
||||
const auto &objectName = defaultObject->GetName();
|
||||
const auto &defaultVariables = defaultObject->GetVariables();
|
||||
const auto &defaultBehaviors = defaultObject->GetAllBehaviorContents();
|
||||
|
||||
// Copy missing objects
|
||||
if (!objects.HasObjectNamed(objectName)) {
|
||||
objects.InsertObject(*defaultObject,
|
||||
defaultObjects.GetObjectPosition(objectName));
|
||||
objects.AddMissingObjectsInRootFolder();
|
||||
continue;
|
||||
}
|
||||
// Change object types
|
||||
auto &object = objects.GetObject(objectName);
|
||||
if (object.GetType() != defaultObject->GetType()) {
|
||||
// Keep a copy of the old object.
|
||||
auto oldObject = objects.GetObject(objectName);
|
||||
objects.RemoveObject(objectName);
|
||||
objects.InsertObject(*defaultObject,
|
||||
defaultObjects.GetObjectPosition(objectName));
|
||||
object.CopyWithoutConfiguration(oldObject);
|
||||
objects.AddMissingObjectsInRootFolder();
|
||||
}
|
||||
|
||||
// Copy missing behaviors
|
||||
for (const auto &pair : defaultBehaviors) {
|
||||
const auto &behaviorName = pair.first;
|
||||
const auto &defaultBehavior = pair.second;
|
||||
|
||||
if (object.HasBehaviorNamed(behaviorName) &&
|
||||
object.GetBehavior(behaviorName).GetTypeName() !=
|
||||
defaultBehavior->GetTypeName()) {
|
||||
object.RemoveBehavior(behaviorName);
|
||||
}
|
||||
if (!object.HasBehaviorNamed(behaviorName)) {
|
||||
auto *behavior = object.AddNewBehavior(
|
||||
project, defaultBehavior->GetTypeName(), behaviorName);
|
||||
gd::SerializerElement element;
|
||||
defaultBehavior->SerializeTo(element);
|
||||
behavior->UnserializeFrom(element);
|
||||
}
|
||||
}
|
||||
// Delete extra behaviors
|
||||
for (auto &behaviorName : object.GetAllBehaviorNames()) {
|
||||
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
|
||||
object.RemoveBehavior(behaviorName);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and copy missing variables
|
||||
auto &variables = object.GetVariables();
|
||||
for (size_t defaultVariableIndex = 0;
|
||||
defaultVariableIndex < defaultVariables.Count();
|
||||
defaultVariableIndex++) {
|
||||
const auto &variableName =
|
||||
defaultVariables.GetNameAt(defaultVariableIndex);
|
||||
const auto &defaultVariable =
|
||||
defaultVariables.Get(defaultVariableIndex);
|
||||
|
||||
auto variableIndex = variables.GetPosition(variableName);
|
||||
if (variableIndex == gd::String::npos) {
|
||||
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
|
||||
} else {
|
||||
variables.Move(variableIndex, defaultVariableIndex);
|
||||
}
|
||||
if (variables.Get(variableName).GetType() != defaultVariable.GetType()) {
|
||||
variables.Remove(variableName);
|
||||
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
|
||||
}
|
||||
}
|
||||
// Remove extra variables
|
||||
auto variableToRemoveCount = variables.Count() - defaultVariables.Count();
|
||||
for (size_t iteration = 0; iteration < variableToRemoveCount;
|
||||
iteration++) {
|
||||
variables.Remove(variables.GetNameAt(variables.Count() - 1));
|
||||
}
|
||||
|
||||
// Remove extra instance variables
|
||||
variant->GetInitialInstances().IterateOverInstances(
|
||||
[&objectName,
|
||||
&defaultVariables](gd::InitialInstance &initialInstance) {
|
||||
if (initialInstance.GetObjectName() != objectName) {
|
||||
return false;
|
||||
}
|
||||
auto &instanceVariables = initialInstance.GetVariables();
|
||||
for (size_t instanceVariableIndex = 0;
|
||||
instanceVariableIndex < instanceVariables.Count();
|
||||
instanceVariableIndex++) {
|
||||
const auto &variableName =
|
||||
defaultVariables.GetNameAt(instanceVariableIndex);
|
||||
|
||||
if (!defaultVariables.Has(variableName)) {
|
||||
instanceVariables.Remove(variableName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
auto &defaultObjectGroups =
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjectGroups();
|
||||
auto &objectGroups = variant->GetObjects().GetObjectGroups();
|
||||
auto objectGroupsCount = objectGroups.Count();
|
||||
// Clear groups
|
||||
for (size_t index = 0; index < objectGroupsCount; index++) {
|
||||
objectGroups.Remove(objectGroups.Get(0).GetName());
|
||||
}
|
||||
// Copy groups
|
||||
for (size_t index = 0; index < defaultObjectGroups.Count(); index++) {
|
||||
objectGroups.Insert(defaultObjectGroups.Get(index), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
26
Core/GDCore/IDE/EventsBasedObjectVariantHelper.h
Normal file
26
Core/GDCore/IDE/EventsBasedObjectVariantHelper.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace gd {
|
||||
class EventsBasedObject;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API EventsBasedObjectVariantHelper {
|
||||
public:
|
||||
/**
|
||||
* @brief Apply the changes done on events-based object children to all its
|
||||
* variants.
|
||||
*/
|
||||
static void
|
||||
ComplyVariantsToEventsBasedObject(const gd::Project &project,
|
||||
gd::EventsBasedObject &eventsBasedObject);
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -17,6 +17,8 @@
|
||||
#include "GDCore/IDE/Project/ResourcesRenamer.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/CustomBehavior.h"
|
||||
#include "GDCore/Project/CustomObjectConfiguration.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
@@ -60,9 +62,6 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
element.SetAttribute("version", "");
|
||||
element.SetIntAttribute("animationsCount", 1);
|
||||
element.SetIntAttribute("maxFramesCount", 1);
|
||||
// TODO Find the right object dimensions.
|
||||
element.SetIntAttribute("width", 0);
|
||||
element.SetIntAttribute("height", 0);
|
||||
SerializerElement &authorsElement = element.AddChild("authors");
|
||||
authorsElement.ConsiderAsArrayOf("author");
|
||||
SerializerElement &tagsElement = element.AddChild("tags");
|
||||
@@ -75,6 +74,28 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
|
||||
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
|
||||
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
if (project.HasEventsBasedObject(object.GetType())) {
|
||||
SerializerElement &variantsElement =
|
||||
objectAssetElement.AddChild("variants");
|
||||
variantsElement.ConsiderAsArrayOf("variant");
|
||||
|
||||
const auto *variant = ObjectAssetSerializer::GetVariant(project, object);
|
||||
if (variant) {
|
||||
width = variant->GetAreaMaxX() - variant->GetAreaMinX();
|
||||
height = variant->GetAreaMaxY() - variant->GetAreaMinY();
|
||||
}
|
||||
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
|
||||
// TODO Find the right object dimensions when their is no variant.
|
||||
element.SetIntAttribute("width", width);
|
||||
element.SetIntAttribute("height", height);
|
||||
|
||||
SerializerElement &resourcesElement =
|
||||
objectAssetElement.AddChild("resources");
|
||||
resourcesElement.ConsiderAsArrayOf("resource");
|
||||
@@ -108,4 +129,59 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
objectAssetElement.AddChild("customization");
|
||||
customizationElement.ConsiderAsArrayOf("empty");
|
||||
}
|
||||
|
||||
void ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
gd::Project &project, const gd::Object &object,
|
||||
SerializerElement &variantsElement,
|
||||
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers) {
|
||||
const auto *variant = ObjectAssetSerializer::GetVariant(project, object);
|
||||
if (!variant) {
|
||||
return;
|
||||
}
|
||||
const auto &variantIdentifier =
|
||||
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
|
||||
variant->GetName();
|
||||
auto insertResult = alreadyUsedVariantIdentifiers.insert(variantIdentifier);
|
||||
if (!insertResult.second) {
|
||||
return;
|
||||
}
|
||||
SerializerElement &pairElement = variantsElement.AddChild("variant");
|
||||
pairElement.SetAttribute("objectType", object.GetType());
|
||||
SerializerElement &variantElement = pairElement.AddChild("variant");
|
||||
variant->SerializeTo(variantElement);
|
||||
|
||||
for (auto &object : variant->GetObjects().GetObjects()) {
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, *object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
const gd::EventsBasedObjectVariant *
|
||||
ObjectAssetSerializer::GetVariant(gd::Project &project,
|
||||
const gd::Object &object) {
|
||||
if (!project.HasEventsBasedObject(object.GetType())) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto &eventsBasedObject =
|
||||
project.GetEventsBasedObject(object.GetType());
|
||||
const auto &variants = eventsBasedObject.GetVariants();
|
||||
const auto *customObjectConfiguration =
|
||||
dynamic_cast<const gd::CustomObjectConfiguration *>(
|
||||
&object.GetConfiguration());
|
||||
const auto &variantName = customObjectConfiguration->GetVariantName();
|
||||
if (!variants.HasVariantNamed(variantName) &&
|
||||
(customObjectConfiguration
|
||||
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
|
||||
customObjectConfiguration
|
||||
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration())) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto &variantIdentifier =
|
||||
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
|
||||
variantName;
|
||||
const auto &variant = variants.HasVariantNamed(variantName)
|
||||
? variants.GetVariant(variantName)
|
||||
: eventsBasedObject.GetDefaultVariant();
|
||||
return &variant;
|
||||
}
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -20,6 +21,7 @@ class InitialInstance;
|
||||
class SerializerElement;
|
||||
class EffectsContainer;
|
||||
class AbstractFileSystem;
|
||||
class EventsBasedObjectVariant;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -52,6 +54,13 @@ private:
|
||||
ObjectAssetSerializer(){};
|
||||
|
||||
static gd::String GetObjectExtensionName(const gd::Object &object);
|
||||
|
||||
static void SerializeUsedVariantsTo(
|
||||
gd::Project &project, const gd::Object &object,
|
||||
SerializerElement &variantsElement,
|
||||
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers);
|
||||
|
||||
static const gd::EventsBasedObjectVariant* GetVariant(gd::Project &project, const gd::Object &object);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "ObjectVariableHelper.h"
|
||||
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
@@ -173,6 +174,7 @@ void ObjectVariableHelper::ApplyChangesToObjects(
|
||||
groupVariablesContainer.Get(variableName),
|
||||
variablesContainer.Count());
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
@@ -215,6 +217,7 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
destinationVariablesContainer.Remove(variableName);
|
||||
}
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
@@ -236,6 +239,66 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectVariableHelper::ApplyChangesToVariants(
|
||||
gd::EventsBasedObject &eventsBasedObject, const gd::String &objectName,
|
||||
const gd::VariablesChangeset &changeset) {
|
||||
auto &defaultVariablesContainer = eventsBasedObject.GetDefaultVariant()
|
||||
.GetObjects()
|
||||
.GetObject(objectName)
|
||||
.GetVariables();
|
||||
for (auto &variant : eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
if (!variant->GetObjects().HasObjectNamed(objectName)) {
|
||||
continue;
|
||||
}
|
||||
auto &object = variant->GetObjects().GetObject(objectName);
|
||||
auto &variablesContainer = object.GetVariables();
|
||||
|
||||
for (const gd::String &variableName : changeset.removedVariableNames) {
|
||||
variablesContainer.Remove(variableName);
|
||||
}
|
||||
for (const gd::String &variableName : changeset.addedVariableNames) {
|
||||
if (variablesContainer.Has(variableName)) {
|
||||
// It can happens if a child-object already had the variable but it was
|
||||
// missing in other variant child-object.
|
||||
continue;
|
||||
}
|
||||
variablesContainer.Insert(variableName,
|
||||
defaultVariablesContainer.Get(variableName),
|
||||
variablesContainer.Count());
|
||||
}
|
||||
// TODO Check what happens if 2 variables exchange their names.
|
||||
for (const auto &pair : changeset.oldToNewVariableNames) {
|
||||
const gd::String &oldVariableName = pair.first;
|
||||
const gd::String &newVariableName = pair.second;
|
||||
if (variablesContainer.Has(newVariableName)) {
|
||||
// It can happens if a child-object already had the variable but it was
|
||||
// missing in other variant child-object.
|
||||
variablesContainer.Remove(oldVariableName);
|
||||
} else {
|
||||
variablesContainer.Rename(oldVariableName, newVariableName);
|
||||
}
|
||||
}
|
||||
// Apply type changes
|
||||
for (const gd::String &variableName : changeset.valueChangedVariableNames) {
|
||||
size_t index = variablesContainer.GetPosition(variableName);
|
||||
|
||||
if (variablesContainer.Has(variableName) &&
|
||||
variablesContainer.Get(variableName).GetType() !=
|
||||
defaultVariablesContainer.Get(variableName).GetType()) {
|
||||
variablesContainer.Remove(variableName);
|
||||
variablesContainer.Insert(
|
||||
variableName, defaultVariablesContainer.Get(variableName), index);
|
||||
}
|
||||
}
|
||||
|
||||
gd::ObjectVariableHelper::ApplyChangesToObjectInstances(
|
||||
variablesContainer, variant->GetInitialInstances(), objectName,
|
||||
changeset);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
|
||||
namespace gd {
|
||||
class EventsBasedObject;
|
||||
class InitialInstancesContainer;
|
||||
class ObjectsContainersList;
|
||||
class ObjectsContainer;
|
||||
@@ -53,7 +54,7 @@ public:
|
||||
* Objects can be added during the group edition and may not necessarily have
|
||||
* all the variables initially shared by the group.
|
||||
*
|
||||
* \see gd::GroupVariableHelper::MergeVariableContainers
|
||||
* \see gd::ObjectVariableHelper::MergeVariableContainers
|
||||
*/
|
||||
static void FillMissingGroupVariablesToObjects(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
@@ -72,16 +73,21 @@ public:
|
||||
const gd::ObjectGroup &objectGroup,
|
||||
const gd::VariablesChangeset &changeset);
|
||||
|
||||
/**
|
||||
* @brief Apply the changes done on an object to all its instances.
|
||||
*/
|
||||
static void ApplyChangesToObjectInstances(
|
||||
gd::VariablesContainer &objectVariablesContainer,
|
||||
gd::InitialInstancesContainer &initialInstancesContainer,
|
||||
const gd::String &objectName, const gd::VariablesChangeset &changeset);
|
||||
|
||||
private:
|
||||
static void ApplyChangesToVariableContainer(
|
||||
const gd::VariablesContainer &originalVariablesContainer,
|
||||
gd::VariablesContainer &destinationVariablesContainer,
|
||||
const gd::VariablesChangeset &changeset, bool shouldApplyValueChanges);
|
||||
/**
|
||||
* @brief Apply the changes done on events-based object child to all its
|
||||
* variants.
|
||||
*/
|
||||
static void ApplyChangesToVariants(gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &objectName,
|
||||
const gd::VariablesChangeset &changeset);
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -314,6 +314,12 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
auto eventsBasedObject = eventsBasedObjectUniquePtr.get();
|
||||
worker.Launch(eventsBasedObject->GetObjects());
|
||||
|
||||
for (auto &&variantUniquePtr :
|
||||
eventsBasedObject->GetVariants().GetInternalVector()) {
|
||||
auto variant = variantUniquePtr.get();
|
||||
worker.Launch(variant->GetObjects());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1781,6 +1781,14 @@ void WholeProjectRefactorer::DoRenameBehavior(
|
||||
projectBrowser.ExposeFunctions(project, behaviorParameterRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::UpdateBehaviorsSharedData(gd::Project &project) {
|
||||
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
gd::Layout &layout = project.GetLayout(i);
|
||||
|
||||
layout.UpdateBehaviorsSharedData(project);
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::DoRenameObject(
|
||||
gd::Project &project, const gd::String &oldObjectType,
|
||||
const gd::String &newObjectType, const gd::ProjectBrowser &projectBrowser) {
|
||||
@@ -2129,6 +2137,26 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
groups[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &variant : eventsBasedObject.GetVariants().GetInternalVector()) {
|
||||
auto &variantObjects = variant->GetObjects();
|
||||
auto &variantObjectGroups = variantObjects.GetObjectGroups();
|
||||
if (isObjectGroup) {
|
||||
if (variantObjectGroups.Has(oldName)) {
|
||||
variantObjectGroups.Get(oldName).SetName(newName);
|
||||
}
|
||||
// Object groups can't have instances or be in other groups
|
||||
}
|
||||
else {
|
||||
if (variantObjects.HasObjectNamed(oldName)) {
|
||||
variantObjects.GetObject(oldName).SetName(newName);
|
||||
}
|
||||
variant->GetInitialInstances().RenameInstancesOfObject(oldName, newName);
|
||||
for (std::size_t g = 0; g < variantObjectGroups.size(); ++g) {
|
||||
variantObjectGroups[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
|
@@ -704,6 +704,16 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
static size_t GetLayoutAndExternalLayoutLayerInstancesCount(
|
||||
gd::Project &project, gd::Layout &layout, const gd::String &layerName);
|
||||
|
||||
/**
|
||||
* This ensures that the scenes had an instance of shared data for
|
||||
* every behavior of every object that can be used on the scene
|
||||
* (i.e. the objects of the scene and the global objects)
|
||||
*
|
||||
* Must be called when a behavior have been added/deleted
|
||||
* from a global object or an object has been made global.
|
||||
*/
|
||||
static void UpdateBehaviorsSharedData(gd::Project &project);
|
||||
|
||||
virtual ~WholeProjectRefactorer(){};
|
||||
|
||||
private:
|
||||
|
@@ -19,6 +19,7 @@ using namespace gd;
|
||||
|
||||
void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& objectConfiguration) {
|
||||
project = objectConfiguration.project;
|
||||
variantName = objectConfiguration.variantName;
|
||||
objectContent = objectConfiguration.objectContent;
|
||||
animations = objectConfiguration.animations;
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
@@ -165,6 +166,7 @@ void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const
|
||||
animations.SerializeTo(animatableElement);
|
||||
}
|
||||
|
||||
element.SetAttribute("variant", variantName);
|
||||
if (IsOverridingEventsBasedObjectChildrenConfiguration()) {
|
||||
auto &childrenContentElement = element.AddChild("childrenContent");
|
||||
for (auto &pair : childObjectConfigurations) {
|
||||
@@ -184,6 +186,7 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
|
||||
animations.UnserializeFrom(animatableElement);
|
||||
}
|
||||
|
||||
variantName = element.GetStringAttribute("variant");
|
||||
isMarkedAsOverridingEventsBasedObjectChildrenConfiguration =
|
||||
element.HasChild("childrenContent");
|
||||
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
@@ -247,9 +250,29 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
|
||||
}
|
||||
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
|
||||
|
||||
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
if (IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
|
||||
for (auto &childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
}
|
||||
}
|
||||
else if (eventsBasedObject.GetVariants().HasVariantNamed(variantName)) {
|
||||
for (auto &childObject : eventsBasedObject.GetVariants()
|
||||
.GetVariant(variantName)
|
||||
.GetObjects()
|
||||
.GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
} else if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
for (auto &childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
}
|
||||
} else {
|
||||
for (auto &childObject :
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,9 +29,9 @@ namespace gd {
|
||||
* "resource".
|
||||
*/
|
||||
class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
public:
|
||||
CustomObjectConfiguration(const Project& project_, const String& type_)
|
||||
: project(&project_), isMarkedAsOverridingEventsBasedObjectChildrenConfiguration(false) {
|
||||
public:
|
||||
CustomObjectConfiguration(const Project &project_, const String &type_)
|
||||
: project(&project_) {
|
||||
SetType(type_);
|
||||
}
|
||||
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
|
||||
@@ -66,6 +66,27 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
|
||||
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
|
||||
|
||||
/**
|
||||
* \brief Get the name of the events-based object variant used by this custom object.
|
||||
*/
|
||||
const gd::String &GetVariantName() const { return variantName; };
|
||||
|
||||
/**
|
||||
* \brief Set the name of the events-based object variant used by this custom object.
|
||||
*/
|
||||
void SetVariantName(const gd::String &variantName_) {
|
||||
variantName = variantName_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy events-based objects don't have any instance in their default
|
||||
* variant since there wasn't a graphical editor at the time. In this case,
|
||||
* the editor doesn't allow to choose a variant, but a variant may have stayed
|
||||
* after a user rolled back the extension. This variant must be ignored.
|
||||
*
|
||||
* @return true when its events-based object doesn't have any initial
|
||||
* instance.
|
||||
*/
|
||||
bool IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
bool IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() const {
|
||||
@@ -145,6 +166,7 @@ protected:
|
||||
gd::SerializerElement objectContent;
|
||||
std::unordered_set<gd::String> unfoldedChildren;
|
||||
|
||||
gd::String variantName = "";
|
||||
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration = false;
|
||||
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
|
||||
|
||||
|
@@ -8,6 +8,8 @@
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String Effect::badStringParameterValue;
|
||||
|
||||
void Effect::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
|
@@ -3,8 +3,7 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EFFECT_H
|
||||
#define GDCORE_EFFECT_H
|
||||
#pragma once
|
||||
#include <map>
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -35,28 +34,43 @@ class GD_CORE_API Effect {
|
||||
void SetFolded(bool fold = true) { folded = fold; }
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
void SetDoubleParameter(const gd::String& name, double value) {
|
||||
void SetDoubleParameter(const gd::String &name, double value) {
|
||||
doubleParameters[name] = value;
|
||||
}
|
||||
|
||||
double GetDoubleParameter(const gd::String& name) {
|
||||
return doubleParameters[name];
|
||||
double GetDoubleParameter(const gd::String &name) const {
|
||||
auto itr = doubleParameters.find(name);
|
||||
return itr == doubleParameters.end() ? 0 : itr->second;
|
||||
}
|
||||
|
||||
void SetStringParameter(const gd::String& name, const gd::String& value) {
|
||||
bool HasDoubleParameter(const gd::String &name) const {
|
||||
return doubleParameters.find(name) != doubleParameters.end();
|
||||
}
|
||||
|
||||
void SetStringParameter(const gd::String &name, const gd::String &value) {
|
||||
stringParameters[name] = value;
|
||||
}
|
||||
|
||||
const gd::String& GetStringParameter(const gd::String& name) {
|
||||
return stringParameters[name];
|
||||
const gd::String &GetStringParameter(const gd::String &name) const {
|
||||
auto itr = stringParameters.find(name);
|
||||
return itr == stringParameters.end() ? badStringParameterValue : itr->second;
|
||||
}
|
||||
|
||||
void SetBooleanParameter(const gd::String& name, bool value) {
|
||||
bool HasStringParameter(const gd::String &name) const {
|
||||
return stringParameters.find(name) != stringParameters.end();
|
||||
}
|
||||
|
||||
void SetBooleanParameter(const gd::String &name, bool value) {
|
||||
booleanParameters[name] = value;
|
||||
}
|
||||
|
||||
bool GetBooleanParameter(const gd::String& name) {
|
||||
return booleanParameters[name];
|
||||
bool GetBooleanParameter(const gd::String &name) const {
|
||||
auto itr = booleanParameters.find(name);
|
||||
return itr == booleanParameters.end() ? false : itr->second;
|
||||
}
|
||||
|
||||
bool HasBooleanParameter(const gd::String &name) const {
|
||||
return booleanParameters.find(name) != booleanParameters.end();
|
||||
}
|
||||
|
||||
const std::map<gd::String, double>& GetAllDoubleParameters() const {
|
||||
@@ -94,7 +108,9 @@ class GD_CORE_API Effect {
|
||||
std::map<gd::String, double> doubleParameters; ///< Values of parameters being doubles, keyed by names.
|
||||
std::map<gd::String, gd::String> stringParameters; ///< Values of parameters being strings, keyed by names.
|
||||
std::map<gd::String, bool> booleanParameters; ///< Values of parameters being booleans, keyed by names.
|
||||
|
||||
static gd::String badStringParameterValue; ///< Empty string returned by
|
||||
///< GeStringParameter
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
#endif
|
||||
|
@@ -17,19 +17,13 @@ EventsBasedObject::EventsBasedObject()
|
||||
isAnimatable(false),
|
||||
isTextContainer(false),
|
||||
isInnerAreaFollowingParentSize(false),
|
||||
isUsingLegacyInstancesRenderer(false),
|
||||
areaMinX(0),
|
||||
areaMinY(0),
|
||||
areaMinZ(0),
|
||||
areaMaxX(64),
|
||||
areaMaxY(64),
|
||||
areaMaxZ(64),
|
||||
objectsContainer(gd::ObjectsContainer::SourceType::Object) {
|
||||
isUsingLegacyInstancesRenderer(false) {
|
||||
}
|
||||
|
||||
EventsBasedObject::~EventsBasedObject() {}
|
||||
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
void EventsBasedObject::SerializeToExternal(SerializerElement& element) const {
|
||||
element.SetAttribute("defaultName", defaultName);
|
||||
if (isRenderedIn3D) {
|
||||
element.SetBoolAttribute("is3D", true);
|
||||
@@ -44,20 +38,16 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
element.SetBoolAttribute("isInnerAreaFollowingParentSize", true);
|
||||
}
|
||||
element.SetBoolAttribute("isUsingLegacyInstancesRenderer", isUsingLegacyInstancesRenderer);
|
||||
element.SetIntAttribute("areaMinX", areaMinX);
|
||||
element.SetIntAttribute("areaMinY", areaMinY);
|
||||
element.SetIntAttribute("areaMinZ", areaMinZ);
|
||||
element.SetIntAttribute("areaMaxX", areaMaxX);
|
||||
element.SetIntAttribute("areaMaxY", areaMaxY);
|
||||
element.SetIntAttribute("areaMaxZ", areaMaxZ);
|
||||
|
||||
// The EventsBasedObjectVariant SerializeTo method override the name.
|
||||
// AbstractEventsBasedEntity::SerializeTo must be done after.
|
||||
defaultVariant.SerializeTo(element);
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
objectsContainer.GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
|
||||
}
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
SerializeToExternal(element);
|
||||
variants.SerializeVariantsTo(element.AddChild("variants"));
|
||||
}
|
||||
|
||||
void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
@@ -68,36 +58,22 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
|
||||
isInnerAreaFollowingParentSize =
|
||||
element.GetBoolAttribute("isInnerAreaFollowingParentSize", false);
|
||||
areaMinX = element.GetIntAttribute("areaMinX", 0);
|
||||
areaMinY = element.GetIntAttribute("areaMinY", 0);
|
||||
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
|
||||
areaMaxX = element.GetIntAttribute("areaMaxX", 64);
|
||||
areaMaxY = element.GetIntAttribute("areaMaxY", 64);
|
||||
areaMaxZ = element.GetIntAttribute("areaMaxZ", 64);
|
||||
|
||||
defaultVariant.UnserializeFrom(project, element);
|
||||
defaultVariant.SetName("");
|
||||
AbstractEventsBasedEntity::UnserializeFrom(project, element);
|
||||
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
objectsContainer.UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
objectsContainer.AddMissingObjectsInRootFolder();
|
||||
objectsContainer.GetObjectGroups().UnserializeFrom(
|
||||
element.GetChild("objectsGroups"));
|
||||
|
||||
if (element.HasChild("layers")) {
|
||||
layers.UnserializeLayersFrom(element.GetChild("layers"));
|
||||
} else {
|
||||
layers.Reset();
|
||||
if (element.HasChild("variants")) {
|
||||
variants.UnserializeVariantsFrom(project, element.GetChild("variants"));
|
||||
}
|
||||
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
if (element.HasChild("isUsingLegacyInstancesRenderer")) {
|
||||
isUsingLegacyInstancesRenderer =
|
||||
element.GetBoolAttribute("isUsingLegacyInstancesRenderer", false);
|
||||
}
|
||||
else {
|
||||
// Compatibility with GD <= 5.4.212
|
||||
isUsingLegacyInstancesRenderer = initialInstances.GetInstancesCount() == 0;
|
||||
isUsingLegacyInstancesRenderer = GetInitialInstances().GetInstancesCount() == 0;
|
||||
// end of compatibility code
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/AbstractEventsBasedEntity.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariantsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
@@ -162,18 +164,38 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
*/
|
||||
bool IsTextContainer() const { return isTextContainer; }
|
||||
|
||||
/**
|
||||
* \brief Get the default variant of the custom object.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetDefaultVariant() const { return defaultVariant; }
|
||||
|
||||
/**
|
||||
* \brief Get the default variant of the custom object.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetDefaultVariant() { return defaultVariant; }
|
||||
|
||||
/**
|
||||
* \brief Get the variants of the custom object.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariantsContainer& GetVariants() const { return variants; }
|
||||
|
||||
/**
|
||||
* \brief Get the variants of the custom object.
|
||||
*/
|
||||
gd::EventsBasedObjectVariantsContainer& GetVariants() { return variants; }
|
||||
|
||||
/** \name Layers
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the layers of the custom object.
|
||||
*/
|
||||
const gd::LayersContainer& GetLayers() const { return layers; }
|
||||
const gd::LayersContainer& GetLayers() const { return defaultVariant.GetLayers(); }
|
||||
|
||||
/**
|
||||
* \brief Get the layers of the custom object.
|
||||
*/
|
||||
gd::LayersContainer& GetLayers() { return layers; }
|
||||
gd::LayersContainer& GetLayers() { return defaultVariant.GetLayers(); }
|
||||
///@}
|
||||
|
||||
/** \name Child objects
|
||||
@@ -183,14 +205,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \brief Get the objects of the custom object.
|
||||
*/
|
||||
gd::ObjectsContainer& GetObjects() {
|
||||
return objectsContainer;
|
||||
return defaultVariant.GetObjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the objects of the custom object.
|
||||
*/
|
||||
const gd::ObjectsContainer& GetObjects() const {
|
||||
return objectsContainer;
|
||||
return defaultVariant.GetObjects();
|
||||
}
|
||||
///@}
|
||||
|
||||
@@ -201,14 +223,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \brief Get the instances of the custom object.
|
||||
*/
|
||||
gd::InitialInstancesContainer& GetInitialInstances() {
|
||||
return initialInstances;
|
||||
return defaultVariant.GetInitialInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the instances of the custom object.
|
||||
*/
|
||||
const gd::InitialInstancesContainer& GetInitialInstances() const {
|
||||
return initialInstances;
|
||||
return defaultVariant.GetInitialInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,14 +241,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinX() const {
|
||||
return areaMinX;
|
||||
return defaultVariant.GetAreaMinX();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the left bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinX(int areaMinX_) {
|
||||
areaMinX = areaMinX_;
|
||||
void SetAreaMinX(int areaMinX) {
|
||||
defaultVariant.SetAreaMinX(areaMinX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,14 +259,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinY() const {
|
||||
return areaMinY;
|
||||
return defaultVariant.GetAreaMinY();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the top bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinY(int areaMinY_) {
|
||||
areaMinY = areaMinY_;
|
||||
void SetAreaMinY(int areaMinY) {
|
||||
defaultVariant.SetAreaMinY(areaMinY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,14 +277,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinZ() const {
|
||||
return areaMinZ;
|
||||
return defaultVariant.GetAreaMinZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the min Z bound of the custom object.
|
||||
*/
|
||||
void SetAreaMinZ(int areaMinZ_) {
|
||||
areaMinZ = areaMinZ_;
|
||||
void SetAreaMinZ(int areaMinZ) {
|
||||
defaultVariant.SetAreaMinZ(areaMinZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,14 +295,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxX() const {
|
||||
return areaMaxX;
|
||||
return defaultVariant.GetAreaMaxX();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the right bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxX(int areaMaxX_) {
|
||||
areaMaxX = areaMaxX_;
|
||||
void SetAreaMaxX(int areaMaxX) {
|
||||
defaultVariant.SetAreaMaxX(areaMaxX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,14 +313,14 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxY() const {
|
||||
return areaMaxY;
|
||||
return defaultVariant.GetAreaMaxY();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxY(int areaMaxY_) {
|
||||
areaMaxY = areaMaxY_;
|
||||
void SetAreaMaxY(int areaMaxY) {
|
||||
defaultVariant.SetAreaMaxY(areaMaxY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,16 +331,22 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
* \see EventsBasedObject::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxZ() const {
|
||||
return areaMaxZ;
|
||||
return defaultVariant.GetAreaMaxZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the custom object.
|
||||
*/
|
||||
void SetAreaMaxZ(int areaMaxZ_) {
|
||||
areaMaxZ = areaMaxZ_;
|
||||
void SetAreaMaxZ(int areaMaxZ) {
|
||||
defaultVariant.SetAreaMaxZ(areaMaxZ);
|
||||
}
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @brief Serialize the events-based object for an extension in an external file.
|
||||
* Variants are not serialized.
|
||||
*/
|
||||
void SerializeToExternal(SerializerElement& element) const;
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
@@ -332,15 +360,8 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
|
||||
bool isTextContainer;
|
||||
bool isInnerAreaFollowingParentSize;
|
||||
bool isUsingLegacyInstancesRenderer;
|
||||
gd::InitialInstancesContainer initialInstances;
|
||||
gd::LayersContainer layers;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
double areaMinX;
|
||||
double areaMinY;
|
||||
double areaMinZ;
|
||||
double areaMaxX;
|
||||
double areaMaxY;
|
||||
double areaMaxZ;
|
||||
gd::EventsBasedObjectVariant defaultVariant;
|
||||
gd::EventsBasedObjectVariantsContainer variants;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
71
Core/GDCore/Project/EventsBasedObjectVariant.cpp
Normal file
71
Core/GDCore/Project/EventsBasedObjectVariant.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
EventsBasedObjectVariant::EventsBasedObjectVariant()
|
||||
: areaMinX(0), areaMinY(0), areaMinZ(0), areaMaxX(64), areaMaxY(64),
|
||||
areaMaxZ(64), objectsContainer(gd::ObjectsContainer::SourceType::Object) {
|
||||
}
|
||||
|
||||
EventsBasedObjectVariant::~EventsBasedObjectVariant() {}
|
||||
|
||||
void EventsBasedObjectVariant::SerializeTo(SerializerElement &element) const {
|
||||
element.SetAttribute("name", name);
|
||||
if (!GetAssetStoreAssetId().empty() && !GetAssetStoreOriginalName().empty()) {
|
||||
element.SetAttribute("assetStoreAssetId", GetAssetStoreAssetId());
|
||||
element.SetAttribute("assetStoreOriginalName", GetAssetStoreOriginalName());
|
||||
}
|
||||
element.SetIntAttribute("areaMinX", areaMinX);
|
||||
element.SetIntAttribute("areaMinY", areaMinY);
|
||||
element.SetIntAttribute("areaMinZ", areaMinZ);
|
||||
element.SetIntAttribute("areaMaxX", areaMaxX);
|
||||
element.SetIntAttribute("areaMaxY", areaMaxY);
|
||||
element.SetIntAttribute("areaMaxZ", areaMaxZ);
|
||||
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(
|
||||
element.AddChild("objectsFolderStructure"));
|
||||
objectsContainer.GetObjectGroups().SerializeTo(
|
||||
element.AddChild("objectsGroups"));
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
}
|
||||
|
||||
void EventsBasedObjectVariant::UnserializeFrom(
|
||||
gd::Project &project, const SerializerElement &element) {
|
||||
name = element.GetStringAttribute("name");
|
||||
assetStoreAssetId = element.GetStringAttribute("assetStoreAssetId");
|
||||
assetStoreOriginalName = element.GetStringAttribute("assetStoreOriginalName");
|
||||
areaMinX = element.GetIntAttribute("areaMinX", 0);
|
||||
areaMinY = element.GetIntAttribute("areaMinY", 0);
|
||||
areaMinZ = element.GetIntAttribute("areaMinZ", 0);
|
||||
areaMaxX = element.GetIntAttribute("areaMaxX", 64);
|
||||
areaMaxY = element.GetIntAttribute("areaMaxY", 64);
|
||||
areaMaxZ = element.GetIntAttribute("areaMaxZ", 64);
|
||||
|
||||
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
objectsContainer.UnserializeFoldersFrom(
|
||||
project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
objectsContainer.AddMissingObjectsInRootFolder();
|
||||
objectsContainer.GetObjectGroups().UnserializeFrom(
|
||||
element.GetChild("objectsGroups"));
|
||||
|
||||
if (element.HasChild("layers")) {
|
||||
layers.UnserializeLayersFrom(element.GetChild("layers"));
|
||||
} else {
|
||||
layers.Reset();
|
||||
}
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
229
Core/GDCore/Project/EventsBasedObjectVariant.h
Normal file
229
Core/GDCore/Project/EventsBasedObjectVariant.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2025 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include <vector>
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
/**
|
||||
* \brief Represents a variation of style of an events-based object.
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API EventsBasedObjectVariant {
|
||||
public:
|
||||
EventsBasedObjectVariant();
|
||||
virtual ~EventsBasedObjectVariant();
|
||||
|
||||
/**
|
||||
* \brief Return a pointer to a new EventsBasedObjectVariant constructed from
|
||||
* this one.
|
||||
*/
|
||||
EventsBasedObjectVariant *Clone() const {
|
||||
return new EventsBasedObjectVariant(*this);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the name of the variant.
|
||||
*/
|
||||
const gd::String &GetName() const { return name; };
|
||||
|
||||
/**
|
||||
* \brief Set the name of the variant.
|
||||
*/
|
||||
EventsBasedObjectVariant &SetName(const gd::String &name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \name Layers
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the layers of the variant.
|
||||
*/
|
||||
const gd::LayersContainer &GetLayers() const { return layers; }
|
||||
|
||||
/**
|
||||
* \brief Get the layers of the variant.
|
||||
*/
|
||||
gd::LayersContainer &GetLayers() { return layers; }
|
||||
///@}
|
||||
|
||||
/** \name Child objects
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the objects of the variant.
|
||||
*/
|
||||
gd::ObjectsContainer &GetObjects() { return objectsContainer; }
|
||||
|
||||
/**
|
||||
* \brief Get the objects of the variant.
|
||||
*/
|
||||
const gd::ObjectsContainer &GetObjects() const { return objectsContainer; }
|
||||
///@}
|
||||
|
||||
/** \name Instances
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Get the instances of the variant.
|
||||
*/
|
||||
gd::InitialInstancesContainer &GetInitialInstances() {
|
||||
return initialInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the instances of the variant.
|
||||
*/
|
||||
const gd::InitialInstancesContainer &GetInitialInstances() const {
|
||||
return initialInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the left bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinX() const { return areaMinX; }
|
||||
|
||||
/**
|
||||
* \brief Set the left bound of the variant.
|
||||
*/
|
||||
void SetAreaMinX(int areaMinX_) { areaMinX = areaMinX_; }
|
||||
|
||||
/**
|
||||
* \brief Get the top bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinY() const { return areaMinY; }
|
||||
|
||||
/**
|
||||
* \brief Set the top bound of the variant.
|
||||
*/
|
||||
void SetAreaMinY(int areaMinY_) { areaMinY = areaMinY_; }
|
||||
|
||||
/**
|
||||
* \brief Get the min Z bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMinZ() const { return areaMinZ; }
|
||||
|
||||
/**
|
||||
* \brief Set the min Z bound of the variant.
|
||||
*/
|
||||
void SetAreaMinZ(int areaMinZ_) { areaMinZ = areaMinZ_; }
|
||||
|
||||
/**
|
||||
* \brief Get the right bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxX() const { return areaMaxX; }
|
||||
|
||||
/**
|
||||
* \brief Set the right bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxX(int areaMaxX_) { areaMaxX = areaMaxX_; }
|
||||
|
||||
/**
|
||||
* \brief Get the bottom bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxY() const { return areaMaxY; }
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxY(int areaMaxY_) { areaMaxY = areaMaxY_; }
|
||||
|
||||
/**
|
||||
* \brief Get the max Z bound of the variant.
|
||||
*
|
||||
* This is used only if there is any initial instances.
|
||||
*
|
||||
* \see EventsBasedObjectVariant::GetInitialInstances
|
||||
*/
|
||||
int GetAreaMaxZ() const { return areaMaxZ; }
|
||||
|
||||
/**
|
||||
* \brief Set the bottom bound of the variant.
|
||||
*/
|
||||
void SetAreaMaxZ(int areaMaxZ_) { areaMaxZ = areaMaxZ_; }
|
||||
///@}
|
||||
|
||||
/** \brief Change the object asset store id of this variant.
|
||||
*/
|
||||
void SetAssetStoreAssetId(const gd::String &assetStoreId_) {
|
||||
assetStoreAssetId = assetStoreId_;
|
||||
};
|
||||
|
||||
/** \brief Return the object asset store id of this variant.
|
||||
*/
|
||||
const gd::String &GetAssetStoreAssetId() const { return assetStoreAssetId; };
|
||||
|
||||
/** \brief Change the original name of the variant in the asset.
|
||||
*/
|
||||
void SetAssetStoreOriginalName(const gd::String &assetStoreOriginalName_) {
|
||||
assetStoreOriginalName = assetStoreOriginalName_;
|
||||
};
|
||||
|
||||
/** \brief Return the original name of the variant in the asset.
|
||||
*/
|
||||
const gd::String &GetAssetStoreOriginalName() const {
|
||||
return assetStoreOriginalName;
|
||||
};
|
||||
|
||||
void SerializeTo(SerializerElement &element) const;
|
||||
|
||||
void UnserializeFrom(gd::Project &project, const SerializerElement &element);
|
||||
|
||||
private:
|
||||
gd::String name;
|
||||
gd::InitialInstancesContainer initialInstances;
|
||||
gd::LayersContainer layers;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
double areaMinX;
|
||||
double areaMinY;
|
||||
double areaMinZ;
|
||||
double areaMaxX;
|
||||
double areaMaxY;
|
||||
double areaMaxZ;
|
||||
/**
|
||||
* The ID of the asset if the object comes from the store.
|
||||
*/
|
||||
gd::String assetStoreAssetId;
|
||||
/**
|
||||
* The original name of the variant in the asset if the object comes from the
|
||||
* store.
|
||||
*/
|
||||
gd::String assetStoreOriginalName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
160
Core/GDCore/Project/EventsBasedObjectVariantsContainer.h
Normal file
160
Core/GDCore/Project/EventsBasedObjectVariantsContainer.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Used as a base class for classes that will own events-backed
|
||||
* variants.
|
||||
*
|
||||
* \see gd::EventsBasedObjectVariantContainer
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API EventsBasedObjectVariantsContainer
|
||||
: private SerializableWithNameList<gd::EventsBasedObjectVariant> {
|
||||
public:
|
||||
EventsBasedObjectVariantsContainer() {}
|
||||
|
||||
EventsBasedObjectVariantsContainer(const EventsBasedObjectVariantsContainer &other) {
|
||||
Init(other);
|
||||
}
|
||||
|
||||
EventsBasedObjectVariantsContainer &operator=(const EventsBasedObjectVariantsContainer &other) {
|
||||
if (this != &other) {
|
||||
Init(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \name Events Functions management
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Check if the variant with the specified name exists.
|
||||
*/
|
||||
bool HasVariantNamed(const gd::String& name) const {
|
||||
return Has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetVariant(const gd::String& name) {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant with the specified name.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetVariant(const gd::String& name) const {
|
||||
return Get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
gd::EventsBasedObjectVariant& GetVariant(std::size_t index) {
|
||||
return Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the variant at the specified index in the list.
|
||||
*
|
||||
* \warning Trying to access to a not existing variant will result in
|
||||
* undefined behavior.
|
||||
*/
|
||||
const gd::EventsBasedObjectVariant& GetVariant(std::size_t index) const {
|
||||
return Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the number of variants.
|
||||
*/
|
||||
std::size_t GetVariantsCount() const { return GetCount(); }
|
||||
|
||||
gd::EventsBasedObjectVariant& InsertNewVariant(const gd::String& name,
|
||||
std::size_t position) {
|
||||
return InsertNew(name, position);
|
||||
}
|
||||
gd::EventsBasedObjectVariant& InsertVariant(const gd::EventsBasedObjectVariant& object,
|
||||
std::size_t position) {
|
||||
return Insert(object, position);
|
||||
}
|
||||
void RemoveVariant(const gd::String& name) { return Remove(name); }
|
||||
void ClearVariants() { return Clear(); }
|
||||
void MoveVariant(std::size_t oldIndex, std::size_t newIndex) {
|
||||
return Move(oldIndex, newIndex);
|
||||
};
|
||||
std::size_t GetVariantPosition(const gd::EventsBasedObjectVariant& eventsFunction) {
|
||||
return GetPosition(eventsFunction);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the variants.
|
||||
*/
|
||||
const std::vector<std::unique_ptr<gd::EventsBasedObjectVariant>>& GetInternalVector()
|
||||
const {
|
||||
return elements;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the variants.
|
||||
*/
|
||||
std::vector<std::unique_ptr<gd::EventsBasedObjectVariant>>& GetInternalVector() {
|
||||
return elements;
|
||||
};
|
||||
///@}
|
||||
|
||||
/** \name Serialization
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize events variants.
|
||||
*/
|
||||
void SerializeVariantsTo(SerializerElement& element) const {
|
||||
return SerializeElementsTo("variant", element);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Unserialize the events variants.
|
||||
*/
|
||||
void UnserializeVariantsFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
return UnserializeElementsFrom("variant", project, element);
|
||||
};
|
||||
///@}
|
||||
protected:
|
||||
/**
|
||||
* Initialize object using another object. Used by copy-ctor and assign-op.
|
||||
* Don't forget to update me if members were changed!
|
||||
*/
|
||||
void Init(const gd::EventsBasedObjectVariantsContainer& other) {
|
||||
return SerializableWithNameList<gd::EventsBasedObjectVariant>::Init(other);
|
||||
};
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -55,7 +55,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
|
||||
sceneVariables = other.GetSceneVariables();
|
||||
}
|
||||
|
||||
void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
void EventsFunctionsExtension::SerializeTo(SerializerElement& element, bool isExternal) const {
|
||||
element.SetAttribute("version", version);
|
||||
element.SetAttribute("extensionNamespace", extensionNamespace);
|
||||
element.SetAttribute("shortDescription", shortDescription);
|
||||
@@ -83,6 +83,9 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("iconUrl", iconUrl);
|
||||
element.SetAttribute("helpPath", helpPath);
|
||||
element.SetAttribute("gdevelopVersion", gdevelopVersion);
|
||||
if (changelog.GetChangesCount() > 0) {
|
||||
changelog.SerializeTo(element.AddChild("changelog"));
|
||||
}
|
||||
auto& dependenciesElement = element.AddChild("dependencies");
|
||||
dependenciesElement.ConsiderAsArray();
|
||||
for (auto& dependency : dependencies)
|
||||
@@ -102,8 +105,18 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("eventsFunctions"));
|
||||
eventsBasedBehaviors.SerializeElementsTo(
|
||||
"eventsBasedBehavior", element.AddChild("eventsBasedBehaviors"));
|
||||
eventsBasedObjects.SerializeElementsTo(
|
||||
"eventsBasedObject", element.AddChild("eventsBasedObjects"));
|
||||
if (isExternal) {
|
||||
auto &eventsBasedObjectElement = element.AddChild("eventsBasedObjects");
|
||||
eventsBasedObjectElement.ConsiderAsArrayOf("eventsBasedObject");
|
||||
for (const auto &eventsBasedObject :
|
||||
eventsBasedObjects.GetInternalVector()) {
|
||||
eventsBasedObject->SerializeToExternal(
|
||||
eventsBasedObjectElement.AddChild("eventsBasedObject"));
|
||||
}
|
||||
} else {
|
||||
eventsBasedObjects.SerializeElementsTo(
|
||||
"eventsBasedObject", element.AddChild("eventsBasedObjects"));
|
||||
}
|
||||
}
|
||||
|
||||
void EventsFunctionsExtension::UnserializeFrom(
|
||||
@@ -129,6 +142,9 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
|
||||
iconUrl = element.GetStringAttribute("iconUrl");
|
||||
helpPath = element.GetStringAttribute("helpPath");
|
||||
gdevelopVersion = element.GetStringAttribute("gdevelopVersion");
|
||||
if (element.HasChild("changelog")) {
|
||||
changelog.UnserializeFrom(element.GetChild("changelog"));
|
||||
}
|
||||
|
||||
if (element.HasChild("origin")) {
|
||||
gd::String originName =
|
||||
|
@@ -12,9 +12,11 @@
|
||||
#include "GDCore/Project/EventsBasedBehavior.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtensionChangelog.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
class Project;
|
||||
@@ -286,7 +288,14 @@ class GD_CORE_API EventsFunctionsExtension {
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtension to the specified element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement& element) const;
|
||||
void SerializeTo(gd::SerializerElement& element, bool isExternal = false) const;
|
||||
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtension to the specified element
|
||||
*/
|
||||
void SerializeToExternal(gd::SerializerElement& element) const {
|
||||
SerializeTo(element, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load the EventsFunctionsExtension from the specified element.
|
||||
@@ -399,6 +408,7 @@ class GD_CORE_API EventsFunctionsExtension {
|
||||
gd::String helpPath; ///< The relative path to the help for this extension in
|
||||
///< the documentation (or an absolute URL).
|
||||
gd::String gdevelopVersion;
|
||||
gd::EventsFunctionsExtensionChangelog changelog;
|
||||
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
|
||||
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
|
||||
std::vector<gd::DependencyMetadata> dependencies;
|
||||
|
105
Core/GDCore/Project/EventsFunctionsExtensionChangelog.h
Normal file
105
Core/GDCore/Project/EventsFunctionsExtensionChangelog.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* @brief The change of a specific extension version (only the breaking
|
||||
* changes).
|
||||
*/
|
||||
class GD_CORE_API EventsFunctionsExtensionVersionChange {
|
||||
public:
|
||||
EventsFunctionsExtensionVersionChange(){};
|
||||
virtual ~EventsFunctionsExtensionVersionChange(){};
|
||||
|
||||
const gd::String &GetVersion() const { return version; };
|
||||
gd::EventsFunctionsExtensionVersionChange &
|
||||
SetVersion(const gd::String &version_) {
|
||||
version = version_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String &GetBreakingChangesDescription() const { return version; };
|
||||
gd::EventsFunctionsExtensionVersionChange &
|
||||
GetBreakingChangesDescription(const gd::String &breakingChangesDescription_) {
|
||||
breakingChangesDescription = breakingChangesDescription_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtensionVersionChange to the specified
|
||||
* element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement &element) const {
|
||||
element.SetAttribute("version", version);
|
||||
element.AddChild("breaking")
|
||||
.SetMultilineStringValue(breakingChangesDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load the EventsFunctionsExtensionVersionChange from the specified
|
||||
* element.
|
||||
*/
|
||||
void UnserializeFrom(const gd::SerializerElement &element) {
|
||||
version = element.GetStringAttribute("version");
|
||||
breakingChangesDescription =
|
||||
element.GetChild("breaking").GetMultilineStringValue();
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String version;
|
||||
gd::String breakingChangesDescription;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The changelog of an extension (only the breaking changes).
|
||||
*/
|
||||
class GD_CORE_API EventsFunctionsExtensionChangelog {
|
||||
public:
|
||||
EventsFunctionsExtensionChangelog(){};
|
||||
virtual ~EventsFunctionsExtensionChangelog(){};
|
||||
|
||||
/**
|
||||
* \brief Return the number of variants.
|
||||
*/
|
||||
std::size_t GetChangesCount() const { return versionChanges.size(); }
|
||||
|
||||
/**
|
||||
* \brief Serialize the EventsFunctionsExtensionChangelog to the specified
|
||||
* element
|
||||
*/
|
||||
void SerializeTo(gd::SerializerElement &element) const {
|
||||
element.ConsiderAsArray();
|
||||
for (const auto &versionChange : versionChanges) {
|
||||
versionChange.SerializeTo(element.AddChild(""));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load the EventsFunctionsExtensionChangelog from the specified
|
||||
* element.
|
||||
*/
|
||||
void UnserializeFrom(const gd::SerializerElement &element) {
|
||||
versionChanges.clear();
|
||||
element.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
|
||||
gd::EventsFunctionsExtensionVersionChange versionChange;
|
||||
versionChange.UnserializeFrom(element.GetChild(i));
|
||||
versionChanges.push_back(versionChange);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<gd::EventsFunctionsExtensionVersionChange> versionChanges;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -365,6 +365,8 @@ class GD_CORE_API InitialInstance {
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
///@}
|
||||
|
||||
private:
|
||||
|
@@ -42,8 +42,13 @@ void InitialInstancesContainer::IterateOverInstances(
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::IterateOverInstances(
|
||||
const std::function< void(gd::InitialInstance &) >& func) {
|
||||
for (auto& instance : initialInstances) func(instance);
|
||||
const std::function< bool(gd::InitialInstance &) >& func) {
|
||||
for (auto& instance : initialInstances) {
|
||||
bool shouldStop = func(instance);
|
||||
if (shouldStop) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::IterateOverInstancesWithZOrdering(
|
||||
|
@@ -92,7 +92,7 @@ class GD_CORE_API InitialInstancesContainer {
|
||||
* \see InitialInstanceFunctor
|
||||
*/
|
||||
void IterateOverInstances(
|
||||
const std::function< void(gd::InitialInstance &) >& func);
|
||||
const std::function< bool(gd::InitialInstance &) >& func);
|
||||
|
||||
/**
|
||||
* Get the instances on the specified layer,
|
||||
|
@@ -36,7 +36,7 @@ namespace gd {
|
||||
|
||||
gd::BehaviorsSharedData Layout::badBehaviorSharedData("", "");
|
||||
|
||||
Layout::Layout(const Layout &other)
|
||||
Layout::Layout(const Layout& other)
|
||||
: objectsContainer(gd::ObjectsContainer::SourceType::Scene) {
|
||||
Init(other);
|
||||
}
|
||||
@@ -54,6 +54,8 @@ Layout::Layout()
|
||||
backgroundColorG(209),
|
||||
backgroundColorB(209),
|
||||
stopSoundsOnStartup(true),
|
||||
resourcesPreloading("inherit"),
|
||||
resourcesUnloading("inherit"),
|
||||
standardSortMethod(true),
|
||||
disableInputWhenNotFocused(true),
|
||||
variables(gd::VariablesContainer::SourceType::Scene),
|
||||
@@ -244,6 +246,10 @@ void Layout::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("title", GetWindowDefaultTitle());
|
||||
element.SetAttribute("standardSortMethod", standardSortMethod);
|
||||
element.SetAttribute("stopSoundsOnStartup", stopSoundsOnStartup);
|
||||
if (resourcesPreloading != "inherit")
|
||||
element.SetAttribute("resourcesPreloading", resourcesPreloading);
|
||||
if (resourcesUnloading != "inherit")
|
||||
element.SetAttribute("resourcesUnloading", resourcesUnloading);
|
||||
element.SetAttribute("disableInputWhenNotFocused",
|
||||
disableInputWhenNotFocused);
|
||||
|
||||
@@ -304,6 +310,10 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
element.GetStringAttribute("title", "(No title)", "titre"));
|
||||
standardSortMethod = element.GetBoolAttribute("standardSortMethod");
|
||||
stopSoundsOnStartup = element.GetBoolAttribute("stopSoundsOnStartup");
|
||||
resourcesPreloading =
|
||||
element.GetStringAttribute("resourcesPreloading", "inherit");
|
||||
resourcesUnloading =
|
||||
element.GetStringAttribute("resourcesUnloading", "inherit");
|
||||
disableInputWhenNotFocused =
|
||||
element.GetBoolAttribute("disableInputWhenNotFocused");
|
||||
|
||||
@@ -391,6 +401,8 @@ void Layout::Init(const Layout& other) {
|
||||
standardSortMethod = other.standardSortMethod;
|
||||
title = other.title;
|
||||
stopSoundsOnStartup = other.stopSoundsOnStartup;
|
||||
resourcesPreloading = other.resourcesPreloading;
|
||||
resourcesUnloading = other.resourcesUnloading;
|
||||
disableInputWhenNotFocused = other.disableInputWhenNotFocused;
|
||||
initialInstances = other.initialInstances;
|
||||
layers = other.layers;
|
||||
|
@@ -349,6 +349,36 @@ class GD_CORE_API Layout {
|
||||
* launched
|
||||
*/
|
||||
bool StopSoundsOnStartup() const { return stopSoundsOnStartup; }
|
||||
|
||||
/**
|
||||
* Set when the scene must preload its resources: `at-startup`, `never` or
|
||||
* `inherit` (default).
|
||||
*/
|
||||
void SetResourcesPreloading(gd::String resourcesPreloading_) {
|
||||
resourcesPreloading = resourcesPreloading_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get when the scene must preload its resources: `at-startup`, `never` or
|
||||
* `inherit` (default).
|
||||
*/
|
||||
const gd::String& GetResourcesPreloading() const {
|
||||
return resourcesPreloading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the scene must unload its resources: `at-scene-exit`, `never` or
|
||||
* `inherit` (default).
|
||||
*/
|
||||
void SetResourcesUnloading(gd::String resourcesUnloading_) {
|
||||
resourcesUnloading = resourcesUnloading_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get when the scene must unload its resources: `at-scene-exit`, `never` or
|
||||
* `inherit` (default).
|
||||
*/
|
||||
const gd::String& GetResourcesUnloading() const { return resourcesUnloading; }
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
@@ -381,6 +411,10 @@ class GD_CORE_API Layout {
|
||||
behaviorsSharedData; ///< Initial shared datas of behaviors
|
||||
bool stopSoundsOnStartup = true; ///< True to make the scene stop all sounds at
|
||||
///< startup.
|
||||
gd::String
|
||||
resourcesPreloading; ///< `at-startup`, `never` or `inherit` (default).
|
||||
gd::String
|
||||
resourcesUnloading; ///< `at-scene-exit`, `never` or `inherit` (default).
|
||||
bool standardSortMethod = true; ///< True to sort objects using standard sort.
|
||||
bool disableInputWhenNotFocused = true; /// If set to true, the input must be
|
||||
/// disabled when the window do not have the
|
||||
|
@@ -41,6 +41,11 @@ Object::Object(const gd::String& name_,
|
||||
}
|
||||
|
||||
void Object::Init(const gd::Object& object) {
|
||||
CopyWithoutConfiguration(object);
|
||||
configuration = object.configuration->Clone();
|
||||
}
|
||||
|
||||
void Object::CopyWithoutConfiguration(const gd::Object& object) {
|
||||
persistentUuid = object.persistentUuid;
|
||||
name = object.name;
|
||||
assetStoreId = object.assetStoreId;
|
||||
@@ -51,8 +56,6 @@ void Object::Init(const gd::Object& object) {
|
||||
for (auto& it : object.behaviors) {
|
||||
behaviors[it.first] = gd::make_unique<gd::Behavior>(*it.second);
|
||||
}
|
||||
|
||||
configuration = object.configuration->Clone();
|
||||
}
|
||||
|
||||
gd::ObjectConfiguration& Object::GetConfiguration() { return *configuration; }
|
||||
|
@@ -82,6 +82,8 @@ class GD_CORE_API Object {
|
||||
return gd::make_unique<gd::Object>(*this);
|
||||
}
|
||||
|
||||
void CopyWithoutConfiguration(const gd::Object& object);
|
||||
|
||||
/**
|
||||
* \brief Return the object configuration.
|
||||
*/
|
||||
|
@@ -81,6 +81,11 @@ class GD_CORE_API ObjectsContainersList {
|
||||
/**
|
||||
* \brief Return the container of the variables for the specified object or
|
||||
* group of objects.
|
||||
*
|
||||
* \warning In most cases, prefer to use other methods to access variables or use
|
||||
* ObjectVariableHelper::MergeVariableContainers if you know you're dealing with a group.
|
||||
* This is because the variables container of an object group does not exist and the one from
|
||||
* first object of the group will be returned.
|
||||
*/
|
||||
const gd::VariablesContainer* GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const;
|
||||
|
@@ -74,7 +74,9 @@ Project::Project()
|
||||
gdMinorVersion(gd::VersionWrapper::Minor()),
|
||||
gdBuildVersion(gd::VersionWrapper::Build()),
|
||||
variables(gd::VariablesContainer::SourceType::Global),
|
||||
objectsContainer(gd::ObjectsContainer::SourceType::Global) {}
|
||||
objectsContainer(gd::ObjectsContainer::SourceType::Global),
|
||||
sceneResourcesPreloading("at-startup"),
|
||||
sceneResourcesUnloading("never") {}
|
||||
|
||||
Project::~Project() {}
|
||||
|
||||
@@ -920,6 +922,7 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
"eventsFunctionsExtension");
|
||||
|
||||
std::map<gd::String, size_t> extensionNameToElementIndex;
|
||||
std::map<gd::String, gd::SerializerElement> objectTypeToVariantsElement;
|
||||
|
||||
// First, only unserialize behaviors and objects names.
|
||||
// As event based objects can contains custom behaviors and custom objects,
|
||||
@@ -938,6 +941,16 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
? GetEventsFunctionsExtension(name)
|
||||
: InsertNewEventsFunctionsExtension(
|
||||
name, GetEventsFunctionsExtensionsCount());
|
||||
|
||||
// Backup the events-based object variants
|
||||
for (auto &eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
gd::SerializerElement variantsElement;
|
||||
eventsBasedObject->GetVariants().SerializeVariantsTo(variantsElement);
|
||||
objectTypeToVariantsElement[gd::PlatformExtension::GetObjectFullType(
|
||||
name, eventsBasedObject->GetName())] = variantsElement;
|
||||
}
|
||||
|
||||
eventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
}
|
||||
@@ -966,6 +979,15 @@ void Project::UnserializeAndInsertExtensionsFrom(
|
||||
partiallyLoadedExtension
|
||||
->UnserializeExtensionImplementationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
|
||||
for (auto &pair : objectTypeToVariantsElement) {
|
||||
auto &objectType = pair.first;
|
||||
auto &variantsElement = pair.second;
|
||||
|
||||
auto &eventsBasedObject = GetEventsBasedObject(objectType);
|
||||
eventsBasedObject.GetVariants().UnserializeVariantsFrom(*this,
|
||||
variantsElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1146,6 +1168,13 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
else
|
||||
std::cout << "ERROR: The project current platform is NULL.";
|
||||
|
||||
if (sceneResourcesPreloading != "at-startup") {
|
||||
propElement.SetAttribute("sceneResourcesPreloading", sceneResourcesPreloading);
|
||||
}
|
||||
if (sceneResourcesUnloading != "never") {
|
||||
propElement.SetAttribute("sceneResourcesUnloading", sceneResourcesUnloading);
|
||||
}
|
||||
|
||||
resourcesManager.SerializeTo(element.AddChild("resources"));
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
@@ -1287,6 +1316,9 @@ void Project::Init(const gd::Project& game) {
|
||||
variables = game.GetVariables();
|
||||
|
||||
projectFile = game.GetProjectFile();
|
||||
|
||||
sceneResourcesPreloading = game.sceneResourcesPreloading;
|
||||
sceneResourcesUnloading = game.sceneResourcesUnloading;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -964,6 +964,37 @@ class GD_CORE_API Project {
|
||||
*/
|
||||
ResourcesManager& GetResourcesManager() { return resourcesManager; }
|
||||
|
||||
/**
|
||||
* Set when the scenes must preload their resources: `at-startup`, `never`
|
||||
* (default).
|
||||
*/
|
||||
void SetSceneResourcesPreloading(gd::String sceneResourcesPreloading_) {
|
||||
sceneResourcesPreloading = sceneResourcesPreloading_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get when the scenes must preload their resources: `at-startup`, `never`
|
||||
* (default).
|
||||
*/
|
||||
const gd::String& GetSceneResourcesPreloading() const {
|
||||
return sceneResourcesPreloading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the scenes must unload their resources: `at-scene-exit`, `never`
|
||||
* (default).
|
||||
*/
|
||||
void SetSceneResourcesUnloading(gd::String sceneResourcesUnloading_) {
|
||||
sceneResourcesUnloading = sceneResourcesUnloading_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get when the scenes must unload their resources: `at-scene-exit`, `never`
|
||||
* (default).
|
||||
*/
|
||||
const gd::String& GetSceneResourcesUnloading() const {
|
||||
return sceneResourcesUnloading;
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Variable management
|
||||
@@ -1121,6 +1152,10 @@ class GD_CORE_API Project {
|
||||
ExtensionProperties
|
||||
extensionProperties; ///< The properties of the extensions.
|
||||
gd::WholeProjectDiagnosticReport wholeProjectDiagnosticReport;
|
||||
gd::String sceneResourcesPreloading; ///< `at-startup` or `never`
|
||||
///< (default: `at-startup`).
|
||||
gd::String sceneResourcesUnloading; ///< `at-scene-exit` or `never`
|
||||
///< (default: `never`).
|
||||
mutable unsigned int gdMajorVersion =
|
||||
0; ///< The GD major version used the last
|
||||
///< time the project was saved.
|
||||
|
@@ -21,14 +21,19 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("unit").SetStringValue(measurementUnit.GetName());
|
||||
}
|
||||
element.AddChild("label").SetStringValue(label);
|
||||
element.AddChild("description").SetStringValue(description);
|
||||
element.AddChild("group").SetStringValue(group);
|
||||
SerializerElement& extraInformationElement =
|
||||
element.AddChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (const gd::String& information : extraInformation) {
|
||||
extraInformationElement.AddChild("").SetStringValue(information);
|
||||
if (!description.empty())
|
||||
element.AddChild("description").SetStringValue(description);
|
||||
if (!group.empty()) element.AddChild("group").SetStringValue(group);
|
||||
|
||||
if (!extraInformation.empty()) {
|
||||
SerializerElement& extraInformationElement =
|
||||
element.AddChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (const gd::String& information : extraInformation) {
|
||||
extraInformationElement.AddChild("").SetStringValue(information);
|
||||
}
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
element.AddChild("hidden").SetBoolValue(hidden);
|
||||
}
|
||||
@@ -59,16 +64,21 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
: gd::MeasurementUnit::GetUndefined();
|
||||
}
|
||||
label = element.GetChild("label").GetStringValue();
|
||||
description = element.GetChild("description").GetStringValue();
|
||||
group = element.GetChild("group").GetStringValue();
|
||||
description = element.HasChild("description")
|
||||
? element.GetChild("description").GetStringValue()
|
||||
: "";
|
||||
group = element.HasChild("group") ? element.GetChild("group").GetStringValue()
|
||||
: "";
|
||||
|
||||
extraInformation.clear();
|
||||
const SerializerElement& extraInformationElement =
|
||||
element.GetChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < extraInformationElement.GetChildrenCount(); ++i)
|
||||
extraInformation.push_back(
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
if (element.HasChild("extraInformation")) {
|
||||
const SerializerElement& extraInformationElement =
|
||||
element.GetChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < extraInformationElement.GetChildrenCount(); ++i)
|
||||
extraInformation.push_back(
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
|
||||
hidden = element.HasChild("hidden")
|
||||
? element.GetChild("hidden").GetBoolValue()
|
||||
|
@@ -7,9 +7,9 @@
|
||||
#define GDCORE_PROPERTYDESCRIPTOR
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/MeasurementUnit.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -17,6 +17,19 @@ class SerializerElement;
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API PropertyDescriptorChoice {
|
||||
public:
|
||||
PropertyDescriptorChoice(const gd::String& value, const gd::String& label)
|
||||
: value(value), label(label) {}
|
||||
|
||||
const gd::String& GetValue() const { return value; }
|
||||
const gd::String& GetLabel() const { return label; }
|
||||
|
||||
private:
|
||||
gd::String value;
|
||||
gd::String label;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Used to describe a property shown in a property grid.
|
||||
* \see gd::Object
|
||||
@@ -31,8 +44,12 @@ class GD_CORE_API PropertyDescriptor {
|
||||
* \param propertyValue The value of the property.
|
||||
*/
|
||||
PropertyDescriptor(gd::String propertyValue)
|
||||
: currentValue(propertyValue), type("string"), label(""), hidden(false),
|
||||
deprecated(false), advanced(false),
|
||||
: currentValue(propertyValue),
|
||||
type("string"),
|
||||
label(""),
|
||||
hidden(false),
|
||||
deprecated(false),
|
||||
advanced(false),
|
||||
hasImpactOnOtherProperties(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {}
|
||||
@@ -41,10 +58,13 @@ class GD_CORE_API PropertyDescriptor {
|
||||
* \brief Empty constructor creating an empty property to be displayed.
|
||||
*/
|
||||
PropertyDescriptor()
|
||||
: hidden(false), deprecated(false), advanced(false),
|
||||
: hidden(false),
|
||||
deprecated(false),
|
||||
advanced(false),
|
||||
hasImpactOnOtherProperties(false),
|
||||
measurementUnit(gd::MeasurementUnit::GetUndefined()),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
@@ -88,13 +108,20 @@ class GD_CORE_API PropertyDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Change the group where this property is displayed to the user, if any.
|
||||
* \brief Change the group where this property is displayed to the user, if
|
||||
* any.
|
||||
*/
|
||||
PropertyDescriptor& SetGroup(gd::String group_) {
|
||||
group = group_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyDescriptor& AddChoice(const gd::String& value,
|
||||
const gd::String& label) {
|
||||
choices.push_back(PropertyDescriptorChoice(value, label));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set and replace the additional information for the property.
|
||||
*/
|
||||
@@ -118,7 +145,8 @@ class GD_CORE_API PropertyDescriptor {
|
||||
/**
|
||||
* \brief Change the unit of measurement of the property value.
|
||||
*/
|
||||
PropertyDescriptor& SetMeasurementUnit(const gd::MeasurementUnit &measurementUnit_) {
|
||||
PropertyDescriptor& SetMeasurementUnit(
|
||||
const gd::MeasurementUnit& measurementUnit_) {
|
||||
measurementUnit = measurementUnit_;
|
||||
return *this;
|
||||
}
|
||||
@@ -128,14 +156,18 @@ class GD_CORE_API PropertyDescriptor {
|
||||
const gd::String& GetLabel() const { return label; }
|
||||
const gd::String& GetDescription() const { return description; }
|
||||
const gd::String& GetGroup() const { return group; }
|
||||
const gd::MeasurementUnit& GetMeasurementUnit() const { return measurementUnit; }
|
||||
const gd::MeasurementUnit& GetMeasurementUnit() const {
|
||||
return measurementUnit;
|
||||
}
|
||||
|
||||
const std::vector<gd::String>& GetExtraInfo() const {
|
||||
return extraInformation;
|
||||
}
|
||||
|
||||
std::vector<gd::String>& GetExtraInfo() {
|
||||
return extraInformation;
|
||||
std::vector<gd::String>& GetExtraInfo() { return extraInformation; }
|
||||
|
||||
const std::vector<PropertyDescriptorChoice>& GetChoices() const {
|
||||
return choices;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,23 +210,26 @@ class GD_CORE_API PropertyDescriptor {
|
||||
bool IsAdvanced() const { return advanced; }
|
||||
|
||||
/**
|
||||
* \brief Check if the property has impact on other properties - which means a change
|
||||
* must re-render other properties.
|
||||
* \brief Check if the property has impact on other properties - which means a
|
||||
* change must re-render other properties.
|
||||
*/
|
||||
bool HasImpactOnOtherProperties() const { return hasImpactOnOtherProperties; }
|
||||
|
||||
/**
|
||||
* \brief Set if the property has impact on other properties - which means a change
|
||||
* must re-render other properties.
|
||||
* \brief Set if the property has impact on other properties - which means a
|
||||
* change must re-render other properties.
|
||||
*/
|
||||
PropertyDescriptor& SetHasImpactOnOtherProperties(bool enable) {
|
||||
hasImpactOnOtherProperties = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const { return quickCustomizationVisibility; }
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
|
||||
PropertyDescriptor& SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
PropertyDescriptor& SetQuickCustomizationVisibility(
|
||||
QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
return *this;
|
||||
}
|
||||
@@ -231,15 +266,17 @@ class GD_CORE_API PropertyDescriptor {
|
||||
gd::String label; //< The user-friendly property name
|
||||
gd::String description; //< The user-friendly property description
|
||||
gd::String group; //< The user-friendly property group
|
||||
std::vector<PropertyDescriptorChoice>
|
||||
choices; //< The optional choices for the property.
|
||||
std::vector<gd::String>
|
||||
extraInformation; ///< Can be used to store for example the available
|
||||
///< choices, if a property is a displayed as a combo
|
||||
///< box.
|
||||
extraInformation; ///< Can be used to store an additional information
|
||||
///< like an object type.
|
||||
bool hidden;
|
||||
bool deprecated;
|
||||
bool advanced;
|
||||
bool hasImpactOnOtherProperties;
|
||||
gd::MeasurementUnit measurementUnit; //< The unit of measurement of the property vale.
|
||||
gd::MeasurementUnit
|
||||
measurementUnit; //< The unit of measurement of the property vale.
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
};
|
||||
|
||||
|
522
Core/tests/EventsBasedObjectVariantHelper.cpp
Normal file
522
Core/tests/EventsBasedObjectVariantHelper.cpp
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
/**
|
||||
* @file Tests covering events of GDevelop Core.
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/EventsBasedObjectVariantHelper.h"
|
||||
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
gd::InitialInstance *
|
||||
GetFirstInstanceOf(const gd::String objectName,
|
||||
gd::InitialInstancesContainer &initialInstances) {
|
||||
gd::InitialInstance *variantInstance = nullptr;
|
||||
initialInstances.IterateOverInstances(
|
||||
[&variantInstance, &objectName](gd::InitialInstance &instance) {
|
||||
if (instance.GetObjectName() == objectName) {
|
||||
variantInstance = &instance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return variantInstance;
|
||||
}
|
||||
|
||||
gd::EventsBasedObject &SetupEventsBasedObject(gd::Project &project) {
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &object = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject", 0);
|
||||
object.GetVariables().InsertNew("MyVariable").SetValue(123);
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior");
|
||||
auto &instance =
|
||||
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
|
||||
instance.SetObjectName("MyChildObject");
|
||||
instance.GetVariables().InsertNew("MyVariable").SetValue(111);
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew(
|
||||
"MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject");
|
||||
return eventsBasedObject;
|
||||
}
|
||||
|
||||
TEST_CASE("EventsBasedObjectVariantHelper", "[common]") {
|
||||
SECTION("Can add missing objects") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject2"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject3"));
|
||||
}
|
||||
|
||||
SECTION("Can remove objects") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
variant.GetInitialInstances().InsertNewInitialInstance().SetObjectName(
|
||||
"MyChildObject2");
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject(
|
||||
"MyChildObject2") == true);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject2");
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject2") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject3") == false);
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject(
|
||||
"MyChildObject2") == false);
|
||||
}
|
||||
|
||||
SECTION("Can change object type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().RemoveObject("MyChildObject");
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::FakeObjectWithDefaultBehavior", "MyChildObject",
|
||||
0);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects().GetObject("MyChildObject").GetType() ==
|
||||
"MyExtension::FakeObjectWithDefaultBehavior");
|
||||
REQUIRE(variant.GetInitialInstances().GetInstancesCount() == 1);
|
||||
}
|
||||
|
||||
SECTION("Can add missing object groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup2",
|
||||
0);
|
||||
eventsBasedObject.GetObjects()
|
||||
.GetObjectGroups()
|
||||
.InsertNew("MyObjectGroup3", 0)
|
||||
.AddObject("MyChildObject");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup2"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup3"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup2").GetAllObjectsNames().size() ==
|
||||
0);
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup3").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
}
|
||||
|
||||
SECTION("Can remove object groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup2",
|
||||
0);
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().InsertNew("MyObjectGroup3",
|
||||
0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Remove("MyObjectGroup2");
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Remove("MyObjectGroup3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup2") == false);
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup3") == false);
|
||||
}
|
||||
|
||||
SECTION("Can add objects to groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Get("MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject2");
|
||||
objectGroup.AddObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
3);
|
||||
}
|
||||
|
||||
SECTION("Can remove objects from groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject2", 0);
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject3", 0);
|
||||
auto &objectGroup =
|
||||
eventsBasedObject.GetObjects().GetObjectGroups().Get("MyObjectGroup");
|
||||
objectGroup.AddObject("MyChildObject2");
|
||||
objectGroup.AddObject("MyChildObject3");
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
objectGroup.RemoveObject("MyChildObject2");
|
||||
objectGroup.RemoveObject("MyChildObject3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
auto &variantObjectGroups = variant.GetObjects().GetObjectGroups();
|
||||
REQUIRE(variantObjectGroups.Has("MyObjectGroup"));
|
||||
REQUIRE(
|
||||
variantObjectGroups.Get("MyObjectGroup").GetAllObjectsNames().size() ==
|
||||
1);
|
||||
}
|
||||
|
||||
SECTION("Can add missing behaviors") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior2");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior2"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior3"));
|
||||
}
|
||||
|
||||
SECTION("Can remove missing behaviors") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior2");
|
||||
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior3");
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.RemoveBehavior("MyBehavior2");
|
||||
object.RemoveBehavior("MyBehavior3");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior2") == false);
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior3") == false);
|
||||
}
|
||||
|
||||
SECTION("Can change behavior type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.RemoveBehavior("MyBehavior");
|
||||
object.AddNewBehavior(project, "MyExtension::MyOtherBehavior",
|
||||
"MyBehavior");
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.HasBehaviorNamed("MyBehavior"));
|
||||
REQUIRE(variantObject.GetBehavior("MyBehavior").GetTypeName() ==
|
||||
"MyExtension::MyOtherBehavior");
|
||||
}
|
||||
|
||||
SECTION("Can add missing variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable2").GetValue() == 456);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable3").GetValue() == 789);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable"));
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable3") == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can keep variable value") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Get("MyVariable").SetValue(456);
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Get("MyVariable").GetValue() ==
|
||||
111);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Must not propagate instance variable value changes") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable").SetValue(222);
|
||||
}
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Get("MyVariable").GetValue() ==
|
||||
111);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can move variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable2").SetValue(222);
|
||||
objectInstance->GetVariables().Get("MyVariable3").SetValue(333);
|
||||
}
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Move(2, 0);
|
||||
object.GetVariables().Get("MyVariable").SetValue(111);
|
||||
object.GetVariables().Get("MyVariable2").SetValue(222);
|
||||
object.GetVariables().Get("MyVariable3").SetValue(333);
|
||||
REQUIRE(object.GetVariables().GetNameAt(0) == "MyVariable3");
|
||||
REQUIRE(object.GetVariables().GetNameAt(1) == "MyVariable");
|
||||
REQUIRE(object.GetVariables().GetNameAt(2) == "MyVariable2");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetValue() == 123);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable2").GetValue() == 456);
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable3").GetValue() == 789);
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(0) == "MyVariable3");
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(1) == "MyVariable");
|
||||
REQUIRE(variantObject.GetVariables().GetNameAt(2) == "MyVariable2");
|
||||
}
|
||||
|
||||
SECTION("Can remove variables") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
object.GetVariables().InsertNew("MyVariable2", 1).SetValue(456);
|
||||
object.GetVariables().InsertNew("MyVariable3", 2).SetValue(789);
|
||||
{
|
||||
auto *objectInstance = GetFirstInstanceOf(
|
||||
"MyChildObject", eventsBasedObject.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
objectInstance->GetVariables().Get("MyVariable2").SetValue(222);
|
||||
objectInstance->GetVariables().Get("MyVariable3").SetValue(333);
|
||||
}
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Remove("MyVariable2");
|
||||
object.GetVariables().Remove("MyVariable3");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable"));
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(variantObject.GetVariables().Has("MyVariable3") == false);
|
||||
{
|
||||
auto *objectInstance =
|
||||
GetFirstInstanceOf("MyChildObject", variant.GetInitialInstances());
|
||||
REQUIRE(objectInstance != nullptr);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable"));
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable2") == false);
|
||||
REQUIRE(objectInstance->GetVariables().Has("MyVariable3") == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Can change variable type") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsBasedObject = SetupEventsBasedObject(project);
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
auto &object = eventsBasedObject.GetObjects().GetObject("MyChildObject");
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().Get("MyVariable").SetString("abc");
|
||||
gd::EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
|
||||
project, eventsBasedObject);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
auto &variantObject = variant.GetObjects().GetObject("MyChildObject");
|
||||
REQUIRE(variantObject.GetVariables().Get("MyVariable").GetString() ==
|
||||
"abc");
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("MyVariable") ==
|
||||
false);
|
||||
}
|
||||
}
|
@@ -33,7 +33,132 @@ using namespace gd;
|
||||
|
||||
TEST_CASE("ObjectAssetSerializer", "[common]") {
|
||||
|
||||
SECTION("Can serialize custom objects as assets") {
|
||||
SECTION("Can serialize custom objects as assets with variant") {
|
||||
gd::Platform platform;
|
||||
gd::Project project;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
eventsBasedObject.SetFullName("My events based object");
|
||||
eventsBasedObject.SetDescription("An events based object for test");
|
||||
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChild", 0);
|
||||
auto &childInstance =
|
||||
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
|
||||
childInstance.SetObjectName("MyChild");
|
||||
|
||||
auto &resourceManager = project.GetResourcesManager();
|
||||
gd::ImageResource imageResource;
|
||||
imageResource.SetName("assets/Idle.png");
|
||||
imageResource.SetFile("assets/Idle.png");
|
||||
imageResource.SetSmooth(true);
|
||||
resourceManager.AddResource(imageResource);
|
||||
|
||||
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
|
||||
gd::Object &object = layout.GetObjects().InsertNewObject(
|
||||
project, "MyEventsExtension::MyEventsBasedObject", "MyObject", 0);
|
||||
auto *spriteConfiguration =
|
||||
dynamic_cast<gd::SpriteObject *>(&childObject.GetConfiguration());
|
||||
REQUIRE(spriteConfiguration != nullptr);
|
||||
{
|
||||
gd::Animation animation;
|
||||
animation.SetName("Idle");
|
||||
animation.SetDirectionsCount(1);
|
||||
auto &direction = animation.GetDirection(0);
|
||||
gd::Sprite frame;
|
||||
frame.SetImageName("assets/Idle.png");
|
||||
direction.AddSprite(frame);
|
||||
|
||||
spriteConfiguration->GetAnimations().AddAnimation(animation);
|
||||
}
|
||||
|
||||
SerializerElement assetElement;
|
||||
std::vector<gd::String> usedResourceNames;
|
||||
ObjectAssetSerializer::SerializeTo(project, object, "My Object",
|
||||
assetElement, usedResourceNames);
|
||||
|
||||
// This list is used to copy resource files.
|
||||
REQUIRE(usedResourceNames.size() == 1);
|
||||
REQUIRE(usedResourceNames[0] == "assets/Idle.png");
|
||||
|
||||
// Check that the project is left untouched.
|
||||
REQUIRE(resourceManager.HasResource("assets/Idle.png"));
|
||||
REQUIRE(resourceManager.GetResource("assets/Idle.png").GetFile() ==
|
||||
"assets/Idle.png");
|
||||
REQUIRE(!resourceManager.HasResource("Idle.png"));
|
||||
|
||||
REQUIRE(assetElement.HasChild("objectAssets"));
|
||||
auto &objectAssetsElement = assetElement.GetChild("objectAssets");
|
||||
objectAssetsElement.ConsiderAsArrayOf("objectAsset");
|
||||
REQUIRE(objectAssetsElement.GetChildrenCount() == 1);
|
||||
auto &objectAssetElement = objectAssetsElement.GetChild(0);
|
||||
|
||||
REQUIRE(objectAssetElement.HasChild("variants"));
|
||||
auto &variantsElement = objectAssetElement.GetChild("variants");
|
||||
variantsElement.ConsiderAsArrayOf("variant");
|
||||
REQUIRE(variantsElement.GetChildrenCount() == 1);
|
||||
auto &variantPairElement = variantsElement.GetChild(0);
|
||||
REQUIRE(variantPairElement.GetStringAttribute("objectType") ==
|
||||
"MyEventsExtension::MyEventsBasedObject");
|
||||
REQUIRE(variantPairElement.HasChild("variant"));
|
||||
auto &variantElement = variantPairElement.GetChild("variant");
|
||||
REQUIRE(variantElement.GetStringAttribute("name") == "");
|
||||
REQUIRE(variantElement.HasChild("objects"));
|
||||
auto &objectsElement = variantElement.GetChild("objects");
|
||||
objectsElement.ConsiderAsArrayOf("object");
|
||||
REQUIRE(objectsElement.GetChildrenCount() == 1);
|
||||
auto &childElement = objectsElement.GetChild(0);
|
||||
|
||||
REQUIRE(childElement.HasChild("animations"));
|
||||
auto &animationsElement = childElement.GetChild("animations");
|
||||
animationsElement.ConsiderAsArrayOf("animation");
|
||||
REQUIRE(animationsElement.GetChildrenCount() == 1);
|
||||
auto &animationElement = animationsElement.GetChild(0);
|
||||
|
||||
REQUIRE(animationElement.GetStringAttribute("name") == "Idle");
|
||||
auto &directionsElement = animationElement.GetChild("directions");
|
||||
directionsElement.ConsiderAsArrayOf("direction");
|
||||
REQUIRE(directionsElement.GetChildrenCount() == 1);
|
||||
auto &directionElement = directionsElement.GetChild(0);
|
||||
auto &spritesElement = directionElement.GetChild("sprites");
|
||||
spritesElement.ConsiderAsArrayOf("sprite");
|
||||
REQUIRE(spritesElement.GetChildrenCount() == 1);
|
||||
auto &spriteElement = spritesElement.GetChild(0);
|
||||
REQUIRE(spriteElement.GetStringAttribute("image") == "assets/Idle.png");
|
||||
|
||||
REQUIRE(objectAssetElement.HasChild("requiredExtensions"));
|
||||
auto &requiredExtensionsElement =
|
||||
objectAssetElement.GetChild("requiredExtensions");
|
||||
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
|
||||
REQUIRE(requiredExtensionsElement.GetChildrenCount() == 1);
|
||||
auto &requiredExtensionElement = requiredExtensionsElement.GetChild(0);
|
||||
REQUIRE(requiredExtensionElement.GetStringAttribute("extensionName") ==
|
||||
"MyEventsExtension");
|
||||
|
||||
// Resources are renamed according to asset script naming conventions.
|
||||
REQUIRE(objectAssetElement.HasChild("resources"));
|
||||
auto &resourcesElement = objectAssetElement.GetChild("resources");
|
||||
resourcesElement.ConsiderAsArrayOf("resource");
|
||||
REQUIRE(resourcesElement.GetChildrenCount() == 1);
|
||||
{
|
||||
auto &resourceElement = resourcesElement.GetChild(0);
|
||||
REQUIRE(resourceElement.GetStringAttribute("name") == "assets/Idle.png");
|
||||
REQUIRE(resourceElement.GetStringAttribute("file") == "assets/Idle.png");
|
||||
REQUIRE(resourceElement.GetStringAttribute("kind") == "image");
|
||||
REQUIRE(resourceElement.GetBoolAttribute("smoothed") == true);
|
||||
}
|
||||
|
||||
// Resources used in object configuration are updated.
|
||||
REQUIRE(objectAssetElement.HasChild("object"));
|
||||
auto &objectElement = objectAssetElement.GetChild("object");
|
||||
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
|
||||
REQUIRE(objectElement.GetStringAttribute("type") ==
|
||||
"MyEventsExtension::MyEventsBasedObject");
|
||||
}
|
||||
|
||||
SECTION("Can serialize custom objects as assets with children overriding") {
|
||||
gd::Platform platform;
|
||||
gd::Project project;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
@@ -59,6 +184,8 @@ TEST_CASE("ObjectAssetSerializer", "[common]") {
|
||||
auto &configuration = object.GetConfiguration();
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(&configuration);
|
||||
customObjectConfiguration
|
||||
->SetMarkedAsOverridingEventsBasedObjectChildrenConfiguration(true);
|
||||
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(
|
||||
&customObjectConfiguration->GetChildObjectConfiguration("MyChild"));
|
||||
REQUIRE(spriteConfiguration != nullptr);
|
||||
|
@@ -150,7 +150,7 @@ TEST_CASE("ObjectContainersList (GetTypeOfObject)", "[common]") {
|
||||
gd::Object &object1 = layout.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyObject1", 0);
|
||||
gd::Object &object2 = layout.GetObjects().InsertNewObject(
|
||||
project, "FakeObjectWithDefaultBehavior", "MyObject2", 0);
|
||||
project, "MyExtension::FakeObjectWithDefaultBehavior", "MyObject2", 0);
|
||||
|
||||
auto &group = layout.GetObjects().GetObjectGroups().InsertNew("MyGroup", 0);
|
||||
group.AddObject(object1.GetName());
|
||||
|
@@ -1072,6 +1072,99 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
|
||||
REQUIRE(instance.GetVariables().Get("MyRenamedVariable").GetValue() == 456);
|
||||
}
|
||||
|
||||
SECTION("Can rename an object variable (in events-based object)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &object = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyChildObject", 0);
|
||||
object.GetVariables().InsertNew("MyVariable").SetValue(123);
|
||||
auto &instance =
|
||||
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
|
||||
instance.SetObjectName("MyChildObject");
|
||||
instance.GetVariables().InsertNew("MyVariable").SetValue(456);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
gd::InitialInstance *variantInstance = nullptr;
|
||||
variant.GetInitialInstances().IterateOverInstances(
|
||||
[&variantInstance](gd::InitialInstance &instance) {
|
||||
variantInstance = &instance;
|
||||
return true;
|
||||
});
|
||||
REQUIRE(variantInstance != nullptr);
|
||||
variant.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyVariable")
|
||||
.SetValue(111);
|
||||
variantInstance->GetVariables().Get("MyVariable").SetValue(222);
|
||||
|
||||
auto &objectFunction =
|
||||
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
|
||||
objectFunction.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::Standard"));
|
||||
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("SetNumberObjectVariable");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MyChildObject"));
|
||||
action.SetParameter(1, gd::Expression("MyVariable"));
|
||||
action.SetParameter(2, gd::Expression("="));
|
||||
action.SetParameter(3, gd::Expression("MyChildObject.MyVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
object.GetVariables().ResetPersistentUuid();
|
||||
gd::SerializerElement originalSerializedVariables;
|
||||
object.GetVariables().SerializeTo(originalSerializedVariables);
|
||||
|
||||
object.GetVariables().Rename("MyVariable", "MyRenamedVariable");
|
||||
auto changeset =
|
||||
gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
originalSerializedVariables, object.GetVariables());
|
||||
|
||||
REQUIRE(changeset.oldToNewVariableNames.size() == 1);
|
||||
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForObjectVariablesContainer(
|
||||
project, object.GetVariables(), eventsBasedObject.GetInitialInstances(),
|
||||
object.GetName(), changeset, originalSerializedVariables);
|
||||
gd::ObjectVariableHelper::ApplyChangesToVariants(
|
||||
eventsBasedObject, "MyChildObject", changeset);
|
||||
|
||||
REQUIRE(event.GetActions()[0].GetParameter(1).GetPlainString() ==
|
||||
"MyRenamedVariable");
|
||||
REQUIRE(event.GetActions()[0].GetParameter(3).GetPlainString() ==
|
||||
"MyChildObject.MyRenamedVariable");
|
||||
|
||||
REQUIRE(eventsBasedObject.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(eventsBasedObject.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyRenamedVariable")
|
||||
.GetValue() == 123);
|
||||
REQUIRE(instance.GetVariables().Get("MyRenamedVariable").GetValue() == 456);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("MyChildObject"));
|
||||
REQUIRE(variant.GetObjects()
|
||||
.GetObject("MyChildObject")
|
||||
.GetVariables()
|
||||
.Get("MyRenamedVariable")
|
||||
.GetValue() == 111);
|
||||
REQUIRE(
|
||||
variantInstance->GetVariables().Get("MyRenamedVariable").GetValue() ==
|
||||
222);
|
||||
}
|
||||
|
||||
SECTION("Can delete an object variable") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
|
@@ -1754,6 +1754,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsBasedObject.GetObjects().InsertNewObject(
|
||||
project, "MyExtension::Sprite", "Object2", 0);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer parametersObjectsContainer(
|
||||
gd::ObjectsContainer::SourceType::Function);
|
||||
@@ -1765,6 +1768,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
project, projectScopedContainers, eventsBasedObject, "Object1",
|
||||
"Object3", /* isObjectGroup =*/false);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object1") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object2") == true);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object3") == true);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups().size() == 1);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups()[0].Find(
|
||||
"Object1") == false);
|
||||
@@ -1772,6 +1779,17 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"Object2") == true);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectGroups()[0].Find(
|
||||
"Object3") == true);
|
||||
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object1") == false);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object2") == true);
|
||||
REQUIRE(variant.GetObjects().HasObjectNamed("Object3") == true);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups().size() == 1);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object1") ==
|
||||
false);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object2") ==
|
||||
true);
|
||||
REQUIRE(variant.GetObjects().GetObjectGroups()[0].Find("Object3") ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Initial instances") {
|
||||
@@ -1796,6 +1814,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance1);
|
||||
eventsBasedObject.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
|
||||
auto &variant = eventsBasedObject.GetVariants().InsertVariant(
|
||||
eventsBasedObject.GetDefaultVariant(), 0);
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer parametersObjectsContainer(
|
||||
gd::ObjectsContainer::SourceType::Function);
|
||||
@@ -1807,10 +1828,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
|
||||
project, projectScopedContainers, eventsBasedObject, "Object1",
|
||||
"Object3", /* isObjectGroup =*/false);
|
||||
|
||||
REQUIRE(eventsBasedObject.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object1") == false);
|
||||
REQUIRE(eventsBasedObject.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object3") == true);
|
||||
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("Object1") ==
|
||||
false);
|
||||
REQUIRE(variant.GetInitialInstances().HasInstancesOfObject("Object3") ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("Events") {
|
||||
|
@@ -5,8 +5,6 @@ namespace gdjs {
|
||||
type Object3DNetworkSyncDataType = {
|
||||
// z is position on the Z axis, different from zo, which is Z order
|
||||
z: number;
|
||||
w: number;
|
||||
h: number;
|
||||
d: number;
|
||||
rx: number;
|
||||
ry: number;
|
||||
@@ -116,8 +114,6 @@ namespace gdjs {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
z: this.getZ(),
|
||||
w: this.getWidth(),
|
||||
h: this.getHeight(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
ry: this.getRotationY(),
|
||||
@@ -130,8 +126,6 @@ namespace gdjs {
|
||||
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
|
||||
if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
this.setRotationX(networkSyncData.rx);
|
||||
|
@@ -25,6 +25,8 @@ namespace gdjs {
|
||||
topFaceVisible: boolean;
|
||||
bottomFaceVisible: boolean;
|
||||
tint: string | undefined;
|
||||
isCastingShadow: boolean;
|
||||
isReceivingShadow: boolean;
|
||||
materialType: 'Basic' | 'StandardWithoutMetalness';
|
||||
};
|
||||
}
|
||||
@@ -71,8 +73,10 @@ namespace gdjs {
|
||||
string,
|
||||
];
|
||||
_materialType: gdjs.Cube3DRuntimeObject.MaterialType =
|
||||
gdjs.Cube3DRuntimeObject.MaterialType.Basic;
|
||||
gdjs.Cube3DRuntimeObject.MaterialType.StandardWithoutMetalness;
|
||||
_tint: string;
|
||||
_isCastingShadow: boolean = true;
|
||||
_isReceivingShadow: boolean = true;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -121,6 +125,8 @@ namespace gdjs {
|
||||
];
|
||||
|
||||
this._tint = objectData.content.tint || '255;255;255';
|
||||
this._isCastingShadow = objectData.content.isCastingShadow || false;
|
||||
this._isReceivingShadow = objectData.content.isReceivingShadow || false;
|
||||
|
||||
this._materialType = this._convertMaterialType(
|
||||
objectData.content.materialType
|
||||
@@ -430,6 +436,18 @@ namespace gdjs {
|
||||
) {
|
||||
this.setMaterialType(newObjectData.content.materialType);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.isCastingShadow !==
|
||||
newObjectData.content.isCastingShadow
|
||||
) {
|
||||
this.updateShadowCasting(newObjectData.content.isCastingShadow);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.isReceivingShadow !==
|
||||
newObjectData.content.isReceivingShadow
|
||||
) {
|
||||
this.updateShadowReceiving(newObjectData.content.isReceivingShadow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -531,6 +549,14 @@ namespace gdjs {
|
||||
this._materialType = newMaterialType;
|
||||
this._renderer._updateMaterials();
|
||||
}
|
||||
updateShadowCasting(value: boolean) {
|
||||
this._isCastingShadow = value;
|
||||
this._renderer.updateShadowCasting();
|
||||
}
|
||||
updateShadowReceiving(value: boolean) {
|
||||
this._isReceivingShadow = value;
|
||||
this._renderer.updateShadowReceiving();
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Cube3DRuntimeObject {
|
||||
|
@@ -81,13 +81,14 @@ namespace gdjs {
|
||||
.map((_, index) =>
|
||||
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[index])
|
||||
);
|
||||
|
||||
const boxMesh = new THREE.Mesh(geometry, materials);
|
||||
|
||||
super(runtimeObject, instanceContainer, boxMesh);
|
||||
this._boxMesh = boxMesh;
|
||||
this._cube3DRuntimeObject = runtimeObject;
|
||||
|
||||
boxMesh.receiveShadow = this._cube3DRuntimeObject._isReceivingShadow;
|
||||
boxMesh.castShadow = this._cube3DRuntimeObject._isCastingShadow;
|
||||
this.updateSize();
|
||||
this.updatePosition();
|
||||
this.updateRotation();
|
||||
@@ -114,6 +115,13 @@ namespace gdjs {
|
||||
new THREE.BufferAttribute(new Float32Array(tints), 3)
|
||||
);
|
||||
}
|
||||
updateShadowCasting() {
|
||||
this._boxMesh.castShadow = this._cube3DRuntimeObject._isCastingShadow;
|
||||
}
|
||||
updateShadowReceiving() {
|
||||
this._boxMesh.receiveShadow =
|
||||
this._cube3DRuntimeObject._isReceivingShadow;
|
||||
}
|
||||
|
||||
updateFace(faceIndex: integer) {
|
||||
const materialIndex = faceIndexToMaterialIndex[faceIndex];
|
||||
|
@@ -1,4 +1,12 @@
|
||||
namespace gdjs {
|
||||
type CustomObject3DNetworkSyncDataType = CustomObjectNetworkSyncDataType & {
|
||||
z: float;
|
||||
d: float;
|
||||
rx: float;
|
||||
ry: float;
|
||||
ifz: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for 3D custom objects.
|
||||
*/
|
||||
@@ -34,7 +42,6 @@ namespace gdjs {
|
||||
objectData: gdjs.Object3DData & gdjs.CustomObjectConfiguration
|
||||
) {
|
||||
super(parent, objectData);
|
||||
this._renderer.reinitialize(this, parent);
|
||||
}
|
||||
|
||||
protected override _createRender() {
|
||||
@@ -78,6 +85,30 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSyncData(): CustomObject3DNetworkSyncDataType {
|
||||
return {
|
||||
...super.getNetworkSyncData(),
|
||||
z: this.getZ(),
|
||||
d: this.getDepth(),
|
||||
rx: this.getRotationX(),
|
||||
ry: this.getRotationY(),
|
||||
ifz: this.isFlippedZ(),
|
||||
};
|
||||
}
|
||||
|
||||
updateFromNetworkSyncData(
|
||||
networkSyncData: CustomObject3DNetworkSyncDataType
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData);
|
||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||
if (networkSyncData.rx !== undefined)
|
||||
this.setRotationX(networkSyncData.rx);
|
||||
if (networkSyncData.ry !== undefined)
|
||||
this.setRotationY(networkSyncData.ry);
|
||||
if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object position on the Z axis.
|
||||
*/
|
||||
|
@@ -44,10 +44,7 @@ namespace gdjs {
|
||||
) {
|
||||
this._object = object;
|
||||
this._isContainerDirty = true;
|
||||
const layer = parent.getLayer('');
|
||||
if (layer) {
|
||||
layer.getRenderer().add3DRendererObject(this._threeGroup);
|
||||
}
|
||||
this._threeGroup.clear();
|
||||
}
|
||||
|
||||
_updateThreeGroup() {
|
||||
|
@@ -6,6 +6,7 @@ namespace gdjs {
|
||||
r: number;
|
||||
t: string;
|
||||
}
|
||||
const shadowHelper = false;
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Scene3D::DirectionalLight',
|
||||
new (class implements gdjs.PixiFiltersTools.FilterCreator {
|
||||
@@ -17,19 +18,63 @@ namespace gdjs {
|
||||
return new gdjs.PixiFiltersTools.EmptyFilter();
|
||||
}
|
||||
return new (class implements gdjs.PixiFiltersTools.Filter {
|
||||
light: THREE.DirectionalLight;
|
||||
rotationObject: THREE.Group;
|
||||
_isEnabled: boolean = false;
|
||||
top: string = 'Y-';
|
||||
elevation: float = 45;
|
||||
rotation: float = 0;
|
||||
private _top: string = 'Z+';
|
||||
private _elevation: float = 45;
|
||||
private _rotation: float = 0;
|
||||
private _shadowMapSize: float = 1024;
|
||||
private _minimumShadowBias: float = 0;
|
||||
private _distanceFromCamera: float = 1500;
|
||||
private _frustumSize: float = 4000;
|
||||
|
||||
private _isEnabled: boolean = false;
|
||||
private _light: THREE.DirectionalLight;
|
||||
private _shadowMapDirty = true;
|
||||
private _shadowCameraDirty = true;
|
||||
private _shadowCameraHelper: THREE.CameraHelper | null;
|
||||
|
||||
constructor() {
|
||||
this.light = new THREE.DirectionalLight();
|
||||
this.light.position.set(1, 0, 0);
|
||||
this.rotationObject = new THREE.Group();
|
||||
this.rotationObject.add(this.light);
|
||||
this.updateRotation();
|
||||
this._light = new THREE.DirectionalLight();
|
||||
|
||||
if (shadowHelper) {
|
||||
this._shadowCameraHelper = new THREE.CameraHelper(
|
||||
this._light.shadow.camera
|
||||
);
|
||||
} else {
|
||||
this._shadowCameraHelper = null;
|
||||
}
|
||||
|
||||
this._light.shadow.camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
private _updateShadowCamera(): void {
|
||||
if (!this._shadowCameraDirty) {
|
||||
return;
|
||||
}
|
||||
this._shadowCameraDirty = false;
|
||||
|
||||
this._light.shadow.camera.near = 1;
|
||||
this._light.shadow.camera.far = this._distanceFromCamera + 10000;
|
||||
this._light.shadow.camera.right = this._frustumSize / 2;
|
||||
this._light.shadow.camera.left = -this._frustumSize / 2;
|
||||
this._light.shadow.camera.top = this._frustumSize / 2;
|
||||
this._light.shadow.camera.bottom = -this._frustumSize / 2;
|
||||
}
|
||||
|
||||
private _updateShadowMapSize(): void {
|
||||
if (!this._shadowMapDirty) {
|
||||
return;
|
||||
}
|
||||
this._shadowMapDirty = false;
|
||||
|
||||
this._light.shadow.mapSize.set(
|
||||
this._shadowMapSize,
|
||||
this._shadowMapSize
|
||||
);
|
||||
|
||||
// Force the recreation of the shadow map texture:
|
||||
this._light.shadow.map?.dispose();
|
||||
this._light.shadow.map = null;
|
||||
this._light.shadow.needsUpdate = true;
|
||||
}
|
||||
|
||||
isEnabled(target: EffectsTarget): boolean {
|
||||
@@ -53,7 +98,12 @@ namespace gdjs {
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.add(this.rotationObject);
|
||||
scene.add(this._light);
|
||||
scene.add(this._light.target);
|
||||
if (this._shadowCameraHelper) {
|
||||
scene.add(this._shadowCameraHelper);
|
||||
}
|
||||
|
||||
this._isEnabled = true;
|
||||
return true;
|
||||
}
|
||||
@@ -65,82 +115,164 @@ namespace gdjs {
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.remove(this.rotationObject);
|
||||
scene.remove(this._light);
|
||||
scene.remove(this._light.target);
|
||||
if (this._shadowCameraHelper) {
|
||||
scene.remove(this._shadowCameraHelper);
|
||||
}
|
||||
this._isEnabled = false;
|
||||
return true;
|
||||
}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {
|
||||
// Apply any update to the camera or shadow map size.
|
||||
this._updateShadowCamera();
|
||||
this._updateShadowMapSize();
|
||||
|
||||
// Avoid shadow acne due to depth buffer precision.
|
||||
const biasMultiplier =
|
||||
this._shadowMapSize < 1024
|
||||
? 2
|
||||
: this._shadowMapSize < 2048
|
||||
? 1.25
|
||||
: 1;
|
||||
this._light.shadow.bias = -this._minimumShadowBias * biasMultiplier;
|
||||
|
||||
// Apply update to the light position and its target.
|
||||
// By doing this, the shadows are "following" the GDevelop camera.
|
||||
if (!target.getRuntimeLayer) {
|
||||
return;
|
||||
}
|
||||
const layer = target.getRuntimeLayer();
|
||||
const x = layer.getCameraX();
|
||||
const y = layer.getCameraY();
|
||||
const z = layer.getCameraZ(layer.getInitialCamera3DFieldOfView());
|
||||
|
||||
const roundedX = Math.floor(x / 100) * 100;
|
||||
const roundedY = Math.floor(y / 100) * 100;
|
||||
const roundedZ = Math.floor(z / 100) * 100;
|
||||
if (this._top === 'Y-') {
|
||||
const posLightX =
|
||||
roundedX +
|
||||
this._distanceFromCamera *
|
||||
Math.cos(gdjs.toRad(-this._rotation + 90)) *
|
||||
Math.cos(gdjs.toRad(this._elevation));
|
||||
const posLightY =
|
||||
roundedY -
|
||||
this._distanceFromCamera *
|
||||
Math.sin(gdjs.toRad(this._elevation));
|
||||
const posLightZ =
|
||||
roundedZ +
|
||||
this._distanceFromCamera *
|
||||
Math.sin(gdjs.toRad(-this._rotation + 90)) *
|
||||
Math.cos(gdjs.toRad(this._elevation));
|
||||
this._light.position.set(posLightX, posLightY, posLightZ);
|
||||
this._light.target.position.set(roundedX, roundedY, roundedZ);
|
||||
} else {
|
||||
const posLightX =
|
||||
roundedX +
|
||||
this._distanceFromCamera *
|
||||
Math.cos(gdjs.toRad(this._rotation)) *
|
||||
Math.cos(gdjs.toRad(this._elevation));
|
||||
const posLightY =
|
||||
roundedY +
|
||||
this._distanceFromCamera *
|
||||
Math.sin(gdjs.toRad(this._rotation)) *
|
||||
Math.cos(gdjs.toRad(this._elevation));
|
||||
const posLightZ =
|
||||
roundedZ +
|
||||
this._distanceFromCamera *
|
||||
Math.sin(gdjs.toRad(this._elevation));
|
||||
|
||||
this._light.position.set(posLightX, posLightY, posLightZ);
|
||||
this._light.target.position.set(roundedX, roundedY, roundedZ);
|
||||
}
|
||||
}
|
||||
updateDoubleParameter(parameterName: string, value: number): void {
|
||||
if (parameterName === 'intensity') {
|
||||
this.light.intensity = value;
|
||||
this._light.intensity = value;
|
||||
} else if (parameterName === 'elevation') {
|
||||
this.elevation = value;
|
||||
this.updateRotation();
|
||||
this._elevation = value;
|
||||
} else if (parameterName === 'rotation') {
|
||||
this.rotation = value;
|
||||
this.updateRotation();
|
||||
this._rotation = value;
|
||||
} else if (parameterName === 'distanceFromCamera') {
|
||||
this._distanceFromCamera = value;
|
||||
} else if (parameterName === 'frustumSize') {
|
||||
this._frustumSize = value;
|
||||
} else if (parameterName === 'minimumShadowBias') {
|
||||
this._minimumShadowBias = value;
|
||||
}
|
||||
}
|
||||
getDoubleParameter(parameterName: string): number {
|
||||
if (parameterName === 'intensity') {
|
||||
return this.light.intensity;
|
||||
return this._light.intensity;
|
||||
} else if (parameterName === 'elevation') {
|
||||
return this.elevation;
|
||||
return this._elevation;
|
||||
} else if (parameterName === 'rotation') {
|
||||
return this.rotation;
|
||||
return this._rotation;
|
||||
} else if (parameterName === 'distanceFromCamera') {
|
||||
return this._distanceFromCamera;
|
||||
} else if (parameterName === 'frustumSize') {
|
||||
return this._frustumSize;
|
||||
} else if (parameterName === 'minimumShadowBias') {
|
||||
return this._minimumShadowBias;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'color') {
|
||||
this.light.color = new THREE.Color(
|
||||
this._light.color = new THREE.Color(
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'top') {
|
||||
this.top = value;
|
||||
this.updateRotation();
|
||||
this._top = value;
|
||||
}
|
||||
if (parameterName === 'shadowQuality') {
|
||||
if (value === 'low' && this._shadowMapSize !== 512) {
|
||||
this._shadowMapSize = 512;
|
||||
this._shadowMapDirty = true;
|
||||
}
|
||||
if (value === 'medium' && this._shadowMapSize !== 1024) {
|
||||
this._shadowMapSize = 1024;
|
||||
this._shadowMapDirty = true;
|
||||
}
|
||||
if (value === 'high' && this._shadowMapSize !== 2048) {
|
||||
this._shadowMapSize = 2048;
|
||||
this._shadowMapDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateColorParameter(parameterName: string, value: number): void {
|
||||
if (parameterName === 'color') {
|
||||
this.light.color.setHex(value);
|
||||
this._light.color.setHex(value);
|
||||
}
|
||||
}
|
||||
getColorParameter(parameterName: string): number {
|
||||
if (parameterName === 'color') {
|
||||
return this.light.color.getHex();
|
||||
return this._light.color.getHex();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
updateBooleanParameter(parameterName: string, value: boolean): void {
|
||||
if (parameterName === 'isCastingShadow') {
|
||||
this._light.castShadow = value;
|
||||
}
|
||||
}
|
||||
getNetworkSyncData(): DirectionalLightFilterNetworkSyncData {
|
||||
return {
|
||||
i: this.light.intensity,
|
||||
c: this.light.color.getHex(),
|
||||
e: this.elevation,
|
||||
r: this.rotation,
|
||||
t: this.top,
|
||||
i: this._light.intensity,
|
||||
c: this._light.color.getHex(),
|
||||
e: this._elevation,
|
||||
r: this._rotation,
|
||||
t: this._top,
|
||||
};
|
||||
}
|
||||
updateFromNetworkSyncData(syncData: any): void {
|
||||
this.light.intensity = syncData.i;
|
||||
this.light.color.setHex(syncData.c);
|
||||
this.elevation = syncData.e;
|
||||
this.rotation = syncData.r;
|
||||
this.top = syncData.t;
|
||||
this.updateRotation();
|
||||
this._light.intensity = syncData.i;
|
||||
this._light.color.setHex(syncData.c);
|
||||
this._elevation = syncData.e;
|
||||
this._rotation = syncData.r;
|
||||
this._top = syncData.t;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
@@ -18,18 +18,15 @@ namespace gdjs {
|
||||
return new gdjs.PixiFiltersTools.EmptyFilter();
|
||||
}
|
||||
return new (class implements gdjs.PixiFiltersTools.Filter {
|
||||
light: THREE.HemisphereLight;
|
||||
rotationObject: THREE.Group;
|
||||
_top: string = 'Z+';
|
||||
_elevation: float = 90;
|
||||
_rotation: float = 0;
|
||||
|
||||
_isEnabled: boolean = false;
|
||||
top: string = 'Y-';
|
||||
elevation: float = 45;
|
||||
rotation: float = 0;
|
||||
_light: THREE.HemisphereLight;
|
||||
|
||||
constructor() {
|
||||
this.light = new THREE.HemisphereLight();
|
||||
this.light.position.set(1, 0, 0);
|
||||
this.rotationObject = new THREE.Group();
|
||||
this.rotationObject.add(this.light);
|
||||
this._light = new THREE.HemisphereLight();
|
||||
this.updateRotation();
|
||||
}
|
||||
|
||||
@@ -54,7 +51,7 @@ namespace gdjs {
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.add(this.rotationObject);
|
||||
scene.add(this._light);
|
||||
this._isEnabled = true;
|
||||
return true;
|
||||
}
|
||||
@@ -66,96 +63,106 @@ namespace gdjs {
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.remove(this.rotationObject);
|
||||
scene.remove(this._light);
|
||||
this._isEnabled = false;
|
||||
return true;
|
||||
}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {}
|
||||
updateDoubleParameter(parameterName: string, value: number): void {
|
||||
if (parameterName === 'intensity') {
|
||||
this.light.intensity = value;
|
||||
this._light.intensity = value;
|
||||
} else if (parameterName === 'elevation') {
|
||||
this.elevation = value;
|
||||
this._elevation = value;
|
||||
this.updateRotation();
|
||||
} else if (parameterName === 'rotation') {
|
||||
this.rotation = value;
|
||||
this._rotation = value;
|
||||
this.updateRotation();
|
||||
}
|
||||
}
|
||||
getDoubleParameter(parameterName: string): number {
|
||||
if (parameterName === 'intensity') {
|
||||
return this.light.intensity;
|
||||
return this._light.intensity;
|
||||
} else if (parameterName === 'elevation') {
|
||||
return this.elevation;
|
||||
return this._elevation;
|
||||
} else if (parameterName === 'rotation') {
|
||||
return this.rotation;
|
||||
return this._rotation;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'skyColor') {
|
||||
this.light.color = new THREE.Color(
|
||||
this._light.color = new THREE.Color(
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'groundColor') {
|
||||
this.light.groundColor = new THREE.Color(
|
||||
this._light.groundColor = new THREE.Color(
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'top') {
|
||||
this.top = value;
|
||||
this._top = value;
|
||||
this.updateRotation();
|
||||
}
|
||||
}
|
||||
updateColorParameter(parameterName: string, value: number): void {
|
||||
if (parameterName === 'skyColor') {
|
||||
this.light.color.setHex(value);
|
||||
this._light.color.setHex(value);
|
||||
}
|
||||
if (parameterName === 'groundColor') {
|
||||
this.light.groundColor.setHex(value);
|
||||
this._light.groundColor.setHex(value);
|
||||
}
|
||||
}
|
||||
getColorParameter(parameterName: string): number {
|
||||
if (parameterName === 'skyColor') {
|
||||
return this.light.color.getHex();
|
||||
return this._light.color.getHex();
|
||||
}
|
||||
if (parameterName === 'groundColor') {
|
||||
return this.light.groundColor.getHex();
|
||||
return this._light.groundColor.getHex();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
if (this._top === 'Y-') {
|
||||
// `rotation` at 0° becomes a light from Z+.
|
||||
this._light.position.set(
|
||||
Math.cos(gdjs.toRad(-this._rotation + 90)) *
|
||||
Math.cos(gdjs.toRad(this._elevation)),
|
||||
-Math.sin(gdjs.toRad(this._elevation)),
|
||||
Math.sin(gdjs.toRad(-this._rotation + 90)) *
|
||||
Math.cos(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);
|
||||
// `rotation` at 0° is a light from the right of the screen.
|
||||
this._light.position.set(
|
||||
Math.cos(gdjs.toRad(this._rotation)) *
|
||||
Math.cos(gdjs.toRad(this._elevation)),
|
||||
Math.sin(gdjs.toRad(this._rotation)) *
|
||||
Math.cos(gdjs.toRad(this._elevation)),
|
||||
Math.sin(gdjs.toRad(this._elevation))
|
||||
);
|
||||
}
|
||||
}
|
||||
getNetworkSyncData(): HemisphereLightFilterNetworkSyncData {
|
||||
return {
|
||||
i: this.light.intensity,
|
||||
sc: this.light.color.getHex(),
|
||||
gc: this.light.groundColor.getHex(),
|
||||
e: this.elevation,
|
||||
r: this.rotation,
|
||||
t: this.top,
|
||||
i: this._light.intensity,
|
||||
sc: this._light.color.getHex(),
|
||||
gc: this._light.groundColor.getHex(),
|
||||
e: this._elevation,
|
||||
r: this._rotation,
|
||||
t: this._top,
|
||||
};
|
||||
}
|
||||
updateFromNetworkSyncData(
|
||||
syncData: HemisphereLightFilterNetworkSyncData
|
||||
): void {
|
||||
this.light.intensity = syncData.i;
|
||||
this.light.color.setHex(syncData.sc);
|
||||
this.light.groundColor.setHex(syncData.gc);
|
||||
this.elevation = syncData.e;
|
||||
this.rotation = syncData.r;
|
||||
this.top = syncData.t;
|
||||
this._light.intensity = syncData.i;
|
||||
this._light.color.setHex(syncData.sc);
|
||||
this._light.groundColor.setHex(syncData.gc);
|
||||
this._elevation = syncData.e;
|
||||
this._rotation = syncData.r;
|
||||
this._top = syncData.t;
|
||||
this.updateRotation();
|
||||
}
|
||||
})();
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Scene3D',
|
||||
_('3D'),
|
||||
_('Support for 3D in GDevelop.'),
|
||||
_(
|
||||
'Support for 3D in GDevelop: this provides 3D objects and the common features for all 3D objects.'
|
||||
),
|
||||
'Florian Rival',
|
||||
'MIT'
|
||||
)
|
||||
@@ -36,7 +38,9 @@ module.exports = {
|
||||
'Base3DBehavior',
|
||||
_('3D capability'),
|
||||
'Object3D',
|
||||
_('Move the object in 3D space.'),
|
||||
_(
|
||||
'Common features for all 3D objects: position in 3D space (including the Z axis, in addition to X and Y), size (including depth, in addition to width and height), rotation (on X and Y axis, in addition to the Z axis), scale (including Z axis, in addition to X and Y), flipping (on Z axis, in addition to horizontal (Y)/vertical (X) flipping).'
|
||||
),
|
||||
'',
|
||||
'res/conditions/3d_box.svg',
|
||||
'Base3DBehavior',
|
||||
@@ -158,7 +162,12 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationX')
|
||||
.setGetter('getRotationX');
|
||||
|
||||
@@ -174,7 +183,12 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationY')
|
||||
.setGetter('getRotationY');
|
||||
|
||||
@@ -192,7 +206,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundX');
|
||||
|
||||
@@ -210,7 +224,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundY');
|
||||
|
||||
@@ -228,7 +242,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('3D object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setFunctionName('turnAroundZ');
|
||||
}
|
||||
@@ -238,7 +252,7 @@ module.exports = {
|
||||
.addObject(
|
||||
'Model3DObject',
|
||||
_('3D Model'),
|
||||
_('An animated 3D model.'),
|
||||
_('An animated 3D model, useful for most elements of a 3D game.'),
|
||||
'JsPlatform/Extensions/3d_model.svg',
|
||||
new gd.Model3DObjectConfiguration()
|
||||
)
|
||||
@@ -590,7 +604,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setHidden()
|
||||
.setFunctionName('setRotationX')
|
||||
.setGetter('getRotationX');
|
||||
@@ -607,7 +626,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setHidden()
|
||||
.setFunctionName('setRotationY')
|
||||
.setGetter('getRotationY');
|
||||
@@ -626,7 +650,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundX');
|
||||
@@ -645,7 +669,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundY');
|
||||
@@ -664,7 +688,7 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D model'), 'Model3DObject', false)
|
||||
.addParameter('number', _('Rotation angle'), '', false)
|
||||
.addParameter('number', _('Angle to add (in degrees)'), '', false)
|
||||
.markAsAdvanced()
|
||||
.setHidden()
|
||||
.setFunctionName('turnAroundZ');
|
||||
@@ -855,7 +879,9 @@ module.exports = {
|
||||
propertyName === 'rightFaceResourceRepeat' ||
|
||||
propertyName === 'topFaceResourceRepeat' ||
|
||||
propertyName === 'bottomFaceResourceRepeat' ||
|
||||
propertyName === 'enableTextureTransparency'
|
||||
propertyName === 'enableTextureTransparency' ||
|
||||
propertyName === 'isCastingShadow' ||
|
||||
propertyName === 'isReceivingShadow'
|
||||
) {
|
||||
objectContent[propertyName] = newValue === '1';
|
||||
return true;
|
||||
@@ -883,8 +909,8 @@ module.exports = {
|
||||
.getOrCreate('facesOrientation')
|
||||
.setValue(objectContent.facesOrientation || 'Y')
|
||||
.setType('choice')
|
||||
.addExtraInfo('Y')
|
||||
.addExtraInfo('Z')
|
||||
.addChoice('Y', 'Y')
|
||||
.addChoice('Z', 'Z')
|
||||
.setLabel(_('Faces orientation'))
|
||||
.setDescription(
|
||||
_(
|
||||
@@ -944,8 +970,8 @@ module.exports = {
|
||||
.getOrCreate('backFaceUpThroughWhichAxisRotation')
|
||||
.setValue(objectContent.backFaceUpThroughWhichAxisRotation || 'X')
|
||||
.setType('choice')
|
||||
.addExtraInfo('X')
|
||||
.addExtraInfo('Y')
|
||||
.addChoice('X', 'X')
|
||||
.addChoice('Y', 'Y')
|
||||
.setLabel(_('Back face orientation'))
|
||||
.setDescription(
|
||||
_(
|
||||
@@ -1079,11 +1105,29 @@ module.exports = {
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('materialType')
|
||||
.setValue(objectContent.materialType || 'Basic')
|
||||
.setValue(objectContent.materialType || 'StandardWithoutMetalness')
|
||||
.setType('choice')
|
||||
.addExtraInfo('Basic')
|
||||
.addExtraInfo('StandardWithoutMetalness')
|
||||
.setLabel(_('Material type'));
|
||||
.addChoice('Basic', _('Basic (no lighting, no shadows)'))
|
||||
.addChoice(
|
||||
'StandardWithoutMetalness',
|
||||
_('Standard (without metalness)')
|
||||
)
|
||||
.setLabel(_('Material type'))
|
||||
.setGroup(_('Lighting'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('isCastingShadow')
|
||||
.setValue(objectContent.isCastingShadow ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Shadow casting'))
|
||||
.setGroup(_('Lighting'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('isReceivingShadow')
|
||||
.setValue(objectContent.isReceivingShadow ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Shadow receiving'))
|
||||
.setGroup(_('Lighting'));
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
@@ -1101,7 +1145,7 @@ module.exports = {
|
||||
topFaceResourceName: '',
|
||||
bottomFaceResourceName: '',
|
||||
frontFaceVisible: true,
|
||||
backFaceVisible: false,
|
||||
backFaceVisible: true,
|
||||
leftFaceVisible: true,
|
||||
rightFaceVisible: true,
|
||||
topFaceVisible: true,
|
||||
@@ -1112,8 +1156,10 @@ module.exports = {
|
||||
rightFaceResourceRepeat: false,
|
||||
topFaceResourceRepeat: false,
|
||||
bottomFaceResourceRepeat: false,
|
||||
materialType: 'Basic',
|
||||
materialType: 'StandardWithoutMetalness',
|
||||
tint: '255;255;255',
|
||||
isCastingShadow: true,
|
||||
isReceivingShadow: true,
|
||||
};
|
||||
|
||||
Cube3DObject.updateInitialInstanceProperty = function (
|
||||
@@ -1467,7 +1513,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationX')
|
||||
.setHidden()
|
||||
.setGetter('getRotationX');
|
||||
@@ -1484,7 +1535,12 @@ module.exports = {
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setRotationY')
|
||||
.setHidden()
|
||||
.setGetter('getRotationY');
|
||||
@@ -1890,11 +1946,11 @@ module.exports = {
|
||||
.setType('number');
|
||||
properties
|
||||
.getOrCreate('top')
|
||||
.setValue('Y-')
|
||||
.setValue('Z+')
|
||||
.setLabel(_('3D world top'))
|
||||
.setType('choice')
|
||||
.addExtraInfo('Y-')
|
||||
.addExtraInfo('Z+')
|
||||
.addExtraInfo('Y-')
|
||||
.setGroup(_('Orientation'));
|
||||
properties
|
||||
.getOrCreate('elevation')
|
||||
@@ -1909,6 +1965,47 @@ module.exports = {
|
||||
.setLabel(_('Rotation (in degrees)'))
|
||||
.setType('number')
|
||||
.setGroup(_('Orientation'));
|
||||
properties
|
||||
.getOrCreate('isCastingShadow')
|
||||
.setValue('false')
|
||||
.setLabel(_('Shadow casting'))
|
||||
.setType('boolean')
|
||||
.setGroup(_('Shadows'));
|
||||
properties
|
||||
.getOrCreate('shadowQuality')
|
||||
.setValue('medium')
|
||||
.addChoice('low', _('Low quality'))
|
||||
.addChoice('medium', _('Medium quality'))
|
||||
.addChoice('high', _('High quality'))
|
||||
.setLabel(_('Shadow quality'))
|
||||
.setType('choice')
|
||||
.setGroup(_('Shadows'));
|
||||
properties
|
||||
.getOrCreate('minimumShadowBias')
|
||||
.setValue('0')
|
||||
.setLabel(_('Shadow bias'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Use this to avoid "shadow acne" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.'
|
||||
)
|
||||
)
|
||||
.setType('number')
|
||||
.setGroup(_('Shadows'))
|
||||
.setAdvanced(true);
|
||||
properties
|
||||
.getOrCreate('frustumSize')
|
||||
.setValue('4000')
|
||||
.setLabel(_('Shadow frustum size'))
|
||||
.setType('number')
|
||||
.setGroup(_('Shadows'))
|
||||
.setAdvanced(true);
|
||||
properties
|
||||
.getOrCreate('distanceFromCamera')
|
||||
.setValue('1500')
|
||||
.setLabel(_("Distance from layer's camera"))
|
||||
.setType('number')
|
||||
.setGroup(_('Shadows'))
|
||||
.setAdvanced(true);
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
@@ -1940,11 +2037,11 @@ module.exports = {
|
||||
.setType('number');
|
||||
properties
|
||||
.getOrCreate('top')
|
||||
.setValue('Y-')
|
||||
.setValue('Z+')
|
||||
.setLabel(_('3D world top'))
|
||||
.setType('choice')
|
||||
.addExtraInfo('Y-')
|
||||
.addExtraInfo('Z+')
|
||||
.addExtraInfo('Y-')
|
||||
.setGroup(_('Orientation'));
|
||||
properties
|
||||
.getOrCreate('elevation')
|
||||
@@ -3206,6 +3303,8 @@ module.exports = {
|
||||
|
||||
this._threeObject = new THREE.Group();
|
||||
this._threeObject.rotation.order = 'ZYX';
|
||||
this._threeObject.castShadow = true;
|
||||
this._threeObject.receiveShadow = true;
|
||||
this._threeGroup.add(this._threeObject);
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ Model3DObjectConfiguration::Model3DObjectConfiguration()
|
||||
: width(100), height(100), depth(100), rotationX(0), rotationY(0),
|
||||
rotationZ(0), modelResourceName(""), materialType("StandardWithoutMetalness"),
|
||||
originLocation("ModelOrigin"), centerLocation("ModelOrigin"),
|
||||
keepAspectRatio(true), crossfadeDuration(0.1f) {}
|
||||
keepAspectRatio(true), crossfadeDuration(0.1f), isCastingShadow(true), isReceivingShadow(true) {}
|
||||
|
||||
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
|
||||
const gd::String &newValue) {
|
||||
@@ -75,6 +75,16 @@ bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
|
||||
crossfadeDuration = newValue.To<double>();
|
||||
return true;
|
||||
}
|
||||
if(propertyName == "isCastingShadow")
|
||||
{
|
||||
isCastingShadow = newValue == "1";
|
||||
return true;
|
||||
}
|
||||
if(propertyName == "isReceivingShadow")
|
||||
{
|
||||
isReceivingShadow = newValue == "1";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -143,19 +153,20 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
objectProperties["materialType"]
|
||||
.SetValue(materialType.empty() ? "Basic" : materialType)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("Basic")
|
||||
.AddExtraInfo("StandardWithoutMetalness")
|
||||
.AddExtraInfo("KeepOriginal")
|
||||
.SetLabel(_("Material"));
|
||||
.AddChoice("Basic", _("Basic (no lighting, no shadows)"))
|
||||
.AddChoice("StandardWithoutMetalness", _("Standard (without metalness)"))
|
||||
.AddChoice("KeepOriginal", _("Keep original"))
|
||||
.SetLabel(_("Material"))
|
||||
.SetGroup(_("Lighting"));
|
||||
|
||||
objectProperties["originLocation"]
|
||||
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("ModelOrigin")
|
||||
.AddExtraInfo("TopLeft")
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.AddChoice("ModelOrigin", _("Model origin"))
|
||||
.AddChoice("TopLeft", _("Top left"))
|
||||
.AddChoice("ObjectCenter", _("Object center"))
|
||||
.AddChoice("BottomCenterZ", _("Bottom center (Z)"))
|
||||
.AddChoice("BottomCenterY", _("Bottom center (Y)"))
|
||||
.SetLabel(_("Origin point"))
|
||||
.SetGroup(_("Points"))
|
||||
.SetAdvanced(true);
|
||||
@@ -163,10 +174,10 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
objectProperties["centerLocation"]
|
||||
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
|
||||
.SetType("choice")
|
||||
.AddExtraInfo("ModelOrigin")
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.AddChoice("ModelOrigin", _("Model origin"))
|
||||
.AddChoice("ObjectCenter", _("Object center"))
|
||||
.AddChoice("BottomCenterZ", _("Bottom center (Z)"))
|
||||
.AddChoice("BottomCenterY", _("Bottom center (Y)"))
|
||||
.SetLabel(_("Center point"))
|
||||
.SetGroup(_("Points"))
|
||||
.SetAdvanced(true);
|
||||
@@ -178,6 +189,20 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
.SetGroup(_("Animations"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetSecond());
|
||||
|
||||
objectProperties["isCastingShadow"]
|
||||
.SetValue(isCastingShadow ? "true" : "false")
|
||||
.SetType("boolean")
|
||||
.SetLabel(_("Shadow casting"))
|
||||
.SetGroup(_("Lighting"));
|
||||
|
||||
objectProperties["isReceivingShadow"]
|
||||
.SetValue(isReceivingShadow ? "true" : "false")
|
||||
.SetType("boolean")
|
||||
.SetLabel(_("Shadow receiving"))
|
||||
.SetGroup(_("Lighting"));
|
||||
|
||||
|
||||
|
||||
return objectProperties;
|
||||
}
|
||||
|
||||
@@ -210,6 +235,8 @@ void Model3DObjectConfiguration::DoUnserializeFrom(
|
||||
centerLocation = content.GetStringAttribute("centerLocation");
|
||||
keepAspectRatio = content.GetBoolAttribute("keepAspectRatio");
|
||||
crossfadeDuration = content.GetDoubleAttribute("crossfadeDuration");
|
||||
isCastingShadow = content.GetBoolAttribute("isCastingShadow");
|
||||
isReceivingShadow = content.GetBoolAttribute("isReceivingShadow");
|
||||
|
||||
RemoveAllAnimations();
|
||||
auto &animationsElement = content.GetChild("animations");
|
||||
@@ -239,6 +266,8 @@ void Model3DObjectConfiguration::DoSerializeTo(
|
||||
content.SetAttribute("centerLocation", centerLocation);
|
||||
content.SetAttribute("keepAspectRatio", keepAspectRatio);
|
||||
content.SetAttribute("crossfadeDuration", crossfadeDuration);
|
||||
content.SetAttribute("isCastingShadow", isCastingShadow);
|
||||
content.SetAttribute("isReceivingShadow", isReceivingShadow);
|
||||
|
||||
auto &animationsElement = content.AddChild("animations");
|
||||
animationsElement.ConsiderAsArrayOf("animation");
|
||||
|
@@ -160,6 +160,8 @@ public:
|
||||
const gd::String& GetCenterLocation() const { return centerLocation; };
|
||||
|
||||
bool shouldKeepAspectRatio() const { return keepAspectRatio; };
|
||||
bool shouldCastShadow() const { return isCastingShadow; };
|
||||
bool shouldReceiveShadow() const { return isReceivingShadow; };
|
||||
///@}
|
||||
|
||||
protected:
|
||||
@@ -182,6 +184,8 @@ private:
|
||||
gd::String centerLocation;
|
||||
|
||||
bool keepAspectRatio;
|
||||
bool isCastingShadow;
|
||||
bool isReceivingShadow;
|
||||
|
||||
std::vector<Model3DAnimation> animations;
|
||||
static Model3DAnimation badAnimation; //< Bad animation when an out of bound
|
||||
|
@@ -38,6 +38,8 @@ namespace gdjs {
|
||||
| 'BottomCenterY';
|
||||
animations: Model3DAnimation[];
|
||||
crossfadeDuration: float;
|
||||
isCastingShadow: boolean;
|
||||
isReceivingShadow: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,6 +103,8 @@ namespace gdjs {
|
||||
_animationSpeedScale: float = 1;
|
||||
_animationPaused: boolean = false;
|
||||
_crossfadeDuration: float = 0;
|
||||
_isCastingShadow: boolean = true;
|
||||
_isReceivingShadow: boolean = true;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -123,6 +127,8 @@ namespace gdjs {
|
||||
objectData.content.materialType
|
||||
);
|
||||
|
||||
this.setIsCastingShadow(objectData.content.isCastingShadow);
|
||||
this.setIsReceivingShadow(objectData.content.isReceivingShadow);
|
||||
this.onModelChanged(objectData);
|
||||
|
||||
this._crossfadeDuration = objectData.content.crossfadeDuration || 0;
|
||||
@@ -195,6 +201,18 @@ namespace gdjs {
|
||||
newObjectData.content.centerLocation
|
||||
);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.isCastingShadow !==
|
||||
newObjectData.content.isCastingShadow
|
||||
) {
|
||||
this.setIsCastingShadow(newObjectData.content.isCastingShadow);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.isReceivingShadow !==
|
||||
newObjectData.content.isReceivingShadow
|
||||
) {
|
||||
this.setIsReceivingShadow(newObjectData.content.isReceivingShadow);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -358,6 +376,16 @@ namespace gdjs {
|
||||
return this._renderer.hasAnimationEnded();
|
||||
}
|
||||
|
||||
setIsCastingShadow(value: boolean): void {
|
||||
this._isCastingShadow = value;
|
||||
this._renderer._updateShadow();
|
||||
}
|
||||
|
||||
setIsReceivingShadow(value: boolean): void {
|
||||
this._isReceivingShadow = value;
|
||||
this._renderer._updateShadow();
|
||||
}
|
||||
|
||||
setCrossfadeDuration(duration: number): void {
|
||||
if (this._crossfadeDuration === duration) return;
|
||||
this._crossfadeDuration = duration;
|
||||
|
@@ -286,6 +286,7 @@ namespace gdjs {
|
||||
this.get3DRendererObject().remove(this._threeObject);
|
||||
this.get3DRendererObject().add(threeObject);
|
||||
this._threeObject = threeObject;
|
||||
this._updateShadow();
|
||||
|
||||
// Start the current animation on the new 3D object.
|
||||
this._animationMixer = new THREE.AnimationMixer(root);
|
||||
@@ -323,6 +324,13 @@ namespace gdjs {
|
||||
return this._originalModel.animations[animationIndex].name;
|
||||
}
|
||||
|
||||
_updateShadow() {
|
||||
this._threeObject.traverse((child) => {
|
||||
child.castShadow = this._model3DRuntimeObject._isCastingShadow;
|
||||
child.receiveShadow = this._model3DRuntimeObject._isReceivingShadow;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if animation has ended.
|
||||
* The animation had ended if:
|
||||
|
@@ -65,7 +65,7 @@ namespace gdjs {
|
||||
: behaviorData.useLegacyBottomAndRightAnchors;
|
||||
}
|
||||
|
||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
override updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||
if (oldBehaviorData.leftEdgeAnchor !== newBehaviorData.leftEdgeAnchor) {
|
||||
this._leftEdgeAnchor = newBehaviorData.leftEdgeAnchor;
|
||||
}
|
||||
@@ -96,26 +96,39 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
const objectHasMoved =
|
||||
this._oldDrawableX !== this.owner.getDrawableX() ||
|
||||
this._oldDrawableY !== this.owner.getDrawableY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight();
|
||||
if (objectHasMoved) {
|
||||
this._updateAnchorDistances(instanceContainer);
|
||||
}
|
||||
const parentHasResized =
|
||||
this._parentOldMinX !== instanceContainer.getUnrotatedViewportMinX() ||
|
||||
this._parentOldMinY !== instanceContainer.getUnrotatedViewportMinY() ||
|
||||
this._parentOldMaxX !== instanceContainer.getUnrotatedViewportMaxX() ||
|
||||
this._parentOldMaxY !== instanceContainer.getUnrotatedViewportMaxY();
|
||||
if (parentHasResized) {
|
||||
this._followAnchor(instanceContainer);
|
||||
}
|
||||
override onActivate(): void {
|
||||
// This only has a side effect if the camera moved while the behavior was
|
||||
// deactivated.
|
||||
// The new position on the viewport is where the object should stay.
|
||||
this._hasJustBeenCreated = true;
|
||||
}
|
||||
|
||||
private _updateAnchorDistances(
|
||||
override doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
if (this._hasJustBeenCreated) {
|
||||
this._initializeAnchorDistances(instanceContainer);
|
||||
this._hasJustBeenCreated = false;
|
||||
|
||||
this._oldDrawableX = this.owner.getDrawableX();
|
||||
this._oldDrawableY = this.owner.getDrawableY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
}
|
||||
this._updateAnchorDistances(instanceContainer);
|
||||
this._followAnchor(instanceContainer);
|
||||
|
||||
this._oldDrawableX = this.owner.getDrawableX();
|
||||
this._oldDrawableY = this.owner.getDrawableY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the anchor distance according to the object position on the
|
||||
* screen.
|
||||
*
|
||||
* The camera is taken into account.
|
||||
*/
|
||||
private _initializeAnchorDistances(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
const workingPoint: FloatPoint = gdjs.staticArray(
|
||||
@@ -123,38 +136,39 @@ namespace gdjs {
|
||||
) as FloatPoint;
|
||||
const layer = instanceContainer.getLayer(this.owner.getLayer());
|
||||
|
||||
let parentMinX = this._parentOldMinX;
|
||||
let parentMinY = this._parentOldMinY;
|
||||
let parentMaxX = this._parentOldMaxX;
|
||||
let parentMaxY = this._parentOldMaxY;
|
||||
if (this._hasJustBeenCreated) {
|
||||
if (this._relativeToOriginalWindowSize) {
|
||||
parentMinX = instanceContainer.getInitialUnrotatedViewportMinX();
|
||||
parentMinY = instanceContainer.getInitialUnrotatedViewportMinY();
|
||||
parentMaxX = instanceContainer.getInitialUnrotatedViewportMaxX();
|
||||
parentMaxY = instanceContainer.getInitialUnrotatedViewportMaxY();
|
||||
} else {
|
||||
parentMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
parentMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
}
|
||||
if (this._relativeToOriginalWindowSize) {
|
||||
this._parentOldMinX =
|
||||
instanceContainer.getInitialUnrotatedViewportMinX();
|
||||
this._parentOldMinY =
|
||||
instanceContainer.getInitialUnrotatedViewportMinY();
|
||||
this._parentOldMaxX =
|
||||
instanceContainer.getInitialUnrotatedViewportMaxX();
|
||||
this._parentOldMaxY =
|
||||
instanceContainer.getInitialUnrotatedViewportMaxY();
|
||||
} else {
|
||||
this._parentOldMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
this._parentOldMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
this._parentOldMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
this._parentOldMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
}
|
||||
const parentMinX = this._parentOldMinX;
|
||||
const parentMinY = this._parentOldMinY;
|
||||
const parentMaxX = this._parentOldMaxX;
|
||||
const parentMaxY = this._parentOldMaxY;
|
||||
|
||||
const parentCenterX = (parentMaxX + parentMinX) / 2;
|
||||
const parentCenterY = (parentMaxY + parentMinY) / 2;
|
||||
const parentWidth = parentMaxX - parentMinX;
|
||||
const parentHeight = parentMaxY - parentMinY;
|
||||
|
||||
//Calculate the distances from the window's bounds.
|
||||
const topLeftPixel = this._relativeToOriginalWindowSize
|
||||
? [this.owner.getDrawableX(), this.owner.getDrawableY()]
|
||||
: this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX(),
|
||||
this.owner.getDrawableY(),
|
||||
workingPoint
|
||||
);
|
||||
// Calculate the distances from the window's bounds.
|
||||
const topLeftPixel = this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX(),
|
||||
this.owner.getDrawableY(),
|
||||
workingPoint
|
||||
);
|
||||
|
||||
// Left edge
|
||||
if (this._leftEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
@@ -179,18 +193,13 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
|
||||
const bottomRightPixel = this._relativeToOriginalWindowSize
|
||||
? [
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight(),
|
||||
]
|
||||
: this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight(),
|
||||
workingPoint
|
||||
);
|
||||
const bottomRightPixel = this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight(),
|
||||
workingPoint
|
||||
);
|
||||
|
||||
// Right edge
|
||||
if (this._rightEdgeAnchor === HorizontalAnchor.WindowLeft) {
|
||||
@@ -215,20 +224,90 @@ namespace gdjs {
|
||||
} else if (this._bottomEdgeAnchor === VerticalAnchor.WindowCenter) {
|
||||
this._bottomEdgeDistance = bottomRightPixel[1] - parentCenterY;
|
||||
}
|
||||
|
||||
this._hasJustBeenCreated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the anchor distances according to the object position change in
|
||||
* the scene.
|
||||
*
|
||||
* The camera is not taken into account. Indeed, a camera scrolling should
|
||||
* not shift the anchored object on screen.
|
||||
*/
|
||||
private _updateAnchorDistances(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
if (
|
||||
this._oldDrawableX !== this.owner.getDrawableX() ||
|
||||
this._oldWidth !== this.owner.getWidth()
|
||||
) {
|
||||
const parentOldWidth = this._parentOldMaxX - this._parentOldMinX;
|
||||
|
||||
// Left edge
|
||||
const deltaMinX = this.owner.getDrawableX() - this._oldDrawableX;
|
||||
if (this._leftEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
this._leftEdgeDistance += deltaMinX / parentOldWidth;
|
||||
} else {
|
||||
this._leftEdgeDistance += deltaMinX;
|
||||
}
|
||||
|
||||
// Right edge
|
||||
const deltaMaxX = deltaMinX + this.owner.getWidth() - this._oldWidth;
|
||||
if (this._rightEdgeAnchor === HorizontalAnchor.Proportional) {
|
||||
this._rightEdgeDistance += deltaMaxX / parentOldWidth;
|
||||
} else {
|
||||
this._rightEdgeDistance += deltaMaxX;
|
||||
}
|
||||
}
|
||||
if (
|
||||
this._oldDrawableY !== this.owner.getDrawableY() ||
|
||||
this._oldHeight !== this.owner.getHeight()
|
||||
) {
|
||||
const parentOldHeight = this._parentOldMaxY - this._parentOldMinY;
|
||||
|
||||
// Top edge
|
||||
const deltaMinY = this.owner.getDrawableY() - this._oldDrawableY;
|
||||
if (this._topEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
this._topEdgeDistance += deltaMinY / parentOldHeight;
|
||||
} else {
|
||||
this._topEdgeDistance += deltaMinY;
|
||||
}
|
||||
|
||||
// Bottom edge
|
||||
const deltaMaxY = deltaMinY + this.owner.getHeight() - this._oldHeight;
|
||||
if (this._bottomEdgeAnchor === VerticalAnchor.Proportional) {
|
||||
this._bottomEdgeDistance += deltaMaxY / parentOldHeight;
|
||||
} else {
|
||||
this._bottomEdgeDistance += deltaMaxY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the object position to keep the object on screen according to the
|
||||
* anchor distances.
|
||||
*
|
||||
* The camera is taken into account.
|
||||
*/
|
||||
private _followAnchor(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
let parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
|
||||
if (
|
||||
this._parentOldMinX === parentMinX &&
|
||||
this._parentOldMinY === parentMinY &&
|
||||
this._parentOldMaxX === parentMaxX &&
|
||||
this._parentOldMaxY === parentMaxY
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workingPoint: FloatPoint = gdjs.staticArray(
|
||||
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
|
||||
) as FloatPoint;
|
||||
const layer = instanceContainer.getLayer(this.owner.getLayer());
|
||||
|
||||
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
let parentMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
const parentCenterX = (parentMaxX + parentMinX) / 2;
|
||||
const parentCenterY = (parentMaxY + parentMinY) / 2;
|
||||
const parentWidth = parentMaxX - parentMinX;
|
||||
@@ -388,11 +467,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
}
|
||||
this._oldDrawableX = this.owner.getDrawableX();
|
||||
this._oldDrawableY = this.owner.getDrawableY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
|
||||
this._parentOldMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
this._parentOldMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
this._parentOldMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
|
@@ -0,0 +1,47 @@
|
||||
// @ts-check
|
||||
describe('gdjs.AnchorRuntimeBehavior', () => {
|
||||
it('can fill a custom object with an child', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
|
||||
// The corresponding event-based object declaration is done by
|
||||
// getPixiRuntimeGame.
|
||||
const customObject = new gdjs.CustomRuntimeObject2D(runtimeScene, {
|
||||
name: 'MyCustomObject',
|
||||
type: 'MyExtension::MyLayoutedEventsBasedObject',
|
||||
variant: '',
|
||||
variables: [],
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
content: {},
|
||||
childrenContent: {},
|
||||
isInnerAreaFollowingParentSize: false,
|
||||
});
|
||||
runtimeScene.addObject(customObject);
|
||||
customObject.setPosition(500, 250);
|
||||
|
||||
const childObjects = customObject
|
||||
.getChildrenContainer()
|
||||
.getObjects('MySprite');
|
||||
if (!childObjects || childObjects.length === 0) {
|
||||
throw new Error("Can't get child objects.");
|
||||
}
|
||||
const childObject = childObjects[0];
|
||||
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
// The child object keeps its initial location.
|
||||
expect(childObject.getX()).to.equal(0);
|
||||
expect(childObject.getY()).to.equal(0);
|
||||
expect(childObject.getWidth()).to.equal(64);
|
||||
expect(childObject.getHeight()).to.equal(64);
|
||||
|
||||
customObject.setWidth(2000);
|
||||
customObject.setHeight(3000);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
expect(childObject.getX()).to.equal(0);
|
||||
expect(childObject.getY()).to.equal(0);
|
||||
expect(childObject.getWidth()).to.equal(2000);
|
||||
expect(childObject.getHeight()).to.equal(3000);
|
||||
});
|
||||
});
|
@@ -1,39 +1,49 @@
|
||||
// @ts-check
|
||||
describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame({
|
||||
propertiesOverrides: { windowHeight: 1000, windowWidth: 1000 },
|
||||
});
|
||||
describe('gdjs.AnchorRuntimeBehavior', () => {
|
||||
const anchorBehaviorName = 'Anchor';
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
sceneData: {
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
|
||||
/** @type {gdjs.RuntimeGame} */
|
||||
let runtimeGame;
|
||||
/** @type {gdjs.RuntimeScene} */
|
||||
let runtimeScene;
|
||||
/** @type {gdjs.RuntimeLayer} */
|
||||
let layer;
|
||||
beforeEach(() => {
|
||||
runtimeGame = gdjs.getPixiRuntimeGame({
|
||||
propertiesOverrides: { windowHeight: 1000, windowWidth: 1000 },
|
||||
});
|
||||
runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
sceneData: {
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
layer = runtimeScene.getLayer('');
|
||||
});
|
||||
|
||||
const setGameResolutionSizeAndStep = (width, height) => {
|
||||
@@ -43,6 +53,11 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
};
|
||||
|
||||
const setCamera = (x, y) => {
|
||||
layer.setCameraX(x);
|
||||
layer.setCameraY(y);
|
||||
};
|
||||
|
||||
function createObject(behaviorProperties) {
|
||||
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
@@ -114,10 +129,50 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
return object;
|
||||
};
|
||||
|
||||
describe('(anchor horizontal edge)', function () {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
describe('(anchor horizontal edge)', () => {
|
||||
describe('(basic)', () => {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window left (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window right (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the right and left edge of an object (fixed)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
@@ -125,25 +180,11 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
expect(object.getWidth()).to.equal(1010);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window right (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
it('anchors the left edge of an object (proportional)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
@@ -154,36 +195,267 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving object)', () => {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window left (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
it('anchors the right and left edge of object (fixed)', function () {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window right (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(1010);
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1600);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1100);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the right and left edge of an object (fixed)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the left edge of an object (proportional)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1200);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving camera)', () => {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window left (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
it('anchors the left edge of object (proportional)', function () {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
expect(object.getX()).to.equal(800);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window right (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1800);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1300);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the right and left edge of an object (fixed)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(800);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the left edge of an object (proportional)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1300);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving object and camera)', () => {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window left (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(900);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window right (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1900);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of an object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1400);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the right and left edge of an object (fixed)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 1, rightEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(900);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the left edge of an object (proportional)', () => {
|
||||
const object = createObject({ leftEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1500);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('(anchor vertical edge)', function () {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
describe('(anchor vertical edge)', () => {
|
||||
describe('(basic)', () => {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1000);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
@@ -191,25 +463,11 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
expect(object.getHeight()).to.equal(1010);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, function () {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
it('anchors the top edge of object (proportional)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
@@ -220,71 +478,262 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving object)', () => {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', function () {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(500);
|
||||
expect(object.getHeight()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the top edge of object (proportional)', function () {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1000);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
|
||||
it('can fill the screen with an object (with custom origin)', function () {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 1,
|
||||
topEdgeAnchor: 1,
|
||||
rightEdgeAnchor: 2,
|
||||
bottomEdgeAnchor: 2,
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
});
|
||||
|
||||
it('can fill the screen with an object using proportional anchors (with custom origin)', () => {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 3,
|
||||
topEdgeAnchor: 3,
|
||||
rightEdgeAnchor: 3,
|
||||
bottomEdgeAnchor: 3,
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1700);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1200);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(700);
|
||||
expect(object.getHeight()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the top edge of object (proportional)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1400);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving camera)', () => {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(900);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1900);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1400);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(900);
|
||||
expect(object.getHeight()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the top edge of object (proportional)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(500);
|
||||
expect(object.getY()).to.equal(1400);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
describe('(moving object and camera)', () => {
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window top (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 1 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1100);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window bottom (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(2100);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
['topEdgeAnchor', 'bottomEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window center (fixed)`, () => {
|
||||
const object = createObject({ [objectEdge]: 4 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1600);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('anchors the top and bottom edge of object (fixed)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 1, bottomEdgeAnchor: 2 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1100);
|
||||
expect(object.getHeight()).to.equal(1010);
|
||||
});
|
||||
|
||||
it('anchors the top edge of object (proportional)', () => {
|
||||
const object = createObject({ topEdgeAnchor: 3 });
|
||||
object.setPosition(500, 500);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
object.setPosition(600, 700);
|
||||
setCamera(1300, 1400);
|
||||
setGameResolutionSizeAndStep(2000, 2000);
|
||||
|
||||
expect(object.getX()).to.equal(600);
|
||||
expect(object.getY()).to.equal(1800);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can fill the screen with an object (with custom origin)', () => {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 1,
|
||||
topEdgeAnchor: 1,
|
||||
rightEdgeAnchor: 2,
|
||||
bottomEdgeAnchor: 2,
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
});
|
||||
|
||||
it('can fill the screen with an object using proportional anchors (with custom origin)', () => {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 3,
|
||||
topEdgeAnchor: 3,
|
||||
rightEdgeAnchor: 3,
|
||||
bottomEdgeAnchor: 3,
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
});
|
||||
});
|
||||
|
@@ -75,9 +75,9 @@ module.exports = {
|
||||
.getOrCreate('align')
|
||||
.setValue(objectContent.align)
|
||||
.setType('choice')
|
||||
.addExtraInfo('left')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('right')
|
||||
.addChoice('left', _('Left'))
|
||||
.addChoice('center', _('Center'))
|
||||
.addChoice('right', _('Right'))
|
||||
.setLabel(_('Base alignment'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
@@ -88,9 +88,9 @@ module.exports = {
|
||||
.getOrCreate('verticalTextAlignment')
|
||||
.setValue(objectContent.verticalTextAlignment)
|
||||
.setType('choice')
|
||||
.addExtraInfo('top')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('bottom')
|
||||
.addChoice('top', _('Top'))
|
||||
.addChoice('center', _('Center'))
|
||||
.addChoice('bottom', _('Bottom'))
|
||||
.setLabel(_('Vertical alignment'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
@@ -507,14 +507,16 @@ module.exports = {
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
getPropertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
getPropertyOverridings
|
||||
);
|
||||
|
||||
const bbTextStyles = {
|
||||
@@ -553,7 +555,11 @@ module.exports = {
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
const rawText = object.content.text;
|
||||
const propertyOverridings = this.getPropertyOverridings();
|
||||
const rawText =
|
||||
propertyOverridings && propertyOverridings.has('Text')
|
||||
? propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
if (rawText !== this._pixiObject.text) {
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
@@ -614,7 +620,7 @@ module.exports = {
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
if (this._instance.hasCustomSize()) {
|
||||
if (this._instance.hasCustomSize() && this._pixiObject.width !== 0) {
|
||||
const alignmentX =
|
||||
object.content.align === 'right'
|
||||
? 1
|
||||
|
@@ -103,7 +103,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updatePosition(): void {
|
||||
if (this._object.isWrapping()) {
|
||||
if (this._object.isWrapping() && this._pixiObject.width !== 0) {
|
||||
const alignmentX =
|
||||
this._object._textAlign === 'right'
|
||||
? 1
|
||||
|
@@ -376,13 +376,17 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override getWidth(): float {
|
||||
return this._renderer.getWidth();
|
||||
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
|
||||
}
|
||||
|
||||
override getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override setWidth(width: float): void {
|
||||
this.setWrappingWidth(width);
|
||||
}
|
||||
|
||||
override getDrawableY(): float {
|
||||
return (
|
||||
this.getY() -
|
||||
|
@@ -61,9 +61,9 @@ module.exports = {
|
||||
.getOrCreate('align')
|
||||
.setValue(objectContent.align)
|
||||
.setType('choice')
|
||||
.addExtraInfo('left')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('right')
|
||||
.addChoice('left', _('Left'))
|
||||
.addChoice('center', _('Center'))
|
||||
.addChoice('right', _('Right'))
|
||||
.setLabel(_('Alignment'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
@@ -74,9 +74,9 @@ module.exports = {
|
||||
.getOrCreate('verticalTextAlignment')
|
||||
.setValue(objectContent.verticalTextAlignment)
|
||||
.setType('choice')
|
||||
.addExtraInfo('top')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('bottom')
|
||||
.addChoice('top', _('Top'))
|
||||
.addChoice('center', _('Center'))
|
||||
.addChoice('bottom', _('Bottom'))
|
||||
.setLabel(_('Vertical alignment'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
@@ -630,14 +630,16 @@ module.exports = {
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
getPropertyOverridings
|
||||
) {
|
||||
super(
|
||||
project,
|
||||
instance,
|
||||
associatedObjectConfiguration,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
pixiResourcesLoader,
|
||||
getPropertyOverridings
|
||||
);
|
||||
|
||||
// We'll track changes of the font to trigger the loading of the new font.
|
||||
@@ -663,8 +665,11 @@ module.exports = {
|
||||
|
||||
// Update the rendered text properties (note: Pixi is only
|
||||
// applying changes if there were changed).
|
||||
const rawText = object.content.text;
|
||||
this._pixiObject.text = rawText;
|
||||
const propertyOverridings = this.getPropertyOverridings();
|
||||
this._pixiObject.text =
|
||||
propertyOverridings && propertyOverridings.has('Text')
|
||||
? propertyOverridings.get('Text')
|
||||
: object.content.text;
|
||||
|
||||
const align = object.content.align;
|
||||
this._pixiObject.align = align;
|
||||
@@ -718,7 +723,7 @@ module.exports = {
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
if (this._instance.hasCustomSize()) {
|
||||
if (this._instance.hasCustomSize() && this.getDefaultWidth() !== 0) {
|
||||
const alignmentX =
|
||||
object.content.align === 'right'
|
||||
? 1
|
||||
@@ -727,17 +732,16 @@ module.exports = {
|
||||
: 0;
|
||||
|
||||
const width = this.getCustomWidth();
|
||||
const renderedWidth = this.getDefaultWidth();
|
||||
|
||||
// A vector from the custom size center to the renderer center.
|
||||
const centerToCenterX =
|
||||
(width - this._pixiObject.width) * (alignmentX - 0.5);
|
||||
const centerToCenterX = (width - renderedWidth) * (alignmentX - 0.5);
|
||||
|
||||
this._pixiObject.position.x = this._instance.getX() + width / 2;
|
||||
this._pixiObject.anchor.x =
|
||||
0.5 - centerToCenterX / this._pixiObject.width;
|
||||
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
|
||||
} else {
|
||||
this._pixiObject.position.x =
|
||||
this._instance.getX() + this._pixiObject.width / 2;
|
||||
this._instance.getX() + this.getDefaultWidth() / 2;
|
||||
this._pixiObject.anchor.x = 0.5;
|
||||
}
|
||||
const alignmentY =
|
||||
@@ -747,7 +751,7 @@ module.exports = {
|
||||
? 0.5
|
||||
: 0;
|
||||
this._pixiObject.position.y =
|
||||
this._instance.getY() + this._pixiObject.height * (0.5 - alignmentY);
|
||||
this._instance.getY() + this.getDefaultHeight() * (0.5 - alignmentY);
|
||||
this._pixiObject.anchor.y = 0.5;
|
||||
|
||||
this._pixiObject.rotation = RenderedInstance.toRad(
|
||||
@@ -771,11 +775,11 @@ module.exports = {
|
||||
}
|
||||
|
||||
getDefaultWidth() {
|
||||
return this._pixiObject.width;
|
||||
return this._pixiObject.textWidth * this._pixiObject.scale.x;
|
||||
}
|
||||
|
||||
getDefaultHeight() {
|
||||
return this._pixiObject.height;
|
||||
return this._pixiObject.textHeight * this._pixiObject.scale.y;
|
||||
}
|
||||
|
||||
getOriginY() {
|
||||
|
@@ -146,7 +146,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updatePosition(): void {
|
||||
if (this._object.isWrapping()) {
|
||||
if (this._object.isWrapping() && this.getWidth() !== 0) {
|
||||
const alignmentX =
|
||||
this._object._textAlign === 'right'
|
||||
? 1
|
||||
@@ -155,17 +155,15 @@ namespace gdjs {
|
||||
: 0;
|
||||
|
||||
const width = this._object.getWrappingWidth();
|
||||
const renderedWidth = this.getWidth();
|
||||
|
||||
// A vector from the custom size center to the renderer center.
|
||||
const centerToCenterX =
|
||||
(width - this._pixiObject.width) * (alignmentX - 0.5);
|
||||
const centerToCenterX = (width - renderedWidth) * (alignmentX - 0.5);
|
||||
|
||||
this._pixiObject.position.x = this._object.x + width / 2;
|
||||
this._pixiObject.anchor.x =
|
||||
0.5 - centerToCenterX / this._pixiObject.width;
|
||||
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
|
||||
} else {
|
||||
this._pixiObject.position.x =
|
||||
this._object.x + this._pixiObject.width / 2;
|
||||
this._pixiObject.position.x = this._object.x + this.getWidth() / 2;
|
||||
this._pixiObject.anchor.x = 0.5;
|
||||
}
|
||||
|
||||
@@ -176,7 +174,7 @@ namespace gdjs {
|
||||
? 0.5
|
||||
: 0;
|
||||
this._pixiObject.position.y =
|
||||
this._object.y + this._pixiObject.height * (0.5 - alignmentY);
|
||||
this._object.y + this.getHeight() * (0.5 - alignmentY);
|
||||
this._pixiObject.anchor.y = 0.5;
|
||||
}
|
||||
|
||||
|
@@ -419,13 +419,17 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
override getWidth(): float {
|
||||
return this._renderer.getWidth();
|
||||
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
|
||||
}
|
||||
|
||||
override getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override setWidth(width: float): void {
|
||||
this.setWrappingWidth(width);
|
||||
}
|
||||
|
||||
override getDrawableY(): float {
|
||||
return (
|
||||
this.getY() -
|
||||
|
@@ -12,7 +12,7 @@ This project is released under the MIT License.
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
void DestroyOutsideBehavior::InitializeContent(gd::SerializerElement& content) {
|
||||
content.SetAttribute("extraBorder", 0);
|
||||
content.SetAttribute("extraBorder", 300);
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -12,35 +12,42 @@ This project is released under the MIT License.
|
||||
|
||||
void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("DestroyOutsideBehavior",
|
||||
_("Destroy Outside Screen Behavior"),
|
||||
_("This behavior can be used to destroy "
|
||||
"objects when they go outside of "
|
||||
"the bounds of the camera. Useful for bullets "
|
||||
"or other short-lived objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionInformation(
|
||||
"DestroyOutsideBehavior",
|
||||
_("Destroy Outside Screen Behavior"),
|
||||
_("This behavior can be used to destroy objects when they go "
|
||||
"outside of the bounds of the 2D camera. Useful for 2D bullets or "
|
||||
"other short-lived objects. Don't use it for 3D objects in a "
|
||||
"FPS/TPS game or any game with a camera not being a top view "
|
||||
"(for 3D objects, prefer comparing "
|
||||
"the position, for example Z position to see if an object goes "
|
||||
"outside of the bound of the map). Be careful when using this "
|
||||
"behavior because if the object appears outside of the screen, it "
|
||||
"will be immediately removed."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Game mechanic")
|
||||
.SetTags("screen")
|
||||
.SetExtensionHelpPath("/behaviors/destroyoutside");
|
||||
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension.AddBehavior("DestroyOutside",
|
||||
_("Destroy when outside of the screen"),
|
||||
_("DestroyOutside"),
|
||||
_("Destroy objects automatically when they go "
|
||||
"outside of the screen's borders."),
|
||||
"",
|
||||
"CppPlatform/Extensions/destroyoutsideicon.png",
|
||||
"DestroyOutsideBehavior",
|
||||
std::make_shared<DestroyOutsideBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
extension
|
||||
.AddBehavior("DestroyOutside",
|
||||
_("Destroy when outside of the screen"),
|
||||
_("DestroyOutside"),
|
||||
_("Destroy objects automatically when they go "
|
||||
"outside of the 2D camera borders."),
|
||||
"",
|
||||
"CppPlatform/Extensions/destroyoutsideicon.png",
|
||||
"DestroyOutsideBehavior",
|
||||
std::make_shared<DestroyOutsideBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>())
|
||||
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
|
||||
|
||||
aut.AddCondition("ExtraBorder",
|
||||
_("Additional border"),
|
||||
_("Compare the additional border that the object must cross "
|
||||
"before being deleted."),
|
||||
_("Additional border (extra distance before deletion)"),
|
||||
_("Compare the extra distance (in pixels) the object must "
|
||||
"travel beyond the screen before it gets deleted."),
|
||||
_("the additional border"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
@@ -53,9 +60,9 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("GetExtraBorder");
|
||||
|
||||
aut.AddAction("ExtraBorder",
|
||||
_("Additional border"),
|
||||
_("Change the additional border that the object must cross "
|
||||
"before being deleted."),
|
||||
_("Additional border (extra distance before deletion)"),
|
||||
_("Change the extra distance (in pixels) the object must "
|
||||
"travel beyond the screen before it gets deleted."),
|
||||
_("the additional border"),
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
|
@@ -46,7 +46,7 @@ namespace gdjs {
|
||||
layer.getCameraY() + layer.getCameraHeight() / 2
|
||||
) {
|
||||
//We are outside the camera area.
|
||||
this.owner.deleteFromScene(instanceContainer);
|
||||
this.owner.deleteFromScene();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,25 +35,32 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
std::make_shared<DraggableBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||
|
||||
aut.AddCondition("Dragged",
|
||||
_("Being dragged"),
|
||||
_("Check if the object is being dragged."),
|
||||
_("_PARAM0_ is being dragged"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
aut.AddCondition(
|
||||
"Dragged",
|
||||
_("Being dragged"),
|
||||
_("Check if the object is being dragged. This means the mouse button "
|
||||
"or touch is pressed on it. When the mouse button or touch is "
|
||||
"released, the object is no longer being considered dragged (use "
|
||||
"the condition \"Was just dropped\" to check when the dragging is "
|
||||
"ending)."),
|
||||
_("_PARAM0_ is being dragged"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "Draggable")
|
||||
.SetFunctionName("IsDragged");
|
||||
|
||||
aut.AddCondition("Dropped",
|
||||
_("Was just dropped"),
|
||||
_("Check if the object was just dropped after being dragged."),
|
||||
_("_PARAM0_ was just dropped"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
aut.AddCondition(
|
||||
"Dropped",
|
||||
_("Was just dropped"),
|
||||
_("Check if the object was just dropped after being dragged (the "
|
||||
"mouse button or touch was just released this frame)."),
|
||||
_("_PARAM0_ was just dropped"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "Draggable")
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'FileSystem',
|
||||
_('File system'),
|
||||
_('Access the filesystem of the operating system.'),
|
||||
_(
|
||||
'Access the filesystem of the operating system - only works on native, desktop games exported to Windows, Linux or macOS.'
|
||||
),
|
||||
'Matthias Meike',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
|
@@ -50,6 +50,11 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
|
||||
.replace('.', '-')}-${Date.now()}`;
|
||||
|
||||
before(async function setupFirebase() {
|
||||
// Delete any existing Firebase app before setup
|
||||
if (firebase.apps.length !== 0) {
|
||||
await firebase.app().delete();
|
||||
}
|
||||
|
||||
await gdjs.evtTools.firebaseTools._setupFirebase({
|
||||
getGame: () => ({
|
||||
getExtensionProperty: () => JSON.stringify(firebaseConfig),
|
||||
|
@@ -5,23 +5,25 @@ Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
extension.SetExtensionInformation(
|
||||
"Inventory",
|
||||
_("Inventories"),
|
||||
_("Provides actions and conditions to add an inventory to your game, "
|
||||
"with items in memory."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
extension
|
||||
.SetExtensionInformation(
|
||||
"Inventory",
|
||||
_("Inventories"),
|
||||
_("Actions and conditions to store named inventories in memory, "
|
||||
"with items (indexed by their name), a count for each of them, "
|
||||
"a maximum count and an equipped state. Can be loaded/saved "
|
||||
"from/to a GDevelop variable."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/inventory")
|
||||
.SetCategory("Game mechanic");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
|
||||
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");
|
||||
|
||||
extension
|
||||
@@ -164,14 +166,15 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("InventoryTools::IsEquipped");
|
||||
|
||||
extension
|
||||
.AddAction("SerializeToVariable",
|
||||
_("Save an inventory in a scene variable"),
|
||||
_("Save all the items of the inventory in a scene variable, so that "
|
||||
"it can be restored later."),
|
||||
_("Save inventory _PARAM1_ in variable _PARAM2_"),
|
||||
_("Variables"),
|
||||
"CppPlatform/Extensions/Inventoryicon.png",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddAction(
|
||||
"SerializeToVariable",
|
||||
_("Save an inventory in a scene variable"),
|
||||
_("Save all the items of the inventory in a scene variable, so that "
|
||||
"it can be restored later."),
|
||||
_("Save inventory _PARAM1_ in variable _PARAM2_"),
|
||||
_("Variables"),
|
||||
"CppPlatform/Extensions/Inventoryicon.png",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
@@ -204,13 +207,14 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("InventoryTools::Count");
|
||||
|
||||
extension
|
||||
.AddExpression("Maximum",
|
||||
_("Item maximum"),
|
||||
_("Get the maximum of an item in the inventory, or 0 if it is unlimited"),
|
||||
"",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
.AddParameter("string", _("Item name"))
|
||||
.SetFunctionName("InventoryTools::Maximum");
|
||||
.AddExpression("Maximum",
|
||||
_("Item maximum"),
|
||||
_("Get the maximum of an item in the inventory, or 0 if "
|
||||
"it is unlimited"),
|
||||
"",
|
||||
"CppPlatform/Extensions/Inventoryicon.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Inventory name"))
|
||||
.AddParameter("string", _("Item name"))
|
||||
.SetFunctionName("InventoryTools::Maximum");
|
||||
}
|
||||
|
11
Extensions/JsExtensionTypes.d.ts
vendored
11
Extensions/JsExtensionTypes.d.ts
vendored
@@ -15,6 +15,7 @@ class RenderedInstance {
|
||||
_pixiContainer: PIXI.Container;
|
||||
_pixiResourcesLoader: Class<PixiResourcesLoader>;
|
||||
_pixiObject: PIXI.DisplayObject | null;
|
||||
_propertyOverridings: Map<string, string>;
|
||||
wasUsed: boolean;
|
||||
|
||||
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
|
||||
@@ -25,7 +26,8 @@ class RenderedInstance {
|
||||
instance: gdInitialInstance,
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>,
|
||||
getPropertyOverridings: (() => Map<string, string>) | null = null
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -78,6 +80,8 @@ class RenderedInstance {
|
||||
getDefaultHeight(): number;
|
||||
|
||||
getDefaultDepth(): number;
|
||||
|
||||
getPropertyOverridings(): Map<string, string> | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +109,8 @@ class Rendered3DInstance {
|
||||
associatedObjectConfiguration: gdObjectConfiguration,
|
||||
pixiContainer: PIXI.Container,
|
||||
threeGroup: THREE.Group,
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>
|
||||
pixiResourcesLoader: Class<PixiResourcesLoader>,
|
||||
getPropertyOverridings: (() => Map<string, string>) | null = null
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -172,6 +177,8 @@ class Rendered3DInstance {
|
||||
* Return the depth of the instance when the instance doesn't have a custom size.
|
||||
*/
|
||||
getDefaultDepth(): number;
|
||||
|
||||
getPropertyOverridings(): Map<string, string> | null;
|
||||
}
|
||||
|
||||
declare type ObjectsRenderingService = {
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Leaderboards',
|
||||
_('Leaderboards'),
|
||||
_('Allow your game to send scores to your leaderboards.'),
|
||||
_(
|
||||
'Allow your game to send scores to your leaderboards (anonymously or from the logged-in player) or display existing leaderboards to the player.'
|
||||
),
|
||||
'Florian Rival',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
@@ -30,6 +32,12 @@ module.exports = {
|
||||
.addInstructionOrExpressionGroupMetadata(_('Leaderboards'))
|
||||
.setIcon('JsPlatform/Extensions/leaderboard.svg');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Safari View Controller Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('@gdevelop/cordova-plugin-safariviewcontroller');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SavePlayerScore',
|
||||
|
@@ -22,7 +22,7 @@ module.exports = {
|
||||
'Lighting',
|
||||
_('Lights'),
|
||||
|
||||
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||||
'This provides a 2D light object, and a behavior to mark other 2D objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||||
'Harsimran Virk',
|
||||
'MIT'
|
||||
)
|
||||
@@ -51,7 +51,7 @@ module.exports = {
|
||||
_('Light Obstacle Behavior'),
|
||||
'LightObstacleBehavior',
|
||||
_(
|
||||
'Flag objects as being obstacles to light. The light emitted by light objects will be stopped by the object.'
|
||||
'Flag objects as being obstacles to 2D lights. The light emitted by light objects will be stopped by the object. This does not work on 3D objects and 3D games.'
|
||||
),
|
||||
'',
|
||||
'CppPlatform/Extensions/lightObstacleIcon32.png',
|
||||
@@ -164,7 +164,7 @@ module.exports = {
|
||||
'LightObject',
|
||||
_('Light'),
|
||||
_(
|
||||
'Displays a light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||||
'Displays a 2D light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||||
),
|
||||
'CppPlatform/Extensions/lightIcon32.png',
|
||||
lightObject
|
||||
|
@@ -239,7 +239,7 @@ namespace gdjs {
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
|
||||
obj: gdjs.RuntimeObject | null,
|
||||
eventsFunctionContext: EventsFunctionContext | undefined
|
||||
eventsFunctionContext: EventsFunctionContext | null | undefined
|
||||
) {
|
||||
if (obj === null) {
|
||||
return false;
|
||||
|
@@ -21,7 +21,9 @@ module.exports = {
|
||||
.setExtensionInformation(
|
||||
'Multiplayer',
|
||||
_('Multiplayer'),
|
||||
_('Allow players to connect to lobbies and play together.'),
|
||||
_(
|
||||
'This allows players to join online lobbies and synchronize gameplay across devices without needing to manage servers or networking.\n\nUse the "Open game lobbies" action to let players join a game, and use conditions like "Lobby game has just started" to begin gameplay. Add the "Multiplayer object" behavior to game objects that should be synchronized, and assign or change their ownership using player numbers. Variables and game state (like scenes, scores, or timers) are automatically synced by the host, with options to change ownership or disable sync when needed. Common multiplayer logic —like handling joins/leaves, collisions, and host migration— is supported out-of-the-box for up to 8 players per game.'
|
||||
),
|
||||
'Florian Rival',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
@@ -31,6 +33,79 @@ module.exports = {
|
||||
.addInstructionOrExpressionGroupMetadata(_('Multiplayer'))
|
||||
.setIcon('JsPlatform/Extensions/multiplayer.svg');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Safari View Controller Cordova plugin')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('@gdevelop/cordova-plugin-safariviewcontroller');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'CurrentLobbyID',
|
||||
_('Current lobby ID'),
|
||||
_('Returns current lobby ID.'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.getLobbyID');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'QuickJoinWithLobbyID',
|
||||
_('Join a specific lobby by its ID'),
|
||||
_(
|
||||
'Join a specific lobby. The player will join the game instantly if this is possible.'
|
||||
),
|
||||
_('Join a specific lobby by its ID _PARAM1_'),
|
||||
_('Lobbies'),
|
||||
'JsPlatform/Extensions/multiplayer.svg',
|
||||
'JsPlatform/Extensions/multiplayer.svg'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('string', _('Lobby ID'), '', false)
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Display loader while joining a lobby.'),
|
||||
'',
|
||||
true
|
||||
)
|
||||
.setDefaultValue('yes')
|
||||
.addParameter(
|
||||
'yesorno',
|
||||
_('Display game lobbies if unable to join a specific one.'),
|
||||
'',
|
||||
true
|
||||
)
|
||||
.setDefaultValue('yes')
|
||||
.setHelpPath('/all-features/multiplayer')
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/Multiplayer/peer.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/peerJsHelper.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationcomponents.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/PlayerAuthentication/playerauthenticationtools.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayercomponents.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/messageManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayerVariablesManager.js')
|
||||
.addIncludeFile('Extensions/Multiplayer/multiplayertools.js')
|
||||
.setFunctionName('gdjs.multiplayer.authenticateAndQuickJoinWithLobbyID');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'QuickJoinLobby',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user