Compare commits

..

36 Commits

Author SHA1 Message Date
Florian Rival
a996bb16a7 Add wait time to check certificate is ready in AppVeyor 2025-05-30 18:17:17 +02:00
Florian Rival
f6c43b2db3 Improve description of the 2D particle emitter 2025-05-30 17:17:57 +02:00
Florian Rival
f00156a654 Improve "Destroy Outside" behavior descriptions 2025-05-30 16:52:17 +02:00
Florian Rival
41fd1cbcee Make Events Sheet more robust against changes in events made outside of the tab (#7625)
Only show in developer changelog
2025-05-29 18:17:14 +02:00
Florian Rival
52c807d74a Use separately built GDevelop.js for Windows and Linux builds (#7627)
Only show in developer changelog
2025-05-28 18:51:10 +02:00
D8H
8713a496b4 Declare the assets dimensions according to their variants in exported GDO (#7623) 2025-05-27 19:30:25 +02:00
Clément Pasteau
a3033f2db1 Update badge link to suggest opening profile after first click (#7624) 2025-05-27 10:59:57 +02:00
Florian Rival
5fb8faffc1 Improve Particle emitter description
Don't show in changelog
2025-05-25 17:02:02 +02:00
Clément Pasteau
a883955703 Add the Premium course to the Education curriculum (#7619) 2025-05-22 15:38:29 +02:00
Clément Pasteau
b39d7adcbc Fix displaying Teach tab if no members (#7617)
Do not show in changelog
2025-05-20 17:19:24 +02:00
github-actions[bot]
c577c8db71 Update extension translations [skip ci] (#7615)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-05-16 15:53:02 +02:00
Clément Pasteau
d7d17400dd Fix error accessing deleted objects after an object is deleted from a scene (#7612) 2025-05-15 18:18:09 +02:00
D8H
b219d50fd8 Fix a typo in the "hand brake" action (#7613)
- Don't show in changelog
2025-05-15 16:48:30 +02:00
Clément Pasteau
88f318e6df Bump to 5.5.231 (#7610)
Do not show in changelog
2025-05-15 11:03:54 +02:00
github-actions[bot]
d12ac44b7d Update translations [skip ci] (#7611)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-05-15 11:03:32 +02:00
Clément Pasteau
7155eea716 Show gd.games homepage on play section by default on mobile (#7609) 2025-05-15 10:46:49 +02:00
github-actions[bot]
ca0796f131 Update translations [skip ci] (#7595)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-05-15 10:35:56 +02:00
Clément Pasteau
c4b33e2481 Prevent continuing to use GDevelop if not all extensions have been loaded (#7608)
* GDevelop sometimes has faulty updates, for instance when the computer is shut down during an update. This was causing the app to be incomplete and projects to become corrupted after a save (ex: BBText objects being broken)
* This check shows an unskippable dialog suggesting a re-install of GDevelop
2025-05-15 10:15:42 +02:00
D8H
ff95564b6b Add new 3D physics-based vehicle behavior (#7479)
* You can now simulate realistic 3D cars using the new Vehicle behavior powered by the 3D physics engine.
* Advanced details can be customized: from wheel setup and gear ratios to engine torque, steering, brakes, anti-roll bars, and drivetrain (front/rear).
* The default setup offers a semi-realistic, arcade-style driving feel. For smoother gameplay, explore the example projects to see how fine-tuning makes a big difference.
* Jolt physics engine was upgraded to version 0.34.0.
2025-05-14 10:52:47 +02:00
Clément Pasteau
cecf1ab791 Fix crash when selecting a resource after switching tabs in the Resource selection dialog (#7607) 2025-05-14 10:28:12 +02:00
D8H
a571445e0e Allow custom objects to self-destruct (#7605) 2025-05-12 14:03:21 +02:00
D8H
89e418cd24 Fix grid snapping of pasted instances (#7602) 2025-05-12 11:41:42 +02:00
Florian Rival
896ccfcffa Add hint that mapper behaviors should be used for platformer (#7601)
Don't show in changelog
2025-05-12 10:51:09 +02:00
Clément Pasteau
d98d181755 Fix typo (#7604)
Do not show in changelog
2025-05-12 10:30:08 +02:00
Florian Rival
3c63f9b617 Update 3D extension internal descriptions
Only show in developer changelog
2025-05-08 12:26:54 +02:00
D8H
8312bbe4f8 Fix 3D Physics "apply force/impulse toward position" action (#7600) 2025-05-06 21:04:02 +02:00
Clément Pasteau
bb77a71f26 Bump to 5.5.230 (#7593)
Do not show in changelog
2025-05-06 10:10:48 +02:00
D8H
4353469554 Fix bitmap text alignment and rotation (#7597)
* Fix BBText rotation
2025-05-05 20:10:21 +02:00
Clément Pasteau
46ea431f62 Fix firebase tests (#7594)
Do not show in changelog
2025-05-05 15:16:39 +02:00
github-actions[bot]
2d29d43355 Update translations [skip ci] (#7577)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-05-05 11:22:08 +02:00
Clément Pasteau
28a4e253a1 Update desktop & web icons with latest GDevelop branding (#7578) 2025-05-05 10:22:40 +02:00
D8H
f3dcb8eec8 Fix asset export for objects with unused children overriding (#7590) 2025-05-05 09:53:11 +02:00
Florian Rival
bf9e38ff31 Improve descriptions of boolean logic conditions
Only show in developer changelog
2025-05-04 16:08:36 +02:00
D8H
87649b9def Ensure variants downloaded from the store comply to their events-based object (#7587) 2025-05-02 12:51:58 +02:00
D8H
60d332e872 Fix rendered custom objects in the editor with an overriding of children configuration (#7585)
- Fix children overriding to only apply to the default variant at runtime
2025-05-02 12:51:14 +02:00
D8H
bdab12b1e6 Fix variant edition: forbid to edit variants from the store (they should be duplicated instead) (#7586) 2025-05-02 12:05:49 +02:00
370 changed files with 7759 additions and 71177 deletions

View File

@@ -14,7 +14,7 @@ orbs:
macos: circleci/macos@2.5.1 # For Rosetta (see below)
node: circleci/node@5.2.0 # For a recent npm version (see below)
jobs:
# Build the **entire** app for macOS.
# Build the **entire** app for macOS (including the GDevelop.js library).
build-macos:
macos:
xcode: 14.2.0
@@ -94,7 +94,7 @@ jobs:
name: Deploy to S3 (latest)
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the **entire** app for Linux.
# Build the app for Linux (using a pre-built GDevelop.js library).
build-linux:
# CircleCI docker workers are failing if they don't have enough memory (no swap)
resource_class: xlarge
@@ -107,44 +107,25 @@ jobs:
- checkout
- aws-cli/setup
# System dependencies (for Electron Builder and Emscripten)
# System dependencies (for Electron Builder)
- run:
name: Install dependencies for Emscripten
command: sudo apt-get update && sudo apt install cmake
- run:
name: Install Python3 dependencies for Emscripten
command: sudo apt install python-is-python3 python3-distutils -y
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
name: Update system dependencies
command: sudo apt-get update
- run:
name: Install system dependencies for Electron builder
command: sudo apt install icnsutils && sudo apt install graphicsmagick && sudo apt install rsync
# GDevelop.js dependencies
- restore_cache:
keys:
- gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# fallback to using the latest cache if no exact match is found
- gd-linux-nodejs-dependencies---
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
# GDevelop IDE dependencies (using an exact version of GDevelop.js, built previously)
- run:
name: Install GDevelop IDE dependencies
command: cd newIDE/app && npm install && cd ../electron-app && npm install
command: export REQUIRES_EXACT_LIBGD_JS_VERSION=true && cd newIDE/app && npm install && cd ../electron-app && npm install
- save_cache:
paths:
@@ -295,10 +276,26 @@ 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 finishes building the Windows app
# (using GDevelop.js built in a previous step).
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
workflows:
gdevelop_js-wasm:
jobs:
- build-gdevelop_js-wasm-only
gdevelop_js-wasm-extra-checks:
jobs:
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
@@ -310,13 +307,28 @@ 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.*/
- trigger-appveyor-windows-build:
requires:
- build-gdevelop_js-wasm-only
filters:
branches:
only:

View File

@@ -59,36 +59,44 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
// end of compatibility code
extension
.AddCondition("Or",
_("Or"),
_("Check if one of the sub conditions is true"),
_("If one of these conditions is true:"),
"",
"res/conditions/or24_black.png",
"res/conditions/or_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
extension
.AddCondition("And",
_("And"),
_("Check if all sub conditions are true"),
_("If all of these conditions are true:"),
"",
"res/conditions/and24_black.png",
"res/conditions/and_black.png")
.AddCondition(
"Or",
_("Or"),
_("Checks if at least one sub-condition is true. If no "
"sub-condition is specified, it will always be false. "
"This is rarely used — multiple events and sub-events are "
"usually a better approach."),
_("If one of these conditions is true:"),
"",
"res/conditions/or24_black.png",
"res/conditions/or_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
extension
.AddCondition(
"Not",
_("Not"),
_("Return the contrary of the result of the sub conditions"),
_("Invert the logical result of these conditions:"),
"And",
_("And"),
_("Checks if all sub-conditions are true. If no sub-condition is "
"specified, it will always be false. This is rarely needed, as "
"events already check all conditions before running actions."),
_("If all of these conditions are true:"),
"",
"res/conditions/not24_black.png",
"res/conditions/not_black.png")
"res/conditions/and24_black.png",
"res/conditions/and_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();
extension
.AddCondition("Not",
_("Not"),
_("Returns the opposite of the sub-condition(s) result. "
"This is rarely needed, as most conditions can be "
"inverted or expressed more simply."),
_("Invert the logical result of these conditions:"),
"",
"res/conditions/not24_black.png",
"res/conditions/not_black.png")
.SetCanHaveSubInstructions()
.MarkAsAdvanced();

View File

@@ -18,6 +18,7 @@
#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"
@@ -61,9 +62,6 @@ void ObjectAssetSerializer::SerializeTo(
element.SetAttribute("version", "");
element.SetIntAttribute("animationsCount", 1);
element.SetIntAttribute("maxFramesCount", 1);
// TODO Find the right object dimensions.
element.SetIntAttribute("width", 0);
element.SetIntAttribute("height", 0);
SerializerElement &authorsElement = element.AddChild("authors");
authorsElement.ConsiderAsArrayOf("author");
SerializerElement &tagsElement = element.AddChild("tags");
@@ -76,16 +74,28 @@ void ObjectAssetSerializer::SerializeTo(
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
double width = 0;
double height = 0;
if (project.HasEventsBasedObject(object.GetType())) {
SerializerElement &variantsElement =
objectAssetElement.AddChild("variants");
variantsElement.ConsiderAsArrayOf("variant");
const auto *variant = ObjectAssetSerializer::GetVariant(project, object);
if (variant) {
width = variant->GetAreaMaxX() - variant->GetAreaMinX();
height = variant->GetAreaMaxY() - variant->GetAreaMinY();
}
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
project, object, variantsElement, alreadyUsedVariantIdentifiers);
}
// TODO Find the right object dimensions when their is no variant.
element.SetIntAttribute("width", width);
element.SetIntAttribute("height", height);
SerializerElement &resourcesElement =
objectAssetElement.AddChild("resources");
resourcesElement.ConsiderAsArrayOf("resource");
@@ -124,41 +134,54 @@ void ObjectAssetSerializer::SerializeUsedVariantsTo(
gd::Project &project, const gd::Object &object,
SerializerElement &variantsElement,
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers) {
if (!project.HasEventsBasedObject(object.GetType())) {
const auto *variant = ObjectAssetSerializer::GetVariant(project, object);
if (!variant) {
return;
}
const auto &variantIdentifier =
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
variant->GetName();
auto insertResult = alreadyUsedVariantIdentifiers.insert(variantIdentifier);
if (!insertResult.second) {
return;
}
SerializerElement &pairElement = variantsElement.AddChild("variant");
pairElement.SetAttribute("objectType", object.GetType());
SerializerElement &variantElement = pairElement.AddChild("variant");
variant->SerializeTo(variantElement);
for (auto &object : variant->GetObjects().GetObjects()) {
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
project, *object, variantsElement, alreadyUsedVariantIdentifiers);
}
}
const gd::EventsBasedObjectVariant *
ObjectAssetSerializer::GetVariant(gd::Project &project,
const gd::Object &object) {
if (!project.HasEventsBasedObject(object.GetType())) {
return nullptr;
}
const auto &eventsBasedObject =
project.GetEventsBasedObject(object.GetType());
const auto &variants = eventsBasedObject.GetVariants();
const auto *customObjectConfiguration =
dynamic_cast<const gd::CustomObjectConfiguration *>(
&object.GetConfiguration());
if (customObjectConfiguration
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
customObjectConfiguration
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
return;
}
const auto &variantName = customObjectConfiguration->GetVariantName();
if (!variants.HasVariantNamed(variantName) &&
(customObjectConfiguration
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
customObjectConfiguration
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration())) {
return nullptr;
}
const auto &variantIdentifier =
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
variantName;
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);
}
}
const auto &variant = variants.HasVariantNamed(variantName)
? variants.GetVariant(variantName)
: eventsBasedObject.GetDefaultVariant();
return &variant;
}
} // namespace gd

View File

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

@@ -33,7 +33,132 @@ using namespace gd;
TEST_CASE("ObjectAssetSerializer", "[common]") {
SECTION("Can serialize custom objects as assets") {
SECTION("Can serialize custom objects as assets with variant") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
eventsBasedObject.SetFullName("My events based object");
eventsBasedObject.SetDescription("An events based object for test");
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
project, "MyExtension::Sprite", "MyChild", 0);
auto &childInstance =
eventsBasedObject.GetInitialInstances().InsertNewInitialInstance();
childInstance.SetObjectName("MyChild");
auto &resourceManager = project.GetResourcesManager();
gd::ImageResource imageResource;
imageResource.SetName("assets/Idle.png");
imageResource.SetFile("assets/Idle.png");
imageResource.SetSmooth(true);
resourceManager.AddResource(imageResource);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object = layout.GetObjects().InsertNewObject(
project, "MyEventsExtension::MyEventsBasedObject", "MyObject", 0);
auto *spriteConfiguration =
dynamic_cast<gd::SpriteObject *>(&childObject.GetConfiguration());
REQUIRE(spriteConfiguration != nullptr);
{
gd::Animation animation;
animation.SetName("Idle");
animation.SetDirectionsCount(1);
auto &direction = animation.GetDirection(0);
gd::Sprite frame;
frame.SetImageName("assets/Idle.png");
direction.AddSprite(frame);
spriteConfiguration->GetAnimations().AddAnimation(animation);
}
SerializerElement assetElement;
std::vector<gd::String> usedResourceNames;
ObjectAssetSerializer::SerializeTo(project, object, "My Object",
assetElement, usedResourceNames);
// This list is used to copy resource files.
REQUIRE(usedResourceNames.size() == 1);
REQUIRE(usedResourceNames[0] == "assets/Idle.png");
// Check that the project is left untouched.
REQUIRE(resourceManager.HasResource("assets/Idle.png"));
REQUIRE(resourceManager.GetResource("assets/Idle.png").GetFile() ==
"assets/Idle.png");
REQUIRE(!resourceManager.HasResource("Idle.png"));
REQUIRE(assetElement.HasChild("objectAssets"));
auto &objectAssetsElement = assetElement.GetChild("objectAssets");
objectAssetsElement.ConsiderAsArrayOf("objectAsset");
REQUIRE(objectAssetsElement.GetChildrenCount() == 1);
auto &objectAssetElement = objectAssetsElement.GetChild(0);
REQUIRE(objectAssetElement.HasChild("variants"));
auto &variantsElement = objectAssetElement.GetChild("variants");
variantsElement.ConsiderAsArrayOf("variant");
REQUIRE(variantsElement.GetChildrenCount() == 1);
auto &variantPairElement = variantsElement.GetChild(0);
REQUIRE(variantPairElement.GetStringAttribute("objectType") ==
"MyEventsExtension::MyEventsBasedObject");
REQUIRE(variantPairElement.HasChild("variant"));
auto &variantElement = variantPairElement.GetChild("variant");
REQUIRE(variantElement.GetStringAttribute("name") == "");
REQUIRE(variantElement.HasChild("objects"));
auto &objectsElement = variantElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
REQUIRE(objectsElement.GetChildrenCount() == 1);
auto &childElement = objectsElement.GetChild(0);
REQUIRE(childElement.HasChild("animations"));
auto &animationsElement = childElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") == "Idle");
auto &directionsElement = animationElement.GetChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
REQUIRE(directionsElement.GetChildrenCount() == 1);
auto &directionElement = directionsElement.GetChild(0);
auto &spritesElement = directionElement.GetChild("sprites");
spritesElement.ConsiderAsArrayOf("sprite");
REQUIRE(spritesElement.GetChildrenCount() == 1);
auto &spriteElement = spritesElement.GetChild(0);
REQUIRE(spriteElement.GetStringAttribute("image") == "assets/Idle.png");
REQUIRE(objectAssetElement.HasChild("requiredExtensions"));
auto &requiredExtensionsElement =
objectAssetElement.GetChild("requiredExtensions");
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
REQUIRE(requiredExtensionsElement.GetChildrenCount() == 1);
auto &requiredExtensionElement = requiredExtensionsElement.GetChild(0);
REQUIRE(requiredExtensionElement.GetStringAttribute("extensionName") ==
"MyEventsExtension");
// Resources are renamed according to asset script naming conventions.
REQUIRE(objectAssetElement.HasChild("resources"));
auto &resourcesElement = objectAssetElement.GetChild("resources");
resourcesElement.ConsiderAsArrayOf("resource");
REQUIRE(resourcesElement.GetChildrenCount() == 1);
{
auto &resourceElement = resourcesElement.GetChild(0);
REQUIRE(resourceElement.GetStringAttribute("name") == "assets/Idle.png");
REQUIRE(resourceElement.GetStringAttribute("file") == "assets/Idle.png");
REQUIRE(resourceElement.GetStringAttribute("kind") == "image");
REQUIRE(resourceElement.GetBoolAttribute("smoothed") == true);
}
// Resources used in object configuration are updated.
REQUIRE(objectAssetElement.HasChild("object"));
auto &objectElement = objectAssetElement.GetChild("object");
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") ==
"MyEventsExtension::MyEventsBasedObject");
}
SECTION("Can serialize custom objects as assets with children overriding") {
gd::Platform platform;
gd::Project project;
SetupProjectWithDummyPlatform(project, platform);

View File

@@ -5,6 +5,8 @@ namespace gdjs {
type Object3DNetworkSyncDataType = {
// z is position on the Z axis, different from zo, which is Z order
z: number;
w: number;
h: number;
d: number;
rx: number;
ry: number;
@@ -110,12 +112,12 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Object3DNetworkSyncData {
getNetworkSyncData(): Object3DNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
z: this.getZ(),
w: this.getWidth(),
h: this.getHeight(),
d: this.getDepth(),
rx: this.getRotationX(),
ry: this.getRotationY(),
@@ -125,12 +127,11 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
networkSyncData: Object3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData, options);
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
if (networkSyncData.rx !== undefined)
this.setRotationX(networkSyncData.rx);

View File

@@ -434,11 +434,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Cube3DObjectNetworkSyncData {
getNetworkSyncData(): Cube3DObjectNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
mt: this._materialType,
fo: this._facesOrientation,
bfu: this._backFaceUpThroughWhichAxisRotation,
@@ -450,10 +448,9 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: Cube3DObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: Cube3DObjectNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;

View File

@@ -1,12 +1,4 @@
namespace gdjs {
type CustomObject3DNetworkSyncDataType = CustomObjectNetworkSyncDataType & {
z: float;
d: float;
rx: float;
ry: float;
ifz: boolean;
};
/**
* Base class for 3D custom objects.
*/
@@ -86,33 +78,6 @@ namespace gdjs {
}
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): CustomObject3DNetworkSyncDataType {
return {
...super.getNetworkSyncData(syncOptions),
z: this.getZ(),
d: this.getDepth(),
rx: this.getRotationX(),
ry: this.getRotationY(),
ifz: this.isFlippedZ(),
};
}
updateFromNetworkSyncData(
networkSyncData: CustomObject3DNetworkSyncDataType,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
if (networkSyncData.rx !== undefined)
this.setRotationX(networkSyncData.rx);
if (networkSyncData.ry !== undefined)
this.setRotationY(networkSyncData.ry);
if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
}
/**
* Set the object position on the Z axis.
*/

View File

@@ -21,7 +21,9 @@ module.exports = {
.setExtensionInformation(
'Scene3D',
_('3D'),
_('Support for 3D in GDevelop.'),
_(
'Support for 3D in GDevelop: this provides 3D objects and the common features for all 3D objects.'
),
'Florian Rival',
'MIT'
)
@@ -36,7 +38,9 @@ module.exports = {
'Base3DBehavior',
_('3D capability'),
'Object3D',
_('Move the object in 3D space.'),
_(
'Common features for all 3D objects: position in 3D space (including the Z axis, in addition to X and Y), size (including depth, in addition to width and height), rotation (on X and Y axis, in addition to the Z axis), scale (including Z axis, in addition to X and Y), flipping (on Z axis, in addition to horizontal (Y)/vertical (X) flipping).'
),
'',
'res/conditions/3d_box.svg',
'Base3DBehavior',

View File

@@ -20,7 +20,6 @@ namespace gdjs {
/** The base parameters of the Model3D object */
content: Object3DDataContent & {
modelResourceName: string;
depth: number;
rotationX: number;
rotationY: number;
rotationZ: number;
@@ -199,11 +198,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): Model3DObjectNetworkSyncData {
getNetworkSyncData(): Model3DObjectNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
mt: this._materialType,
op: this._originPoint,
cp: this._centerPoint,
@@ -212,15 +209,13 @@ namespace gdjs {
ass: this._animationSpeedScale,
ap: this._animationPaused,
cfd: this._crossfadeDuration,
d: this.getDepth(),
};
}
updateFromNetworkSyncData(
networkSyncData: Model3DObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: Model3DObjectNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;
@@ -248,9 +243,6 @@ namespace gdjs {
if (networkSyncData.cfd !== undefined) {
this._crossfadeDuration = networkSyncData.cfd;
}
if (networkSyncData.d !== undefined) {
this.setDepth(networkSyncData.d);
}
}
_reloadModel(objectData: Model3DObjectData) {

View File

@@ -618,7 +618,7 @@ module.exports = {
this._pixiObject.dirty = true;
}
if (this._instance.hasCustomSize()) {
if (this._instance.hasCustomSize() && this._pixiObject.width !== 0) {
const alignmentX =
object.content.align === 'right'
? 1

View File

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

View File

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

View File

@@ -721,7 +721,7 @@ module.exports = {
this._pixiObject.dirty = true;
}
if (this._instance.hasCustomSize()) {
if (this._instance.hasCustomSize() && this.getDefaultWidth() !== 0) {
const alignmentX =
object.content.align === 'right'
? 1
@@ -730,17 +730,16 @@ module.exports = {
: 0;
const width = this.getCustomWidth();
const renderedWidth = this.getDefaultWidth();
// A vector from the custom size center to the renderer center.
const centerToCenterX =
(width - this._pixiObject.width) * (alignmentX - 0.5);
const centerToCenterX = (width - renderedWidth) * (alignmentX - 0.5);
this._pixiObject.position.x = this._instance.getX() + width / 2;
this._pixiObject.anchor.x =
0.5 - centerToCenterX / this._pixiObject.width;
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
} else {
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._instance.getX() + this.getDefaultWidth() / 2;
this._pixiObject.anchor.x = 0.5;
}
const alignmentY =
@@ -750,7 +749,7 @@ module.exports = {
? 0.5
: 0;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height * (0.5 - alignmentY);
this._instance.getY() + this.getDefaultHeight() * (0.5 - alignmentY);
this._pixiObject.anchor.y = 0.5;
this._pixiObject.rotation = RenderedInstance.toRad(
@@ -774,11 +773,11 @@ module.exports = {
}
getDefaultWidth() {
return this._pixiObject.width;
return this._pixiObject.textWidth * this._pixiObject.scale.x;
}
getDefaultHeight() {
return this._pixiObject.height;
return this._pixiObject.textHeight * this._pixiObject.scale.y;
}
getOriginY() {

View File

@@ -146,7 +146,7 @@ namespace gdjs {
}
updatePosition(): void {
if (this._object.isWrapping()) {
if (this._object.isWrapping() && this.getWidth() !== 0) {
const alignmentX =
this._object._textAlign === 'right'
? 1
@@ -155,17 +155,15 @@ namespace gdjs {
: 0;
const width = this._object.getWrappingWidth();
const renderedWidth = this.getWidth();
// A vector from the custom size center to the renderer center.
const centerToCenterX =
(width - this._pixiObject.width) * (alignmentX - 0.5);
const centerToCenterX = (width - renderedWidth) * (alignmentX - 0.5);
this._pixiObject.position.x = this._object.x + width / 2;
this._pixiObject.anchor.x =
0.5 - centerToCenterX / this._pixiObject.width;
this._pixiObject.anchor.x = 0.5 - centerToCenterX / renderedWidth;
} else {
this._pixiObject.position.x =
this._object.x + this._pixiObject.width / 2;
this._pixiObject.position.x = this._object.x + this.getWidth() / 2;
this._pixiObject.anchor.x = 0.5;
}
@@ -176,7 +174,7 @@ namespace gdjs {
? 0.5
: 0;
this._pixiObject.position.y =
this._object.y + this._pixiObject.height * (0.5 - alignmentY);
this._object.y + this.getHeight() * (0.5 - alignmentY);
this._pixiObject.anchor.y = 0.5;
}

View File

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

View File

@@ -12,35 +12,42 @@ This project is released under the MIT License.
void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("DestroyOutsideBehavior",
_("Destroy Outside Screen Behavior"),
_("This behavior can be used to destroy "
"objects when they go outside of "
"the bounds of the camera. Useful for bullets "
"or other short-lived objects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionInformation(
"DestroyOutsideBehavior",
_("Destroy Outside Screen Behavior"),
_("This behavior can be used to destroy objects when they go "
"outside of the bounds of the 2D camera. Useful for 2D bullets or "
"other short-lived objects. Don't use it for 3D objects in a "
"FPS/TPS game or any game with a camera not being a top view "
"(for 3D objects, prefer comparing "
"the position, for example Z position to see if an object goes "
"outside of the bound of the map). Be careful when using this "
"behavior because if the object appears outside of the screen, it "
"will be immediately removed."),
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Game mechanic")
.SetTags("screen")
.SetExtensionHelpPath("/behaviors/destroyoutside");
gd::BehaviorMetadata& aut =
extension.AddBehavior("DestroyOutside",
_("Destroy when outside of the screen"),
_("DestroyOutside"),
_("Destroy objects automatically when they go "
"outside of the screen's borders."),
"",
"CppPlatform/Extensions/destroyoutsideicon.png",
"DestroyOutsideBehavior",
std::make_shared<DestroyOutsideBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>())
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
extension
.AddBehavior("DestroyOutside",
_("Destroy when outside of the screen"),
_("DestroyOutside"),
_("Destroy objects automatically when they go "
"outside of the 2D camera borders."),
"",
"CppPlatform/Extensions/destroyoutsideicon.png",
"DestroyOutsideBehavior",
std::make_shared<DestroyOutsideBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>())
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
aut.AddCondition("ExtraBorder",
_("Additional border"),
_("Compare the additional border that the object must cross "
"before being deleted."),
_("Additional border (extra distance before deletion)"),
_("Compare the extra distance (in pixels) the object must "
"travel beyond the screen before it gets deleted."),
_("the additional border"),
_("Destroy outside configuration"),
"CppPlatform/Extensions/destroyoutsideicon24.png",
@@ -53,9 +60,9 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("GetExtraBorder");
aut.AddAction("ExtraBorder",
_("Additional border"),
_("Change the additional border that the object must cross "
"before being deleted."),
_("Additional border (extra distance before deletion)"),
_("Change the extra distance (in pixels) the object must "
"travel beyond the screen before it gets deleted."),
_("the additional border"),
_("Destroy outside configuration"),
"CppPlatform/Extensions/destroyoutsideicon24.png",

View File

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

View File

@@ -50,6 +50,11 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
.replace('.', '-')}-${Date.now()}`;
before(async function setupFirebase() {
// Delete any existing Firebase app before setup
if (firebase.apps.length !== 0) {
await firebase.app().delete();
}
await gdjs.evtTools.firebaseTools._setupFirebase({
getGame: () => ({
getExtensionProperty: () => JSON.stringify(firebaseConfig),

View File

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

View File

@@ -729,9 +729,7 @@ namespace gdjs {
behavior.playerNumber = ownerPlayerNumber;
}
instance.updateFromNetworkSyncData(messageData, {
clearMemory: false,
});
instance.updateFromNetworkSyncData(messageData);
setLastClockReceivedForInstanceOnScene({
sceneNetworkId,
@@ -1342,7 +1340,7 @@ namespace gdjs {
debugLogger.info(
`Destroying object ${objectName} with instance network ID ${instanceNetworkId}.`
);
instance.deleteFromScene(runtimeScene);
instance.deleteFromScene();
debugLogger.info(
`Sending acknowledgment of destruction of object ${objectName} with instance network ID ${instanceNetworkId} to ${messageSender}.`
@@ -1739,7 +1737,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.
@@ -1892,7 +1890,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.
@@ -1920,7 +1918,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();
@@ -1939,7 +1937,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);
@@ -2282,7 +2280,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(runtimeScene);
instance.deleteFromScene();
} 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(instanceContainer);
owner.deleteFromScene();
}
}, 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.getInstanceContainer());
this.owner.deleteFromScene();
return;
}
@@ -278,9 +278,7 @@ namespace gdjs {
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
const objectName = this.owner.getName();
const objectNetworkSyncData = this.owner.getNetworkSyncData({
forceSyncEverything: false,
});
const objectNetworkSyncData = this.owner.getNetworkSyncData();
// this._logToConsoleWithThrottle(
// `Synchronizing object ${this.owner.getName()} (instance ${
@@ -295,8 +293,6 @@ namespace gdjs {
x: objectNetworkSyncData.x,
y: objectNetworkSyncData.y,
z: objectNetworkSyncData.z,
w: objectNetworkSyncData.w,
h: objectNetworkSyncData.h,
zo: objectNetworkSyncData.zo,
a: objectNetworkSyncData.a,
hid: objectNetworkSyncData.hid,
@@ -304,7 +300,6 @@ namespace gdjs {
if: objectNetworkSyncData.if,
pfx: objectNetworkSyncData.pfx,
pfy: objectNetworkSyncData.pfy,
n: objectNetworkSyncData.n,
});
const shouldSyncObjectBasicInfo =
!this._hasObjectBasicInfoBeenSyncedRecently() ||
@@ -374,8 +369,6 @@ namespace gdjs {
this._lastSentBasicObjectSyncData = {
x: objectNetworkSyncData.x,
y: objectNetworkSyncData.y,
w: objectNetworkSyncData.w,
h: objectNetworkSyncData.h,
zo: objectNetworkSyncData.zo,
a: objectNetworkSyncData.a,
hid: objectNetworkSyncData.hid,
@@ -383,7 +376,6 @@ namespace gdjs {
if: objectNetworkSyncData.if,
pfx: objectNetworkSyncData.pfx,
pfy: objectNetworkSyncData.pfy,
n: objectNetworkSyncData.n,
};
this._numberOfForcedBasicObjectUpdates = Math.max(
this._numberOfForcedBasicObjectUpdates - 1,
@@ -451,9 +443,7 @@ namespace gdjs {
objectOwner: this.playerNumber,
objectName,
instanceNetworkId,
objectNetworkSyncData: this.owner.getNetworkSyncData({
forceSyncEverything: false,
}),
objectNetworkSyncData: this.owner.getNetworkSyncData(),
sceneNetworkId,
});
this._sendDataToPeersWithIncreasedClock(
@@ -603,9 +593,7 @@ namespace gdjs {
debugLogger.info(
'Sending update message to move the object immediately.'
);
const objectNetworkSyncData = this.owner.getNetworkSyncData({
forceSyncEverything: false,
});
const objectNetworkSyncData = this.owner.getNetworkSyncData();
const {
messageName: updateMessageName,
messageData: updateMessageData,

View File

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

View File

@@ -25,6 +25,8 @@ namespace gdjs {
export type PanelSpriteObjectData = ObjectData & PanelSpriteObjectDataType;
export type PanelSpriteNetworkSyncDataType = {
wid: number;
hei: number;
op: number;
color: string;
};
@@ -84,6 +86,12 @@ 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;
@@ -113,24 +121,29 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): PanelSpriteNetworkSyncData {
getNetworkSyncData(): PanelSpriteNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
wid: this.getWidth(),
hei: this.getHeight(),
op: this.getOpacity(),
color: this.getColor(),
};
}
updateFromNetworkSyncData(
networkSyncData: PanelSpriteNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: PanelSpriteNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
// Texture is not synchronized, see if this is asked or not.
if (networkSyncData.wid !== undefined) {
this.setWidth(networkSyncData.wid);
}
if (networkSyncData.hei !== undefined) {
this.setHeight(networkSyncData.hei);
}
if (networkSyncData.op !== undefined) {
this.setOpacity(networkSyncData.op);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ describe('Physics2RuntimeBehavior', () => {
doStepPreEvents(runtimeScene) {
if (this.shouldDeleteInPreEvent) {
this.owner.deleteFromScene(runtimeScene);
this.owner.deleteFromScene();
}
}
}
@@ -159,7 +159,7 @@ describe('Physics2RuntimeBehavior', () => {
);
// Delete object from scene
object.deleteFromScene(runtimeScene);
object.deleteFromScene();
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(runtimeScene);
object.deleteFromScene();
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(runtimeScene);
movingObject.deleteFromScene();
});
// Collision should be reset on destroyed object and

File diff suppressed because it is too large Load Diff

View File

@@ -305,7 +305,14 @@ 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;
@@ -313,7 +320,7 @@ namespace gdjs {
gravityScale: float;
private layers: integer;
private masks: integer;
private shapeScale: number = 1;
shapeScale: number = 1;
/**
* Array containing the beginning of contacts reported by onContactBegin. Each contact
@@ -348,7 +355,10 @@ namespace gdjs {
/**
* When set to `true` the shape will be recreated before the next physics step.
*/
private _needToRecreateShape: boolean = false;
_needToRecreateShape: boolean = false;
_shapeHalfWidth: float = 0;
_shapeHalfHeight: float = 0;
/**
* Used by {@link gdjs.PhysicsCharacter3DRuntimeBehavior} to convert coordinates.
*/
@@ -392,7 +402,14 @@ 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);
@@ -478,9 +495,7 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(
options: GetNetworkSyncDataOptions
): Physics3DNetworkSyncData {
override getNetworkSyncData(): Physics3DNetworkSyncData {
let bodyProps;
if (this._body) {
const position = this._body.GetPosition();
@@ -522,7 +537,7 @@ namespace gdjs {
};
}
return {
...super.getNetworkSyncData(options),
...super.getNetworkSyncData(),
props: {
...bodyProps,
layers: this.layers,
@@ -532,10 +547,9 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: Physics3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: Physics3DNetworkSyncData
) {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
const behaviorSpecificProps = networkSyncData.props;
if (
@@ -637,6 +651,39 @@ namespace gdjs {
}
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;
@@ -682,6 +729,8 @@ 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 =
@@ -697,8 +746,12 @@ 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' ? radius : capsuleDepth / 2;
this.shapeOrientation === 'Z' ? capsuleDepth / 2 : radius;
} else if (this.shape === 'Cylinder') {
const radius =
shapeDimensionA > 0
@@ -719,8 +772,12 @@ 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' ? radius : cylinderDepth / 2;
this.shapeOrientation === 'Z' ? cylinderDepth / 2 : radius;
} else {
// Create a 'Sphere' by default.
const radius =
@@ -731,17 +788,20 @@ 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;
const rotatedShapeSettings = new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(0, 0, 0),
return new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(
this.shapeOffsetX * shapeScale,
this.shapeOffsetY * shapeScale,
this.shapeOffsetZ * shapeScale
),
quat,
shapeSettings
);
const rotatedShape = rotatedShapeSettings.Create().Get();
Jolt.destroy(rotatedShapeSettings);
return rotatedShape;
}
private _getShapeOrientationQuat(): Jolt.Quat {
@@ -936,7 +996,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,
@@ -945,7 +1005,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,
@@ -956,7 +1016,7 @@ namespace gdjs {
return result;
}
moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
_moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
this.owner3D.setCenterXInScene(
physicsPosition.GetX() * this._sharedData.worldScale
);
@@ -968,7 +1028,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();
@@ -1106,6 +1166,18 @@ 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;
}
getFriction(): float {
return this.friction;
}
@@ -1473,11 +1545,11 @@ namespace gdjs {
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) {
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distanceSq === 0) {
return;
}
const ratio = length / distance;
const ratio = length / Math.sqrt(distanceSq);
this._sharedData.bodyInterface.AddForce(
body.GetID(),
@@ -1543,11 +1615,11 @@ namespace gdjs {
const deltaX = towardX - originX;
const deltaY = towardY - originY;
const deltaZ = towardZ - originZ;
const distance = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distance === 0) {
const distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
if (distanceSq === 0) {
return;
}
const ratio = length / distance;
const ratio = length / Math.sqrt(distanceSq);
this._sharedData.bodyInterface.AddImpulse(
body.GetID(),
@@ -1787,8 +1859,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'
@@ -1809,6 +1881,12 @@ 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);
@@ -1827,8 +1905,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());
}
}
@@ -1850,8 +1928,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

@@ -101,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 _clearInputsBetweenFrames: boolean = true;
private _dontClearInputsBetweenFrames: boolean = false;
/**
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
@@ -268,15 +268,13 @@ namespace gdjs {
return true;
}
override getNetworkSyncData(
options: GetNetworkSyncDataOptions
): PhysicsCharacter3DNetworkSyncData {
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
// This method is called, so we are synchronizing this object.
// Let's clear the inputs between frames as we control it.
this._clearInputsBetweenFrames = true;
this._dontClearInputsBetweenFrames = false;
return {
...super.getNetworkSyncData(options),
...super.getNetworkSyncData(),
props: {
fwa: this._forwardAngle,
fws: this._currentForwardSpeed,
@@ -299,10 +297,9 @@ namespace gdjs {
}
override updateFromNetworkSyncData(
networkSyncData: PhysicsCharacter3DNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: PhysicsCharacter3DNetworkSyncData
) {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
const behaviorSpecificProps = networkSyncData.props;
this._forwardAngle = behaviorSpecificProps.fwa;
@@ -322,11 +319,11 @@ namespace gdjs {
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
// Clear user inputs between frames only if requested.
this._clearInputsBetweenFrames = !!options.clearMemory;
// When the object is synchronized from the network, the inputs must not be cleared.
this._dontClearInputsBetweenFrames = true;
}
getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
_getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
const physics3D = this.getPhysics3D();
if (!physics3D) {
result.Set(0, 0, 0);
@@ -346,7 +343,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()))
@@ -361,7 +358,7 @@ namespace gdjs {
return result;
}
moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
_moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
const physics3D = this.getPhysics3D();
if (!physics3D) {
return;
@@ -379,7 +376,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();
@@ -632,7 +629,7 @@ namespace gdjs {
this._wasJumpKeyPressed = this._hasPressedJumpKey;
this._wasStickUsed = this._hasUsedStick;
if (this._clearInputsBetweenFrames) {
if (!this._dontClearInputsBetweenFrames) {
this._hasPressedForwardKey = false;
this._hasPressedBackwardKey = false;
this._hasPressedRightKey = false;
@@ -1506,7 +1503,8 @@ namespace gdjs {
const { behavior } = physics3D;
const { _slopeMaxAngle, owner3D, _sharedData } = this.characterBehavior;
const shape = behavior.createShape();
// Jolt doesn't support center of mass offset for characters.
const shape = behavior.createShapeWithoutMassCenterOffset();
const settings = new Jolt.CharacterVirtualSettings();
// Characters innerBody are Kinematic body, they don't allow other
@@ -1545,10 +1543,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);
@@ -1625,6 +1623,19 @@ namespace gdjs {
contactNormal,
settings
) => {};
characterContactListener.OnContactPersisted = (
inCharacter,
inBodyID2,
inSubShapeID2,
inContactPosition,
inContactNormal,
ioSettings
) => {};
characterContactListener.OnContactRemoved = (
inCharacter,
inBodyID2,
inSubShapeID2
) => {};
characterContactListener.OnCharacterContactAdded = (
character,
otherCharacter,
@@ -1633,6 +1644,19 @@ namespace gdjs {
contactNormal,
settings
) => {};
characterContactListener.OnCharacterContactPersisted = (
inCharacter,
inOtherCharacter,
inSubShapeID2,
inContactPosition,
inContactNormal,
ioSettings
) => {};
characterContactListener.OnCharacterContactRemoved = (
inCharacter,
inOtherCharacter,
inSubShapeID2
) => {};
characterContactListener.OnContactSolve = (
character,
bodyID2,
@@ -1669,10 +1693,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()
);
}
@@ -1700,7 +1724,7 @@ namespace gdjs {
behavior._objectOldRotationZ !== owner3D.getAngle()
) {
character.SetRotation(
this.characterBehavior.getPhysicsRotation(
this.characterBehavior._getPhysicsRotation(
_sharedData.getQuat(0, 0, 0, 1)
)
);
@@ -1713,7 +1737,7 @@ namespace gdjs {
return;
}
character.SetPosition(
this.characterBehavior.getPhysicsPosition(
this.characterBehavior._getPhysicsPosition(
_sharedData.getRVec3(0, 0, 0)
)
);
@@ -1735,7 +1759,7 @@ namespace gdjs {
if (!character) {
return;
}
const shape = behavior.createShape();
const shape = behavior.createShapeWithoutMassCenterOffset();
const isShapeValid = character.SetShape(
shape,
Number.MAX_VALUE,

View File

@@ -26,6 +26,7 @@ declare namespace Jolt {
size(): number;
}
class ArrayVec3 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Vec3;
@@ -36,6 +37,7 @@ declare namespace Jolt {
data(): Vec3MemRef;
}
class ArrayQuat {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Quat;
@@ -46,6 +48,7 @@ declare namespace Jolt {
data(): QuatMemRef;
}
class ArrayMat44 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Mat44;
@@ -56,6 +59,7 @@ declare namespace Jolt {
data(): Mat44MemRef;
}
class ArrayBodyID {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): BodyID;
@@ -66,6 +70,7 @@ declare namespace Jolt {
data(): BodyIDMemRef;
}
class ArrayBodyPtr {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): Body;
@@ -431,6 +436,13 @@ 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 {}
@@ -444,6 +456,7 @@ declare namespace Jolt {
constructor(inV: Float3);
constructor(inX: number, inY: number, inZ: number);
sZero(): Vec3;
sOne(): Vec3;
sAxisX(): Vec3;
sAxisY(): Vec3;
sAxisZ(): Vec3;
@@ -505,6 +518,7 @@ declare namespace Jolt {
constructor();
constructor(inX: number, inY: number, inZ: number);
sZero(): RVec3;
sOne(): RVec3;
sAxisX(): RVec3;
sAxisY(): RVec3;
sAxisZ(): RVec3;
@@ -552,6 +566,7 @@ 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;
@@ -1427,6 +1442,9 @@ 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;
@@ -2523,6 +2541,8 @@ 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,
@@ -2624,9 +2644,9 @@ declare namespace Jolt {
get_mLinearCastMaxPenetration(): number;
set_mLinearCastMaxPenetration(mLinearCastMaxPenetration: number): void;
mLinearCastMaxPenetration: number;
get_mManifoldToleranceSq(): number;
set_mManifoldToleranceSq(mManifoldToleranceSq: number): void;
mManifoldToleranceSq: number;
get_mManifoldTolerance(): number;
set_mManifoldTolerance(mManifoldTolerance: number): void;
mManifoldTolerance: number;
get_mMaxPenetrationDistance(): number;
set_mMaxPenetrationDistance(mMaxPenetrationDistance: number): void;
mMaxPenetrationDistance: number;
@@ -2983,6 +3003,7 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayRayCastResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): RayCastResult;
@@ -3040,6 +3061,7 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayCollidePointResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CollidePointResult;
@@ -3114,6 +3136,7 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayCollideShapeResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CollideShapeResult;
@@ -3188,6 +3211,7 @@ declare namespace Jolt {
AddHit(inResult: number): void;
}
class ArrayShapeCastResult {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): ShapeCastResult;
@@ -3618,6 +3642,7 @@ declare namespace Jolt {
mMaxDistance: number;
}
class ArraySoftBodySharedSettingsVertex {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVertex;
@@ -3627,6 +3652,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsFace {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsFace;
@@ -3636,6 +3662,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsEdge {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsEdge;
@@ -3645,6 +3672,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsDihedralBend {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsDihedralBend;
@@ -3654,6 +3682,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsVolume {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVolume;
@@ -3663,6 +3692,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsInvBind {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsInvBind;
@@ -3672,6 +3702,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsSkinned {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsSkinned;
@@ -3681,6 +3712,7 @@ declare namespace Jolt {
clear(): void;
}
class ArraySoftBodySharedSettingsLRA {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsLRA;
@@ -3708,6 +3740,7 @@ declare namespace Jolt {
mLRAMaxDistanceMultiplier: number;
}
class ArraySoftBodySharedSettingsVertexAttributes {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodySharedSettingsVertexAttributes;
@@ -3856,6 +3889,7 @@ declare namespace Jolt {
readonly mVelocityOffset: number;
}
class ArraySoftBodyVertex {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): SoftBodyVertex;
@@ -3923,8 +3957,18 @@ 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;
@@ -3967,6 +4011,9 @@ 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;
@@ -4008,6 +4055,19 @@ 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,
@@ -4016,6 +4076,19 @@ 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,
@@ -4091,9 +4164,9 @@ declare namespace Jolt {
get_mBodyB(): BodyID;
set_mBodyB(mBodyB: BodyID): void;
mBodyB: BodyID;
get_mCharacterB(): CharacterVirtual;
set_mCharacterB(mCharacterB: CharacterVirtual): void;
mCharacterB: CharacterVirtual;
get_mCharacterIDB(): CharacterID;
set_mCharacterIDB(mCharacterIDB: CharacterID): void;
mCharacterIDB: CharacterID;
get_mSubShapeIDB(): SubShapeID;
set_mSubShapeIDB(mSubShapeIDB: SubShapeID): void;
mSubShapeIDB: SubShapeID;
@@ -4103,6 +4176,9 @@ 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;
@@ -4120,6 +4196,7 @@ declare namespace Jolt {
mCanPushCharacter: boolean;
}
class ArrayCharacterVirtualContact {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): CharacterVirtualContact;
@@ -4234,6 +4311,7 @@ declare namespace Jolt {
inRotation: Quat,
inSystem: PhysicsSystem
);
GetID(): CharacterID;
SetListener(inListener: CharacterContactListener): void;
SetCharacterVsCharacterCollision(
inCharacterVsCharacterCollision: CharacterVsCharacterCollision
@@ -4265,6 +4343,8 @@ declare namespace Jolt {
GetUserData(): number;
SetUserData(inUserData: number): void;
GetInnerBodyID(): BodyID;
StartTrackingContactChanges(): void;
FinishTrackingContactChanges(): void;
CancelVelocityTowardsSteepSlopes(inDesiredVelocity: Vec3): Vec3;
Update(
inDeltaTime: number,
@@ -4326,6 +4406,7 @@ declare namespace Jolt {
SetInnerBodyShape(inShape: Shape): void;
GetTransformedShape(): TransformedShape;
HasCollidedWith(inBodyID: BodyID): boolean;
HasCollidedWithCharacterID(inCharacterID: CharacterID): boolean;
HasCollidedWithCharacter(inCharacter: CharacterVirtual): boolean;
GetActiveContacts(): ArrayCharacterVirtualContact;
}
@@ -4340,6 +4421,7 @@ declare namespace Jolt {
GetValue(inX: number): number;
}
class ArrayFloat {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4350,6 +4432,7 @@ declare namespace Jolt {
data(): FloatMemRef;
}
class ArrayUint {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4360,6 +4443,7 @@ declare namespace Jolt {
data(): UintMemRef;
}
class ArrayUint8 {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): number;
@@ -4370,6 +4454,7 @@ declare namespace Jolt {
data(): Uint8MemRef;
}
class ArrayVehicleAntiRollBar {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): VehicleAntiRollBar;
@@ -4378,6 +4463,7 @@ declare namespace Jolt {
clear(): void;
}
class ArrayWheelSettings {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): WheelSettings;
@@ -4386,6 +4472,7 @@ declare namespace Jolt {
clear(): void;
}
class ArrayVehicleDifferentialSettings {
constructor();
empty(): boolean;
size(): number;
at(inIndex: number): VehicleDifferentialSettings;
@@ -4457,6 +4544,7 @@ declare namespace Jolt {
inWheelRight: Vec3,
inWheelUp: Vec3
): RMat44;
GetAntiRollBars(): ArrayVehicleAntiRollBar;
SetNumStepsBetweenCollisionTestActive(inSteps: number): void;
GetNumStepsBetweenCollisionTestActive(): number;
SetNumStepsBetweenCollisionTestInactive(inSteps: number): void;
@@ -4819,9 +4907,6 @@ declare namespace Jolt {
}
class VehicleControllerSettings {}
class VehicleController {
GetRefCount(): number;
AddRef(): void;
Release(): void;
GetConstraint(): VehicleConstraint;
}
class WheeledVehicleController extends VehicleController {

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,9 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
"held, customizable gravity... It can be used for the player, but "
"also for other objects moving on platforms. In this case though, "
"it's recommended to first check if there is a simpler behavior that "
"could be used.",
"could be used. Default controls for keyboards are included. For "
"touch or gamepads, use the \"Multitouch Joystick\" objects and the "
"associated \"mapper\" behaviors.",
"Florian Rival",
"Open source (MIT License)")
.SetCategory("Movement")
@@ -33,16 +35,16 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.SetIcon("CppPlatform/Extensions/platformerobjecticon.png");
{
gd::BehaviorMetadata& aut = extension.AddBehavior(
"PlatformerObjectBehavior",
_("Platformer character"),
"PlatformerObject",
_("Jump and run on platforms."),
"",
"CppPlatform/Extensions/platformerobjecticon.png",
"PlatformerObjectBehavior",
std::make_shared<PlatformerObjectBehavior>(),
std::make_shared<gd::BehaviorsSharedData>());
gd::BehaviorMetadata& aut =
extension.AddBehavior("PlatformerObjectBehavior",
_("Platformer character"),
"PlatformerObject",
_("Jump and run on platforms."),
"",
"CppPlatform/Extensions/platformerobjecticon.png",
"PlatformerObjectBehavior",
std::make_shared<PlatformerObjectBehavior>(),
std::make_shared<gd::BehaviorsSharedData>());
// Deprecated, use IsMovingEvenALittle instead
aut.AddCondition("IsMoving",
@@ -59,14 +61,15 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.MarkAsSimple()
.SetFunctionName("IsMoving");
aut.AddScopedCondition("IsMovingEvenALittle",
_("Is moving"),
_("Check if the object is moving (whether it is on the "
"floor or in the air)."),
_("_PARAM0_ is moving"),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png",
"CppPlatform/Extensions/platformerobjecticon.png")
aut.AddScopedCondition(
"IsMovingEvenALittle",
_("Is moving"),
_("Check if the object is moving (whether it is on the "
"floor or in the air)."),
_("_PARAM0_ is moving"),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png",
"CppPlatform/Extensions/platformerobjecticon.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.MarkAsSimple();
@@ -525,14 +528,14 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.MarkAsAdvanced()
.SetFunctionName("SimulateLadderKey");
aut.AddAction(
"SimulateReleaseLadderKey",
_("Simulate release ladder key press"),
_("Simulate a press of the Release Ladder key (used to get off a ladder)."),
_("Simulate pressing Release Ladder key for _PARAM0_"),
_("Platformer controls"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
aut.AddAction("SimulateReleaseLadderKey",
_("Simulate release ladder key press"),
_("Simulate a press of the Release Ladder key (used to get "
"off a ladder)."),
_("Simulate pressing Release Ladder key for _PARAM0_"),
_("Platformer controls"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.MarkAsAdvanced();
@@ -550,7 +553,8 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
aut.AddAction("SimulateReleasePlatformKey",
_("Simulate release platform key press"),
_("Simulate a press of the release platform key (used when grabbing a "
_("Simulate a press of the release platform key (used when "
"grabbing a "
"platform ledge)."),
_("Simulate pressing Release Platform key for _PARAM0_"),
_("Platformer controls"),
@@ -561,7 +565,8 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("SimulateReleasePlatformKey");
// Support for deprecated names:
aut.AddDuplicatedAction("SimulateReleaseKey", "SimulateReleasePlatformKey").SetHidden();
aut.AddDuplicatedAction("SimulateReleaseKey", "SimulateReleasePlatformKey")
.SetHidden();
aut.AddAction("SimulateControl",
_("Simulate control"),
@@ -574,23 +579,27 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("stringWithSelector",
_("Key"),
"[\"Left\", \"Right\", \"Jump\", \"Ladder\", \"Release Ladder\", \"Up\", \"Down\"]")
_("Key"),
"[\"Left\", \"Right\", \"Jump\", \"Ladder\", \"Release "
"Ladder\", \"Up\", \"Down\"]")
.MarkAsAdvanced()
.SetFunctionName("SimulateControl");
aut.AddScopedCondition("IsUsingControl",
_("Control pressed or simulated"),
_("A control was applied from a default control or simulated by an action."),
_("_PARAM0_ has the _PARAM2_ key pressed or simulated"),
_("Platformer state"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
aut.AddScopedCondition(
"IsUsingControl",
_("Control pressed or simulated"),
_("A control was applied from a default control or simulated by an "
"action."),
_("_PARAM0_ has the _PARAM2_ key pressed or simulated"),
_("Platformer state"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("stringWithSelector",
_("Key"),
"[\"Left\", \"Right\", \"Jump\", \"Ladder\", \"Release Ladder\", \"Up\", \"Down\"]")
_("Key"),
"[\"Left\", \"Right\", \"Jump\", \"Ladder\", \"Release "
"Ladder\", \"Up\", \"Down\"]")
.MarkAsAdvanced();
aut.AddAction("IgnoreDefaultControls",
@@ -792,59 +801,65 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.SetFunctionName("GetJumpSpeed");
aut.AddExpression("JumpSustainTime",
_("Jump sustain time"),
_("Return the jump sustain time of the object (in seconds)."
"This is the time during which keeping the jump button held "
"allow the initial jump speed to be maintained."),
_("Platformer configuration"),
"CppPlatform/Extensions/platformerobjecticon.png")
aut.AddExpression(
"JumpSustainTime",
_("Jump sustain time"),
_("Return the jump sustain time of the object (in seconds)."
"This is the time during which keeping the jump button held "
"allow the initial jump speed to be maintained."),
_("Platformer configuration"),
"CppPlatform/Extensions/platformerobjecticon.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior");
aut.AddExpression("CurrentFallSpeed",
_("Current fall speed"),
_("Return the current fall speed of the object "
"(in pixels per second). Its value is always positive."),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
aut.AddExpression(
"CurrentFallSpeed",
_("Current fall speed"),
_("Return the current fall speed of the object "
"(in pixels per second). Its value is always positive."),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.SetFunctionName("GetCurrentFallSpeed");
aut.AddExpression("CurrentSpeed",
_("Current horizontal speed"),
_("Return the current horizontal speed of the object "
"(in pixels per second). The object moves to the left "
"with negative values and to the right with positive ones"),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
aut.AddExpression(
"CurrentSpeed",
_("Current horizontal speed"),
_("Return the current horizontal speed of the object "
"(in pixels per second). The object moves to the left "
"with negative values and to the right with positive ones"),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.SetFunctionName("GetCurrentSpeed");
aut.AddExpression("CurrentJumpSpeed",
_("Current jump speed"),
_("Return the current jump speed of the object "
"(in pixels per second). Its value is always positive."),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
aut.AddExpression(
"CurrentJumpSpeed",
_("Current jump speed"),
_("Return the current jump speed of the object "
"(in pixels per second). Its value is always positive."),
_("Platformer state"),
"CppPlatform/Extensions/platformerobjecticon.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.SetFunctionName("GetCurrentJumpSpeed");
}
{
gd::BehaviorMetadata& aut = extension.AddBehavior(
"PlatformBehavior",
_("Platform"),
"Platform",
_("Flag objects as being platforms which characters can run on."),
"",
"CppPlatform/Extensions/platformicon.png",
"PlatformBehavior",
std::make_shared<PlatformBehavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
gd::BehaviorMetadata& aut =
extension
.AddBehavior("PlatformBehavior",
_("Platform"),
"Platform",
_("Flag objects as being platforms which characters "
"can run on."),
"",
"CppPlatform/Extensions/platformicon.png",
"PlatformBehavior",
std::make_shared<PlatformBehavior>(),
std::make_shared<gd::BehaviorsSharedData>())
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
aut.AddAction("ChangePlatformType",
_("Platform type"),
@@ -857,21 +872,23 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformBehavior")
.AddParameter("stringWithSelector",
_("Platform type"),
"[\"Platform\",\"Jumpthru\",\"Ladder\"]")
_("Platform type"),
"[\"Platform\",\"Jumpthru\",\"Ladder\"]")
.MarkAsAdvanced()
.SetFunctionName("ChangePlatformType");
}
extension.AddCondition("IsObjectOnGivenFloor",
_("Character is on given platform"),
_("Check if a platformer character is on a given platform."),
_("_PARAM0_ is on platform _PARAM2_"),
_("Collision"),
"CppPlatform/Extensions/platformicon.png",
"CppPlatform/Extensions/platformicon.png")
.AddParameter("objectList", _("Object"), "", false)
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("objectList", _("Platforms"), "", false)
.AddCodeOnlyParameter("conditionInverted", "");
extension
.AddCondition(
"IsObjectOnGivenFloor",
_("Character is on given platform"),
_("Check if a platformer character is on a given platform."),
_("_PARAM0_ is on platform _PARAM2_"),
_("Collision"),
"CppPlatform/Extensions/platformicon.png",
"CppPlatform/Extensions/platformicon.png")
.AddParameter("objectList", _("Object"), "", false)
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("objectList", _("Platforms"), "", false)
.AddCodeOnlyParameter("conditionInverted", "");
}

View File

@@ -145,7 +145,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 _clearInputsBetweenFrames: boolean = true;
_dontClearInputsBetweenFrames: boolean = false;
// This is useful when the object is synchronized over the network,
// object is controlled by the network and we want to ensure the current player
// cannot control it.
@@ -221,16 +221,14 @@ namespace gdjs {
this._state = this._falling;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): PlatformerObjectNetworkSyncData {
getNetworkSyncData(): PlatformerObjectNetworkSyncData {
// This method is called, so we are synchronizing this object.
// Let's clear the inputs between frames as we control it.
this._clearInputsBetweenFrames = true;
this._dontClearInputsBetweenFrames = false;
this._ignoreDefaultControlsAsSyncedByNetwork = false;
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
props: {
cs: this._currentSpeed,
@@ -258,10 +256,9 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: PlatformerObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: PlatformerObjectNetworkSyncData
) {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
const behaviorSpecificProps = networkSyncData.props;
if (behaviorSpecificProps.cs !== this._currentSpeed) {
@@ -339,10 +336,10 @@ namespace gdjs {
this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
}
// Clear user inputs between frames only if requested.
this._clearInputsBetweenFrames = !!options.clearMemory;
// When the object is synchronized from the network, the inputs must not be cleared.
this._dontClearInputsBetweenFrames = true;
// And we are not using the default controls.
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
this._ignoreDefaultControlsAsSyncedByNetwork = true;
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
@@ -524,9 +521,8 @@ namespace gdjs {
this._wasJumpKeyPressed = this._jumpKey;
this._wasReleasePlatformKeyPressed = this._releasePlatformKey;
this._wasReleaseLadderKeyPressed = this._releaseLadderKey;
//4) Do not forget to reset pressed keys
if (this._clearInputsBetweenFrames) {
if (!this._dontClearInputsBetweenFrames) {
// Reset the keys only if the inputs are not supposed to survive between frames.
// (Most of the time, except if this object is synchronized by an external source)
this._leftKey = false;

View File

@@ -1,99 +0,0 @@
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
* Changes in this file are watched and automatically imported if the editor
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'SaveState',
_('Save State'),
'This allows to save and load whole games.',
'Neyl Mahfouf',
'Gdevelop'
)
.setExtensionHelpPath('/all-features/save-state')
.setCategory('Save & Load');
extension
.addInstructionOrExpressionGroupMetadata(_('Save State'))
.setIcon('JsPlatform/Extensions/snapshotsave.svg');
// TODO: Split save action and load action into 2 different instructions to avoid
// having optional and empty parameters.
extension
.addAction(
'SaveGameSnapshot',
_('Save game'),
_(
'Takes a snapshot of the game and save it to a variable or device storage.'
),
_(
'Save the game to variable _PARAM1_ or to storage under key _PARAM2_.'
),
'',
'res/actions/Save-single-action-down.svg',
'res/actions/Save-single-action-down.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter(
'variable',
_('Variable to store the save to (optional)'),
'',
true
)
.addParameter('string', _('Storage key to save to (optional)'), '', true)
.getCodeExtraInformation()
.setIncludeFile('Extensions/SaveState/savestatetools.js')
.setFunctionName('gdjs.saveState.saveGameSnapshot');
extension
.addAction(
'LoadGameSnapshot',
_('Load game'),
_('Load game from snapshot save from a variable or storage.'),
_(
'Load the game from variable _PARAM1_ or from device storage under key _PARAM2_.'
),
'',
'res/actions/Save-single-action-up.svg',
'res/actions/Save-single-action-up.svg'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter(
'variable',
_('Variable to load game from (optional)'),
'',
true
)
.addParameter(
'string',
_('Storage key to load game from (optional)'),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/SaveState/savestatetools.js')
.setFunctionName('gdjs.saveState.loadGameFromSnapshot');
// TODO: Add condition and expression to get the last save creation datetime.
return extension;
},
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,78 +0,0 @@
namespace gdjs {
export namespace saveState {
export const INDEXED_DB_NAME: string = 'gd-game-snapshot-saves';
export const INDEXED_DB_KEY: string = 'game_save'; // TODO: use game id to change the key.
export const INDEXED_DB_OBJECT_STORE: string = 'saves'; // TODO: should a store be used per game on the user device?
export const saveGameSnapshot = async function (
currentScene: RuntimeScene,
sceneVar?: gdjs.Variable,
storageName?: string
) {
let allSyncData: GameSaveState = {
gameNetworkSyncData: {},
layoutNetworkSyncDatas: [],
};
const gameData = currentScene
.getGame()
.getNetworkSyncData({ forceSyncEverything: true });
const sceneStack = currentScene.getGame()._sceneStack._stack;
if (gameData) {
allSyncData.gameNetworkSyncData = gameData;
}
if (sceneStack) {
sceneStack.forEach((scene, index) => {
const sceneDatas = scene.getNetworkSyncData({
forceSyncEverything: true,
});
if (sceneDatas) {
allSyncData.layoutNetworkSyncDatas[index] = {
sceneData: {} as LayoutNetworkSyncData,
objectDatas: {},
};
allSyncData.layoutNetworkSyncDatas[index].sceneData = sceneDatas;
const sceneRuntimeObjects = scene.getAdhocListOfAllInstances();
const syncOptions: GetNetworkSyncDataOptions = {
forceSyncEverything: true,
};
for (const key in sceneRuntimeObjects) {
if (sceneRuntimeObjects.hasOwnProperty(key)) {
const object = sceneRuntimeObjects[key];
const syncData = object.getNetworkSyncData(syncOptions);
allSyncData.layoutNetworkSyncDatas[index].objectDatas[
object.id
] = syncData;
}
}
}
});
}
// TODO: Store the save creation datetime.
if (sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable) {
sceneVar.fromJSObject(allSyncData);
} else {
await gdjs.saveToIndexedDB(
INDEXED_DB_NAME,
INDEXED_DB_OBJECT_STORE,
storageName || INDEXED_DB_KEY,
allSyncData
);
}
};
export const loadGameFromSnapshot = async function (
currentScene: RuntimeScene,
sceneVar?: gdjs.Variable,
storageName?: string
) {
currentScene.requestLoadSnapshot({
loadVariable:
sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable
? sceneVar
: null,
loadStorageName: storageName || INDEXED_DB_KEY,
});
};
}
}

View File

@@ -113,11 +113,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): SpineNetworkSyncData {
getNetworkSyncData(): SpineNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
opa: this._opacity,
wid: this.getWidth(),
hei: this.getHeight(),
@@ -133,11 +131,8 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
syncData: SpineNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(syncData, options);
updateFromNetworkSyncData(syncData: SpineNetworkSyncData): void {
super.updateFromNetworkSyncData(syncData);
if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
this.setOpacity(syncData.opa);

View File

@@ -146,7 +146,7 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
expect(gameDomElementContainer.querySelector('input')).not.to.be(null);
expect(gameDomElementContainer.querySelector('textarea')).to.be(null);
object.deleteFromScene(runtimeScene);
object.deleteFromScene();
runtimeScene.renderAndStep(1000 / 60);
expect(gameDomElementContainer.querySelector('input')).to.be(null);
expect(gameDomElementContainer.querySelector('textarea')).to.be(null);

View File

@@ -256,11 +256,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): TextInputNetworkSyncData {
getNetworkSyncData(): TextInputNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
opa: this.getOpacity(),
wid: this.getWidth(),
hei: this.getHeight(),
@@ -280,11 +278,8 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
syncData: TextInputNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(syncData, options);
updateFromNetworkSyncData(syncData: TextInputNetworkSyncData): void {
super.updateFromNetworkSyncData(syncData);
if (syncData.opa !== undefined) this.setOpacity(syncData.opa);
if (syncData.wid !== undefined) this.setWidth(syncData.wid);

View File

@@ -98,7 +98,7 @@ namespace gdjs {
}
updatePosition(): void {
if (this._object.isWrapping()) {
if (this._object.isWrapping() && this._text.width !== 0) {
const alignmentX =
this._object._textAlign === 'right'
? 1
@@ -117,7 +117,6 @@ namespace gdjs {
this._text.position.x = this._object.x + this._text.width / 2;
this._text.anchor.x = 0.5;
}
this._text.position.y = this._object.y + this._text.height / 2;
const alignmentY =
this._object._verticalTextAlignment === 'bottom'

View File

@@ -214,11 +214,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): TextObjectNetworkSyncData {
getNetworkSyncData(): TextObjectNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
str: this._str,
o: this.opacity,
cs: this._characterSize,
@@ -245,10 +243,9 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: TextObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: TextObjectNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.str !== undefined) {
this.setText(networkSyncData.str);
}

View File

@@ -17,6 +17,8 @@ namespace gdjs {
export type SimpleTileMapNetworkSyncDataType = {
op: number;
ai: string;
wid: number;
hei: number;
// TODO: Support tilemap synchronization. Find an efficient way to send tiles changes.
};
@@ -163,21 +165,20 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): SimpleTileMapNetworkSyncData {
getNetworkSyncData(): SimpleTileMapNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
op: this._opacity,
ai: this._atlasImage,
wid: this.getWidth(),
hei: this.getHeight(),
};
}
updateFromNetworkSyncData(
networkSyncData: SimpleTileMapNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: SimpleTileMapNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
if (
networkSyncData.op !== undefined &&
@@ -185,6 +186,18 @@ namespace gdjs {
) {
this.setOpacity(networkSyncData.op);
}
if (
networkSyncData.wid !== undefined &&
networkSyncData.wid !== this.getWidth()
) {
this.setWidth(networkSyncData.wid);
}
if (
networkSyncData.hei !== undefined &&
networkSyncData.hei !== this.getHeight()
) {
this.setHeight(networkSyncData.hei);
}
if (networkSyncData.ai !== undefined) {
// TODO: support changing the atlas texture
}

View File

@@ -26,6 +26,8 @@ namespace gdjs {
os: float;
fo: float;
oo: float;
wid: float;
hei: float;
};
export type TilemapCollisionMaskNetworkSyncData = ObjectNetworkSyncData &
@@ -189,11 +191,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): TilemapCollisionMaskNetworkSyncData {
getNetworkSyncData(): TilemapCollisionMaskNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
tmjf: this.getTilemapJsonFile(),
tsjf: this.getTilesetJsonFile(),
dm: this.getDebugMode(),
@@ -202,14 +202,15 @@ namespace gdjs {
os: this.getOutlineSize(),
fo: this.getFillOpacity(),
oo: this.getOutlineOpacity(),
wid: this.getWidth(),
hei: this.getHeight(),
};
}
updateFromNetworkSyncData(
networkSyncData: TilemapCollisionMaskNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: TilemapCollisionMaskNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.tmjf !== undefined) {
this.setTilemapJsonFile(networkSyncData.tmjf);
@@ -235,6 +236,12 @@ namespace gdjs {
if (networkSyncData.oo !== undefined) {
this.setOutlineOpacity(networkSyncData.oo);
}
if (networkSyncData.wid !== undefined) {
this.setWidth(networkSyncData.wid);
}
if (networkSyncData.hei !== undefined) {
this.setHeight(networkSyncData.hei);
}
}
extraInitializationFromInitialInstance(initialInstanceData): void {

View File

@@ -25,6 +25,8 @@ namespace gdjs {
lai: number;
lei: number;
asps: number;
wid: number;
hei: number;
};
export type TilemapNetworkSyncData = ObjectNetworkSyncData &
@@ -145,11 +147,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): TilemapNetworkSyncData {
getNetworkSyncData(): TilemapNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
op: this._opacity,
tmjf: this._tilemapJsonFile,
tsjf: this._tilesetJsonFile,
@@ -158,14 +158,13 @@ namespace gdjs {
lai: this._layerIndex,
lei: this._levelIndex,
asps: this._animationSpeedScale,
wid: this.getWidth(),
hei: this.getHeight(),
};
}
updateFromNetworkSyncData(
networkSyncData: TilemapNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
updateFromNetworkSyncData(networkSyncData: TilemapNetworkSyncData): void {
super.updateFromNetworkSyncData(networkSyncData);
if (networkSyncData.op !== undefined) {
this.setOpacity(networkSyncData.op);
@@ -191,6 +190,12 @@ namespace gdjs {
if (networkSyncData.asps !== undefined) {
this.setAnimationSpeedScale(networkSyncData.asps);
}
if (networkSyncData.wid !== undefined) {
this.setWidth(networkSyncData.wid);
}
if (networkSyncData.hei !== undefined) {
this.setHeight(networkSyncData.hei);
}
}
extraInitializationFromInitialInstance(initialInstanceData): void {

View File

@@ -15,6 +15,8 @@ namespace gdjs {
export type TiledSpriteObjectData = ObjectData & TiledSpriteObjectDataType;
export type TiledSpriteNetworkSyncDataType = {
wid: number;
hei: number;
xo: number;
yo: number;
op: number;
@@ -78,11 +80,11 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptons: GetNetworkSyncDataOptions
): TiledSpriteNetworkSyncData {
getNetworkSyncData(): TiledSpriteNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptons),
...super.getNetworkSyncData(),
wid: this.getWidth(),
hei: this.getHeight(),
xo: this.getXOffset(),
yo: this.getYOffset(),
op: this.getOpacity(),
@@ -91,13 +93,18 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: TiledSpriteNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: TiledSpriteNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
// Texture is not synchronized, see if this is asked or not.
if (networkSyncData.wid !== undefined) {
this.setWidth(networkSyncData.wid);
}
if (networkSyncData.hei !== undefined) {
this.setHeight(networkSyncData.hei);
}
if (networkSyncData.xo !== undefined) {
this.setXOffset(networkSyncData.xo);
}

View File

@@ -62,7 +62,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 _clearInputsBetweenFrames: boolean = true;
_dontClearInputsBetweenFrames: boolean = false;
// This is useful when the object is synchronized over the network,
// object is controlled by the network and we want to ensure the current player
// cannot control it.
@@ -104,16 +104,14 @@ namespace gdjs {
this._movementAngleOffset = behaviorData.movementAngleOffset || 0;
}
getNetworkSyncData(
options: GetNetworkSyncDataOptions
): TopDownMovementNetworkSyncData {
getNetworkSyncData(): TopDownMovementNetworkSyncData {
// This method is called, so we are synchronizing this object.
// Let's clear the inputs between frames as we control it.
this._clearInputsBetweenFrames = true;
this._dontClearInputsBetweenFrames = false;
this._ignoreDefaultControlsAsSyncedByNetwork = false;
return {
...super.getNetworkSyncData(options),
...super.getNetworkSyncData(),
props: {
a: this._angle,
xv: this._xVelocity,
@@ -131,10 +129,9 @@ namespace gdjs {
}
updateFromNetworkSyncData(
networkSyncData: TopDownMovementNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
networkSyncData: TopDownMovementNetworkSyncData
): void {
super.updateFromNetworkSyncData(networkSyncData, options);
super.updateFromNetworkSyncData(networkSyncData);
const behaviorSpecificProps = networkSyncData.props;
if (behaviorSpecificProps.a !== undefined) {
@@ -171,10 +168,10 @@ namespace gdjs {
this._stickForce = behaviorSpecificProps.sf;
}
// Clear user inputs between frames only if requested.
this._clearInputsBetweenFrames = !!options.clearMemory;
// When the object is synchronized from the network, the inputs must not be cleared.
this._dontClearInputsBetweenFrames = true;
// And we are not using the default controls.
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
this._ignoreDefaultControlsAsSyncedByNetwork = true;
}
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
@@ -587,7 +584,7 @@ namespace gdjs {
this._wasRightKeyPressed = this._rightKey;
this._wasUpKeyPressed = this._upKey;
this._wasDownKeyPressed = this._downKey;
if (this._clearInputsBetweenFrames) {
if (!this._dontClearInputsBetweenFrames) {
this._leftKey = false;
this._rightKey = false;
this._upKey = false;

File diff suppressed because it is too large Load Diff

View File

@@ -59,214 +59,6 @@ namespace gdjs {
const exponentialInterpolation =
gdjs.evtTools.common.exponentialInterpolation;
// TODO: Use this factory to get the tween setter and store only type and options
// in tween instance.
const tweenSetterFactory =
(object: RuntimeObject) => (type: string, options: any) => {
if (type === 'variable') {
// TODO: Find variable.
// return (value: float) => variable.setNumber(value)
}
if (type === 'positionX') {
return (value: float) => object.setX(value);
}
if (type === 'positionY') {
return (value: float) => object.setY(value);
}
if (type === 'position') {
return ([x, y]) => object.setPosition(x, y);
}
if (type === 'positionZ') {
if (!is3D(object)) return () => {};
return (value: float) => object.setZ(value);
}
if (type === 'width') {
return (value: float) => object.setWidth(value);
}
if (type === 'height') {
return (value: float) => object.setHeight(value);
}
if (type === 'depth') {
if (!is3D(object)) return () => {};
return (value: float) => object.setDepth(value);
}
if (type === 'angle') {
return (value: float) => object.setAngle(value);
}
if (type === 'rotationX') {
if (!is3D(object)) return () => {};
return (value: float) => object.setRotationX(value);
}
if (type === 'rotationY') {
if (!is3D(object)) return () => {};
return (value: float) => object.setRotationY(value);
}
if (type === 'scale') {
if (!isScalable(object)) return;
// This action doesn't require 3D capabilities.
// So, gdjs.RuntimeObject3D may not exist
// when the 3D extension is not used.
const object3d = is3D(object) ? object : null;
const setValue = options.scaleFromCenterOfObject
? (scale: float) => {
const oldX = object.getCenterXInScene();
const oldY = object.getCenterYInScene();
const oldZ = object3d ? object3d.getCenterZInScene() : 0;
object.setScale(scale);
object.setCenterXInScene(oldX);
object.setCenterYInScene(oldY);
if (object3d) {
object3d.setCenterZInScene(oldZ);
}
}
: (scale: float) => object.setScale(scale);
return setValue;
}
if (type === 'scaleXAndY') {
if (!isScalable(object)) return;
const setValue = options.scaleFromCenterOfObject
? ([scaleX, scaleY]: float[]) => {
const oldX = object.getCenterXInScene();
const oldY = object.getCenterYInScene();
object.setScaleX(scaleX);
object.setScaleY(scaleY);
object.setCenterPositionInScene(oldX, oldY);
}
: ([scaleX, scaleY]: float[]) => {
object.setScaleX(scaleX);
object.setScaleY(scaleY);
};
return setValue;
}
if (type === 'scaleX') {
if (!isScalable(object)) return;
const setValue = options.scaleFromCenterOfObject
? (scaleX: float) => {
const oldX = object.getCenterXInScene();
object.setScaleX(scaleX);
object.setCenterXInScene(oldX);
}
: (scaleX: float) => object.setScaleX(scaleX);
return setValue;
}
if (type === 'scaleY') {
if (!isScalable(object)) return;
const setValue = options.scaleFromCenterOfObject
? (scaleY: float) => {
const oldY = object.getCenterYInScene();
object.setScaleY(scaleY);
object.setCenterYInScene(oldY);
}
: (scaleY: float) => object.setScaleY(scaleY);
return setValue;
}
if (type === 'opacity') {
if (!isOpaque(object)) return () => {};
return (value: float) => object.setOpacity(value);
}
if (type === 'characterSize') {
if (!isCharacterScalable(object)) return () => {};
return (value: float) => object.setCharacterSize(value);
}
if (type === 'numberEffectProperty') {
const effect = object.getRendererEffects()[options.effectName];
if (!effect) {
logger.error(
`The object "${object.name}" doesn't have any effect called "${options.effectName}"`
);
}
return (value: float) => {
effect.updateDoubleParameter(options.propertyName, value);
};
}
if (type === 'colorEffectProperty') {
const effect = object.getRendererEffects()[options.effectName];
if (!effect) {
logger.error(
`The object "${object.name}" doesn't have any effect called "${options.effectName}"`
);
}
return ([hue, saturation, lightness]) => {
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
hue,
saturation,
lightness
);
effect.updateColorParameter(
options.propertyName,
gdjs.rgbToHexNumber(
rgbFromHslColor[0],
rgbFromHslColor[1],
rgbFromHslColor[2]
)
);
};
}
if (type === 'objectColor') {
if (!isColorable(object)) return;
if (options.useHSLColorTransition) {
const setValue = ([hue, saturation, lightness]) => {
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
hue,
saturation,
lightness
);
object.setColor(
Math.floor(rgbFromHslColor[0]) +
';' +
Math.floor(rgbFromHslColor[1]) +
';' +
Math.floor(rgbFromHslColor[2])
);
};
return setValue;
} else {
const setValue = ([red, green, blue]) => {
object.setColor(
Math.floor(red) + ';' + Math.floor(green) + ';' + Math.floor(blue)
);
};
return setValue;
}
}
if (type === 'objectColorHSL') {
if (!isColorable(object)) return;
const setValue = ([hue, saturation, lightness]) => {
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
hue,
saturation,
lightness
);
object.setColor(
Math.floor(rgbFromHslColor[0]) +
';' +
Math.floor(rgbFromHslColor[1]) +
';' +
Math.floor(rgbFromHslColor[2])
);
};
return setValue;
}
// 'objectValue' type doesn't set anything.
return () => {};
};
export class TweenRuntimeBehavior extends gdjs.RuntimeBehavior {
private _tweens = new gdjs.evtTools.tween.TweenManager();
private _isActive: boolean = true;
@@ -297,7 +89,7 @@ namespace gdjs {
}
private _deleteFromScene() {
this.owner.deleteFromScene(this.owner.getInstanceContainer());
this.owner.deleteFromScene();
}
/**

View File

@@ -10,71 +10,6 @@ namespace gdjs {
export namespace tween {
const logger = new gdjs.Logger('Tween');
// TODO: Use this factory to get the tween setter and store only type and options
// in tween instance.
const tweenSetterFactory =
(runtimeScene: RuntimeScene) => (type: string, options: any) => {
if (type === 'variable') {
// TODO: Find variable.
// return (value: float) => variable.setNumber(value)
}
if (type === 'cameraZoom') {
const layer = runtimeScene.getLayer(options.layerName);
return (value: float) => layer.setCameraZoom(value);
}
if (type === 'cameraRotation') {
const layer = runtimeScene.getLayer(options.layerName);
return (value: float) => layer.setCameraRotation(value);
}
if (type === 'cameraPosition') {
const layer = runtimeScene.getLayer(options.layerName);
return ([x, y]) => {
layer.setCameraX(x);
layer.setCameraY(y);
};
}
if (type === 'colorEffectProperty') {
const layer = runtimeScene.getLayer(options.layerName);
const effect = layer.getRendererEffects()[options.effectName];
if (!effect) {
logger.error(
`The layer "${options.layerName}" doesn't have any effect called "${options.effectName}"`
);
}
return ([hue, saturation, lightness]) => {
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
hue,
saturation,
lightness
);
effect.updateColorParameter(
options.propertyName,
gdjs.rgbToHexNumber(
rgbFromHslColor[0],
rgbFromHslColor[1],
rgbFromHslColor[2]
)
);
};
}
if (type === 'numberEffectProperty') {
const layer = runtimeScene.getLayer(options.layerName);
const effect = layer.getRendererEffects()[options.effectName];
if (!effect) {
logger.error(
`The layer "${options.layerName}" doesn't have any effect called "${options.effectName}"`
);
}
return (value: float) => {
effect.updateDoubleParameter(options.propertyName, value);
};
}
// 'layoutValue' and 'layerValue' types don't set anything.
return () => {};
};
export const getTweensMap = (runtimeScene: RuntimeScene) =>
runtimeScene._tweens ||
(runtimeScene._tweens = new gdjs.evtTools.tween.TweenManager());

View File

@@ -16,7 +16,7 @@ namespace gdjs {
export type VideoObjectData = ObjectData & VideoObjectDataType;
export type VideoObjectNetworkSyncDataType = {
export type VideoNetworkSyncDataType = {
op: float;
wid: float;
hei: float;
@@ -27,8 +27,8 @@ namespace gdjs {
ps: number;
};
export type VideoObjectNetworkSyncData = ObjectNetworkSyncData &
VideoObjectNetworkSyncDataType;
export type VideoNetworkSyncData = ObjectNetworkSyncData &
VideoNetworkSyncDataType;
/**
* An object displaying a video on screen.
@@ -101,11 +101,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): VideoObjectNetworkSyncData {
getNetworkSyncData(): VideoNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
op: this._opacity,
wid: this.getWidth(),
hei: this.getHeight(),
@@ -116,11 +114,8 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
syncData: VideoObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
super.updateFromNetworkSyncData(syncData, options);
updateFromNetworkSyncData(syncData: VideoNetworkSyncData): void {
super.updateFromNetworkSyncData(syncData);
if (this._opacity !== undefined && this._opacity && syncData.op) {
this.setOpacity(syncData.op);

View File

@@ -844,7 +844,6 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
InsertUnique(includesFiles, "CustomRuntimeObjectInstanceContainer.js");
InsertUnique(includesFiles, "CustomRuntimeObject.js");
InsertUnique(includesFiles, "CustomRuntimeObject2D.js");
InsertUnique(includesFiles, "indexeddb.js");
// Common includes for events only.
InsertUnique(includesFiles, "events-tools/commontools.js");

View File

@@ -16,11 +16,6 @@ namespace gdjs {
childrenContent: { [objectName: string]: ObjectConfiguration & any };
};
export type CustomObjectNetworkSyncDataType = ObjectNetworkSyncData & {
ifx: boolean;
ify: boolean;
};
/**
* An object that contains other object.
*
@@ -110,7 +105,14 @@ namespace gdjs {
return;
}
let usedVariantData: EventsBasedObjectVariantData = eventsBasedObjectData;
if (!eventsBasedObjectData.defaultVariant) {
eventsBasedObjectData.defaultVariant = {
...eventsBasedObjectData,
name: '',
};
}
let usedVariantData: EventsBasedObjectVariantData =
eventsBasedObjectData.defaultVariant;
if (customObjectData.variant) {
for (
let variantIndex = 0;
@@ -173,29 +175,6 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): CustomObjectNetworkSyncDataType {
return {
...super.getNetworkSyncData(syncOptions),
ifx: this.isFlippedX(),
ify: this.isFlippedY(),
};
}
updateFromNetworkSyncData(
networkSyncData: CustomObjectNetworkSyncDataType,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(networkSyncData, options);
if (networkSyncData.ifx !== undefined) {
this.flipX(networkSyncData.ifx);
}
if (networkSyncData.ify !== undefined) {
this.flipY(networkSyncData.ify);
}
}
override extraInitializationFromInitialInstance(
initialInstanceData: InstanceData
) {
@@ -227,13 +206,13 @@ namespace gdjs {
}
}
override onDeletedFromScene(parent: gdjs.RuntimeInstanceContainer): void {
override onDeletedFromScene(): void {
// Let subclasses do something before the object is destroyed.
this.onDestroy(parent);
this.onDestroy(this._runtimeScene);
// Let behaviors do something before the object is destroyed.
super.onDeletedFromScene(parent);
super.onDeletedFromScene();
// Destroy the children.
this._instanceContainer.onDestroyFromScene(parent);
this._instanceContainer.onDestroyFromScene(this._runtimeScene);
}
override update(parent: gdjs.RuntimeInstanceContainer): void {

View File

@@ -80,7 +80,11 @@ namespace gdjs {
++i
) {
const childObjectData = eventsBasedObjectVariantData.objects[i];
if (customObjectData.childrenContent) {
// The children configuration override only applies to the default variant.
if (
customObjectData.childrenContent &&
!eventsBasedObjectVariantData.name
) {
this.registerObject({
...childObjectData,
// The custom object overrides its events-based object configuration.
@@ -183,7 +187,7 @@ namespace gdjs {
const allInstancesList = this.getAdhocListOfAllInstances();
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
const object = allInstancesList[i];
object.onDeletedFromScene(this);
object.onDeletedFromScene();
// The object can free all its resource directly...
object.onDestroyed();
}

View File

@@ -623,7 +623,7 @@ namespace gdjs {
/**
* Create a new object from its name. The object is also added to the instances
* living in the container (No need to call {@link addObject})
* living in the container (No need to call {@link gdjs.RuntimeScene.addObject})
* @param objectName The name of the object to be created
* @return The created object
*/
@@ -678,7 +678,7 @@ namespace gdjs {
}
// Notify the object it was removed from the container
obj.onDeletedFromScene(this);
obj.onDeletedFromScene();
// Notify the global callbacks
for (let j = 0; j < gdjs.callbacksObjectDeletedFromScene.length; ++j) {

View File

@@ -1376,7 +1376,7 @@ namespace gdjs {
) {
// Instance was deleted (or object name changed, in which case it will be re-created later)
if (runtimeObject) {
runtimeObject.deleteFromScene(runtimeInstanceContainer);
runtimeObject.deleteFromScene();
}
} else {
}

View File

@@ -224,8 +224,7 @@ namespace gdjs {
let yOffset = 0;
if (anticipateMove && !object.hasNoForces()) {
const objectAverageForce = object.getAverageForce();
const elapsedTimeInSeconds =
object.getElapsedTime(instanceContainer) / 1000;
const elapsedTimeInSeconds = object.getElapsedTime() / 1000;
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
}

View File

@@ -84,30 +84,11 @@ namespace gdjs {
*/
private _onPlay: Array<HowlCallback> = [];
/**
* The filepath to the resource
*/
private _audioResourceName: string;
/**
* The channel index on which the sound is played.
*/
private _channel: float | undefined;
constructor(
howl: Howl,
volume: float,
loop: boolean,
rate: float,
audioResourceName: string,
channel: float | undefined
) {
constructor(howl: Howl, volume: float, loop: boolean, rate: float) {
this._howl = howl;
this._initialVolume = clampVolume(volume);
this._loop = loop;
this._rate = rate;
this._audioResourceName = audioResourceName;
this._channel = channel;
}
/**
@@ -376,21 +357,10 @@ namespace gdjs {
if (this._id !== null) this._howl.off(event, handler, this._id);
return this;
}
getNetworkSyncData(): SoundSyncData {
return {
resourceName: this._audioResourceName,
loop: this._loop,
volume: this.getVolume(),
rate: this._rate,
position: this.getSeek(),
channel: this._channel || undefined,
};
}
}
/**
* HowlerSoundManager is used to manage the sounds and musics of a RuntimeGame.
* HowlerSoundManager is used to manage the sounds and musics of a RuntimeScene.
*
* It is basically a container to associate channels to sounds and keep a list
* of all sounds being played.
@@ -604,8 +574,7 @@ namespace gdjs {
isMusic: boolean,
volume: float,
loop: boolean,
rate: float,
channel?: float
rate: float
): HowlerSound {
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
const resource = this._getAudioResource(soundName);
@@ -632,7 +601,8 @@ namespace gdjs {
);
cacheContainer.set(resource, howl);
}
return new gdjs.HowlerSound(howl, volume, loop, rate, soundName, channel);
return new gdjs.HowlerSound(howl, volume, loop, rate);
}
/**
@@ -730,13 +700,7 @@ namespace gdjs {
this._loadedSounds.clear();
}
playSound(
soundName: string,
loop: boolean,
volume: float,
pitch: float,
position?: float
) {
playSound(soundName: string, loop: boolean, volume: float, pitch: float) {
const sound = this.createHowlerSound(
soundName,
/* isMusic= */ false,
@@ -752,9 +716,6 @@ namespace gdjs {
}
});
sound.play();
if (position) {
sound.setSeek(position);
}
}
playSoundOnChannel(
@@ -762,8 +723,7 @@ namespace gdjs {
channel: integer,
loop: boolean,
volume: float,
pitch: float,
position?: float
pitch: float
) {
if (this._sounds[channel]) this._sounds[channel].stop();
@@ -772,8 +732,7 @@ namespace gdjs {
/* isMusic= */ false,
volume / 100,
loop,
pitch,
channel
pitch
);
const spatialPosition = this._cachedSpatialPosition[channel];
if (spatialPosition) {
@@ -789,22 +748,13 @@ namespace gdjs {
}
});
sound.play();
if (position) {
sound.setSeek(position);
}
}
getSoundOnChannel(channel: integer): HowlerSound | null {
return this._sounds[channel] || null;
}
playMusic(
soundName: string,
loop: boolean,
volume: float,
pitch: float,
position?: float
) {
playMusic(soundName: string, loop: boolean, volume: float, pitch: float) {
const music = this.createHowlerSound(
soundName,
/* isMusic= */ true,
@@ -820,9 +770,6 @@ namespace gdjs {
}
});
music.play();
if (position) {
music.setSeek(position);
}
}
playMusicOnChannel(
@@ -830,8 +777,7 @@ namespace gdjs {
channel: integer,
loop: boolean,
volume: float,
pitch: float,
position?: float
pitch: float
) {
if (this._musics[channel]) this._musics[channel].stop();
@@ -840,8 +786,7 @@ namespace gdjs {
/* isMusic= */ true,
volume / 100,
loop,
pitch,
channel
pitch
);
// Musics are played with the html5 backend, that is not compatible with spatialization.
this._musics[channel] = music;
@@ -852,9 +797,6 @@ namespace gdjs {
}
});
music.play();
if (position) {
music.setSeek(position);
}
}
getMusicOnChannel(channel: integer): HowlerSound | null {
@@ -982,88 +924,6 @@ namespace gdjs {
}
}
getNetworkSyncData(): SoundManagerSyncData {
const freeMusicsDatas: SoundSyncData[] = this._freeMusics.map(
(freeMusic) => freeMusic.getNetworkSyncData()
);
const freeSoundsDatas: SoundSyncData[] = this._freeSounds.map(
(freeSound) => freeSound.getNetworkSyncData()
);
const musicsDatas: SoundSyncData[] = Object.values(this._musics).map(
(music) => music.getNetworkSyncData()
);
const soundsDatas: SoundSyncData[] = Object.values(this._sounds).map(
(sound) => sound.getNetworkSyncData()
);
return {
globalVolume: this._globalVolume,
cachedSpatialPosition: this._cachedSpatialPosition,
freeMusics: freeMusicsDatas,
freeSounds: freeSoundsDatas,
musics: musicsDatas,
sounds: soundsDatas,
};
}
updateFromNetworkSyncData(syncData: SoundManagerSyncData): void {
this.clearAll();
if (syncData.globalVolume !== undefined) {
this._globalVolume = syncData.globalVolume;
}
if (syncData.cachedSpatialPosition !== undefined) {
this._cachedSpatialPosition = syncData.cachedSpatialPosition;
}
for (let i = 0; i < syncData.freeSounds.length; i++) {
const freeSoundsSyncData: SoundSyncData = syncData.freeSounds[i];
this.playSound(
freeSoundsSyncData.resourceName,
freeSoundsSyncData.loop,
freeSoundsSyncData.volume * 100,
freeSoundsSyncData.rate,
freeSoundsSyncData.position
);
}
for (let i = 0; i < syncData.freeMusics.length; i++) {
const freeMusicsSyncData: SoundSyncData = syncData.freeMusics[i];
this.playMusic(
freeMusicsSyncData.resourceName,
freeMusicsSyncData.loop,
freeMusicsSyncData.volume * 100,
freeMusicsSyncData.rate,
freeMusicsSyncData.position
);
}
for (let i = 0; i < syncData.sounds.length; i++) {
const soundsSyncData: SoundSyncData = syncData.sounds[i];
this.playSoundOnChannel(
soundsSyncData.resourceName,
soundsSyncData.channel || 0,
soundsSyncData.loop,
soundsSyncData.volume * 100,
soundsSyncData.rate,
soundsSyncData.position
);
}
for (let i = 0; i < syncData.musics.length; i++) {
const musicsSyncData: SoundSyncData = syncData.musics[i];
this.playMusicOnChannel(
musicsSyncData.resourceName,
musicsSyncData.channel || 0,
musicsSyncData.loop,
musicsSyncData.volume * 100,
musicsSyncData.rate,
musicsSyncData.position
);
}
}
/**
* To be called when the game is disposed.
* Unloads all audio from memory, clear Howl cache and stop all audio.

View File

@@ -1,100 +0,0 @@
/*
* GDevelop JS Platform
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
namespace gdjs {
export const loadFromIndexedDB = async function (
dbName: string,
objectStoreName: string,
key: string
): Promise<any> {
return new Promise((resolve, reject) => {
try {
const request = indexedDB.open(dbName, 1);
request.onupgradeneeded = function () {
const db = request.result;
if (!db.objectStoreNames.contains(objectStoreName)) {
db.createObjectStore(objectStoreName);
}
};
request.onsuccess = function () {
const db = request.result;
const tx = db.transaction(objectStoreName, 'readonly');
const store = tx.objectStore(objectStoreName);
const getRequest = store.get(key);
getRequest.onsuccess = function () {
if (getRequest.result !== undefined) {
resolve(getRequest.result);
} else {
resolve(null);
}
};
getRequest.onerror = function () {
console.error(
'Error loading data from IndexedDB:',
getRequest.error
);
reject(getRequest.error);
};
};
request.onerror = function () {
console.error('Error opening IndexedDB:', request.error);
reject(request.error);
};
} catch (err) {
console.error('Exception thrown while opening IndexedDB:', err);
reject(err);
return;
}
});
};
export const saveToIndexedDB = async function (
dbName: string,
objectStoreName: string,
key: string,
data: any
): Promise<void> {
return new Promise((resolve, reject) => {
try {
const request = indexedDB.open(dbName, 1);
request.onupgradeneeded = function (event) {
const db = request.result;
if (!db.objectStoreNames.contains(objectStoreName)) {
db.createObjectStore(objectStoreName);
}
};
request.onsuccess = function () {
const db = request.result;
const tx = db.transaction(objectStoreName, 'readwrite');
const store = tx.objectStore(objectStoreName);
const putRequest = store.put(data, key);
putRequest.onsuccess = function () {
resolve();
};
putRequest.onerror = function () {
console.error('Error saving data to IndexedDB:', putRequest.error);
reject(putRequest.error);
};
};
request.onerror = function () {
console.error('Error opening IndexedDB:', request.error);
reject(request.error);
};
} catch (err) {
console.error('Exception thrown while opening IndexedDB:', err);
reject(err);
return;
}
});
};
}

View File

@@ -77,9 +77,7 @@ namespace gdjs {
return false;
}
getNetworkSyncData(
options: GetNetworkSyncDataOptions
): BehaviorNetworkSyncData {
getNetworkSyncData(): BehaviorNetworkSyncData {
// To be redefined by behaviors that need to synchronize properties
// while calling super() to get the common properties.
return {
@@ -92,10 +90,7 @@ namespace gdjs {
* Update the behavior properties using the provided data.
* @param networkSyncData The new properties of the behavior.
*/
updateFromNetworkSyncData(
networkSyncData: BehaviorNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
): void {
updateFromNetworkSyncData(networkSyncData: BehaviorNetworkSyncData): void {
// Must be redefined by behaviors that need to synchronize properties
// while calling super() to get the common properties.
if (networkSyncData.act !== this._activated) {

View File

@@ -1353,8 +1353,8 @@ namespace gdjs {
const syncData: GameNetworkSyncData = {
var: this._variables.getNetworkSyncData(syncOptions),
ss: this._sceneStack.getNetworkSyncData(syncOptions) || undefined,
sm: this.getSoundManager().getNetworkSyncData() || undefined,
};
const extensionsVariablesSyncData = {};
this._variablesByExtensionName.forEach((variables, extensionName) => {
const extensionVariablesSyncData =
@@ -1368,7 +1368,6 @@ namespace gdjs {
syncData.extVar = extensionsVariablesSyncData;
if (
!syncOptions.forceSyncEverything &&
(!syncData.var || syncData.var.length === 0) &&
!syncData.ss &&
(!syncData.extVar || Object.keys(syncData.extVar).length === 0)
@@ -1380,20 +1379,14 @@ namespace gdjs {
return syncData;
}
updateFromNetworkSyncData(
syncData: GameNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
updateFromNetworkSyncData(syncData: GameNetworkSyncData) {
this._throwIfDisposed();
if (syncData.var) {
this._variables.updateFromNetworkSyncData(syncData.var, options);
this._variables.updateFromNetworkSyncData(syncData.var);
}
if (syncData.ss) {
this._sceneStack.updateFromNetworkSyncData(syncData.ss);
}
if (options.syncSounds && syncData.sm) {
this.getSoundManager().updateFromNetworkSyncData(syncData.sm);
}
if (syncData.extVar) {
for (const extensionName in syncData.extVar) {
if (!syncData.extVar.hasOwnProperty(extensionName)) {
@@ -1404,8 +1397,7 @@ namespace gdjs {
this.getVariablesForExtension(extensionName);
if (extensionVariables) {
extensionVariables.updateFromNetworkSyncData(
extensionVariablesData,
options
extensionVariablesData
);
}
}

View File

@@ -382,10 +382,8 @@ namespace gdjs {
* in milliseconds, for the object.
*
* Objects can have different elapsed time if they are on layers with different time scales.
*
* @param instanceContainer The instance container the object belongs to (deprecated - can be omitted).
*/
getElapsedTime(instanceContainer?: gdjs.RuntimeInstanceContainer): float {
getElapsedTime(): float {
const theLayer = this._runtimeScene.getLayer(this.layer);
return theLayer.getElapsedTime();
}
@@ -454,19 +452,14 @@ namespace gdjs {
* This can be redefined by objects to send more information.
* @returns The full network sync data.
*/
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): ObjectNetworkSyncData {
getNetworkSyncData(): ObjectNetworkSyncData {
const behaviorNetworkSyncData = {};
this._behaviors.forEach((behavior) => {
if (
!behavior.isSyncedOverNetwork() &&
(!syncOptions || !syncOptions.forceSyncEverything)
) {
if (!behavior.isSyncedOverNetwork()) {
return;
}
const networkSyncData = behavior.getNetworkSyncData(syncOptions);
const networkSyncData = behavior.getNetworkSyncData();
if (networkSyncData) {
behaviorNetworkSyncData[behavior.getName()] = networkSyncData;
}
@@ -491,8 +484,6 @@ namespace gdjs {
return {
x: this.x,
y: this.y,
w: this.getWidth(),
h: this.getHeight(),
zo: this.zOrder,
a: this.angle,
hid: this.hidden,
@@ -500,7 +491,6 @@ namespace gdjs {
if: this._instantForces.map((force) => force.getNetworkSyncData()),
pfx: this._permanentForceX,
pfy: this._permanentForceY,
n: syncOptions.forceSyncEverything ? this.name : undefined,
beh: behaviorNetworkSyncData,
var: variablesNetworkSyncData,
eff: effectsNetworkSyncData,
@@ -515,22 +505,13 @@ namespace gdjs {
* @param networkSyncData The new data for the object.
* @returns true if the object was updated, false if it could not (i.e: network sync is not supported).
*/
updateFromNetworkSyncData(
networkSyncData: ObjectNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
updateFromNetworkSyncData(networkSyncData: ObjectNetworkSyncData) {
if (networkSyncData.x !== undefined) {
this.setX(networkSyncData.x);
}
if (networkSyncData.y !== undefined) {
this.setY(networkSyncData.y);
}
if (networkSyncData.w !== undefined) {
this.setWidth(networkSyncData.w);
}
if (networkSyncData.h !== undefined) {
this.setHeight(networkSyncData.h);
}
if (networkSyncData.zo !== undefined) {
this.setZOrder(networkSyncData.zo);
}
@@ -578,13 +559,13 @@ namespace gdjs {
const behaviorNetworkSyncData = networkSyncData.beh[behaviorName];
const behavior = this.getBehavior(behaviorName);
if (behavior) {
behavior.updateFromNetworkSyncData(behaviorNetworkSyncData, options);
behavior.updateFromNetworkSyncData(behaviorNetworkSyncData);
}
}
// If variables are synchronized, update them.
if (networkSyncData.var) {
this._variables.updateFromNetworkSyncData(networkSyncData.var, options);
this._variables.updateFromNetworkSyncData(networkSyncData.var);
}
// If effects are synchronized, update them.
@@ -616,11 +597,10 @@ namespace gdjs {
* Remove an object from a scene.
*
* Do not change/redefine this method. Instead, redefine the onDestroyFromScene method.
* @param instanceContainer The container owning the object.
*/
deleteFromScene(instanceContainer: gdjs.RuntimeInstanceContainer): void {
deleteFromScene(): void {
if (this._livingOnScene) {
instanceContainer.markObjectForDeletion(this);
this._runtimeScene.markObjectForDeletion(this);
this._livingOnScene = false;
}
}
@@ -637,11 +617,9 @@ namespace gdjs {
* Called when the object is destroyed (because it is removed from a scene or the scene
* is being unloaded). If you redefine this function, **make sure to call the original method**
* (`RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);`).
*
* @param instanceContainer The container owning the object.
*/
onDeletedFromScene(instanceContainer: gdjs.RuntimeInstanceContainer): void {
const theLayer = instanceContainer.getLayer(this.layer);
onDeletedFromScene(): void {
const theLayer = this._runtimeScene.getLayer(this.layer);
const rendererObject = this.getRendererObject();
if (rendererObject) {
theLayer.getRenderer().removeRendererObject(rendererObject);
@@ -814,12 +792,7 @@ namespace gdjs {
return this.getY();
}
rotateTowardPosition(
x: float,
y: float,
speed: float,
scene: gdjs.RuntimeScene
): void {
rotateTowardPosition(x: float, y: float, speed: float): void {
this.rotateTowardAngle(
gdjs.toDegrees(
Math.atan2(
@@ -827,21 +800,15 @@ namespace gdjs {
x - (this.getDrawableX() + this.getCenterX())
)
),
speed,
scene
speed
);
}
/**
* @param angle The targeted direction angle.
* @param speed The rotation speed.
* @param instanceContainer The container the object belongs to (deprecated - can be omitted).
*/
rotateTowardAngle(
angle: float,
speed: float,
instanceContainer?: gdjs.RuntimeInstanceContainer
): void {
rotateTowardAngle(angle: float, speed: float): void {
if (speed === 0) {
this.setAngle(angle);
return;
@@ -880,10 +847,7 @@ namespace gdjs {
* @param speed The speed, in degrees per second.
* @param instanceContainer The container the object belongs to (deprecated - can be omitted).
*/
rotate(
speed: float,
instanceContainer?: gdjs.RuntimeInstanceContainer
): void {
rotate(speed: float): void {
this.setAngle(this.getAngle() + (speed * this.getElapsedTime()) / 1000);
}

View File

@@ -23,7 +23,6 @@ namespace gdjs {
_timeManager: TimeManager;
_gameStopRequested: boolean = false;
_requestedScene: string = '';
_loadRequestOptions: LoadRequestOptions | null = null;
private _asyncTasksManager = new gdjs.AsyncTasksManager();
/** True if loadFromScene was called and the scene is being played. */
@@ -125,13 +124,7 @@ namespace gdjs {
* @param sceneAndExtensionsData An object containing the scene data.
* @see gdjs.RuntimeGame#getSceneAndExtensionsData
*/
loadFromScene(
sceneAndExtensionsData: SceneAndExtensionsData | null,
options?: {
preventInitialInstancesCreation: boolean;
preventSoundManagerClearing: boolean;
}
) {
loadFromScene(sceneAndExtensionsData: SceneAndExtensionsData | null) {
if (!sceneAndExtensionsData) {
logger.error('loadFromScene was called without a scene');
return;
@@ -188,16 +181,15 @@ namespace gdjs {
this.registerObject(sceneData.objects[i]);
}
// Create initial instances of objects
if (!options || !options.preventInitialInstancesCreation)
this.createObjectsFrom(
sceneData.instances,
0,
0,
0,
/*trackByPersistentUuid=*/
true
);
//Create initial instances of objects
this.createObjectsFrom(
sceneData.instances,
0,
0,
0,
/*trackByPersistentUuid=*/
true
);
// Set up the default z order (for objects created from events)
this._setLayerDefaultZOrders();
@@ -215,11 +207,7 @@ namespace gdjs {
for (let i = 0; i < gdjs.callbacksRuntimeSceneLoaded.length; ++i) {
gdjs.callbacksRuntimeSceneLoaded[i](this);
}
if (
sceneData.stopSoundsOnStartup &&
this._runtimeGame &&
(!options || !options.preventSoundManagerClearing)
) {
if (sceneData.stopSoundsOnStartup && this._runtimeGame) {
this._runtimeGame.getSoundManager().clearAll();
}
this._isLoaded = true;
@@ -296,7 +284,7 @@ namespace gdjs {
const allInstancesList = this.getAdhocListOfAllInstances();
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
const object = allInstancesList[i];
object.onDeletedFromScene(this);
object.onDeletedFromScene();
object.onDestroyed();
}
@@ -661,6 +649,7 @@ namespace gdjs {
getViewportOriginY(): float {
return this._cachedGameResolutionHeight / 2;
}
convertCoords(x: float, y: float, result: FloatPoint): FloatPoint {
// The result parameter used to be optional.
const point = result || [0, 0];
@@ -759,10 +748,6 @@ namespace gdjs {
if (sceneName) this._requestedScene = sceneName;
}
requestLoadSnapshot(loadRequestOptions: LoadRequestOptions | null): void {
this._loadRequestOptions = loadRequestOptions;
}
/**
* Get the profiler associated with the scene, or null if none.
*/
@@ -851,19 +836,12 @@ namespace gdjs {
var: variablesNetworkSyncData,
extVar: extensionsVariablesSyncData,
id: this.getOrCreateNetworkId(),
timeManager: this._timeManager.getNetworkSyncData(),
tweenManager: gdjs.evtTools.tween
.getTweensMap(this)
.getNetworkSyncData(),
};
}
updateFromNetworkSyncData(
syncData: LayoutNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
updateFromNetworkSyncData(syncData: LayoutNetworkSyncData) {
if (syncData.var) {
this._variables.updateFromNetworkSyncData(syncData.var, options);
this._variables.updateFromNetworkSyncData(syncData.var);
}
if (syncData.extVar) {
for (const extensionName in syncData.extVar) {
@@ -875,22 +853,11 @@ namespace gdjs {
this._variablesByExtensionName.get(extensionName);
if (extensionVariables) {
extensionVariables.updateFromNetworkSyncData(
extensionVariablesData,
options
extensionVariablesData
);
}
}
}
if (syncData.timeManager && options.syncTimers) {
this._timeManager.updateFromNetworkSyncData(syncData.timeManager);
}
if (syncData.tweenManager && options.syncTweens) {
gdjs.evtTools.tween.getTweensMap(this).updateFromNetworkSyncData(
syncData.tweenManager,
// TODO: Use correct time source identifier.
(timeSourceIdentifier) => this
);
}
}
getOrCreateNetworkId(): string {
@@ -900,10 +867,6 @@ namespace gdjs {
}
return this.networkId;
}
getLoadRequestOptions(): LoadRequestOptions | null {
return this._loadRequestOptions;
}
}
//The flags to describe the change request by a scene:

View File

@@ -34,41 +34,6 @@ namespace gdjs {
}
}
_loadGameFromSave(saveState: GameSaveState): void {
const options: UpdateFromNetworkSyncDataOptions = {
clearMemory: true,
keepControl: true,
syncSounds: true,
syncTimers: true,
syncTweens: true,
ignoreVariableOwnership: true,
};
this._runtimeGame.updateFromNetworkSyncData(
saveState.gameNetworkSyncData,
options
);
this.applyUpdateFromNetworkSyncDataIfAny(options);
const sceneStack = this._stack;
sceneStack.forEach((scene, index) => {
const layoutSyncData = saveState.layoutNetworkSyncDatas[index];
if (!layoutSyncData) return;
scene.updateFromNetworkSyncData(layoutSyncData.sceneData, options);
const objectDatas = layoutSyncData.objectDatas;
for (const id in objectDatas) {
const objectNetworkSyncData = objectDatas[id];
const object = scene.createObject(objectNetworkSyncData.n || '');
if (object) {
object.updateFromNetworkSyncData(objectNetworkSyncData, options);
}
}
});
}
step(elapsedTime: float): boolean {
this._throwIfDisposed();
if (this._isNextLayoutLoading || this._stack.length === 0) {
@@ -102,49 +67,11 @@ namespace gdjs {
this.replace(currentScene.getRequestedScene());
} else if (request === gdjs.SceneChangeRequest.CLEAR_SCENES) {
this.replace(currentScene.getRequestedScene(), true);
} else {
logger.error('Unrecognized change in scene stack: ' + request);
}
}
const loadRequestOptions = currentScene.getLoadRequestOptions();
if (!loadRequestOptions) return true;
currentScene.requestLoadSnapshot(null);
if (loadRequestOptions.loadVariable) {
if (
loadRequestOptions.loadVariable !==
gdjs.VariablesContainer.badVariable
) {
throw new Error(
'Requested loading save from wrongly defined variable.'
);
}
const saveState =
loadRequestOptions.loadVariable.toJSObject() as GameSaveState;
try {
this._loadGameFromSave(saveState);
} catch (error) {
logger.error('Error loading from variable:', error);
}
} else {
const storageKey =
loadRequestOptions.loadStorageName || gdjs.saveState.INDEXED_DB_KEY;
gdjs
.loadFromIndexedDB(
gdjs.saveState.INDEXED_DB_NAME,
gdjs.saveState.INDEXED_DB_OBJECT_STORE,
storageKey
)
.then((jsonData) => {
const saveState = jsonData as GameSaveState;
this._loadGameFromSave(saveState);
})
.catch((error) => {
logger.error('Error loading from IndexedDB:', error);
});
}
return true;
}
@@ -192,8 +119,7 @@ namespace gdjs {
*/
push(
newSceneName: string,
externalLayoutName?: string,
options?: UpdateFromNetworkSyncDataOptions
externalLayoutName?: string
): gdjs.RuntimeScene | null {
this._throwIfDisposed();
@@ -206,12 +132,12 @@ namespace gdjs {
// Avoid a risk of displaying an intermediate loading screen
// during 1 frame.
if (this._runtimeGame.areSceneAssetsReady(newSceneName)) {
return this._loadNewScene(newSceneName, externalLayoutName, options);
return this._loadNewScene(newSceneName, externalLayoutName);
}
this._isNextLayoutLoading = true;
this._runtimeGame.loadSceneAssets(newSceneName).then(() => {
this._loadNewScene(newSceneName, undefined, options);
this._loadNewScene(newSceneName);
this._isNextLayoutLoading = false;
});
return null;
@@ -219,21 +145,14 @@ namespace gdjs {
private _loadNewScene(
newSceneName: string,
externalLayoutName?: string,
options?: UpdateFromNetworkSyncDataOptions
externalLayoutName?: string
): gdjs.RuntimeScene {
this._throwIfDisposed();
const preventInitialInstancesCreation = !!options;
const preventSoundManagerClearing = !!options;
// Load the new one
const newScene = new gdjs.RuntimeScene(this._runtimeGame);
newScene.loadFromScene(
this._runtimeGame.getSceneAndExtensionsData(newSceneName),
{
preventInitialInstancesCreation,
preventSoundManagerClearing,
}
this._runtimeGame.getSceneAndExtensionsData(newSceneName)
);
this._wasFirstSceneLoaded = true;
@@ -241,7 +160,7 @@ namespace gdjs {
if (externalLayoutName) {
const externalLayoutData =
this._runtimeGame.getExternalLayoutData(externalLayoutName);
if (externalLayoutData && !preventInitialInstancesCreation) {
if (externalLayoutData) {
newScene.createObjectsFrom(
externalLayoutData.instances,
0,
@@ -260,13 +179,8 @@ namespace gdjs {
* Start the specified scene, replacing the one currently being played.
* If `clear` is set to true, all running scenes are also removed from the stack of scenes.
*/
replace(
newSceneName: string,
clear?: boolean,
options?: UpdateFromNetworkSyncDataOptions
): gdjs.RuntimeScene | null {
replace(newSceneName: string, clear?: boolean): gdjs.RuntimeScene | null {
this._throwIfDisposed();
if (!!clear) {
// Unload all the scenes
while (this._stack.length !== 0) {
@@ -284,7 +198,7 @@ namespace gdjs {
}
}
}
return this.push(newSceneName, undefined, options);
return this.push(newSceneName);
}
/**
@@ -345,9 +259,7 @@ namespace gdjs {
this._sceneStackSyncDataToApply = sceneStackSyncData;
}
applyUpdateFromNetworkSyncDataIfAny(
options?: UpdateFromNetworkSyncDataOptions
): boolean {
applyUpdateFromNetworkSyncDataIfAny(): boolean {
this._throwIfDisposed();
const sceneStackSyncData = this._sceneStackSyncDataToApply;
let hasMadeChangeToStack = false;
@@ -355,23 +267,6 @@ namespace gdjs {
this._sceneStackSyncDataToApply = null;
if (options && options.clearMemory) {
while (this._stack.length !== 0) {
let scene = this._stack.pop();
if (scene) {
scene.unloadScene();
}
}
for (let i = 0; i < sceneStackSyncData.length; ++i) {
const sceneSyncData = sceneStackSyncData[i];
const newScene = this.push(sceneSyncData.name, undefined, options);
if (newScene) {
newScene.networkId = sceneSyncData.networkId;
}
}
hasMadeChangeToStack = true;
return hasMadeChangeToStack;
}
// If this method is called, we are a client.
// We trust the host to be the source of truth for the scene stack.
// So we loop through the scenes in the stack given by the host and either:
@@ -381,13 +276,12 @@ namespace gdjs {
for (let i = 0; i < sceneStackSyncData.length; ++i) {
const sceneSyncData = sceneStackSyncData[i];
const sceneAtThisPositionInOurStack = this._stack[i];
if (!sceneAtThisPositionInOurStack) {
debugLogger.info(
`Scene at position ${i} with name ${sceneSyncData.name} is missing from the stack, adding it.`
);
// We have fewer scenes in the stack than the host, let's add the scene.
const newScene = this.push(sceneSyncData.name, undefined, options);
const newScene = this.push(sceneSyncData.name);
if (newScene) {
newScene.networkId = sceneSyncData.networkId;
}
@@ -404,11 +298,9 @@ namespace gdjs {
);
// The scene does not correspond to the scene at this position in our stack
// Let's unload everything after this position to recreate the stack.
const newScene = this.replace(
sceneSyncData.name,
true, // Clear the stack
options
true // Clear the stack
);
if (newScene) {
newScene.networkId = sceneSyncData.networkId;
@@ -451,8 +343,7 @@ namespace gdjs {
// We need to replace it with a new scene
const newScene = this.replace(
sceneSyncData.name,
false, // Don't clear the stack
options
false // Don't clear the stack
);
if (newScene) {
newScene.networkId = sceneSyncData.networkId;

View File

@@ -115,11 +115,9 @@ namespace gdjs {
return true;
}
getNetworkSyncData(
syncOptions: GetNetworkSyncDataOptions
): SpriteNetworkSyncData {
getNetworkSyncData(): SpriteNetworkSyncData {
return {
...super.getNetworkSyncData(syncOptions),
...super.getNetworkSyncData(),
anim: this._animator.getNetworkSyncData(),
ifx: this.isFlippedX(),
ify: this.isFlippedY(),
@@ -130,11 +128,8 @@ namespace gdjs {
};
}
updateFromNetworkSyncData(
newNetworkSyncData: SpriteNetworkSyncData,
options: UpdateFromNetworkSyncDataOptions
) {
super.updateFromNetworkSyncData(newNetworkSyncData, options);
updateFromNetworkSyncData(newNetworkSyncData: SpriteNetworkSyncData) {
super.updateFromNetworkSyncData(newNetworkSyncData);
if (newNetworkSyncData.ifx !== undefined) {
this.flipX(newNetworkSyncData.ifx);
}
@@ -950,18 +945,16 @@ namespace gdjs {
//Other :
/**
* @param obj The target object
* @param scene The scene containing the object
* @deprecated
*/
turnTowardObject(obj: gdjs.RuntimeObject | null, scene: gdjs.RuntimeScene) {
turnTowardObject(obj: gdjs.RuntimeObject | null) {
if (obj === null) {
return;
}
this.rotateTowardPosition(
obj.getDrawableX() + obj.getCenterX(),
obj.getDrawableY() + obj.getCenterY(),
0,
scene
0
);
}
}

View File

@@ -9,15 +9,6 @@ namespace gdjs {
* frame, since the beginning of the scene and other time related values.
* All durations are expressed in milliseconds.
*/
declare interface TimeManagerSyncData {
elapsedTime: float;
timeScale: float;
timeFromStart: float;
firstFrame: boolean;
timers: Hashtable<TimerNetworkSyncData>;
firstUpdateDone: boolean;
}
export class TimeManager {
_elapsedTime: float = 0;
_timeScale: float = 1;
@@ -68,47 +59,6 @@ namespace gdjs {
}
}
getNetworkSyncData(): TimeManagerSyncData {
const timerNetworkSyncDatas = new Hashtable<TimerNetworkSyncData>();
Object.entries(this._timers.items).forEach(([key, timer]) => {
timerNetworkSyncDatas.put(key, timer.getNetworkSyncData());
});
return {
elapsedTime: this._elapsedTime,
timeScale: this._timeScale,
timeFromStart: this._timeFromStart,
firstFrame: this._firstFrame,
timers: timerNetworkSyncDatas,
firstUpdateDone: this._firstUpdateDone,
};
}
updateFromNetworkSyncData(syncData: TimeManagerSyncData): void {
if (syncData.elapsedTime !== undefined) {
this._elapsedTime = syncData.elapsedTime;
}
if (syncData.timeScale !== undefined) {
this._timeScale = syncData.timeScale;
}
if (syncData.timeFromStart !== undefined) {
this._timeFromStart = syncData.timeFromStart;
}
if (syncData.firstFrame !== undefined) {
this._firstFrame = syncData.firstFrame;
}
if (syncData.timers !== undefined) {
Object.entries(syncData.timers.items).forEach(([key, timerData]) => {
const newTimer = new gdjs.Timer(timerData.name as string);
newTimer.updateFromNetworkSyncData(timerData);
this._timers.put(key, newTimer);
});
}
if (syncData.firstUpdateDone !== undefined) {
this._firstUpdateDone = syncData.firstUpdateDone;
}
}
/**
* Get the time scale.
* @return The time scale (positive, 1 is normal speed).

View File

@@ -77,7 +77,6 @@ namespace gdjs {
getNetworkSyncData(): TimerNetworkSyncData {
return {
name: this._name,
time: this._time,
paused: this._paused,
};

View File

@@ -42,16 +42,6 @@ declare type ObjectData = {
declare type GetNetworkSyncDataOptions = {
playerNumber?: number;
isHost?: boolean;
forceSyncEverything?: boolean;
};
declare type UpdateFromNetworkSyncDataOptions = {
clearMemory?: boolean;
syncSounds?: boolean;
syncTimers?: boolean;
syncTweens?: boolean;
keepControl?: boolean;
ignoreVariableOwnership?: boolean;
};
/** Object containing basic properties for all objects synchronizing over the network. */
@@ -62,10 +52,6 @@ declare type BasicObjectNetworkSyncData = {
y: number;
/** The position of the instance on the Z axis. Defined only for 3D games */
z?: number;
/** The width of the instance */
w: number;
/** The height of the instance */
h: number;
/** Z order of the instance */
zo: number;
/** The angle of the instance. */
@@ -80,8 +66,6 @@ declare type BasicObjectNetworkSyncData = {
pfx: number;
/** Permanent force on Y */
pfy: number;
/* name :*/
n?: string;
};
/**
@@ -114,7 +98,6 @@ declare type ForceNetworkSyncData = {
};
declare type TimerNetworkSyncData = {
name?: string;
time: float;
paused: boolean;
};
@@ -194,8 +177,6 @@ declare interface LayoutNetworkSyncData {
extVar?: {
[extensionName: string]: VariableNetworkSyncData[];
};
timeManager?: TimeManagerSyncData;
tweenManager?: gdjs.evtTools.tween.TweenManagerNetworkSyncData;
}
declare interface SceneStackSceneNetworkSyncData {
@@ -205,36 +186,12 @@ declare interface SceneStackSceneNetworkSyncData {
declare type SceneStackNetworkSyncData = SceneStackSceneNetworkSyncData[];
declare type SoundManagerSyncData = {
globalVolume: float;
cachedSpatialPosition: Record<integer, [number, number, number]>;
freeSounds: SoundSyncData[];
freeMusics: SoundSyncData[];
musics: SoundSyncData[];
sounds: SoundSyncData[];
};
declare type SoundSyncData = {
loop: boolean;
volume: float;
rate: float;
resourceName: string;
position: float;
channel?: float;
};
declare type LoadRequestOptions = {
loadStorageName: string;
loadVariable: gdjs.Variable | null;
};
declare interface GameNetworkSyncData {
var?: VariableNetworkSyncData[];
ss?: SceneStackNetworkSyncData;
extVar?: {
[extensionName: string]: VariableNetworkSyncData[];
};
sm?: SoundManagerSyncData;
}
declare interface EventsFunctionsExtensionData {
@@ -255,6 +212,9 @@ declare interface EventsBasedObjectData
name: string;
isInnerAreaFollowingParentSize: boolean;
variants: Array<EventsBasedObjectVariantData>;
/** Added at runtime to have the default variant with an empty name instead
* of the events-based object name. */
defaultVariant?: EventsBasedObjectVariantData;
}
declare interface EventsBasedObjectVariantData extends InstanceContainerData {

View File

@@ -1,9 +0,0 @@
declare type SceneSaveState = {
sceneData: LayoutNetworkSyncData;
objectDatas: { [objectId: integer]: ObjectNetworkSyncData };
};
declare type GameSaveState = {
gameNetworkSyncData: GameNetworkSyncData;
layoutNetworkSyncDatas: SceneSaveState[];
};

View File

@@ -242,9 +242,8 @@ namespace gdjs {
const variable = this._variables.get(variableName);
const variableOwner = variable.getPlayerOwnership();
if (
(!syncOptions.forceSyncEverything &&
// Variable undefined.
variable.isUndefinedInContainer()) ||
// Variable undefined.
variable.isUndefinedInContainer() ||
// Variable marked as not to be synchronized.
variableOwner === null ||
// Getting sync data for a specific player:
@@ -353,10 +352,7 @@ namespace gdjs {
return undefined;
}
updateFromNetworkSyncData(
networkSyncData: VariableNetworkSyncData[],
options: UpdateFromNetworkSyncDataOptions
) {
updateFromNetworkSyncData(networkSyncData: VariableNetworkSyncData[]) {
const that = this;
for (let j = 0; j < networkSyncData.length; ++j) {
const variableSyncData = networkSyncData[j];
@@ -374,23 +370,20 @@ namespace gdjs {
// - If we are not the owner of the variable, then assume that we missed the ownership change message, so update the variable's
// ownership and then update the variable.
const syncedVariableOwner = variableSyncData.owner;
if (!options.ignoreVariableOwnership) {
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
const currentVariableOwner = variable.getPlayerOwnership();
if (currentPlayerNumber === currentVariableOwner) {
console.info(
`Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
);
return;
}
const currentVariableOwner = variable.getPlayerOwnership();
if (currentPlayerNumber === currentVariableOwner) {
console.info(
`Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
);
return;
}
if (syncedVariableOwner !== currentVariableOwner) {
console.info(
`Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
);
variable.setPlayerOwnership(syncedVariableOwner);
}
if (syncedVariableOwner !== currentVariableOwner) {
console.info(
`Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
);
variable.setPlayerOwnership(syncedVariableOwner);
}
variable.reinitialize(variableData);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

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