Compare commits

..

41 Commits

Author SHA1 Message Date
AlexandreS
c652778cc6 Prettier 2025-05-02 14:21:38 +02:00
AlexandreS
049058eabb Create tween setter factory 2025-05-02 11:24:33 +02:00
AlexandreS
20203d850f WIP: Support saving and restoring tweens 2025-04-30 17:01:46 +02:00
AlexandreS
aeeab60470 Store data as a JS object 2025-04-30 17:01:01 +02:00
AlexandreS
a01507ac5d Use const variable 2025-04-30 15:11:43 +02:00
AlexandreS
f13feaa905 Add TODOs 2025-04-30 15:01:07 +02:00
AlexandreS
c06a5d44ef Simplify logic 2025-04-30 12:18:38 +02:00
AlexandreS
ac691a7590 Rename instructions and parameters 2025-04-30 12:07:27 +02:00
AlexandreS
03dec17c75 Split responsibility into different booleans 2025-04-30 11:50:25 +02:00
AlexandreS
9b76c5c691 Prevent losing control when loading save 2025-04-30 11:07:30 +02:00
AlexandreS
8be1d936b2 Update comment 2025-04-30 10:51:59 +02:00
AlexandreS
31d9ff4ff1 Use array maps to simplify code reading 2025-04-29 18:27:22 +02:00
AlexandreS
78adaa6310 Refactor loading logic 2025-04-29 18:27:22 +02:00
AlexandreS
7efb7014e9 Improve naming and typing 2025-04-29 18:27:22 +02:00
Neyl
0b1f3b74ac 2 exemples for demonstrating the simplicity of the system 2025-04-29 18:27:00 +02:00
Neyl
7711ea8e5c forced "syncOptions" parameter existence, repaired normalized value transmission for volume in audiomanager, added syncOptions for all getNetworkSyncData. 2025-04-29 18:27:00 +02:00
Neyl
5ed0abda37 Update scenestack.ts 2025-04-29 18:26:29 +02:00
Neyl
082e7d287c [NOT WORKING] fixed variable null object misconception in savestate functions, renamed functions for clarity 2025-04-29 18:26:28 +02:00
Neyl
1f3e8cd254 forgot format 2025-04-29 18:26:28 +02:00
Neyl
5f7496704c fixed confusion between constants + added nullification for _loadRequestOptions 2025-04-29 18:26:28 +02:00
Neyl
026f4964f3 updated loadRequest signature and loading system in scenestack 2025-04-29 18:26:28 +02:00
Neyl
0860551f0f created loadRequestOptions type + added security and consistency on indexedDB.ts functions 2025-04-29 18:26:28 +02:00
Neyl
cbc9975a1d Update scenestack.ts 2025-04-29 18:26:28 +02:00
Neyl
0f140c6e54 Update scenestack.ts 2025-04-29 18:26:28 +02:00
Neyl
8ac201c8ca Update howler-sound-manager.ts 2025-04-29 18:26:28 +02:00
Neyl
8606b2571a Update howler-sound-manager.ts 2025-04-29 18:26:28 +02:00
Neyl
d5c455899e final functionnal commit to review 2025-04-29 18:26:28 +02:00
Neyl
ace65de155 small update (abort tween management) 2025-04-29 18:26:28 +02:00
Neyl
83bd850d22 fix soundManager save/load 2025-04-29 18:26:28 +02:00
Neyl
15a6e97e81 wip soundManager management in save/load 2025-04-29 18:26:28 +02:00
Neyl
9e2bd6d1a0 added a try catch for indexeDB opening 2025-04-29 18:26:28 +02:00
Neyl
9b00761419 Update PhysicsCharacter3DRuntimeBehavior.ts 2025-04-29 18:26:28 +02:00
Neyl
d48b55c4b9 last small details for concept (still needs support for expression management) 2025-04-29 18:26:28 +02:00
Neyl
fd9331ca22 Update after clement comments on pr. Fixed several missed points and added some constants in separated files for clarity 2025-04-29 18:26:28 +02:00
Neyl
751f71c770 Update multiplayerobjectruntimebehavior.ts 2025-04-29 18:26:28 +02:00
Neyl
1365d10717 updated all runtimesobjects to have syncOptions, moved loading logic to step function of scenestack, switch from browser localstorage to indexedDB 2025-04-29 18:26:28 +02:00
Neyl
91eb2395d5 Update Model3DRuntimeObject.ts 2025-04-29 18:26:02 +02:00
Neyl
4d6719a74b WIP working version with saving on localstorage 2025-04-29 18:26:02 +02:00
Neyl
806e7591d7 psuh for save 2025-04-29 18:24:38 +02:00
Neyl
824ef9496f TOFIX 2025-04-29 18:24:38 +02:00
Neyl
b9b2b6ad48 first commit 2025-04-29 18:24:37 +02:00
840 changed files with 69009 additions and 42930 deletions

View File

@@ -13,18 +13,17 @@ 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 (including the GDevelop.js library).
# Build the **entire** app for macOS.
build-macos:
macos:
xcode: 16.4.0
resource_class: m4pro.medium
xcode: 14.2.0
resource_class: macos.m1.large.gen1
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.
@@ -47,9 +46,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" }}-{{ checksum "GDJS/package-lock.json" }}
- gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.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
@@ -70,8 +69,7 @@ jobs:
- newIDE/electron-app/node_modules
- newIDE/app/node_modules
- GDevelop.js/node_modules
- 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" }}
key: gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.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).
@@ -88,37 +86,15 @@ jobs:
- store_artifacts:
path: newIDE/electron-app/dist
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
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
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)/
- run:
name: Deploy to S3 (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
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the app for Linux (using a pre-built GDevelop.js library).
# Build the **entire** app for Linux.
build-linux:
# CircleCI docker workers are failing if they don't have enough memory (no swap)
resource_class: xlarge
@@ -131,33 +107,51 @@ jobs:
- checkout
- aws-cli/setup
# System dependencies (for Electron Builder)
# System dependencies (for Electron Builder and Emscripten)
- run:
name: Update system dependencies
command: sudo apt-get update
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 ..
- 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" }}-{{ checksum "GDJS/package-lock.json" }}
- gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# fallback to using the latest cache if no exact match is found
- gd-linux-nodejs-dependencies-
- gd-linux-nodejs-dependencies---
# GDevelop IDE dependencies (using an exact version of GDevelop.js, built previously)
- 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)
- run:
name: Install GDevelop IDE dependencies
command: export REQUIRES_EXACT_LIBGD_JS_VERSION=true && cd newIDE/app && npm install && cd ../electron-app && npm install
command: 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
- 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" }}
key: gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
- run:
@@ -301,203 +295,14 @@ 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 always run them.
# Extra checks are resource intensive so don't all run them.
filters:
branches:
only:
@@ -505,36 +310,13 @@ 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
View File

@@ -33,4 +33,3 @@
.Spotlight-V100
.Trashes
Thumbs.db
.claude

3
.vscode/tasks.json vendored
View File

@@ -38,7 +38,8 @@
"presentation": {
"reveal": "silent"
},
"isBackground": true
"isBackground": true,
"runOptions": { "instanceLimit": 1, "runOn": "folderOpen" }
},
{
"type": "npm",

View File

@@ -61,12 +61,10 @@ void GroupEvent::UnserializeFrom(gd::Project& project,
project, events, element.GetChild("events"));
parameters.clear();
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());
}
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_,

View File

@@ -163,21 +163,6 @@ 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);

View File

@@ -109,10 +109,6 @@ 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;

View File

@@ -286,20 +286,6 @@ 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>
@@ -318,7 +304,6 @@ 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;

View File

@@ -221,8 +221,6 @@ 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());
}
@@ -238,8 +236,6 @@ 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);

View File

@@ -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,8 +235,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("SetAngle",
_("Angle"),
_("Change the angle of rotation of an object (in degrees). For "
"3D objects, this is the rotation around the Z axis."),
_("Change the angle of rotation of an object (in degrees)."),
_("the angle"),
_("Angle"),
"res/actions/direction24_black.png",
@@ -251,8 +250,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("Rotate",
_("Rotate"),
_("Rotate an object, clockwise if the speed is positive, "
"counterclockwise otherwise. For 3D objects, this is the "
"rotation around the Z axis."),
"counterclockwise otherwise."),
_("Rotate _PARAM0_ at speed _PARAM1_ deg/second"),
_("Angle"),
"res/actions/rotate24_black.png",
@@ -636,8 +634,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddCondition("Angle",
_("Angle"),
_("Compare the angle, in degrees, of the specified object. "
"For 3D objects, this is the angle around the Z axis."),
_("Compare the angle of the specified object."),
_("the angle (in degrees)"),
_("Angle"),
"res/conditions/direction24_black.png",
@@ -838,13 +835,14 @@ 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"))
@@ -1270,8 +1268,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("Angle",
_("Angle"),
_("Current angle, in degrees, of the object. For 3D "
"objects, this is the angle around the Z axis."),
_("Current angle, in degrees, of the object"),
_("Angle"),
"res/actions/direction_black.png")
.AddParameter("object", _("Object"));
@@ -1574,9 +1571,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension
.AddAction("Create",
_("Create an object"),
_("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 an object at specified position"),
_("Create object _PARAM1_ at position _PARAM2_;_PARAM3_ "
"(layer: _PARAM4_)"),
"",

View File

@@ -18,21 +18,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("AnimatableCapability",
_("Objects with animations"),
_("Actions and conditions for objects having animations (sprite, 3D models...)."),
_("Animatable capability"),
_("Animate objects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with animations"))
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
.SetIcon("res/actions/animation24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
.SetIcon("res/actions/animation24.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"AnimatableBehavior",
_("Objects with animations"),
_("Animatable capability"),
"Animation",
_("Actions and conditions for objects having animations (sprite, 3D models...).."),
_("Animate objects."),
"",
"res/actions/animation24.png",
"AnimatableBehavior",

View File

@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("EffectCapability",
_("Objects with effects"),
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
_("Effect capability"),
_("Apply visual effects to 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",
_("Objects with effects"),
_("Effect capability"),
"Effect",
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
_("Apply visual effects to objects."),
"",
"res/actions/effect_black.svg",
"EffectBehavior",

View File

@@ -18,8 +18,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("FlippableCapability",
_("Flippable objects"),
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
_("Flippable capability"),
_("Flip objects."),
"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 objects"),
_("Flippable capability"),
"Flippable",
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
_("Flip objects."),
"",
"res/actions/flipX24.png",
"FlippableBehavior",

View File

@@ -18,30 +18,27 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("OpacityCapability",
_("Objects with opacity"),
_("Action/condition/expression to change or "
"check the opacity of an object (0-255)."),
_("Opacity capability"),
_("Change the object opacity."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with opacity"))
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
.SetIcon("res/actions/opacity24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
.SetIcon("res/actions/opacity24.png");
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();
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();
aut.AddExpressionAndConditionAndAction(
"number",
@@ -55,9 +52,8 @@ 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("");

View File

@@ -16,13 +16,11 @@ namespace gd {
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
gd::PlatformExtension &extension) {
extension
.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)")
.SetExtensionInformation("ResizableCapability",
_("Resizable capability"),
_("Change the object dimensions."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
"res/actions/scale24_black.png");
@@ -30,10 +28,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
gd::BehaviorMetadata &aut =
extension
.AddBehavior("ResizableBehavior",
_("Resizable objects"),
_("Resizable capability"),
"Resizable",
_("Change or compare the size (width/height) of an "
"object which can be resized (i.e: most objects)."),
_("Change the object dimensions."),
"",
"res/actions/scale24_black.png",
"ResizableBehavior",

View File

@@ -18,30 +18,27 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("ScalableCapability",
_("Scalable objects"),
_("Actions/conditions/expression to change or "
"check the scale of an object (default: 1)."),
_("Scalable capability"),
_("Change the object scale."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable objects"))
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
.SetIcon("res/actions/scale24_black.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
.SetIcon("res/actions/scale24_black.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
"res/actions/scale24_black.png");
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();
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();
aut.AddExpressionAndConditionAndAction(
"number",

View File

@@ -18,17 +18,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("TextContainerCapability",
_("Objects containing a text"),
_("Text capability"),
_("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(_("Objects containing a text"))
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
.SetIcon("res/conditions/text24_black.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"TextContainerBehavior",
_("Objects containing a text"),
_("Text capability"),
"Text",
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
"",

View File

@@ -16,9 +16,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
.SetExtensionInformation(
"BuiltinCommonConversions",
_("Conversion"),
"Expressions to convert numbers to string, strings to numbers, "
"angles (degrees from/to radians) and a GDevelop variable to/from a "
"JSON string.",
"Expressions to convert number, texts and quantities.",
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/common-conversions");
@@ -43,7 +41,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"),
"",
@@ -74,8 +72,7 @@ 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

View File

@@ -59,44 +59,36 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
// end of compatibility code
extension
.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(
"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/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:"),
.AddCondition("Or",
_("Or"),
_("Check if one of the sub conditions is true"),
_("If one of these conditions is true:"),
"",
"res/conditions/not24_black.png",
"res/conditions/not_black.png")
"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")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
extension
.AddCondition(
"Not",
_("Not"),
_("Return the contrary of the result of the sub conditions"),
_("Invert the logical result of these conditions:"),
"",
"res/conditions/not24_black.png",
"res/conditions/not_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();

View File

@@ -15,11 +15,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
.SetExtensionInformation(
"BuiltinKeyboard",
_("Keyboard"),
_("Conditions to check keys pressed on a keyboard. Note that this "
_("Allows your game to respond to keyboard input. Note that this "
"does not work with on-screen keyboard on touch devices: use "
"instead mouse/touch conditions when making a game for "
"mobile/touchscreen devices or when making a new game from "
"scratch."),
"instead conditions related to touch when making a game for "
"mobile/touchscreen devices."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/keyboard")
@@ -85,7 +84,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"),

View File

@@ -72,8 +72,7 @@ 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")
@@ -125,8 +124,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("mod",
_("Modulo"),
_("Compute \"x mod y\". GDevelop does NOT support the \% "
"operator. Use this mod(x, y) function instead."),
_("x mod y"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("x (as in x mod y)"))
@@ -186,8 +184,11 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
.AddParameter("expression", _("Expression"));
extension
.AddExpression(
"asinh", _("Arcsine"), _("Arcsine"), "", "res/mathfunction.png")
.AddExpression("asinh",
_("Arcsine"),
_("Arcsine"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
extension
@@ -217,8 +218,11 @@ 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
@@ -256,13 +260,12 @@ 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
@@ -290,20 +293,29 @@ 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
@@ -312,8 +324,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
_("Round a number to the Nth decimal place"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Number to Round"))
.AddParameter("expression", _("Decimal Places"), "", true);
.AddParameter("expression", _("Expression"))
.AddParameter("expression", _("Expression"), "", true);
extension
.AddExpression("exp",
@@ -324,13 +336,19 @@ 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"));
@@ -369,8 +387,11 @@ 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
@@ -382,13 +403,12 @@ 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
@@ -408,13 +428,12 @@ 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
@@ -444,28 +463,26 @@ 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"));
@@ -480,8 +497,7 @@ 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"))

View File

@@ -16,11 +16,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.SetExtensionInformation(
"BuiltinMouse",
_("Mouse and touch"),
"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 "
"Conditions and actions to handle either the mouse or touches on "
"touchscreen. By default, conditions related to the mouse will also "
"handle the touches - so that it's easier to handle both in your "
"game. You can disable this behavior if you want to handle them "
"separately in different events.",
@@ -276,26 +273,28 @@ 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();

View File

@@ -15,9 +15,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
.SetExtensionInformation(
"BuiltinNetwork",
_("Network"),
_("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."),
_("Features to send web requests, communicate with external \"APIs\" "
"and other network related tasks."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/network")

View File

@@ -4,8 +4,8 @@
* reserved. This project is released under the MIT License.
*/
#include "AllBuiltinExtensions.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
using namespace std;
namespace gd {
@@ -16,11 +16,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
.SetExtensionInformation(
"BuiltinScene",
_("Scene"),
_("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."),
_("Actions and conditions to manipulate the scenes during the game."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
@@ -170,28 +166,25 @@ 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)."),
_("_PARAM1_ 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"))
@@ -199,14 +192,13 @@ 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"))

View File

@@ -15,13 +15,12 @@ namespace gd {
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation(
"Sprite",
_("Sprite"),
_("Sprite are animated objects which can be used "
"for most elements of a 2D game."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionInformation("Sprite",
_("Sprite"),
_("Sprite are animated object which can be used "
"for most elements of a game."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/sprite");
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
.SetIcon("CppPlatform/Extensions/spriteicon.png");
@@ -31,7 +30,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
.AddObject<SpriteObject>("Sprite",
_("Sprite"),
_("Animated object which can be used for "
"most elements of a 2D game."),
"most elements of a game."),
"CppPlatform/Extensions/spriteicon.png")
.SetCategoryFullName(_("General"))
.SetOpenFullEditorLabel(_("Edit animations"))
@@ -646,12 +645,11 @@ 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

View File

@@ -16,8 +16,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
.SetExtensionInformation(
"BuiltinStringInstructions",
_("Text manipulation"),
"Provides expressions to manipulate strings (also called texts): new "
"line, upper/lowercase, substring, find, replace, etc...",
"Provides expressions to manipulate strings (also called texts).",
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("" /*TODO: Add a documentation page for this */);
@@ -192,8 +191,7 @@ 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",
@@ -201,11 +199,10 @@ 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

View File

@@ -15,12 +15,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
.SetExtensionInformation(
"BuiltinTime",
_("Timers and time"),
"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.",
"Actions and conditions to run timers, get the current time or "
"modify the time scale (speed at which the game is running - useful "
"for slow motion effects).",
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/timers-and-time");
@@ -195,28 +192,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddExpression("TimerElapsedTime",
_("Scene timer value"),
_("Value of a scene timer (in seconds)"),
_("Value of a scene timer"),
"",
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
extension
.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")
.AddExpression("TimeFromStart",
_("Time elapsed since the beginning of the scene"),
_("Time elapsed since the beginning of the scene"),
"",
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "");
extension
.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")
.AddExpression("TempsDebut",
_("Time elapsed since the beginning of the scene"),
_("Time elapsed since the beginning of the scene"),
"",
"res/actions/time.png")
.SetHidden()
.AddCodeOnlyParameter("currentScene", "");
@@ -231,21 +226,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddExpression("Time",
_("Current time"),
_("Gives the current time"),
_("Current time"),
"",
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter(
"stringWithSelector",
_("- 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: 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\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
"\"yday\", \"timestamp\"]");
}

View File

@@ -15,17 +15,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
.SetExtensionInformation(
"BuiltinWindow",
_("Game window and resolution"),
"Actions and conditions to manipulate the game window or change how "
"the game is resized according to the screen size. "
"Provides actions and conditions to manipulate the game window. "
"Depending on the platform on which the game is running, not all of "
"these features can be applied.\n"
"Also contains expressions to read the screen size.",
"these features can be applied.",
"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

View File

@@ -779,26 +779,6 @@ 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,

View File

@@ -651,10 +651,6 @@ 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,

View File

@@ -63,6 +63,7 @@ void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
}
// Copy missing behaviors
auto &behaviors = object.GetAllBehaviorContents();
for (const auto &pair : defaultBehaviors) {
const auto &behaviorName = pair.first;
const auto &defaultBehavior = pair.second;
@@ -81,9 +82,11 @@ void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
}
}
// Delete extra behaviors
for (auto &behaviorName : object.GetAllBehaviorNames()) {
for (auto it = behaviors.begin(); it != behaviors.end(); ++it) {
const auto &behaviorName = it->first;
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
object.RemoveBehavior(behaviorName);
--it;
}
}

View File

@@ -18,7 +18,6 @@
#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"
@@ -62,6 +61,9 @@ 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");
@@ -74,28 +76,16 @@ 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");
@@ -134,54 +124,41 @@ 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;
return;
}
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;
if (customObjectConfiguration
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
customObjectConfiguration
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
return;
}
const auto &variantName = customObjectConfiguration->GetVariantName();
const auto &variantIdentifier =
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
variantName;
const auto &variant = variants.HasVariantNamed(variantName)
? variants.GetVariant(variantName)
: eventsBasedObject.GetDefaultVariant();
return &variant;
auto insertResult = alreadyUsedVariantIdentifiers.insert(variantIdentifier);
if (insertResult.second) {
const auto &eventsBasedObject =
project.GetEventsBasedObject(object.GetType());
const auto &variants = eventsBasedObject.GetVariants();
const auto &variant = variants.HasVariantNamed(variantName)
? variants.GetVariant(variantName)
: eventsBasedObject.GetDefaultVariant();
SerializerElement &pairElement = variantsElement.AddChild("variant");
pairElement.SetAttribute("objectType", object.GetType());
SerializerElement &variantElement = pairElement.AddChild("variant");
variant.SerializeTo(variantElement);
// TODO Recursivity
for (auto &object : variant.GetObjects().GetObjects()) {
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
project, *object, variantsElement, alreadyUsedVariantIdentifiers);
}
}
}
} // namespace gd

View File

@@ -21,7 +21,6 @@ class InitialInstance;
class SerializerElement;
class EffectsContainer;
class AbstractFileSystem;
class EventsBasedObjectVariant;
} // namespace gd
namespace gd {
@@ -59,8 +58,6 @@ private:
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

View File

@@ -1781,14 +1781,6 @@ 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) {

View File

@@ -704,16 +704,6 @@ 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:

View File

@@ -250,28 +250,25 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
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) {
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);
if (variantName.empty() ||
!eventsBasedObject.GetVariants().HasVariantNamed(variantName)) {
for (auto &childObject :
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjects()) {
childObject->GetConfiguration().ExposeResources(worker);
}
} else {
for (auto &childObject : eventsBasedObject.GetVariants()
.GetVariant(variantName)
.GetObjects()
.GetObjects()) {
childObject->GetConfiguration().ExposeResources(worker);
}
}
}
}

View File

@@ -78,15 +78,6 @@ public:
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 {

View File

@@ -8,8 +8,6 @@
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
gd::String Effect::badStringParameterValue;
void Effect::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());

View File

@@ -3,7 +3,8 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#ifndef GDCORE_EFFECT_H
#define GDCORE_EFFECT_H
#include <map>
namespace gd {
class SerializerElement;
@@ -34,43 +35,28 @@ 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) const {
auto itr = doubleParameters.find(name);
return itr == doubleParameters.end() ? 0 : itr->second;
double GetDoubleParameter(const gd::String& name) {
return doubleParameters[name];
}
bool HasDoubleParameter(const gd::String &name) const {
return doubleParameters.find(name) != doubleParameters.end();
}
void SetStringParameter(const gd::String &name, const gd::String &value) {
void SetStringParameter(const gd::String& name, const gd::String& value) {
stringParameters[name] = value;
}
const gd::String &GetStringParameter(const gd::String &name) const {
auto itr = stringParameters.find(name);
return itr == stringParameters.end() ? badStringParameterValue : itr->second;
const gd::String& GetStringParameter(const gd::String& name) {
return stringParameters[name];
}
bool HasStringParameter(const gd::String &name) const {
return stringParameters.find(name) != stringParameters.end();
}
void SetBooleanParameter(const gd::String &name, bool value) {
void SetBooleanParameter(const gd::String& name, bool value) {
booleanParameters[name] = value;
}
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();
bool GetBooleanParameter(const gd::String& name) {
return booleanParameters[name];
}
const std::map<gd::String, double>& GetAllDoubleParameters() const {
@@ -108,9 +94,7 @@ 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

View File

@@ -83,9 +83,6 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element, bool isEx
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)
@@ -142,9 +139,6 @@ 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 =

View File

@@ -12,11 +12,9 @@
#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;
@@ -408,7 +406,6 @@ 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;

View File

@@ -1,105 +0,0 @@
/*
* 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

View File

@@ -365,8 +365,6 @@ class GD_CORE_API InitialInstance {
* the same initial instance between serialization.
*/
InitialInstance& ResetPersistentUuid();
const gd::String& GetPersistentUuid() const { return persistentUuid; }
///@}
private:

View File

@@ -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,8 +54,6 @@ Layout::Layout()
backgroundColorG(209),
backgroundColorB(209),
stopSoundsOnStartup(true),
resourcesPreloading("inherit"),
resourcesUnloading("inherit"),
standardSortMethod(true),
disableInputWhenNotFocused(true),
variables(gd::VariablesContainer::SourceType::Scene),
@@ -246,10 +244,6 @@ 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);
@@ -310,10 +304,6 @@ 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");
@@ -401,8 +391,6 @@ 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;

View File

@@ -349,36 +349,6 @@ 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
@@ -411,10 +381,6 @@ 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

View File

@@ -81,11 +81,6 @@ 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;

View File

@@ -74,9 +74,7 @@ Project::Project()
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()),
variables(gd::VariablesContainer::SourceType::Global),
objectsContainer(gd::ObjectsContainer::SourceType::Global),
sceneResourcesPreloading("at-startup"),
sceneResourcesUnloading("never") {}
objectsContainer(gd::ObjectsContainer::SourceType::Global) {}
Project::~Project() {}
@@ -1168,13 +1166,6 @@ 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"));
@@ -1316,9 +1307,6 @@ void Project::Init(const gd::Project& game) {
variables = game.GetVariables();
projectFile = game.GetProjectFile();
sceneResourcesPreloading = game.sceneResourcesPreloading;
sceneResourcesUnloading = game.sceneResourcesUnloading;
}
} // namespace gd

View File

@@ -964,37 +964,6 @@ 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
@@ -1152,10 +1121,6 @@ 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.

View File

@@ -21,19 +21,14 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
element.AddChild("unit").SetStringValue(measurementUnit.GetName());
}
element.AddChild("label").SetStringValue(label);
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);
}
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 (hidden) {
element.AddChild("hidden").SetBoolValue(hidden);
}
@@ -64,21 +59,16 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
: gd::MeasurementUnit::GetUndefined();
}
label = element.GetChild("label").GetStringValue();
description = element.HasChild("description")
? element.GetChild("description").GetStringValue()
: "";
group = element.HasChild("group") ? element.GetChild("group").GetStringValue()
: "";
description = element.GetChild("description").GetStringValue();
group = element.GetChild("group").GetStringValue();
extraInformation.clear();
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());
}
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()

View File

@@ -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,19 +17,6 @@ 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
@@ -44,12 +31,8 @@ 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) {}
@@ -58,13 +41,10 @@ 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
@@ -108,20 +88,13 @@ 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.
*/
@@ -145,8 +118,7 @@ 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;
}
@@ -156,18 +128,14 @@ 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; }
const std::vector<PropertyDescriptorChoice>& GetChoices() const {
return choices;
std::vector<gd::String>& GetExtraInfo() {
return extraInformation;
}
/**
@@ -210,26 +178,23 @@ 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;
}
@@ -266,17 +231,15 @@ 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 an additional information
///< like an object type.
extraInformation; ///< Can be used to store for example the available
///< choices, if a property is a displayed as a combo
///< box.
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;
};

View File

@@ -33,132 +33,7 @@ using namespace gd;
TEST_CASE("ObjectAssetSerializer", "[common]") {
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") {
SECTION("Can serialize custom objects as assets") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);

View File

@@ -110,9 +110,11 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): Object3DNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Object3DNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
z: this.getZ(),
d: this.getDepth(),
rx: this.getRotationX(),
@@ -123,8 +125,11 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
super.updateFromNetworkSyncData(networkSyncData);
updateFromNetworkSyncData(
networkSyncData: Object3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
if (networkSyncData.rx !== undefined)

View File

@@ -25,8 +25,6 @@ namespace gdjs {
topFaceVisible: boolean;
bottomFaceVisible: boolean;
tint: string | undefined;
isCastingShadow: boolean;
isReceivingShadow: boolean;
materialType: 'Basic' | 'StandardWithoutMetalness';
};
}
@@ -73,10 +71,8 @@ namespace gdjs {
string,
];
_materialType: gdjs.Cube3DRuntimeObject.MaterialType =
gdjs.Cube3DRuntimeObject.MaterialType.StandardWithoutMetalness;
gdjs.Cube3DRuntimeObject.MaterialType.Basic;
_tint: string;
_isCastingShadow: boolean = true;
_isReceivingShadow: boolean = true;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -125,8 +121,6 @@ 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
@@ -436,25 +430,15 @@ 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;
}
getNetworkSyncData(): Cube3DObjectNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Cube3DObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
mt: this._materialType,
fo: this._facesOrientation,
bfu: this._backFaceUpThroughWhichAxisRotation,
@@ -466,9 +450,10 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: Cube3DObjectNetworkSyncData
networkSyncData: Cube3DObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;
@@ -549,14 +534,6 @@ 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 {

View File

@@ -81,14 +81,13 @@ 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();
@@ -115,13 +114,6 @@ 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];

View File

@@ -42,6 +42,7 @@ namespace gdjs {
objectData: gdjs.Object3DData & gdjs.CustomObjectConfiguration
) {
super(parent, objectData);
this._renderer.reinitialize(this, parent);
}
protected override _createRender() {
@@ -85,9 +86,11 @@ namespace gdjs {
}
}
getNetworkSyncData(): CustomObject3DNetworkSyncDataType {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): CustomObject3DNetworkSyncDataType {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
z: this.getZ(),
d: this.getDepth(),
rx: this.getRotationX(),
@@ -97,9 +100,10 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: CustomObject3DNetworkSyncDataType
networkSyncData: CustomObject3DNetworkSyncDataType,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
if (networkSyncData.rx !== undefined)

View File

@@ -44,7 +44,10 @@ namespace gdjs {
) {
this._object = object;
this._isContainerDirty = true;
this._threeGroup.clear();
const layer = parent.getLayer('');
if (layer) {
layer.getRenderer().add3DRendererObject(this._threeGroup);
}
}
_updateThreeGroup() {

View File

@@ -6,7 +6,6 @@ namespace gdjs {
r: number;
t: string;
}
const shadowHelper = false;
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::DirectionalLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -18,63 +17,19 @@ namespace gdjs {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
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;
light: THREE.DirectionalLight;
rotationObject: THREE.Group;
_isEnabled: boolean = false;
top: string = 'Y-';
elevation: float = 45;
rotation: float = 0;
constructor() {
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;
this.light = new THREE.DirectionalLight();
this.light.position.set(1, 0, 0);
this.rotationObject = new THREE.Group();
this.rotationObject.add(this.light);
this.updateRotation();
}
isEnabled(target: EffectsTarget): boolean {
@@ -98,12 +53,7 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.add(this._light);
scene.add(this._light.target);
if (this._shadowCameraHelper) {
scene.add(this._shadowCameraHelper);
}
scene.add(this.rotationObject);
this._isEnabled = true;
return true;
}
@@ -115,164 +65,82 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.remove(this._light);
scene.remove(this._light.target);
if (this._shadowCameraHelper) {
scene.remove(this._shadowCameraHelper);
}
scene.remove(this.rotationObject);
this._isEnabled = false;
return true;
}
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);
}
}
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;
} else if (parameterName === 'distanceFromCamera') {
this._distanceFromCamera = value;
} else if (parameterName === 'frustumSize') {
this._frustumSize = value;
} else if (parameterName === 'minimumShadowBias') {
this._minimumShadowBias = 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;
} else if (parameterName === 'distanceFromCamera') {
return this._distanceFromCamera;
} else if (parameterName === 'frustumSize') {
return this._frustumSize;
} else if (parameterName === 'minimumShadowBias') {
return this._minimumShadowBias;
return this.rotation;
}
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;
}
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;
}
this.top = value;
this.updateRotation();
}
}
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 {
if (parameterName === 'isCastingShadow') {
this._light.castShadow = value;
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);
}
}
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.light.intensity = syncData.i;
this.light.color.setHex(syncData.c);
this.elevation = syncData.e;
this.rotation = syncData.r;
this.top = syncData.t;
this.updateRotation();
}
})();
}

View File

@@ -18,15 +18,18 @@ namespace gdjs {
return new gdjs.PixiFiltersTools.EmptyFilter();
}
return new (class implements gdjs.PixiFiltersTools.Filter {
_top: string = 'Z+';
_elevation: float = 90;
_rotation: float = 0;
light: THREE.HemisphereLight;
rotationObject: THREE.Group;
_isEnabled: boolean = false;
_light: THREE.HemisphereLight;
top: string = 'Y-';
elevation: float = 45;
rotation: float = 0;
constructor() {
this._light = new THREE.HemisphereLight();
this.light = new THREE.HemisphereLight();
this.light.position.set(1, 0, 0);
this.rotationObject = new THREE.Group();
this.rotationObject.add(this.light);
this.updateRotation();
}
@@ -51,7 +54,7 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.add(this._light);
scene.add(this.rotationObject);
this._isEnabled = true;
return true;
}
@@ -63,106 +66,96 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.remove(this._light);
scene.remove(this.rotationObject);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'intensity') {
this._light.intensity = value;
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 === '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))
);
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 {
// `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))
);
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation - 90);
this.rotationObject.rotation.z = -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();
}
})();

View File

@@ -21,9 +21,7 @@ module.exports = {
.setExtensionInformation(
'Scene3D',
_('3D'),
_(
'Support for 3D in GDevelop: this provides 3D objects and the common features for all 3D objects.'
),
_('Support for 3D in GDevelop.'),
'Florian Rival',
'MIT'
)
@@ -38,9 +36,7 @@ module.exports = {
'Base3DBehavior',
_('3D capability'),
'Object3D',
_(
'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).'
),
_('Move the object in 3D space.'),
'',
'res/conditions/3d_box.svg',
'Base3DBehavior',
@@ -162,12 +158,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationX')
.setGetter('getRotationX');
@@ -183,12 +174,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationY')
.setGetter('getRotationY');
@@ -206,7 +192,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundX');
@@ -224,7 +210,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundY');
@@ -242,7 +228,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundZ');
}
@@ -252,7 +238,7 @@ module.exports = {
.addObject(
'Model3DObject',
_('3D Model'),
_('An animated 3D model, useful for most elements of a 3D game.'),
_('An animated 3D model.'),
'JsPlatform/Extensions/3d_model.svg',
new gd.Model3DObjectConfiguration()
)
@@ -604,12 +590,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setHidden()
.setFunctionName('setRotationX')
.setGetter('getRotationX');
@@ -626,12 +607,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setHidden()
.setFunctionName('setRotationY')
.setGetter('getRotationY');
@@ -650,7 +626,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundX');
@@ -669,7 +645,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundY');
@@ -688,7 +664,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.addParameter('number', _('Rotation angle'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundZ');
@@ -879,9 +855,7 @@ module.exports = {
propertyName === 'rightFaceResourceRepeat' ||
propertyName === 'topFaceResourceRepeat' ||
propertyName === 'bottomFaceResourceRepeat' ||
propertyName === 'enableTextureTransparency' ||
propertyName === 'isCastingShadow' ||
propertyName === 'isReceivingShadow'
propertyName === 'enableTextureTransparency'
) {
objectContent[propertyName] = newValue === '1';
return true;
@@ -909,8 +883,8 @@ module.exports = {
.getOrCreate('facesOrientation')
.setValue(objectContent.facesOrientation || 'Y')
.setType('choice')
.addChoice('Y', 'Y')
.addChoice('Z', 'Z')
.addExtraInfo('Y')
.addExtraInfo('Z')
.setLabel(_('Faces orientation'))
.setDescription(
_(
@@ -970,8 +944,8 @@ module.exports = {
.getOrCreate('backFaceUpThroughWhichAxisRotation')
.setValue(objectContent.backFaceUpThroughWhichAxisRotation || 'X')
.setType('choice')
.addChoice('X', 'X')
.addChoice('Y', 'Y')
.addExtraInfo('X')
.addExtraInfo('Y')
.setLabel(_('Back face orientation'))
.setDescription(
_(
@@ -1105,29 +1079,11 @@ module.exports = {
objectProperties
.getOrCreate('materialType')
.setValue(objectContent.materialType || 'StandardWithoutMetalness')
.setValue(objectContent.materialType || 'Basic')
.setType('choice')
.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'));
.addExtraInfo('Basic')
.addExtraInfo('StandardWithoutMetalness')
.setLabel(_('Material type'));
return objectProperties;
};
@@ -1145,7 +1101,7 @@ module.exports = {
topFaceResourceName: '',
bottomFaceResourceName: '',
frontFaceVisible: true,
backFaceVisible: true,
backFaceVisible: false,
leftFaceVisible: true,
rightFaceVisible: true,
topFaceVisible: true,
@@ -1156,10 +1112,8 @@ module.exports = {
rightFaceResourceRepeat: false,
topFaceResourceRepeat: false,
bottomFaceResourceRepeat: false,
materialType: 'StandardWithoutMetalness',
materialType: 'Basic',
tint: '255;255;255',
isCastingShadow: true,
isReceivingShadow: true,
};
Cube3DObject.updateInitialInstanceProperty = function (
@@ -1513,12 +1467,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationX')
.setHidden()
.setGetter('getRotationX');
@@ -1535,12 +1484,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setRotationY')
.setHidden()
.setGetter('getRotationY');
@@ -1946,11 +1890,11 @@ module.exports = {
.setType('number');
properties
.getOrCreate('top')
.setValue('Z+')
.setValue('Y-')
.setLabel(_('3D world top'))
.setType('choice')
.addExtraInfo('Z+')
.addExtraInfo('Y-')
.addExtraInfo('Z+')
.setGroup(_('Orientation'));
properties
.getOrCreate('elevation')
@@ -1965,47 +1909,6 @@ 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
@@ -2037,11 +1940,11 @@ module.exports = {
.setType('number');
properties
.getOrCreate('top')
.setValue('Z+')
.setValue('Y-')
.setLabel(_('3D world top'))
.setType('choice')
.addExtraInfo('Z+')
.addExtraInfo('Y-')
.addExtraInfo('Z+')
.setGroup(_('Orientation'));
properties
.getOrCreate('elevation')
@@ -3303,8 +3206,6 @@ 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);
}

View File

@@ -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), isCastingShadow(true), isReceivingShadow(true) {}
keepAspectRatio(true), crossfadeDuration(0.1f) {}
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
const gd::String &newValue) {
@@ -75,16 +75,6 @@ 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;
}
@@ -153,20 +143,19 @@ Model3DObjectConfiguration::GetProperties() const {
objectProperties["materialType"]
.SetValue(materialType.empty() ? "Basic" : materialType)
.SetType("choice")
.AddChoice("Basic", _("Basic (no lighting, no shadows)"))
.AddChoice("StandardWithoutMetalness", _("Standard (without metalness)"))
.AddChoice("KeepOriginal", _("Keep original"))
.SetLabel(_("Material"))
.SetGroup(_("Lighting"));
.AddExtraInfo("Basic")
.AddExtraInfo("StandardWithoutMetalness")
.AddExtraInfo("KeepOriginal")
.SetLabel(_("Material"));
objectProperties["originLocation"]
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
.SetType("choice")
.AddChoice("ModelOrigin", _("Model origin"))
.AddChoice("TopLeft", _("Top left"))
.AddChoice("ObjectCenter", _("Object center"))
.AddChoice("BottomCenterZ", _("Bottom center (Z)"))
.AddChoice("BottomCenterY", _("Bottom center (Y)"))
.AddExtraInfo("ModelOrigin")
.AddExtraInfo("TopLeft")
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Origin point"))
.SetGroup(_("Points"))
.SetAdvanced(true);
@@ -174,10 +163,10 @@ Model3DObjectConfiguration::GetProperties() const {
objectProperties["centerLocation"]
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
.SetType("choice")
.AddChoice("ModelOrigin", _("Model origin"))
.AddChoice("ObjectCenter", _("Object center"))
.AddChoice("BottomCenterZ", _("Bottom center (Z)"))
.AddChoice("BottomCenterY", _("Bottom center (Y)"))
.AddExtraInfo("ModelOrigin")
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Center point"))
.SetGroup(_("Points"))
.SetAdvanced(true);
@@ -189,20 +178,6 @@ 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;
}
@@ -235,8 +210,6 @@ 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");
@@ -266,8 +239,6 @@ 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");

View File

@@ -160,8 +160,6 @@ public:
const gd::String& GetCenterLocation() const { return centerLocation; };
bool shouldKeepAspectRatio() const { return keepAspectRatio; };
bool shouldCastShadow() const { return isCastingShadow; };
bool shouldReceiveShadow() const { return isReceivingShadow; };
///@}
protected:
@@ -184,8 +182,6 @@ private:
gd::String centerLocation;
bool keepAspectRatio;
bool isCastingShadow;
bool isReceivingShadow;
std::vector<Model3DAnimation> animations;
static Model3DAnimation badAnimation; //< Bad animation when an out of bound

View File

@@ -20,6 +20,7 @@ namespace gdjs {
/** The base parameters of the Model3D object */
content: Object3DDataContent & {
modelResourceName: string;
depth: number;
rotationX: number;
rotationY: number;
rotationZ: number;
@@ -38,8 +39,6 @@ namespace gdjs {
| 'BottomCenterY';
animations: Model3DAnimation[];
crossfadeDuration: float;
isCastingShadow: boolean;
isReceivingShadow: boolean;
};
}
@@ -103,8 +102,6 @@ namespace gdjs {
_animationSpeedScale: float = 1;
_animationPaused: boolean = false;
_crossfadeDuration: float = 0;
_isCastingShadow: boolean = true;
_isReceivingShadow: boolean = true;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -127,8 +124,6 @@ namespace gdjs {
objectData.content.materialType
);
this.setIsCastingShadow(objectData.content.isCastingShadow);
this.setIsReceivingShadow(objectData.content.isReceivingShadow);
this.onModelChanged(objectData);
this._crossfadeDuration = objectData.content.crossfadeDuration || 0;
@@ -201,24 +196,14 @@ 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;
}
getNetworkSyncData(): Model3DObjectNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Model3DObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
mt: this._materialType,
op: this._originPoint,
cp: this._centerPoint,
@@ -227,13 +212,15 @@ namespace gdjs {
ass: this._animationSpeedScale,
ap: this._animationPaused,
cfd: this._crossfadeDuration,
d: this.getDepth(),
};
}
updateFromNetworkSyncData(
networkSyncData: Model3DObjectNetworkSyncData
networkSyncData: Model3DObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;
@@ -261,6 +248,9 @@ namespace gdjs {
if (networkSyncData.cfd !== undefined) {
this._crossfadeDuration = networkSyncData.cfd;
}
if (networkSyncData.d !== undefined) {
this.setDepth(networkSyncData.d);
}
}
_reloadModel(objectData: Model3DObjectData) {
@@ -376,16 +366,6 @@ 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;

View File

@@ -286,7 +286,6 @@ 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);
@@ -324,13 +323,6 @@ 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:

View File

@@ -14,7 +14,6 @@ describe('gdjs.AnchorRuntimeBehavior', () => {
effects: [],
content: {},
childrenContent: {},
isInnerAreaFollowingParentSize: false,
});
runtimeScene.addObject(customObject);
customObject.setPosition(500, 250);

View File

@@ -75,9 +75,9 @@ module.exports = {
.getOrCreate('align')
.setValue(objectContent.align)
.setType('choice')
.addChoice('left', _('Left'))
.addChoice('center', _('Center'))
.addChoice('right', _('Right'))
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Base alignment'))
.setGroup(_('Appearance'));
@@ -88,9 +88,9 @@ module.exports = {
.getOrCreate('verticalTextAlignment')
.setValue(objectContent.verticalTextAlignment)
.setType('choice')
.addChoice('top', _('Top'))
.addChoice('center', _('Center'))
.addChoice('bottom', _('Bottom'))
.addExtraInfo('top')
.addExtraInfo('center')
.addExtraInfo('bottom')
.setLabel(_('Vertical alignment'))
.setGroup(_('Appearance'));
@@ -508,7 +508,7 @@ module.exports = {
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
getPropertyOverridings
propertyOverridings
) {
super(
project,
@@ -516,7 +516,7 @@ module.exports = {
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
getPropertyOverridings
propertyOverridings
);
const bbTextStyles = {
@@ -555,11 +555,9 @@ module.exports = {
gd.ObjectJsImplementation
);
const propertyOverridings = this.getPropertyOverridings();
const rawText =
propertyOverridings && propertyOverridings.has('Text')
? propertyOverridings.get('Text')
: object.content.text;
const rawText = this._propertyOverridings.has('Text')
? this._propertyOverridings.get('Text')
: object.content.text;
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
}
@@ -620,7 +618,7 @@ module.exports = {
this._pixiObject.dirty = true;
}
if (this._instance.hasCustomSize() && this._pixiObject.width !== 0) {
if (this._instance.hasCustomSize()) {
const alignmentX =
object.content.align === 'right'
? 1

View File

@@ -103,7 +103,7 @@ namespace gdjs {
}
updatePosition(): void {
if (this._object.isWrapping() && this._pixiObject.width !== 0) {
if (this._object.isWrapping()) {
const alignmentX =
this._object._textAlign === 'right'
? 1

View File

@@ -145,9 +145,11 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(): BBTextObjectNetworkSyncData {
override getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): BBTextObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
text: this._text,
o: this._opacity,
c: this._color,
@@ -162,9 +164,10 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: BBTextObjectNetworkSyncData
networkSyncData: BBTextObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
if (this._text !== undefined) {
this.setBBText(networkSyncData.text);
}
@@ -376,17 +379,13 @@ namespace gdjs {
}
override getWidth(): float {
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
return this._renderer.getWidth();
}
override getHeight(): float {
return this._renderer.getHeight();
}
override setWidth(width: float): void {
this.setWrappingWidth(width);
}
override getDrawableY(): float {
return (
this.getY() -

View File

@@ -61,9 +61,9 @@ module.exports = {
.getOrCreate('align')
.setValue(objectContent.align)
.setType('choice')
.addChoice('left', _('Left'))
.addChoice('center', _('Center'))
.addChoice('right', _('Right'))
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Alignment'))
.setGroup(_('Appearance'));
@@ -74,9 +74,9 @@ module.exports = {
.getOrCreate('verticalTextAlignment')
.setValue(objectContent.verticalTextAlignment)
.setType('choice')
.addChoice('top', _('Top'))
.addChoice('center', _('Center'))
.addChoice('bottom', _('Bottom'))
.addExtraInfo('top')
.addExtraInfo('center')
.addExtraInfo('bottom')
.setLabel(_('Vertical alignment'))
.setGroup(_('Appearance'));
@@ -631,7 +631,7 @@ module.exports = {
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
getPropertyOverridings
propertyOverridings
) {
super(
project,
@@ -639,7 +639,7 @@ module.exports = {
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
getPropertyOverridings
propertyOverridings
);
// We'll track changes of the font to trigger the loading of the new font.
@@ -665,11 +665,9 @@ module.exports = {
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const propertyOverridings = this.getPropertyOverridings();
this._pixiObject.text =
propertyOverridings && propertyOverridings.has('Text')
? propertyOverridings.get('Text')
: object.content.text;
this._pixiObject.text = this._propertyOverridings.has('Text')
? this._propertyOverridings.get('Text')
: object.content.text;
const align = object.content.align;
this._pixiObject.align = align;
@@ -723,7 +721,7 @@ module.exports = {
this._pixiObject.dirty = true;
}
if (this._instance.hasCustomSize() && this.getDefaultWidth() !== 0) {
if (this._instance.hasCustomSize()) {
const alignmentX =
object.content.align === 'right'
? 1
@@ -732,16 +730,17 @@ 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 - renderedWidth) * (alignmentX - 0.5);
const centerToCenterX =
(width - this._pixiObject.width) * (alignmentX - 0.5);
this._pixiObject.position.x = this._instance.getX() + width / 2;
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
this._pixiObject.anchor.x =
0.5 - centerToCenterX / this._pixiObject.width;
} else {
this._pixiObject.position.x =
this._instance.getX() + this.getDefaultWidth() / 2;
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.anchor.x = 0.5;
}
const alignmentY =
@@ -751,7 +750,7 @@ module.exports = {
? 0.5
: 0;
this._pixiObject.position.y =
this._instance.getY() + this.getDefaultHeight() * (0.5 - alignmentY);
this._instance.getY() + this._pixiObject.height * (0.5 - alignmentY);
this._pixiObject.anchor.y = 0.5;
this._pixiObject.rotation = RenderedInstance.toRad(
@@ -775,11 +774,11 @@ module.exports = {
}
getDefaultWidth() {
return this._pixiObject.textWidth * this._pixiObject.scale.x;
return this._pixiObject.width;
}
getDefaultHeight() {
return this._pixiObject.textHeight * this._pixiObject.scale.y;
return this._pixiObject.height;
}
getOriginY() {

View File

@@ -146,7 +146,7 @@ namespace gdjs {
}
updatePosition(): void {
if (this._object.isWrapping() && this.getWidth() !== 0) {
if (this._object.isWrapping()) {
const alignmentX =
this._object._textAlign === 'right'
? 1
@@ -155,15 +155,17 @@ 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 - renderedWidth) * (alignmentX - 0.5);
const centerToCenterX =
(width - this._pixiObject.width) * (alignmentX - 0.5);
this._pixiObject.position.x = this._object.x + width / 2;
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
this._pixiObject.anchor.x =
0.5 - centerToCenterX / this._pixiObject.width;
} else {
this._pixiObject.position.x = this._object.x + this.getWidth() / 2;
this._pixiObject.position.x =
this._object.x + this._pixiObject.width / 2;
this._pixiObject.anchor.x = 0.5;
}
@@ -174,7 +176,7 @@ namespace gdjs {
? 0.5
: 0;
this._pixiObject.position.y =
this._object.y + this.getHeight() * (0.5 - alignmentY);
this._object.y + this._pixiObject.height * (0.5 - alignmentY);
this._pixiObject.anchor.y = 0.5;
}

View File

@@ -155,9 +155,11 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(): BitmapTextObjectNetworkSyncData {
override getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): BitmapTextObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
text: this._text,
opa: this._opacity,
tint: this._tint,
@@ -172,9 +174,10 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: BitmapTextObjectNetworkSyncData
networkSyncData: BitmapTextObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
if (this._text !== undefined) {
this.setText(networkSyncData.text);
}
@@ -419,17 +422,13 @@ namespace gdjs {
}
override getWidth(): float {
return this._wrapping ? this._wrappingWidth : this._renderer.getWidth();
return this._renderer.getWidth();
}
override getHeight(): float {
return this._renderer.getHeight();
}
override setWidth(width: float): void {
this.setWrappingWidth(width);
}
override getDrawableY(): float {
return (
this.getY() -

View File

@@ -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", 300);
content.SetAttribute("extraBorder", 0);
}
#if defined(GD_IDE_ONLY)

View File

@@ -12,42 +12,35 @@ 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 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)")
.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)")
.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 2D camera 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 screen's borders."),
"",
"CppPlatform/Extensions/destroyoutsideicon.png",
"DestroyOutsideBehavior",
std::make_shared<DestroyOutsideBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>())
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
aut.AddCondition("ExtraBorder",
_("Additional border (extra distance before deletion)"),
_("Compare the extra distance (in pixels) the object must "
"travel beyond the screen before it gets deleted."),
_("Additional border"),
_("Compare the additional border that the object must cross "
"before being deleted."),
_("the additional border"),
_("Destroy outside configuration"),
"CppPlatform/Extensions/destroyoutsideicon24.png",
@@ -60,9 +53,9 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("GetExtraBorder");
aut.AddAction("ExtraBorder",
_("Additional border (extra distance before deletion)"),
_("Change the extra distance (in pixels) the object must "
"travel beyond the screen before it gets deleted."),
_("Additional border"),
_("Change the additional border that the object must cross "
"before being deleted."),
_("the additional border"),
_("Destroy outside configuration"),
"CppPlatform/Extensions/destroyoutsideicon24.png",

View File

@@ -46,7 +46,7 @@ namespace gdjs {
layer.getCameraY() + layer.getCameraHeight() / 2
) {
//We are outside the camera area.
this.owner.deleteFromScene();
this.owner.deleteFromScene(instanceContainer);
}
}

View File

@@ -35,32 +35,25 @@ 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. 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")
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")
.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 (the "
"mouse button or touch was just released this frame)."),
_("_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."),
_("_PARAM0_ was just dropped"),
_("Draggable"),
"CppPlatform/Extensions/draggableicon24.png",
"CppPlatform/Extensions/draggableicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "Draggable")

View File

@@ -21,9 +21,7 @@ module.exports = {
.setExtensionInformation(
'FileSystem',
_('File system'),
_(
'Access the filesystem of the operating system - only works on native, desktop games exported to Windows, Linux or macOS.'
),
_('Access the filesystem of the operating system.'),
'Matthias Meike',
'Open source (MIT License)'
)

View File

@@ -50,11 +50,6 @@ 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),

View File

@@ -5,25 +5,23 @@ 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"),
_("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)")
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)")
.SetExtensionHelpPath("/all-features/inventory")
.SetCategory("Game mechanic");
extension.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
extension
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");
extension
@@ -166,15 +164,14 @@ 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"))
@@ -207,14 +204,13 @@ 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");
}

View File

@@ -27,7 +27,7 @@ class RenderedInstance {
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
pixiResourcesLoader: Class<PixiResourcesLoader>,
getPropertyOverridings: (() => Map<string, string>) | null = null
propertyOverridings: Map<string, string> = new Map<string, string>()
);
/**
@@ -80,8 +80,6 @@ class RenderedInstance {
getDefaultHeight(): number;
getDefaultDepth(): number;
getPropertyOverridings(): Map<string, string> | null;
}
/**
@@ -109,8 +107,7 @@ class Rendered3DInstance {
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
threeGroup: THREE.Group,
pixiResourcesLoader: Class<PixiResourcesLoader>,
getPropertyOverridings: (() => Map<string, string>) | null = null
pixiResourcesLoader: Class<PixiResourcesLoader>
);
/**
@@ -177,8 +174,6 @@ 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 = {

View File

@@ -21,9 +21,7 @@ module.exports = {
.setExtensionInformation(
'Leaderboards',
_('Leaderboards'),
_(
'Allow your game to send scores to your leaderboards (anonymously or from the logged-in player) or display existing leaderboards to the player.'
),
_('Allow your game to send scores to your leaderboards.'),
'Florian Rival',
'Open source (MIT License)'
)
@@ -32,12 +30,6 @@ 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',

View File

@@ -22,7 +22,7 @@ module.exports = {
'Lighting',
_('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.',
'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.',
'Harsimran Virk',
'MIT'
)
@@ -51,7 +51,7 @@ module.exports = {
_('Light Obstacle Behavior'),
'LightObstacleBehavior',
_(
'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.'
'Flag objects as being obstacles to light. The light emitted by light objects will be stopped by the object.'
),
'',
'CppPlatform/Extensions/lightObstacleIcon32.png',
@@ -164,7 +164,7 @@ module.exports = {
'LightObject',
_('Light'),
_(
'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.'
'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.'
),
'CppPlatform/Extensions/lightIcon32.png',
lightObject

View File

@@ -87,16 +87,21 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): LightNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): LightNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
rad: this.getRadius(),
col: this.getColor(),
};
}
updateFromNetworkSyncData(networkSyncData: LightNetworkSyncData): void {
super.updateFromNetworkSyncData(networkSyncData);
updateFromNetworkSyncData(
networkSyncData: LightNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.rad !== undefined) {
this.setRadius(networkSyncData.rad);

View File

@@ -239,7 +239,7 @@ namespace gdjs {
instanceContainer: gdjs.RuntimeInstanceContainer,
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
obj: gdjs.RuntimeObject | null,
eventsFunctionContext: EventsFunctionContext | null | undefined
eventsFunctionContext: EventsFunctionContext | undefined
) {
if (obj === null) {
return false;

View File

@@ -21,9 +21,7 @@ module.exports = {
.setExtensionInformation(
'Multiplayer',
_('Multiplayer'),
_(
'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.'
),
_('Allow players to connect to lobbies and play together.'),
'Florian Rival',
'Open source (MIT License)'
)
@@ -33,79 +31,6 @@ 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',

View File

@@ -729,7 +729,9 @@ namespace gdjs {
behavior.playerNumber = ownerPlayerNumber;
}
instance.updateFromNetworkSyncData(messageData);
instance.updateFromNetworkSyncData(messageData, {
clearMemory: false,
});
setLastClockReceivedForInstanceOnScene({
sceneNetworkId,
@@ -1340,7 +1342,7 @@ namespace gdjs {
debugLogger.info(
`Destroying object ${objectName} with instance network ID ${instanceNetworkId}.`
);
instance.deleteFromScene();
instance.deleteFromScene(runtimeScene);
debugLogger.info(
`Sending acknowledgment of destruction of object ${objectName} with instance network ID ${instanceNetworkId} to ${messageSender}.`
@@ -1737,7 +1739,7 @@ namespace gdjs {
return;
}
runtimeScene.updateFromNetworkSyncData(messageData);
runtimeScene.updateFromNetworkSyncData(messageData, {});
} else {
// If the game is not ready to receive game update messages, we need to save the data for later use.
// This can happen when joining a game that is already running.
@@ -1890,7 +1892,7 @@ namespace gdjs {
const messageData = message.getData();
const messageSender = message.getSender();
if (gdjs.multiplayer.isReadyToSendOrReceiveGameUpdateMessages()) {
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
} else {
// If the game is not ready to receive game update messages, we need to save the data for later use.
// This can happen when joining a game that is already running.
@@ -1918,7 +1920,7 @@ namespace gdjs {
// Reapply the game saved updates.
lastReceivedGameSyncDataUpdates.getUpdates().forEach((messageData) => {
debugLogger.info(`Reapplying saved update of game.`);
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
});
// Game updates are always applied properly, so we can clear them.
lastReceivedGameSyncDataUpdates.clear();
@@ -1937,7 +1939,7 @@ namespace gdjs {
debugLogger.info(`Reapplying saved update of scene ${sceneNetworkId}.`);
runtimeScene.updateFromNetworkSyncData(messageData);
runtimeScene.updateFromNetworkSyncData(messageData, {});
// We only remove the message if it was successfully applied, so it can be reapplied later,
// in case we were not on the right scene.
lastReceivedSceneSyncDataUpdates.remove(messageData);
@@ -2280,7 +2282,7 @@ namespace gdjs {
behavior.getActionOnPlayerDisconnect();
if (actionOnPlayerDisconnect === 'DestroyObject') {
// No need to remove the ownership, as the destroy message will be sent to all players.
instance.deleteFromScene();
instance.deleteFromScene(runtimeScene);
} else if (actionOnPlayerDisconnect === 'GiveOwnershipToHost') {
// Removing the ownership will send a message to all players.
behavior.removeObjectOwnership();

View File

@@ -99,7 +99,7 @@ namespace gdjs {
debugLogger.info(
`Lobby game is running on a synced scene and object ${owner.getName()} has not been assigned a networkId after a short delay, destroying it.`
);
owner.deleteFromScene();
owner.deleteFromScene(instanceContainer);
}
}, this._timeBeforeDestroyingObjectWithoutNetworkIdInMs);
}
@@ -262,7 +262,7 @@ namespace gdjs {
debugLogger.info(
`Player number ${this.playerNumber} does not exist in the lobby at the moment. Destroying the object.`
);
this.owner.deleteFromScene();
this.owner.deleteFromScene(this.owner.getInstanceContainer());
return;
}
@@ -278,7 +278,9 @@ namespace gdjs {
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
const objectName = this.owner.getName();
const objectNetworkSyncData = this.owner.getNetworkSyncData();
const objectNetworkSyncData = this.owner.getNetworkSyncData({
forceSyncEverything: false,
});
// this._logToConsoleWithThrottle(
// `Synchronizing object ${this.owner.getName()} (instance ${
@@ -302,6 +304,7 @@ namespace gdjs {
if: objectNetworkSyncData.if,
pfx: objectNetworkSyncData.pfx,
pfy: objectNetworkSyncData.pfy,
n: objectNetworkSyncData.n,
});
const shouldSyncObjectBasicInfo =
!this._hasObjectBasicInfoBeenSyncedRecently() ||
@@ -371,7 +374,6 @@ namespace gdjs {
this._lastSentBasicObjectSyncData = {
x: objectNetworkSyncData.x,
y: objectNetworkSyncData.y,
z: objectNetworkSyncData.z,
w: objectNetworkSyncData.w,
h: objectNetworkSyncData.h,
zo: objectNetworkSyncData.zo,
@@ -381,6 +383,7 @@ namespace gdjs {
if: objectNetworkSyncData.if,
pfx: objectNetworkSyncData.pfx,
pfy: objectNetworkSyncData.pfy,
n: objectNetworkSyncData.n,
};
this._numberOfForcedBasicObjectUpdates = Math.max(
this._numberOfForcedBasicObjectUpdates - 1,
@@ -448,7 +451,9 @@ namespace gdjs {
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData: this.owner.getNetworkSyncData(),
objectNetworkSyncData: this.owner.getNetworkSyncData({
forceSyncEverything: false,
}),
sceneNetworkId,
});
this._sendDataToPeersWithIncreasedClock(
@@ -598,7 +603,9 @@ namespace gdjs {
debugLogger.info(
'Sending update message to move the object immediately.'
);
const objectNetworkSyncData = this.owner.getNetworkSyncData();
const objectNetworkSyncData = this.owner.getNetworkSyncData({
forceSyncEverything: false,
});
const {
messageName: updateMessageName,
messageData: updateMessageData,

View File

@@ -17,29 +17,9 @@ namespace gdjs {
}[];
};
type LobbyStatus =
| 'waiting'
| 'starting'
| 'playing'
| 'migrating'
| 'migrated';
type LobbyConnectionStatus = 'waiting' | 'ready' | 'connected';
type InGamePlayerStatus = 'playing' | 'left';
type PlayerStatus = LobbyConnectionStatus | InGamePlayerStatus;
type LobbyPlayer = {
playerId: string;
status: PlayerStatus;
playerNumber: number;
};
type Lobby = {
id: string;
minPlayers: number;
maxPlayers: number;
canJoinAfterStart: boolean;
players: LobbyPlayer[];
status: LobbyStatus;
status: 'waiting' | 'starting' | 'playing' | 'migrating' | 'migrated';
};
type QuickJoinLobbyResponse =
@@ -125,7 +105,6 @@ namespace gdjs {
let _quickJoinLobbyFailureReason:
| 'FULL'
| 'NOT_ENOUGH_PLAYERS'
| 'DOES_NOT_EXIST'
| 'UNKNOWN'
| null = null;
let _lobbyId: string | null = null;
@@ -1718,87 +1697,11 @@ namespace gdjs {
}
};
export const getLobbyID = (): string => {
return _lobbyId || '';
};
const quickJoinWithLobbyID = async (
export const authenticateAndQuickJoinLobby = async (
runtimeScene: gdjs.RuntimeScene,
lobbyID: string,
displayLoader: boolean,
openLobbiesPageIfFailure: boolean
) => {
if (_isQuickJoiningOrStartingAGame) return;
const _gameId = gdjs.projectData.properties.projectUuid;
if (!_gameId) {
logger.error(
'The game ID is missing, the quick join lobby action cannot continue.'
);
return;
}
_quickJoinLobbyFailureReason = null;
_isQuickJoiningOrStartingAGame = true;
if (displayLoader) {
gdjs.multiplayerComponents.displayLoader(runtimeScene, true);
}
const quickJoinWithLobbyIDRelativeUrl = `/play/game/${_gameId}/public-lobby/${lobbyID}`;
try {
const lobby: Lobby = await gdjs.evtTools.network.retryIfFailed(
{ times: 2 },
() =>
fetchAsPlayer({
relativeUrl: quickJoinWithLobbyIDRelativeUrl,
method: 'GET',
dev: isUsingGDevelopDevelopmentEnvironment,
})
);
const isFull = lobby.players.length === lobby.maxPlayers;
if (isFull) {
logger.error('Lobby is full - cannot quick join it.');
_quickJoinLobbyJustFailed = true;
_quickJoinLobbyFailureReason = 'FULL';
onLobbyQuickJoinFinished(runtimeScene);
if (openLobbiesPageIfFailure) {
openLobbiesWindow(runtimeScene);
}
return;
}
if (lobby.status === 'playing') {
_actionAfterJoiningLobby = 'JOIN_GAME';
} else if (lobby.status === 'waiting') {
if (lobby.players.length === 0) {
_actionAfterJoiningLobby = 'START_GAME';
} else {
_actionAfterJoiningLobby = 'OPEN_LOBBY_PAGE';
}
} else {
throw new Error(`Lobby in wrong status: ${lobby.status}`);
}
handleJoinLobbyEvent(runtimeScene, lobbyID);
} catch (error) {
const errorCode = parseInt(error.message.match(/\d{3}/)?.[0]);
if (errorCode === 404) {
logger.error('Lobby does not exist.');
_quickJoinLobbyFailureReason = 'DOES_NOT_EXIST';
} else {
logger.error('An error occurred while joining a lobby:', error);
_quickJoinLobbyFailureReason = 'UNKNOWN';
}
_quickJoinLobbyJustFailed = true;
onLobbyQuickJoinFinished(runtimeScene);
if (openLobbiesPageIfFailure) {
openLobbiesWindow(runtimeScene);
}
}
};
const isQuickJoiningTooFast = () => {
const requestDoneAt = Date.now();
if (_lastQuickJoinRequestDoneAt) {
if (requestDoneAt - _lastQuickJoinRequestDoneAt < 500) {
@@ -1806,18 +1709,12 @@ namespace gdjs {
logger.warn(
'Last request to quick join a lobby was sent too little time ago. Ignoring this one.'
);
return true;
return;
}
} else {
_lastQuickJoinRequestDoneAt = requestDoneAt;
}
return false;
};
const isNotCorrectlyAuthenticatedForQuickJoin = async (
runtimeScene: RuntimeScene
) => {
const playerId = gdjs.playerAuthentication.getUserId();
const playerToken = gdjs.playerAuthentication.getUserToken();
if (!playerId || !playerToken) {
@@ -1827,43 +1724,14 @@ namespace gdjs {
.promise;
_isWaitingForLogin = false;
if (status !== 'logged') {
return true;
if (status === 'logged') {
await quickJoinLobby(
runtimeScene,
displayLoader,
openLobbiesPageIfFailure
);
}
}
return false;
};
export const authenticateAndQuickJoinWithLobbyID = async (
runtimeScene: gdjs.RuntimeScene,
lobbyID: string,
displayLoader: boolean,
openLobbiesPageIfFailure: boolean
) => {
if (isQuickJoiningTooFast()) {
return;
}
if (await isNotCorrectlyAuthenticatedForQuickJoin(runtimeScene)) {
return;
}
await quickJoinWithLobbyID(
runtimeScene,
lobbyID,
displayLoader,
openLobbiesPageIfFailure
);
};
export const authenticateAndQuickJoinLobby = async (
runtimeScene: gdjs.RuntimeScene,
displayLoader: boolean,
openLobbiesPageIfFailure: boolean
) => {
if (isQuickJoiningTooFast()) {
return;
}
if (await isNotCorrectlyAuthenticatedForQuickJoin(runtimeScene)) {
return;
}
await quickJoinLobby(

View File

@@ -1130,7 +1130,7 @@ describe('Multiplayer', () => {
'MySpriteObject'
)[0];
p1SpriteObject1.deleteFromScene();
p1SpriteObject1.deleteFromScene(p1RuntimeScene);
p1RuntimeScene.renderAndStep(1000 / 60);
}
@@ -1297,7 +1297,7 @@ describe('Multiplayer', () => {
'MySpriteObject'
)[0];
p2SpriteObject1.deleteFromScene();
p2SpriteObject1.deleteFromScene(p2RuntimeScene);
p2RuntimeScene.renderAndStep(1000 / 60);
}

View File

@@ -116,7 +116,48 @@ namespace gdjs {
_updateLocalPositions() {
const obj = this._object;
this._centerSprite.position.x = obj._lBorder;
this._centerSprite.position.y = obj._tBorder;
//Right
this._borderSprites[0].position.x = obj._width - obj._rBorder;
this._borderSprites[0].position.y = obj._tBorder;
//Top-right
this._borderSprites[1].position.x =
obj._width - this._borderSprites[1].width;
this._borderSprites[1].position.y = 0;
//Top
this._borderSprites[2].position.x = obj._lBorder;
this._borderSprites[2].position.y = 0;
//Top-Left
this._borderSprites[3].position.x = 0;
this._borderSprites[3].position.y = 0;
//Left
this._borderSprites[4].position.x = 0;
this._borderSprites[4].position.y = obj._tBorder;
//Bottom-Left
this._borderSprites[5].position.x = 0;
this._borderSprites[5].position.y =
obj._height - this._borderSprites[5].height;
//Bottom
this._borderSprites[6].position.x = obj._lBorder;
this._borderSprites[6].position.y = obj._height - obj._bBorder;
//Bottom-Right
this._borderSprites[7].position.x =
obj._width - this._borderSprites[7].width;
this._borderSprites[7].position.y =
obj._height - this._borderSprites[7].height;
}
_updateSpritesAndTexturesSize() {
const obj = this._object;
this._centerSprite.width = Math.max(
obj._width - obj._rBorder - obj._lBorder,
0
@@ -126,107 +167,35 @@ namespace gdjs {
0
);
let leftMargin = obj._lBorder;
let rightMargin = obj._rBorder;
if (this._centerSprite.width === 0 && obj._lBorder + obj._rBorder > 0) {
leftMargin =
(obj._width * obj._lBorder) / (obj._lBorder + obj._rBorder);
rightMargin = obj._width - leftMargin;
}
let topMargin = obj._tBorder;
let bottomMargin = obj._bBorder;
if (this._centerSprite.height === 0 && obj._tBorder + obj._bBorder > 0) {
topMargin =
(obj._height * obj._tBorder) / (obj._tBorder + obj._bBorder);
bottomMargin = obj._height - topMargin;
}
//Right
this._borderSprites[0].width = rightMargin;
this._borderSprites[0].width = obj._rBorder;
this._borderSprites[0].height = Math.max(
obj._height - topMargin - bottomMargin,
obj._height - obj._tBorder - obj._bBorder,
0
);
//Top
this._borderSprites[2].height = topMargin;
this._borderSprites[2].height = obj._tBorder;
this._borderSprites[2].width = Math.max(
obj._width - rightMargin - leftMargin,
obj._width - obj._rBorder - obj._lBorder,
0
);
//Left
this._borderSprites[4].width = leftMargin;
this._borderSprites[4].width = obj._lBorder;
this._borderSprites[4].height = Math.max(
obj._height - topMargin - bottomMargin,
obj._height - obj._tBorder - obj._bBorder,
0
);
//Bottom
this._borderSprites[6].height = bottomMargin;
this._borderSprites[6].height = obj._bBorder;
this._borderSprites[6].width = Math.max(
obj._width - rightMargin - leftMargin,
obj._width - obj._rBorder - obj._lBorder,
0
);
//Top-right
this._borderSprites[1].width = rightMargin;
this._borderSprites[1].height = topMargin;
//Top-Left
this._borderSprites[3].width = leftMargin;
this._borderSprites[3].height = topMargin;
//Bottom-Left
this._borderSprites[5].width = leftMargin;
this._borderSprites[5].height = bottomMargin;
//Bottom-Right
this._borderSprites[7].width = rightMargin;
this._borderSprites[7].height = bottomMargin;
this._wasRendered = true;
this._spritesContainer.cacheAsBitmap = false;
const leftBorder = leftMargin;
const topBorder = topMargin;
const rightBorder = obj._width - rightMargin;
const bottomBorder = obj._height - bottomMargin;
this._centerSprite.position.x = leftBorder;
this._centerSprite.position.y = topBorder;
//Right
this._borderSprites[0].position.x = rightBorder;
this._borderSprites[0].position.y = topBorder;
//Top-right
this._borderSprites[1].position.x = rightBorder;
this._borderSprites[1].position.y = 0;
//Top
this._borderSprites[2].position.x = leftBorder;
this._borderSprites[2].position.y = 0;
//Top-Left
this._borderSprites[3].position.x = 0;
this._borderSprites[3].position.y = 0;
//Left
this._borderSprites[4].position.x = 0;
this._borderSprites[4].position.y = topBorder;
//Bottom-Left
this._borderSprites[5].position.x = 0;
this._borderSprites[5].position.y = bottomBorder;
//Bottom
this._borderSprites[6].position.x = leftBorder;
this._borderSprites[6].position.y = bottomBorder;
//Bottom-Right
this._borderSprites[7].position.x = rightBorder;
this._borderSprites[7].position.y = bottomBorder;
}
setTexture(
@@ -371,6 +340,7 @@ namespace gdjs {
)
)
);
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
this._wrapperContainer.pivot.x = this._object._width / 2;
@@ -379,25 +349,38 @@ namespace gdjs {
updateWidth(): void {
this._wrapperContainer.pivot.x = this._object._width / 2;
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
}
updateHeight(): void {
this._wrapperContainer.pivot.y = this._object._height / 2;
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
}
setColor(rgbOrHexColor: string): void {
const tint = gdjs.rgbOrHexStringToNumber(rgbOrHexColor);
this._centerSprite.tint = tint;
setColor(rgbColor): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
}
this._centerSprite.tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
for (
let borderCounter = 0;
borderCounter < this._borderSprites.length;
borderCounter++
) {
this._borderSprites[borderCounter].tint = tint;
this._borderSprites[borderCounter].tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
}
this._spritesContainer.cacheAsBitmap = false;
}
@@ -405,11 +388,11 @@ namespace gdjs {
getColor() {
const rgb = new PIXI.Color(this._centerSprite.tint).toRgbArray();
return (
Math.round(rgb[0] * 255) +
Math.floor(rgb[0] * 255) +
';' +
Math.round(rgb[1] * 255) +
Math.floor(rgb[1] * 255) +
';' +
Math.round(rgb[2] * 255)
Math.floor(rgb[2] * 255)
);
}

View File

@@ -84,12 +84,6 @@ namespace gdjs {
oldObjectData: PanelSpriteObjectData,
newObjectData: PanelSpriteObjectData
): boolean {
if (oldObjectData.width !== newObjectData.width) {
this.setWidth(newObjectData.width);
}
if (oldObjectData.height !== newObjectData.height) {
this.setHeight(newObjectData.height);
}
let updateTexture = false;
if (oldObjectData.rightMargin !== newObjectData.rightMargin) {
this._rBorder = newObjectData.rightMargin;
@@ -119,18 +113,21 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): PanelSpriteNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): PanelSpriteNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
op: this.getOpacity(),
color: this.getColor(),
};
}
updateFromNetworkSyncData(
networkSyncData: PanelSpriteNetworkSyncData
networkSyncData: PanelSpriteNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
// Texture is not synchronized, see if this is asked or not.

View File

@@ -20,10 +20,9 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
.SetExtensionInformation(
"ParticleSystem",
_("Particle system"),
"A 2D particle emitter allows to create various effects by showing a "
"A particle emitter allows to create various effects by showing a "
"lot of tiny images called particles. It's ideal for fires, smoke, "
"explosions, magical effects, etc... in 2D games. For 3D games, use "
"the 3D particle emitter instead.",
"explosions, magical effects, etc...",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Visual effect")
@@ -37,9 +36,9 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
extension
.AddObject<ParticleEmitterObject>(
"ParticleEmitter",
_("2D particles emitter"),
_("Displays a large number of small 2D particles to create "
"visual effects in a 2D game or user interface."),
_("Particles emitter"),
_("Displays a large number of small particles to create visual "
"effects."),
"CppPlatform/Extensions/particleSystemicon.png")
.SetCategoryFullName(_("Visual effect"))
.AddDefaultBehavior("EffectCapability::EffectBehavior");

View File

@@ -194,9 +194,9 @@ ParticleEmitterObject::GetProperties() const {
: GetRendererType() == Line ? "Line"
: "Image")
.SetType("choice")
.AddChoice("Circle", _("Circle"))
.AddChoice("Line", _("Line"))
.AddChoice("Image", _("Image"))
.AddExtraInfo("Circle")
.AddExtraInfo("Line")
.AddExtraInfo("Image")
.SetLabel(_("Particle type"))
.SetHasImpactOnOtherProperties(true);

View File

@@ -370,9 +370,11 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): ParticleEmitterObjectNetworkSyncData {
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): ParticleEmitterObjectNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(syncOptions),
prms: this.particleRotationMinSpeed,
prmx: this.particleRotationMaxSpeed,
mpc: this.maxParticlesCount,
@@ -399,9 +401,10 @@ namespace gdjs {
}
updateFromNetworkSyncData(
syncData: ParticleEmitterObjectNetworkSyncData
syncData: ParticleEmitterObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(syncData);
super.updateFromNetworkSyncData(syncData, options);
if (syncData.x !== undefined) {
this.setX(syncData.x);
}
@@ -560,7 +563,7 @@ namespace gdjs {
!this._isEmissionPaused &&
this._renderer._mayHaveEndedEmission()
) {
this.deleteFromScene();
this.deleteFromScene(instanceContainer);
}
if (
this.jumpForwardInTimeOnCreation > 0 &&

View File

@@ -133,9 +133,11 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): PathfindingNetworkSyncData {
getNetworkSyncData(
options: GetNetworkSyncDataOptions
): PathfindingNetworkSyncData {
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(options),
props: {
path: this._path,
pf: this._pathFound,
@@ -150,9 +152,10 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: PathfindingNetworkSyncData
networkSyncData: PathfindingNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
const behaviorSpecificProps = networkSyncData.props;
if (behaviorSpecificProps.path !== undefined) {
this._path = behaviorSpecificProps.path;

View File

@@ -818,7 +818,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('yesorno', _('Treat as bullet'), '', false)
.addParameter('yesorno', _('Treat as bullet?'), '', false)
.setDefaultValue('false')
.getCodeExtraInformation()
.setFunctionName('setBullet');
@@ -852,7 +852,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('yesorno', _('Fixed rotation'), '', false)
.addParameter('yesorno', _('Fixed rotation?'), '', false)
.setDefaultValue('false')
.getCodeExtraInformation()
.setFunctionName('setFixedRotation');
@@ -886,7 +886,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('yesorno', _('Can sleep'), '', false)
.addParameter('yesorno', _('Can sleep?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('setSleepingAllowed');
@@ -1296,7 +1296,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Layer (1 - 16)'))
.addParameter('yesorno', _('Enable'), '', false)
.addParameter('yesorno', _('Enable?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('enableLayer');
@@ -1332,7 +1332,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Mask (1 - 16)'))
.addParameter('yesorno', _('Enable'), '', false)
.addParameter('yesorno', _('Enable?'), '', false)
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('enableMask');
@@ -2409,7 +2409,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Joint ID'))
.addParameter('yesorno', _('Enable'))
.addParameter('yesorno', _('Enable?'))
.getCodeExtraInformation()
.setFunctionName('enableRevoluteJointLimits');
@@ -2488,7 +2488,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Joint ID'))
.addParameter('yesorno', _('Enable'))
.addParameter('yesorno', _('Enable?'))
.getCodeExtraInformation()
.setFunctionName('enableRevoluteJointMotor');
@@ -2727,7 +2727,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Joint ID'))
.addParameter('yesorno', _('Enable'))
.addParameter('yesorno', _('Enable?'))
.getCodeExtraInformation()
.setFunctionName('enablePrismaticJointLimits');
@@ -2806,7 +2806,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Joint ID'))
.addParameter('yesorno', _('Enable'))
.addParameter('yesorno', _('Enable?'))
.getCodeExtraInformation()
.setFunctionName('enablePrismaticJointMotor');
@@ -3486,7 +3486,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Joint ID'))
.addParameter('yesorno', _('Enable'))
.addParameter('yesorno', _('Enable?'))
.getCodeExtraInformation()
.setFunctionName('enableWheelJointMotor');

View File

@@ -499,7 +499,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(): Physics2NetworkSyncData {
getNetworkSyncData(
options: GetNetworkSyncDataOptions
): Physics2NetworkSyncData {
const bodyProps = this._body
? {
tpx: this._body.GetTransform().get_p().get_x(),
@@ -520,7 +522,7 @@ namespace gdjs {
aw: undefined,
};
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(options),
props: {
...bodyProps,
layers: this.layers,
@@ -529,8 +531,11 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(networkSyncData: Physics2NetworkSyncData) {
super.updateFromNetworkSyncData(networkSyncData);
updateFromNetworkSyncData(
networkSyncData: Physics2NetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData, options);
const behaviorSpecificProps = networkSyncData.props;
if (

View File

@@ -24,7 +24,7 @@ describe('Physics2RuntimeBehavior', () => {
doStepPreEvents(runtimeScene) {
if (this.shouldDeleteInPreEvent) {
this.owner.deleteFromScene();
this.owner.deleteFromScene(runtimeScene);
}
}
}
@@ -159,7 +159,7 @@ describe('Physics2RuntimeBehavior', () => {
);
// Delete object from scene
object.deleteFromScene();
object.deleteFromScene(runtimeScene);
expect(behavior.destroyedDuringFrameLogic).to.be(true);
expect(behavior.getBody()).to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
@@ -194,7 +194,7 @@ describe('Physics2RuntimeBehavior', () => {
false
);
object.deleteFromScene();
object.deleteFromScene(runtimeScene);
expect(behavior.destroyedDuringFrameLogic).to.be(true);
expect(behavior.getBody()).to.be(null);
@@ -712,7 +712,7 @@ describe('Physics2RuntimeBehavior', () => {
// Destroy (handled by postEvent).
runtimeScene.renderAndStepWithEventsFunction(1000 / fps, () => {
movingObject.deleteFromScene();
movingObject.deleteFromScene(runtimeScene);
});
// Collision should be reset on destroyed object and

File diff suppressed because it is too large Load Diff

View File

@@ -305,14 +305,7 @@ namespace gdjs {
private shapeDimensionA: float;
private shapeDimensionB: float;
private shapeDimensionC: float;
private shapeOffsetX: float;
private shapeOffsetY: float;
shapeOffsetZ: float;
private massCenterOffsetX: float;
private massCenterOffsetY: float;
private massCenterOffsetZ: float;
private density: float;
massOverride: float;
friction: float;
restitution: float;
linearDamping: float;
@@ -320,7 +313,7 @@ namespace gdjs {
gravityScale: float;
private layers: integer;
private masks: integer;
shapeScale: number = 1;
private shapeScale: number = 1;
/**
* Array containing the beginning of contacts reported by onContactBegin. Each contact
@@ -355,10 +348,7 @@ namespace gdjs {
/**
* When set to `true` the shape will be recreated before the next physics step.
*/
_needToRecreateShape: boolean = false;
_shapeHalfWidth: float = 0;
_shapeHalfHeight: float = 0;
private _needToRecreateShape: boolean = false;
/**
* Used by {@link gdjs.PhysicsCharacter3DRuntimeBehavior} to convert coordinates.
*/
@@ -402,14 +392,7 @@ namespace gdjs {
this.shapeDimensionA = behaviorData.shapeDimensionA;
this.shapeDimensionB = behaviorData.shapeDimensionB;
this.shapeDimensionC = behaviorData.shapeDimensionC;
this.shapeOffsetX = behaviorData.shapeOffsetX || 0;
this.shapeOffsetY = behaviorData.shapeOffsetY || 0;
this.shapeOffsetZ = behaviorData.shapeOffsetZ || 0;
this.massCenterOffsetX = behaviorData.massCenterOffsetX || 0;
this.massCenterOffsetY = behaviorData.massCenterOffsetY || 0;
this.massCenterOffsetZ = behaviorData.massCenterOffsetZ || 0;
this.density = behaviorData.density;
this.massOverride = behaviorData.massOverride || 0;
this.friction = behaviorData.friction;
this.restitution = behaviorData.restitution;
this.linearDamping = Math.max(0, behaviorData.linearDamping);
@@ -495,7 +478,9 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(): Physics3DNetworkSyncData {
override getNetworkSyncData(
options: GetNetworkSyncDataOptions
): Physics3DNetworkSyncData {
let bodyProps;
if (this._body) {
const position = this._body.GetPosition();
@@ -537,7 +522,7 @@ namespace gdjs {
};
}
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(options),
props: {
...bodyProps,
layers: this.layers,
@@ -547,9 +532,10 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: Physics3DNetworkSyncData
networkSyncData: Physics3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
const behaviorSpecificProps = networkSyncData.props;
if (
@@ -630,11 +616,19 @@ namespace gdjs {
override onDeActivate() {
this._sharedData.removeFromBehaviorsList(this);
this._destroyBody();
this.bodyUpdater.destroyBody();
this._contactsEndedThisFrame.length = 0;
this._contactsStartedThisFrame.length = 0;
this._currentContacts.length = 0;
}
override onActivate() {
this._sharedData.addToBehaviorsList(this);
this._contactsEndedThisFrame.length = 0;
this._contactsStartedThisFrame.length = 0;
this._currentContacts.length = 0;
this.updateBodyFromObject();
}
override onDestroy() {
@@ -642,58 +636,7 @@ namespace gdjs {
this.onDeActivate();
}
_destroyBody() {
this.bodyUpdater.destroyBody();
this._contactsEndedThisFrame.length = 0;
this._contactsStartedThisFrame.length = 0;
this._currentContacts.length = 0;
}
resetToDefaultBodyUpdater() {
this.bodyUpdater = new gdjs.Physics3DRuntimeBehavior.DefaultBodyUpdater(
this
);
}
resetToDefaultCollisionChecker() {
this.collisionChecker =
new gdjs.Physics3DRuntimeBehavior.DefaultCollisionChecker(this);
}
createShape(): Jolt.Shape {
if (
this.massCenterOffsetX === 0 &&
this.massCenterOffsetY === 0 &&
this.massCenterOffsetZ === 0
) {
return this.createShapeWithoutMassCenterOffset();
}
const rotatedShapeSettings =
this._createNewShapeSettingsWithoutMassCenterOffset();
const shapeScale = this.shapeScale * this._sharedData.worldInvScale;
const offsetCenterShapeSettings =
new Jolt.OffsetCenterOfMassShapeSettings(
this.getVec3(
this.massCenterOffsetX * shapeScale,
this.massCenterOffsetY * shapeScale,
this.massCenterOffsetZ * shapeScale
),
rotatedShapeSettings
);
const shape = offsetCenterShapeSettings.Create().Get();
Jolt.destroy(offsetCenterShapeSettings);
return shape;
}
createShapeWithoutMassCenterOffset(): Jolt.Shape {
const rotatedShapeSettings =
this._createNewShapeSettingsWithoutMassCenterOffset();
const shape = rotatedShapeSettings.Create().Get();
Jolt.destroy(rotatedShapeSettings);
return shape;
}
private _createNewShapeSettingsWithoutMassCenterOffset(): Jolt.RotatedTranslatedShapeSettings {
let width = this.owner3D.getWidth() * this._sharedData.worldInvScale;
let height = this.owner3D.getHeight() * this._sharedData.worldInvScale;
let depth = this.owner3D.getDepth() * this._sharedData.worldInvScale;
@@ -739,8 +682,6 @@ namespace gdjs {
convexRadius
);
quat = this.getQuat(0, 0, 0, 1);
this._shapeHalfWidth = boxWidth / 2;
this._shapeHalfHeight = boxHeight / 2;
this._shapeHalfDepth = boxDepth / 2;
} else if (this.shape === 'Capsule') {
const radius =
@@ -756,12 +697,8 @@ namespace gdjs {
radius
);
quat = this._getShapeOrientationQuat();
this._shapeHalfWidth =
this.shapeOrientation === 'X' ? capsuleDepth / 2 : radius;
this._shapeHalfHeight =
this.shapeOrientation === 'Y' ? capsuleDepth / 2 : radius;
this._shapeHalfDepth =
this.shapeOrientation === 'Z' ? capsuleDepth / 2 : radius;
this.shapeOrientation !== 'Z' ? radius : capsuleDepth / 2;
} else if (this.shape === 'Cylinder') {
const radius =
shapeDimensionA > 0
@@ -782,12 +719,8 @@ namespace gdjs {
convexRadius
);
quat = this._getShapeOrientationQuat();
this._shapeHalfWidth =
this.shapeOrientation === 'X' ? cylinderDepth / 2 : radius;
this._shapeHalfHeight =
this.shapeOrientation === 'Y' ? cylinderDepth / 2 : radius;
this._shapeHalfDepth =
this.shapeOrientation === 'Z' ? cylinderDepth / 2 : radius;
this.shapeOrientation !== 'Z' ? radius : cylinderDepth / 2;
} else {
// Create a 'Sphere' by default.
const radius =
@@ -798,20 +731,17 @@ namespace gdjs {
: onePixel;
shapeSettings = new Jolt.SphereShapeSettings(radius);
quat = this.getQuat(0, 0, 0, 1);
this._shapeHalfWidth = radius;
this._shapeHalfHeight = radius;
this._shapeHalfDepth = radius;
}
shapeSettings.mDensity = this.density;
return new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(
this.shapeOffsetX * shapeScale,
this.shapeOffsetY * shapeScale,
this.shapeOffsetZ * shapeScale
),
const rotatedShapeSettings = new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(0, 0, 0),
quat,
shapeSettings
);
const rotatedShape = rotatedShapeSettings.Create().Get();
Jolt.destroy(rotatedShapeSettings);
return rotatedShape;
}
private _getShapeOrientationQuat(): Jolt.Quat {
@@ -937,7 +867,9 @@ namespace gdjs {
const angularVelocityY = angularVelocity.GetY();
const angularVelocityZ = angularVelocity.GetZ();
this.bodyUpdater.destroyBody();
let bodyID = this._body.GetID();
bodyInterface.RemoveBody(bodyID);
bodyInterface.DestroyBody(bodyID);
this._contactsEndedThisFrame.length = 0;
this._contactsStartedThisFrame.length = 0;
this._currentContacts.length = 0;
@@ -946,7 +878,7 @@ namespace gdjs {
if (!this._body) {
return;
}
const bodyID = this._body.GetID();
bodyID = this._body.GetID();
bodyInterface.SetLinearVelocity(
bodyID,
this.getVec3(linearVelocityX, linearVelocityY, linearVelocityZ)
@@ -1004,7 +936,7 @@ namespace gdjs {
);
}
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
result.Set(
this.owner3D.getCenterXInScene() * this._sharedData.worldInvScale,
this.owner3D.getCenterYInScene() * this._sharedData.worldInvScale,
@@ -1013,7 +945,7 @@ namespace gdjs {
return result;
}
_getPhysicsRotation(result: Jolt.Quat): Jolt.Quat {
getPhysicsRotation(result: Jolt.Quat): Jolt.Quat {
const threeObject = this.owner3D.get3DRendererObject();
result.Set(
threeObject.quaternion.x,
@@ -1024,7 +956,7 @@ namespace gdjs {
return result;
}
_moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
this.owner3D.setCenterXInScene(
physicsPosition.GetX() * this._sharedData.worldScale
);
@@ -1036,7 +968,7 @@ namespace gdjs {
);
}
_moveObjectToPhysicsRotation(physicsRotation: Jolt.Quat): void {
moveObjectToPhysicsRotation(physicsRotation: Jolt.Quat): void {
const threeObject = this.owner3D.get3DRendererObject();
threeObject.quaternion.x = physicsRotation.GetX();
threeObject.quaternion.y = physicsRotation.GetY();
@@ -1174,45 +1106,6 @@ namespace gdjs {
this._needToRecreateShape = true;
}
getMassOverride(): float {
return this.massOverride;
}
setMassOverride(mass: float): void {
if (this.massOverride === mass) {
return;
}
this.massOverride = mass;
this._needToRecreateBody = true;
}
getShapeOffsetX(): float {
return this.shapeOffsetX;
}
setShapeOffsetX(shapeOffsetX: float): void {
this.shapeOffsetX = shapeOffsetX;
this._needToRecreateShape = true;
}
getShapeOffsetY(): float {
return this.shapeOffsetY;
}
setShapeOffsetY(shapeOffsetY: float): void {
this.shapeOffsetY = shapeOffsetY;
this._needToRecreateShape = true;
}
getShapeOffsetZ(): float {
return this.shapeOffsetZ;
}
setShapeOffsetZ(shapeOffsetZ: float): void {
this.shapeOffsetZ = shapeOffsetZ;
this._needToRecreateShape = true;
}
getFriction(): float {
return this.friction;
}
@@ -1577,14 +1470,14 @@ namespace gdjs {
}
const body = this._body!;
const deltaX = towardX - this.owner3D.getX();
const deltaY = towardY - this.owner3D.getY();
const deltaZ = towardZ - this.owner3D.getZ();
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distanceSq === 0) {
const deltaX = towardX - body.GetPosition().GetX();
const deltaY = towardY - body.GetPosition().GetY();
const deltaZ = towardZ - body.GetPosition().GetZ();
const distance = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distance === 0) {
return;
}
const ratio = length / Math.sqrt(distanceSq);
const ratio = length / distance;
this._sharedData.bodyInterface.AddForce(
body.GetID(),
@@ -1637,25 +1530,33 @@ namespace gdjs {
length: float,
towardX: float,
towardY: float,
towardZ: float
towardZ: float,
originX: float,
originY: float,
originZ: float
): void {
if (this._body === null) {
if (!this._createBody()) return;
}
const body = this._body!;
const deltaX = towardX - this.owner3D.getX();
const deltaY = towardY - this.owner3D.getY();
const deltaZ = towardZ - this.owner3D.getZ();
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distanceSq === 0) {
const deltaX = towardX - originX;
const deltaY = towardY - originY;
const deltaZ = towardZ - originZ;
const distance = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distance === 0) {
return;
}
const ratio = length / Math.sqrt(distanceSq);
const ratio = length / distance;
this._sharedData.bodyInterface.AddImpulse(
body.GetID(),
this.getVec3(deltaX * ratio, deltaY * ratio, deltaZ * ratio)
this.getVec3(deltaX * ratio, deltaY * ratio, deltaZ * ratio),
this.getRVec3(
originX * this._sharedData.worldInvScale,
originY * this._sharedData.worldInvScale,
originZ * this._sharedData.worldInvScale
)
);
}
@@ -1886,8 +1787,8 @@ namespace gdjs {
const shape = behavior.createShape();
const bodyCreationSettings = new Jolt.BodyCreationSettings(
shape,
behavior._getPhysicsPosition(_sharedData.getRVec3(0, 0, 0)),
behavior._getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
behavior.getPhysicsPosition(_sharedData.getRVec3(0, 0, 0)),
behavior.getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
behavior.bodyType === 'Static'
? Jolt.EMotionType_Static
: behavior.bodyType === 'Kinematic'
@@ -1908,12 +1809,6 @@ namespace gdjs {
bodyCreationSettings.mLinearDamping = behavior.linearDamping;
bodyCreationSettings.mAngularDamping = behavior.angularDamping;
bodyCreationSettings.mGravityFactor = behavior.gravityScale;
if (behavior.massOverride > 0) {
bodyCreationSettings.mOverrideMassProperties =
Jolt.EOverrideMassProperties_CalculateInertia;
bodyCreationSettings.mMassPropertiesOverride.mMass =
behavior.massOverride;
}
const bodyInterface = _sharedData.bodyInterface;
const body = bodyInterface.CreateBody(bodyCreationSettings);
@@ -1932,8 +1827,8 @@ namespace gdjs {
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (_body !== null && _body.IsActive()) {
behavior._moveObjectToPhysicsPosition(_body.GetPosition());
behavior._moveObjectToPhysicsRotation(_body.GetRotation());
behavior.moveObjectToPhysicsPosition(_body.GetPosition());
behavior.moveObjectToPhysicsRotation(_body.GetRotation());
}
}
@@ -1955,8 +1850,8 @@ namespace gdjs {
) {
_sharedData.bodyInterface.SetPositionAndRotationWhenChanged(
body.GetID(),
this.behavior._getPhysicsPosition(_sharedData.getRVec3(0, 0, 0)),
this.behavior._getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
this.behavior.getPhysicsPosition(_sharedData.getRVec3(0, 0, 0)),
this.behavior.getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
Jolt.EActivation_Activate
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,6 @@ namespace gdjs {
owner3D: gdjs.RuntimeObject3D;
private _physics3DBehaviorName: string;
private _physics3D: Physics3D | null = null;
private _isHookedToPhysicsStep = false;
character: Jolt.CharacterVirtual | null = null;
/**
* sharedData is a reference to the shared data of the scene, that registers
@@ -102,7 +101,7 @@ namespace gdjs {
// This is useful when the object is synchronized by an external source
// like in a multiplayer game, and we want to be able to predict the
// movement of the object, even if the inputs are not updated every frame.
private _dontClearInputsBetweenFrames: boolean = false;
private _clearInputsBetweenFrames: boolean = true;
/**
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
@@ -170,15 +169,10 @@ namespace gdjs {
if (this._physics3D) {
return this._physics3D;
}
if (!this.activated()) {
return null;
}
const behavior = this.owner.getBehavior(
this._physics3DBehaviorName
) as gdjs.Physics3DRuntimeBehavior;
if (!behavior.activated()) {
return null;
}
const sharedData = behavior._sharedData;
const jolt = sharedData.jolt;
const extendedUpdateSettings = new Jolt.ExtendedUpdateSettings();
@@ -202,10 +196,7 @@ namespace gdjs {
shapeFilter,
};
this.setStairHeightMax(this._stairHeightMax);
if (!this._isHookedToPhysicsStep) {
sharedData.registerHook(this);
this._isHookedToPhysicsStep = true;
}
sharedData.registerHook(this);
behavior.bodyUpdater =
new gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterBodyUpdater(this);
@@ -277,13 +268,15 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
override getNetworkSyncData(
options: GetNetworkSyncDataOptions
): PhysicsCharacter3DNetworkSyncData {
// This method is called, so we are synchronizing this object.
// Let's clear the inputs between frames as we control it.
this._dontClearInputsBetweenFrames = false;
this._clearInputsBetweenFrames = true;
return {
...super.getNetworkSyncData(),
...super.getNetworkSyncData(options),
props: {
fwa: this._forwardAngle,
fws: this._currentForwardSpeed,
@@ -306,9 +299,10 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: PhysicsCharacter3DNetworkSyncData
networkSyncData: PhysicsCharacter3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData);
super.updateFromNetworkSyncData(networkSyncData, options);
const behaviorSpecificProps = networkSyncData.props;
this._forwardAngle = behaviorSpecificProps.fwa;
@@ -328,11 +322,11 @@ namespace gdjs {
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
// When the object is synchronized from the network, the inputs must not be cleared.
this._dontClearInputsBetweenFrames = true;
// Clear user inputs between frames only if requested.
this._clearInputsBetweenFrames = !!options.clearMemory;
}
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
const physics3D = this.getPhysics3D();
if (!physics3D) {
result.Set(0, 0, 0);
@@ -352,7 +346,7 @@ namespace gdjs {
return result;
}
_getPhysicsRotation(result: Jolt.Quat): Jolt.Quat {
getPhysicsRotation(result: Jolt.Quat): Jolt.Quat {
// Characters body should not rotate around X and Y.
const rotation = result.sEulerAngles(
this.getVec3(0, 0, gdjs.toRad(this.owner3D.getAngle()))
@@ -367,7 +361,7 @@ namespace gdjs {
return result;
}
_moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
const physics3D = this.getPhysics3D();
if (!physics3D) {
return;
@@ -385,7 +379,7 @@ namespace gdjs {
);
}
_moveObjectToPhysicsRotation(physicsRotation: Jolt.Quat): void {
moveObjectToPhysicsRotation(physicsRotation: Jolt.Quat): void {
const threeObject = this.owner3D.get3DRendererObject();
threeObject.quaternion.x = physicsRotation.GetX();
threeObject.quaternion.y = physicsRotation.GetY();
@@ -399,48 +393,36 @@ namespace gdjs {
}
override onDeActivate() {
if (!this._physics3D) {
return;
}
this._destroyBody();
this.collisionChecker.clearContacts();
}
override onActivate() {
const behavior = this.owner.getBehavior(
this._physics3DBehaviorName
) as gdjs.Physics3DRuntimeBehavior;
if (!behavior) {
return;
}
behavior._destroyBody();
}
override onActivate() {}
override onDestroy() {
this._destroyedDuringFrameLogic = true;
this.onDeActivate();
this._destroyCharacter();
}
/**
* Remove the character and its body from the physics engine.
* This method is called when:
* - The Physics3D behavior is deactivated
* - This behavior is deactivated
* - The object is destroyed
*
* Only deactivating the character behavior won't destroy the character.
* Indeed, deactivated characters don't move as characters but still have collisions.
*/
_destroyBody() {
_destroyCharacter() {
if (this.character) {
if (this._canBePushed) {
this.charactersManager.removeCharacter(this.character);
Jolt.destroy(this.character.GetListener());
}
this.collisionChecker.clearContacts();
// The body is destroyed with the character.
Jolt.destroy(this.character);
this.character = null;
if (this._physics3D) {
const { behavior } = this._physics3D;
behavior.resetToDefaultBodyUpdater();
behavior.resetToDefaultCollisionChecker();
this._physics3D.behavior._body = null;
const {
extendedUpdateSettings,
@@ -650,7 +632,7 @@ namespace gdjs {
this._wasJumpKeyPressed = this._hasPressedJumpKey;
this._wasStickUsed = this._hasUsedStick;
if (!this._dontClearInputsBetweenFrames) {
if (this._clearInputsBetweenFrames) {
this._hasPressedForwardKey = false;
this._hasPressedBackwardKey = false;
this._hasPressedRightKey = false;
@@ -1524,8 +1506,7 @@ namespace gdjs {
const { behavior } = physics3D;
const { _slopeMaxAngle, owner3D, _sharedData } = this.characterBehavior;
// Jolt doesn't support center of mass offset for characters.
const shape = behavior.createShapeWithoutMassCenterOffset();
const shape = behavior.createShape();
const settings = new Jolt.CharacterVirtualSettings();
// Characters innerBody are Kinematic body, they don't allow other
@@ -1564,10 +1545,10 @@ namespace gdjs {
);
const character = new Jolt.CharacterVirtual(
settings,
this.characterBehavior._getPhysicsPosition(
this.characterBehavior.getPhysicsPosition(
_sharedData.getRVec3(0, 0, 0)
),
behavior._getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
behavior.getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1)),
_sharedData.physicsSystem
);
Jolt.destroy(settings);
@@ -1644,19 +1625,6 @@ namespace gdjs {
contactNormal,
settings
) => {};
characterContactListener.OnContactPersisted = (
inCharacter,
inBodyID2,
inSubShapeID2,
inContactPosition,
inContactNormal,
ioSettings
) => {};
characterContactListener.OnContactRemoved = (
inCharacter,
inBodyID2,
inSubShapeID2
) => {};
characterContactListener.OnCharacterContactAdded = (
character,
otherCharacter,
@@ -1665,19 +1633,6 @@ namespace gdjs {
contactNormal,
settings
) => {};
characterContactListener.OnCharacterContactPersisted = (
inCharacter,
inOtherCharacter,
inSubShapeID2,
inContactPosition,
inContactNormal,
ioSettings
) => {};
characterContactListener.OnCharacterContactRemoved = (
inCharacter,
inOtherCharacter,
inSubShapeID2
) => {};
characterContactListener.OnContactSolve = (
character,
bodyID2,
@@ -1714,10 +1669,10 @@ namespace gdjs {
return;
}
// We can't rely on the body position because of mCharacterPadding.
this.characterBehavior._moveObjectToPhysicsPosition(
this.characterBehavior.moveObjectToPhysicsPosition(
character.GetPosition()
);
this.characterBehavior._moveObjectToPhysicsRotation(
this.characterBehavior.moveObjectToPhysicsRotation(
character.GetRotation()
);
}
@@ -1745,7 +1700,7 @@ namespace gdjs {
behavior._objectOldRotationZ !== owner3D.getAngle()
) {
character.SetRotation(
this.characterBehavior._getPhysicsRotation(
this.characterBehavior.getPhysicsRotation(
_sharedData.getQuat(0, 0, 0, 1)
)
);
@@ -1758,7 +1713,7 @@ namespace gdjs {
return;
}
character.SetPosition(
this.characterBehavior._getPhysicsPosition(
this.characterBehavior.getPhysicsPosition(
_sharedData.getRVec3(0, 0, 0)
)
);
@@ -1780,7 +1735,7 @@ namespace gdjs {
if (!character) {
return;
}
const shape = behavior.createShapeWithoutMassCenterOffset();
const shape = behavior.createShape();
const isShapeValid = character.SetShape(
shape,
Number.MAX_VALUE,
@@ -1801,7 +1756,7 @@ namespace gdjs {
}
destroyBody() {
this.characterBehavior._destroyBody();
this.characterBehavior._destroyCharacter();
}
}

View File

@@ -26,7 +26,6 @@ declare namespace Jolt {
size(): number;
}
class ArrayVec3 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Vec3;
@@ -37,7 +36,6 @@ declare namespace Jolt {
data(): Vec3MemRef;
}
class ArrayQuat {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Quat;
@@ -48,7 +46,6 @@ declare namespace Jolt {
data(): QuatMemRef;
}
class ArrayMat44 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Mat44;
@@ -59,7 +56,6 @@ declare namespace Jolt {
data(): Mat44MemRef;
}
class ArrayBodyID {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): BodyID;
@@ -70,7 +66,6 @@ declare namespace Jolt {
data(): BodyIDMemRef;
}
class ArrayBodyPtr {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Body;
@@ -436,13 +431,6 @@ declare namespace Jolt {
function _emscripten_enum_SoftBodySharedSettings_ELRAType_SoftBodySharedSettings_ELRAType_None(): SoftBodySharedSettings_ELRAType;
function _emscripten_enum_SoftBodySharedSettings_ELRAType_SoftBodySharedSettings_ELRAType_EuclideanDistance(): SoftBodySharedSettings_ELRAType;
function _emscripten_enum_SoftBodySharedSettings_ELRAType_SoftBodySharedSettings_ELRAType_GeodesicDistance(): SoftBodySharedSettings_ELRAType;
const MeshShapeSettings_EBuildQuality_FavorRuntimePerformance: number;
const MeshShapeSettings_EBuildQuality_FavorBuildSpeed: number;
type MeshShapeSettings_EBuildQuality =
| typeof MeshShapeSettings_EBuildQuality_FavorRuntimePerformance
| typeof MeshShapeSettings_EBuildQuality_FavorBuildSpeed;
function _emscripten_enum_MeshShapeSettings_EBuildQuality_MeshShapeSettings_EBuildQuality_FavorRuntimePerformance(): MeshShapeSettings_EBuildQuality;
function _emscripten_enum_MeshShapeSettings_EBuildQuality_MeshShapeSettings_EBuildQuality_FavorBuildSpeed(): MeshShapeSettings_EBuildQuality;
class Vec3MemRef {}
class QuatMemRef {}
class Mat44MemRef {}
@@ -456,7 +444,6 @@ declare namespace Jolt {
constructor(inV: Float3);
constructor(inX: number, inY: number, inZ: number);
sZero(): Vec3;
sOne(): Vec3;
sAxisX(): Vec3;
sAxisY(): Vec3;
sAxisZ(): Vec3;
@@ -518,7 +505,6 @@ declare namespace Jolt {
constructor();
constructor(inX: number, inY: number, inZ: number);
sZero(): RVec3;
sOne(): RVec3;
sAxisX(): RVec3;
sAxisY(): RVec3;
sAxisZ(): RVec3;
@@ -566,7 +552,6 @@ declare namespace Jolt {
constructor(inV: Vec3, inW: number);
constructor(inX: number, inY: number, inZ: number, inW: number);
sZero(): Vec4;
sOne(): Vec4;
sReplicate(inV: number): Vec4;
sMin(inLHS: Vec4, inRHS: Vec4): Vec4;
sMax(inLHS: Vec4, inRHS: Vec4): Vec4;
@@ -1442,9 +1427,6 @@ declare namespace Jolt {
get_mPerTriangleUserData(): boolean;
set_mPerTriangleUserData(mPerTriangleUserData: boolean): void;
mPerTriangleUserData: boolean;
get_mBuildQuality(): MeshShapeSettings_EBuildQuality;
set_mBuildQuality(mBuildQuality: MeshShapeSettings_EBuildQuality): void;
mBuildQuality: MeshShapeSettings_EBuildQuality;
}
class MeshShape extends Shape {
GetTriangleUserData(inSubShapeID: SubShapeID): number;
@@ -2541,8 +2523,6 @@ declare namespace Jolt {
GetGravityFactor(inBodyID: BodyID): number;
SetUseManifoldReduction(inBodyID: BodyID, inUseReduction: boolean): void;
GetUseManifoldReduction(inBodyID: BodyID): boolean;
SetCollisionGroup(inBodyID: BodyID, inCollisionGroup: CollisionGroup): void;
GetCollisionGroup(inBodyID: BodyID): CollisionGroup;
AddForce(
inBodyID: BodyID,
inForce: Vec3,
@@ -2644,9 +2624,9 @@ declare namespace Jolt {
get_mLinearCastMaxPenetration(): number;
set_mLinearCastMaxPenetration(mLinearCastMaxPenetration: number): void;
mLinearCastMaxPenetration: number;
get_mManifoldTolerance(): number;
set_mManifoldTolerance(mManifoldTolerance: number): void;
mManifoldTolerance: number;
get_mManifoldToleranceSq(): number;
set_mManifoldToleranceSq(mManifoldToleranceSq: number): void;
mManifoldToleranceSq: number;
get_mMaxPenetrationDistance(): number;
set_mMaxPenetrationDistance(mMaxPenetrationDistance: number): void;
mMaxPenetrationDistance: number;
@@ -3003,7 +2983,6 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayRayCastResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): RayCastResult;
@@ -3061,7 +3040,6 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayCollidePointResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CollidePointResult;
@@ -3136,7 +3114,6 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayCollideShapeResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CollideShapeResult;
@@ -3211,7 +3188,6 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayShapeCastResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): ShapeCastResult;
@@ -3642,7 +3618,6 @@ declare namespace Jolt {
mMaxDistance: number;
}
class ArraySoftBodySharedSettingsVertex {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVertex;
@@ -3652,7 +3627,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsFace {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsFace;
@@ -3662,7 +3636,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsEdge {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsEdge;
@@ -3672,7 +3645,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsDihedralBend {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsDihedralBend;
@@ -3682,7 +3654,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsVolume {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVolume;
@@ -3692,7 +3663,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsInvBind {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsInvBind;
@@ -3702,7 +3672,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsSkinned {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsSkinned;
@@ -3712,7 +3681,6 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsLRA {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsLRA;
@@ -3740,7 +3708,6 @@ declare namespace Jolt {
mLRAMaxDistanceMultiplier: number;
}
class ArraySoftBodySharedSettingsVertexAttributes {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVertexAttributes;
@@ -3889,7 +3856,6 @@ declare namespace Jolt {
readonly mVelocityOffset: number;
}
class ArraySoftBodyVertex {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodyVertex;
@@ -3957,18 +3923,8 @@ declare namespace Jolt {
set_mShape(mShape: Shape): void;
mShape: Shape;
}
class CharacterID {
constructor();
GetValue(): number;
IsInvalid(): boolean;
sNextCharacterID(): CharacterID;
sSetNextCharacterID(inNextValue: number): void;
}
class CharacterVirtualSettings extends CharacterBaseSettings {
constructor();
get_mID(): CharacterID;
set_mID(mID: CharacterID): void;
mID: CharacterID;
get_mMass(): number;
set_mMass(mMass: number): void;
mMass: number;
@@ -4011,9 +3967,6 @@ declare namespace Jolt {
get_mInnerBodyShape(): Shape;
set_mInnerBodyShape(mInnerBodyShape: Shape): void;
mInnerBodyShape: Shape;
get_mInnerBodyIDOverride(): BodyID;
set_mInnerBodyIDOverride(mInnerBodyIDOverride: BodyID): void;
mInnerBodyIDOverride: BodyID;
get_mInnerBodyLayer(): number;
set_mInnerBodyLayer(mInnerBodyLayer: number): void;
mInnerBodyLayer: number;
@@ -4055,19 +4008,6 @@ declare namespace Jolt {
inContactNormal: number,
ioSettings: number
): void;
OnContactPersisted(
inCharacter: number,
inBodyID2: number,
inSubShapeID2: number,
inContactPosition: number,
inContactNormal: number,
ioSettings: number
): void;
OnContactRemoved(
inCharacter: number,
inBodyID2: number,
inSubShapeID2: number
): void;
OnCharacterContactAdded(
inCharacter: number,
inOtherCharacter: number,
@@ -4076,19 +4016,6 @@ declare namespace Jolt {
inContactNormal: number,
ioSettings: number
): void;
OnCharacterContactPersisted(
inCharacter: number,
inOtherCharacter: number,
inSubShapeID2: number,
inContactPosition: number,
inContactNormal: number,
ioSettings: number
): void;
OnCharacterContactRemoved(
inCharacter: number,
inOtherCharacter: number,
inSubShapeID2: number
): void;
OnContactSolve(
inCharacter: number,
inBodyID2: number,
@@ -4164,9 +4091,9 @@ declare namespace Jolt {
get_mBodyB(): BodyID;
set_mBodyB(mBodyB: BodyID): void;
mBodyB: BodyID;
get_mCharacterIDB(): CharacterID;
set_mCharacterIDB(mCharacterIDB: CharacterID): void;
mCharacterIDB: CharacterID;
get_mCharacterB(): CharacterVirtual;
set_mCharacterB(mCharacterB: CharacterVirtual): void;
mCharacterB: CharacterVirtual;
get_mSubShapeIDB(): SubShapeID;
set_mSubShapeIDB(mSubShapeIDB: SubShapeID): void;
mSubShapeIDB: SubShapeID;
@@ -4176,9 +4103,6 @@ declare namespace Jolt {
get_mIsSensorB(): boolean;
set_mIsSensorB(mIsSensorB: boolean): void;
mIsSensorB: boolean;
get_mCharacterB(): CharacterVirtual;
set_mCharacterB(mCharacterB: CharacterVirtual): void;
mCharacterB: CharacterVirtual;
get_mUserData(): number;
set_mUserData(mUserData: number): void;
mUserData: number;
@@ -4196,7 +4120,6 @@ declare namespace Jolt {
mCanPushCharacter: boolean;
}
class ArrayCharacterVirtualContact {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CharacterVirtualContact;
@@ -4311,7 +4234,6 @@ declare namespace Jolt {
inRotation: Quat,
inSystem: PhysicsSystem
);
GetID(): CharacterID;
SetListener(inListener: CharacterContactListener): void;
SetCharacterVsCharacterCollision(
inCharacterVsCharacterCollision: CharacterVsCharacterCollision
@@ -4343,8 +4265,6 @@ declare namespace Jolt {
GetUserData(): number;
SetUserData(inUserData: number): void;
GetInnerBodyID(): BodyID;
StartTrackingContactChanges(): void;
FinishTrackingContactChanges(): void;
CancelVelocityTowardsSteepSlopes(inDesiredVelocity: Vec3): Vec3;
Update(
inDeltaTime: number,
@@ -4406,7 +4326,6 @@ declare namespace Jolt {
SetInnerBodyShape(inShape: Shape): void;
GetTransformedShape(): TransformedShape;
HasCollidedWith(inBodyID: BodyID): boolean;
HasCollidedWithCharacterID(inCharacterID: CharacterID): boolean;
HasCollidedWithCharacter(inCharacter: CharacterVirtual): boolean;
GetActiveContacts(): ArrayCharacterVirtualContact;
}
@@ -4421,7 +4340,6 @@ declare namespace Jolt {
GetValue(inX: number): number;
}
class ArrayFloat {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4432,7 +4350,6 @@ declare namespace Jolt {
data(): FloatMemRef;
}
class ArrayUint {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4443,7 +4360,6 @@ declare namespace Jolt {
data(): UintMemRef;
}
class ArrayUint8 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4454,7 +4370,6 @@ declare namespace Jolt {
data(): Uint8MemRef;
}
class ArrayVehicleAntiRollBar {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): VehicleAntiRollBar;
@@ -4463,7 +4378,6 @@ declare namespace Jolt {
clear(): void;
}
class ArrayWheelSettings {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): WheelSettings;
@@ -4472,7 +4386,6 @@ declare namespace Jolt {
clear(): void;
}
class ArrayVehicleDifferentialSettings {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): VehicleDifferentialSettings;
@@ -4544,7 +4457,6 @@ declare namespace Jolt {
inWheelRight: Vec3,
inWheelUp: Vec3
): RMat44;
GetAntiRollBars(): ArrayVehicleAntiRollBar;
SetNumStepsBetweenCollisionTestActive(inSteps: number): void;
GetNumStepsBetweenCollisionTestActive(): number;
SetNumStepsBetweenCollisionTestInactive(inSteps: number): void;
@@ -4907,6 +4819,9 @@ declare namespace Jolt {
}
class VehicleControllerSettings {}
class VehicleController {
GetRefCount(): number;
AddRef(): void;
Release(): void;
GetConstraint(): VehicleConstraint;
}
class WheeledVehicleController extends VehicleController {

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