Compare commits

...

32 Commits

Author SHA1 Message Date
Clément Pasteau
c8477629d0 Improvements of full screen mode on mac + rework of callbacks 2025-01-14 15:13:30 +01:00
Clément Pasteau
08b0a9ff37 Improve title bar on multiple platforms optimizing the space at the top of the app 2025-01-14 11:04:57 +01:00
AlexandreS
8be099d50a Add possibility to open GDevelop in the browser on the premium course (#7304) 2025-01-13 19:12:14 +01:00
Clément Pasteau
713698d3d6 Fix wrong margins for dense design of sub card (#7303)
Do not show in changelog
2025-01-13 13:32:11 +01:00
Clément Pasteau
8b36908fd8 Merge project manager & app burger menu buttons (#7289)
* The goal of this change is to simplify the top left corner of the app, by merging the App burger menu and the Project manager button into one. Having 2 buttons was causing confusion in the flow of using the app, by mistakenly pressing the wrong button.
* The app menu buttons that are useful are now displayed at the top of the project manager drawer, on the relevant platforms: Web, Windows & mobile, because other platforms have a built-in OS menu.
2025-01-13 10:11:32 +01:00
D8H
c0722dc441 [2D Physics] Reduce the CPU footprint of static objects (#7299) 2025-01-10 16:39:20 +01:00
D8H
9c6a1bed2c [3D Physics] Reduce the CPU footprint of static objects (#7298) 2025-01-10 12:58:53 +01:00
github-actions[bot]
a8a4d14ee1 Update translations [skip ci] (#7291)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-10 10:23:16 +01:00
Florian Rival
b27abfbe5d Add text vertical alignment in quick customzation
Don't show in changelog
2025-01-10 10:07:37 +01:00
Neyl
28b585aefa Allow behavior properties to be an animation name, displayed with a selector 2025-01-09 14:21:04 +01:00
AlexandreS
0623b6acd9 Fix variable list usability (#7290)
- When renaming a variable in the instance panel:
  - the caret position is kept
  - the user can erase the whole field without the name being replaced by "Unnamed" instantly
- When opening the variables editor from a variable field, the currently selected variable is selected in the list (even if child of a variable)
2025-01-08 16:44:13 +01:00
github-actions[bot]
1a833bc388 Update translations [skip ci] (#7272)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-07 11:04:19 +01:00
D8H
827f187e10 Add Pixi and Three type definitions for JS events (#7266) 2025-01-07 10:34:51 +01:00
Florian Rival
0f25b80a66 Fix margins
Don't show in changelog
2025-01-06 22:06:15 +01:00
Clément Pasteau
fbf9710cc5 Use premium colors in most places where a subscription can be purchased (#7284) 2025-01-06 20:56:49 +01:00
Clément Pasteau
3d80709029 Fix correctly exporting desktop icon when project is saved in the cloud (#7282) 2025-01-06 17:54:59 +01:00
AlexandreS
5ab9c565b0 Simplify shop navigation state to avoid useless renderings (#7283)
Only show in developer changelog
2025-01-06 17:01:41 +01:00
Florian Rival
e237fc4b21 Add experimental support for importing JS source files in extensions (#7278)
* See more about using [JavaScript on this page](https://wiki.gdevelop.io/gdevelop5/events/js-code/javascript-in-extensions/#experimental-new-option-javascript-files-in-your-project).

Only show in developer changelog
2025-01-06 11:02:28 +01:00
D8H
18892f97f3 Remove unused import and fix a typo (#7277) 2025-01-04 11:19:42 +01:00
D8H
0ec0654032 Allow to make custom objects private to an extension (#7275) 2025-01-03 19:36:38 +01:00
D8H
8e29c723e8 [3D physics character] Fix a 1-frame delay on the "is falling" condition (#7276) 2025-01-03 19:33:33 +01:00
Clément Pasteau
6acde2865a Fix opening windows in foreground on windows everywhere in app (#7274)
* Ensure electron/remote is used
2025-01-02 16:36:43 +01:00
Clément Pasteau
49e176a98f Scroll to projects when opening from dashboard (#7273)
Do not show in changelog
2025-01-02 15:16:30 +01:00
D8H
71c2a0be01 Add a property to choose the maximum stair height a 3D character can walk (#7265) 2025-01-02 13:18:46 +01:00
Clément Pasteau
4ad1a0dd68 Bump 221 (#7271) 2025-01-02 11:20:00 +01:00
github-actions[bot]
a556690307 Update translations [skip ci] (#7269)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-02 11:17:46 +01:00
Clément Pasteau
6950f323b3 Disable type checking in JS Events until all autocompletions are properly handled (#7270) 2025-01-02 11:09:59 +01:00
github-actions[bot]
de129f6cc4 Update translations [skip ci] (#7252)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-02 10:57:33 +01:00
Clément Pasteau
b3b88a0445 Fix wrong prop name & add translations (#7268)
Do not show in changelog
2025-01-02 10:57:08 +01:00
D8H
8e8f3a7d55 Fix the collision conditions for 3D physics characters (#7264)
* Migrate to Jolt 0.31.0
2024-12-31 16:58:08 +01:00
D8H
cc6b4a283a Enable type checking on the Dialog Tree implementation (#7263)
- Don't show in changelog
2024-12-31 13:03:16 +01:00
D8H
f393f36bad Add a tooltip on some physics properties (#7262) 2024-12-30 18:23:20 +01:00
234 changed files with 7194 additions and 5836 deletions

View File

@@ -18,8 +18,6 @@ namespace gd {
EventsList BaseEvent::badSubEvents;
VariablesContainer BaseEvent::badLocalVariables;
std::vector<gd::String> BaseEvent::emptyDependencies;
gd::String BaseEvent::emptySourceFile;
BaseEvent::BaseEvent()
: totalTimeDuringLastSession(0),

View File

@@ -175,26 +175,6 @@ class GD_CORE_API BaseEvent {
noExpr;
return noExpr;
};
/**
* \brief Returns the dependencies on source files of the project.
* \note Default implementation returns an empty list of dependencies. This is
* fine for most events that are not related to adding custom user source
* code.
*/
virtual const std::vector<gd::String>& GetSourceFileDependencies() const {
return emptyDependencies;
};
/**
* \brief Returns the name of the source file associated with the event
* \note Default implementation returns an empty string. This is fine for most
* events that are not related to adding custom user source code.
*/
virtual const gd::String& GetAssociatedGDManagedSourceFile(
gd::Project& project) const {
return emptySourceFile;
};
///@}
/** \name Code generation
@@ -327,8 +307,6 @@ class GD_CORE_API BaseEvent {
static gd::EventsList badSubEvents;
static gd::VariablesContainer badLocalVariables;
static std::vector<gd::String> emptyDependencies;
static gd::String emptySourceFile;
};
/**

View File

@@ -272,7 +272,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its

View File

@@ -174,6 +174,7 @@ public:
virtual const gd::String &GetFullName() const = 0;
virtual const gd::String &GetDescription() const = 0;
virtual const gd::String &GetIconFilename() const = 0;
virtual bool IsPrivate() const = 0;
/**
* \brief Return a reference to a map containing the names of the actions

View File

@@ -300,6 +300,22 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
ObjectMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
/**
* \brief Set the object to be hidden in the IDE.
*
@@ -356,6 +372,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
gd::String iconFilename;
gd::String categoryFullName;
std::set<gd::String> defaultBehaviorTypes;
bool isPrivate = false;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;

View File

@@ -0,0 +1,56 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/String.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
/**
* \brief Contains information about a source file that must be included
* when an extension is used.
*/
class GD_CORE_API SourceFileMetadata {
public:
/**
* Construct a new dependency metadata, though you probably want to call
* `AddSourceFile` on gd::PlatformExtension.
*
* \see gd::PlatformExtension
*/
SourceFileMetadata() {};
SourceFileMetadata& SetResourceName(const gd::String& resourceName_) {
resourceName = resourceName_;
return *this;
};
SourceFileMetadata& SetIncludePosition(const gd::String& includePosition_) {
includePosition = includePosition_;
return *this;
};
const gd::String& GetResourceName() const { return resourceName; };
gd::String& GetResourceName() { return resourceName; };
const gd::String& GetIncludePosition() const { return includePosition; };
void SerializeTo(SerializerElement& element) const {
element.AddChild("resourceName").SetStringValue(resourceName);
element.AddChild("includePosition").SetStringValue(includePosition);
}
void UnserializeFrom(const SerializerElement& element) {
resourceName = element.GetStringAttribute("resourceName");
includePosition = element.GetStringAttribute("includePosition", "last");
}
private:
gd::String resourceName; ///< The name of the resource in the project.
gd::String includePosition = "last"; ///< "first" or "last".
};
} // namespace gd

View File

@@ -215,6 +215,11 @@ gd::DependencyMetadata& PlatformExtension::AddDependency() {
return extensionDependenciesMetadata.back();
}
gd::SourceFileMetadata& PlatformExtension::AddSourceFile() {
extensionSourceFilesMetadata.push_back(SourceFileMetadata());
return extensionSourceFilesMetadata.back();
}
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
const gd::String& fullname,
@@ -463,10 +468,22 @@ PlatformExtension::GetAllStrExpressions() {
return strExpressionsInfos;
}
const std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() const {
return extensionDependenciesMetadata;
}
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
return extensionDependenciesMetadata;
}
const std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() const {
return extensionSourceFilesMetadata;
}
std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() {
return extensionSourceFilesMetadata;
}
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
return eventsInfos;
}

View File

@@ -13,6 +13,7 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionOrExpressionGroupMetadata.h"
@@ -214,6 +215,7 @@ class GD_CORE_API PlatformExtension {
const gd::String& icon);
gd::DependencyMetadata& AddDependency();
gd::SourceFileMetadata& AddSourceFile();
/**
* \brief Declare a new object as being part of the extension.
@@ -552,6 +554,24 @@ class GD_CORE_API PlatformExtension {
*/
std::vector<gd::DependencyMetadata>& GetAllDependencies();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::DependencyMetadata>& GetAllDependencies() const;
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const;
/**
* \brief Return a reference to a map containing the names of the actions,
* related to the object type, and the metadata associated with.
@@ -687,6 +707,7 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
std::vector<gd::SourceFileMetadata> extensionSourceFilesMetadata;
std::map<gd::String, gd::EventMetadata> eventsInfos;
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
std::map<gd::String, InstructionOrExpressionGroupMetadata>

View File

@@ -12,7 +12,6 @@
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
const gd::Layout& layout_)
@@ -74,16 +73,6 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
}
}
// Search for source files dependencies
std::vector<gd::String> dependencies =
events[i].GetSourceFileDependencies();
sourceFilesDependencies.insert(dependencies.begin(), dependencies.end());
const gd::String& associatedSourceFile =
events[i].GetAssociatedGDManagedSourceFile(const_cast<gd::Project&>(project));
if (!associatedSourceFile.empty())
sourceFilesDependencies.insert(associatedSourceFile);
// Analyze sub events dependencies
if (events[i].CanHaveSubEvents()) {
if (!Analyze(events[i].GetSubEvents())) return false;

View File

@@ -71,14 +71,6 @@ class GD_CORE_API DependenciesAnalyzer {
return externalEventsDependencies;
};
/**
* \brief Return the source files being dependencies of the scene or external
* events passed in the constructor.
*/
const std::set<gd::String>& GetSourceFilesDependencies() const {
return sourceFilesDependencies;
};
private:
/**
* \brief Analyze the dependencies of the events.
@@ -92,7 +84,6 @@ class GD_CORE_API DependenciesAnalyzer {
std::set<gd::String> scenesDependencies;
std::set<gd::String> externalEventsDependencies;
std::set<gd::String> sourceFilesDependencies;
std::vector<gd::String>
parentScenes; ///< Used to check for circular dependencies.
std::vector<gd::String>

View File

@@ -13,6 +13,18 @@
namespace gd {
void UsedExtensionsResult::AddUsedExtension(const gd::PlatformExtension& extension) {
usedExtensions.insert(extension.GetName());
usedSourceFiles.insert(usedSourceFiles.end(),
extension.GetAllSourceFiles().begin(),
extension.GetAllSourceFiles().end());
}
void UsedExtensionsResult::AddUsedBuiltinExtension(const gd::String& extensionName) {
usedExtensions.insert(extensionName);
}
const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
@@ -28,9 +40,9 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
if (metadata.GetMetadata().IsRenderedIn3D()) {
result.MarkAsHaving3DObjects();
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};
@@ -39,12 +51,12 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
for (auto &&includeFile : metadata.GetMetadata().requiredFiles) {
result.GetUsedRequiredFiles().insert(includeFile);
result.AddUsedRequiredFiles(includeFile);
}
};
@@ -57,9 +69,9 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
gd::ParameterMetadataTools::IterateOverParameters(
@@ -77,7 +89,7 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
rootType = "number";
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
});
return false;
@@ -110,7 +122,7 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
@@ -123,9 +135,9 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
// This represents an object.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.name);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}, [&]() {
// This is a variable.
@@ -143,13 +155,13 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
@@ -163,9 +175,9 @@ void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
// An object or object variable is used.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}
};
@@ -187,9 +199,9 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};

View File

@@ -9,6 +9,8 @@
#include <set>
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/String.h"
@@ -44,6 +46,10 @@ public:
return usedRequiredFiles;
}
const std::vector<gd::SourceFileMetadata>& GetUsedSourceFiles() const {
return usedSourceFiles;
}
/**
* \brief Return true when at least 1 object uses the 3D renderer.
*/
@@ -51,20 +57,10 @@ public:
return has3DObjects;
}
/**
* The extensions used by the project (or part of it).
*/
std::set<gd::String> &GetUsedExtensions() { return usedExtensions; }
/**
* The include files used at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedIncludeFiles() { return usedIncludeFiles; }
/**
* The additional files required at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
void AddUsedExtension(const gd::PlatformExtension& extension);
void AddUsedBuiltinExtension(const gd::String& extensionName);
void AddUsedIncludeFiles(const gd::String& includeFile) { usedIncludeFiles.insert(includeFile); }
void AddUsedRequiredFiles(const gd::String& requiredFile) { usedRequiredFiles.insert(requiredFile); }
void MarkAsHaving3DObjects() {
has3DObjects = true;
@@ -74,6 +70,7 @@ private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
std::vector<gd::SourceFileMetadata> usedSourceFiles;
bool has3DObjects = false;
};

View File

@@ -62,6 +62,11 @@ void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
// do.
};
void ArbitraryResourceWorker::ExposeJavaScript(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
// Nothing to do by default - each child class can define here the action to
// do.
@@ -195,6 +200,10 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
ExposeSpine(resourceName);
return;
}
if (resourceType == "javascript") {
ExposeJavaScript(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}

View File

@@ -96,7 +96,7 @@ public:
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);
/**
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
*/
@@ -112,6 +112,11 @@ public:
*/
virtual void ExposeVideo(gd::String &videoName);
/**
* \brief Expose a JavaScript file, which is always a reference to a "javascript" resource.
*/
virtual void ExposeJavaScript(gd::String &javaScriptName);
/**
* \brief Expose a bitmap font, which is always a reference to a "bitmapFont" resource.
*/

View File

@@ -38,6 +38,10 @@ void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) {
ExposeResourceAsFile(videoName);
}
void AssetResourcePathCleaner::ExposeJavaScript(gd::String &javaScriptResourceName) {
ExposeResourceAsFile(javaScriptResourceName);
}
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) {
ExposeResourceAsFile(bitmapFontName);
}

View File

@@ -46,6 +46,7 @@ public:
void ExposeTileset(gd::String &tilesetName) override;
void ExposeVideo(gd::String &videoName) override;
void ExposeBitmapFont(gd::String &bitmapFontName) override;
void ExposeJavaScript(gd::String &javaScriptResourceName) override;
void ExposeFile(gd::String &resource) override;
protected:

View File

@@ -73,6 +73,9 @@ public:
virtual void ExposeVideo(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeJavaScript(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};

View File

@@ -60,6 +60,7 @@ public:
if (resourceType == "model3D") return allModel3Ds;
if (resourceType == "atlas") return allAtlases;
if (resourceType == "spine") return allSpines;
if (resourceType == "javascript") return allJavaScripts;
return emptyResources;
};
@@ -88,6 +89,9 @@ public:
virtual void ExposeVideo(gd::String& resourceName) override {
allVideos.insert(resourceName);
};
virtual void ExposeJavaScript(gd::String& resourceName) override {
allJavaScripts.insert(resourceName);
};
virtual void ExposeBitmapFont(gd::String& resourceName) override {
allBitmapFonts.insert(resourceName);
};
@@ -114,6 +118,7 @@ public:
std::set<gd::String> allModel3Ds;
std::set<gd::String> allAtlases;
std::set<gd::String> allSpines;
std::set<gd::String> allJavaScripts;
std::set<gd::String> emptyResources;
static const std::vector<gd::String> resourceTypes;

View File

@@ -59,6 +59,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeVideo(gd::String& videoResourceName) override {
RenameIfNeeded(videoResourceName);
};
virtual void ExposeJavaScript(gd::String& javaScriptResourceName) override {
RenameIfNeeded(javaScriptResourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontName) override {
RenameIfNeeded(bitmapFontName);
};

View File

@@ -74,6 +74,9 @@ private:
void ExposeVideo(gd::String &videoResourceName) override {
AddUsedResource(videoResourceName);
};
void ExposeJavaScript(gd::String &javaScriptResourceName) override {
AddUsedResource(javaScriptResourceName);
};
void ExposeBitmapFont(gd::String &bitmapFontName) override {
AddUsedResource(bitmapFontName);
};

View File

@@ -26,8 +26,8 @@ namespace gd {
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorker &worker) {
// See also gd::Project::ExposeResources for a method that traverses the whole
// project (this time for resources).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources).
ExposeProjectEventsWithoutExtensions(project, worker);
@@ -106,16 +106,16 @@ void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
worker.Launch(dependencyLayout.GetEvents());
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and ExposeProjectEffects (this time for
// effects).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources)
// and ExposeProjectEffects (this time for effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {

View File

@@ -5,21 +5,22 @@
*/
#include "ResourceExposer.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/String.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
namespace gd {
void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeWholeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// See also gd::ProjectBrowserHelper::ExposeProjectEvents for a method that
// traverse the whole project (this time for events) and ExposeProjectEffects
// (this time for effects).
@@ -31,13 +32,11 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeProjectEvents(
project, eventWorker);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventWorker);
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeProjectObjects(
project, objectWorker);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, objectWorker);
// Expose layer effect resources
for (std::size_t layoutIndex = 0; layoutIndex < project.GetLayoutsCount();
@@ -52,28 +51,36 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
}
// Expose loading screen background image if present
auto& loadingScreen = project.GetLoadingScreen();
auto &loadingScreen = project.GetLoadingScreen();
if (loadingScreen.GetBackgroundImageResourceName() != "")
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
// Expose extension source files
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ExposeExtensionResources(eventsFunctionsExtension, worker);
}
}
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// Expose global objects configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
objectWorker.Launch(project.GetObjects());
}
void ResourceExposer::ExposeLayoutResources(
gd::Project &project, gd::Layout &layout,
gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker) {
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
@@ -87,15 +94,15 @@ void ResourceExposer::ExposeLayoutResources(
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
eventWorker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
project, layout, eventWorker);
// Exposed extension event resources
// Note that using resources in extensions is very unlikely and probably not
@@ -103,12 +110,14 @@ void ResourceExposer::ExposeLayoutResources(
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
project, eventsFunctionsExtension, eventWorker);
}
}
void ResourceExposer::ExposeEffectResources(
gd::Platform &platform, gd::Effect &effect,
gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker) {
auto &effectMetadata =
MetadataProvider::GetEffectMetadata(platform, effect.GetEffectType());
@@ -127,11 +136,20 @@ void ResourceExposer::ExposeEffectResources(
worker.ExposeResourceWithType(resourceType,
potentiallyUpdatedResourceName);
if (potentiallyUpdatedResourceName != resourceName) {
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
effect.SetStringParameter(propertyName,
potentiallyUpdatedResourceName);
}
}
}
}
}
} // namespace gd
void ResourceExposer::ExposeExtensionResources(
gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker) {
for (auto &sourceFile : extension.GetAllSourceFiles()) {
worker.ExposeJavaScript(sourceFile.GetResourceName());
}
}
} // namespace gd

View File

@@ -9,9 +9,10 @@ namespace gd {
class Platform;
class Project;
class ArbitraryResourceWorker;
class EventsFunctionsExtension;
class Effect;
class Layout;
} // namespace gd
} // namespace gd
namespace gd {
@@ -19,7 +20,7 @@ namespace gd {
* \brief
*/
class GD_CORE_API ResourceExposer {
public:
public:
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources, sometimes update their filename or any other work or resources.
@@ -34,7 +35,7 @@ public:
/**
* @brief Expose only the resources used globally on a project.
*
*
* It doesn't include resources used in layouts.
*/
static void ExposeProjectResources(gd::Project &project,
@@ -42,17 +43,25 @@ public:
/**
* @brief Expose the resources used in a given layout.
*
*
* It doesn't include resources used globally.
*/
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
static void ExposeLayoutResources(gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in a given effect.
*/
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
static void ExposeEffectResources(gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in an extension.
*/
static void ExposeExtensionResources(gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker);
};
} // namespace gd
} // namespace gd

View File

@@ -23,6 +23,9 @@ void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
gd::SerializerElement& eventsFunctionsElement =
element.AddChild("eventsFunctions");
@@ -36,6 +39,7 @@ void AbstractEventsBasedEntity::UnserializeFrom(
description = element.GetStringAttribute("description");
name = element.GetStringAttribute("name");
fullName = element.GetStringAttribute("fullName");
isPrivate = element.GetBoolAttribute("private");
const gd::SerializerElement& eventsFunctionsElement =
element.GetChild("eventsFunctions");

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#define GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#pragma once
#include <vector>
#include "GDCore/Project/NamedPropertyDescriptor.h"
@@ -40,6 +39,21 @@ class GD_CORE_API AbstractEventsBasedEntity {
*/
AbstractEventsBasedEntity* Clone() const { return new AbstractEventsBasedEntity(*this); };
/**
* \brief Check if the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
AbstractEventsBasedEntity& SetPrivate(bool isPrivate_) {
isPrivate = isPrivate_;
return *this;
}
/**
* \brief Get the description of the behavior or object, that is displayed in the
* editor.
@@ -151,8 +165,7 @@ class GD_CORE_API AbstractEventsBasedEntity {
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::PropertiesContainer propertyDescriptors;
gd::String extensionName;
bool isPrivate = false;
};
} // namespace gd
#endif // GDCORE_ABSTRACTEVENTSBASEDENTITY_H

View File

@@ -53,7 +53,8 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
propertyType == "Resource" || propertyType == "LeaderboardId" ||
propertyType == "AnimationName") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
@@ -89,7 +90,8 @@ bool CustomConfigurationHelper::UpdateProperty(
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
propertyType == "Resource" || propertyType == "LeaderboardId" ||
propertyType == "AnimationName") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
element.SetDoubleValue(newValue.To<double>());

View File

@@ -21,9 +21,6 @@ EventsBasedBehavior::EventsBasedBehavior()
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
AbstractEventsBasedEntity::SerializeTo(element);
element.SetAttribute("objectType", objectType);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
sharedPropertyDescriptors.SerializeElementsTo(
"propertyDescriptor", element.AddChild("sharedPropertyDescriptors"));
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
@@ -39,7 +36,6 @@ void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
AbstractEventsBasedEntity::UnserializeFrom(project, element);
objectType = element.GetStringAttribute("objectType");
isPrivate = element.GetBoolAttribute("private");
sharedPropertyDescriptors.UnserializeElementsFrom(
"propertyDescriptor", element.GetChild("sharedPropertyDescriptors"));
if (element.HasChild("quickCustomizationVisibility")) {

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EVENTSBASEDBEHAVIOR_H
#define GDCORE_EVENTSBASEDBEHAVIOR_H
#pragma once
#include <vector>
#include "GDCore/Project/AbstractEventsBasedEntity.h"
@@ -75,17 +74,11 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
EventsBasedBehavior& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
@@ -149,11 +142,8 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
gd::PropertiesContainer sharedPropertyDescriptors;
QuickCustomization::Visibility quickCustomizationVisibility;
};
} // namespace gd
#endif // GDCORE_EVENTSBASEDBEHAVIOR_H

View File

@@ -72,6 +72,15 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
return *this;
}
/**
* \brief Set that the object is private - it can't be used outside of its
* extension.
*/
EventsBasedObject& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
/**
* \brief Declare a usage of the 3D renderer.
*/

View File

@@ -87,6 +87,13 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
for (auto& dependency : dependencies)
SerializeDependencyTo(dependency, dependenciesElement.AddChild(""));
if (!sourceFiles.empty()) {
auto& sourceFilesElement = element.AddChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (auto& sourceFile : sourceFiles)
sourceFile.SerializeTo(sourceFilesElement.AddChild(""));
}
GetGlobalVariables().SerializeTo(element.AddChild("globalVariables"));
GetSceneVariables().SerializeTo(element.AddChild("sceneVariables"));
@@ -159,6 +166,17 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
dependencies.push_back(
UnserializeDependencyFrom(dependenciesElement.GetChild(i)));
sourceFiles.clear();
if (element.HasChild("sourceFiles")) {
const auto& sourceFilesElement = element.GetChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (size_t i = 0; i < sourceFilesElement.GetChildrenCount(); ++i) {
SourceFileMetadata sourceFile;
sourceFile.UnserializeFrom(sourceFilesElement.GetChild(i));
sourceFiles.push_back(sourceFile);
}
}
globalVariables.UnserializeFrom(element.GetChild("globalVariables"));
sceneVariables.UnserializeFrom(element.GetChild("sceneVariables"));

View File

@@ -8,6 +8,7 @@
#include <vector>
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
@@ -289,6 +290,42 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
const gd::String& eventsFunctionName);
///@}
/** \name Source files
*/
///@{
/**
* \brief Adds a new source file.
*/
gd::SourceFileMetadata& AddSourceFile() {
gd::SourceFileMetadata sourceFile;
sourceFiles.push_back(sourceFile);
return sourceFiles.back();
};
/**
* \brief Removes a source file.
*/
void RemoveSourceFileAt(size_t index) {
sourceFiles.erase(sourceFiles.begin() + index);
};
/**
* \brief Returns the list of source files.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() {
return sourceFiles;
};
/**
* \brief Returns the list of source files.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const {
return sourceFiles;
};
///@}
private:
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
@@ -336,7 +373,8 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
std::vector<gd::DependencyMetadata> dependencies;
std::vector<gd::SourceFileMetadata> sourceFiles;
gd::VariablesContainer globalVariables;
gd::VariablesContainer sceneVariables;
};

View File

@@ -30,7 +30,6 @@
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -67,7 +66,6 @@ Project::Project()
isAntialisingEnabledOnMobile(false),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
@@ -742,9 +740,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
watermark.UnserializeFrom(propElement.GetChild("watermark"));
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
authorIds.clear();
auto& authorIdsElement = propElement.GetChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -917,19 +912,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
InsertNewExternalLayout("", GetExternalLayoutsCount());
newExternalLayout.UnserializeFrom(externalLayoutElement);
}
externalSourceFiles.clear();
const SerializerElement& externalSourceFilesElement =
element.GetChild("externalSourceFiles", 0, "ExternalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile", "SourceFile");
for (std::size_t i = 0; i < externalSourceFilesElement.GetChildrenCount();
++i) {
const SerializerElement& sourceFileElement =
externalSourceFilesElement.GetChild(i);
gd::SourceFile& newSourceFile = InsertNewSourceFile("", "");
newSourceFile.UnserializeFrom(sourceFileElement);
}
}
void Project::UnserializeAndInsertExtensionsFrom(
@@ -1109,7 +1091,6 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
watermark.SerializeTo(propElement.AddChild("watermark"));
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
auto& authorIdsElement = propElement.AddChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -1197,13 +1178,6 @@ void Project::SerializeTo(SerializerElement& element) const {
for (std::size_t i = 0; i < externalLayouts.size(); ++i)
externalLayouts[i]->SerializeTo(
externalLayoutsElement.AddChild("externalLayout"));
SerializerElement& externalSourceFilesElement =
element.AddChild("externalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile");
for (std::size_t i = 0; i < externalSourceFiles.size(); ++i)
externalSourceFiles[i]->SerializeTo(
externalSourceFilesElement.AddChild("sourceFile"));
}
bool Project::IsNameSafe(const gd::String& name) {
@@ -1244,63 +1218,6 @@ gd::String Project::GetSafeName(const gd::String& name) {
return newName;
}
bool Project::HasSourceFile(gd::String name, gd::String language) const {
vector<std::unique_ptr<SourceFile> >::const_iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return false;
return language.empty() || (*sourceFile)->GetLanguage() == language;
}
gd::SourceFile& Project::GetSourceFile(const gd::String& name) {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
const gd::SourceFile& Project::GetSourceFile(const gd::String& name) const {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
void Project::RemoveSourceFile(const gd::String& name) {
std::vector<std::unique_ptr<gd::SourceFile> >::iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return;
externalSourceFiles.erase(sourceFile);
}
gd::SourceFile& Project::InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position) {
if (HasSourceFile(name, language)) return GetSourceFile(name);
gd::SourceFile& newlyInsertedSourceFile = *(
*(externalSourceFiles.emplace(position < externalSourceFiles.size()
? externalSourceFiles.begin() + position
: externalSourceFiles.end(),
new SourceFile())));
newlyInsertedSourceFile.SetLanguage(language);
newlyInsertedSourceFile.SetFileName(name);
return newlyInsertedSourceFile;
}
Project::Project(const Project &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Global) {
Init(other);
@@ -1367,10 +1284,6 @@ void Project::Init(const gd::Project& game) {
externalLayouts = gd::Clone(game.externalLayouts);
eventsFunctionsExtensions = gd::Clone(game.eventsFunctionsExtensions);
useExternalSourceFiles = game.useExternalSourceFiles;
externalSourceFiles = gd::Clone(game.externalSourceFiles);
variables = game.GetVariables();
projectFile = game.GetProjectFile();

View File

@@ -32,7 +32,6 @@ class Object;
class ObjectConfiguration;
class VariablesContainer;
class ArbitraryResourceWorker;
class SourceFile;
class Behavior;
class BehaviorsSharedData;
class BaseEvent;
@@ -1019,56 +1018,6 @@ class GD_CORE_API Project {
static gd::String GetSafeName(const gd::String& name);
///@}
/** \name External source files
* To manage external C++ or Javascript source files used by the game
*/
///@{
/**
* \brief Return true if the game activated the use of external source files.
*/
bool UseExternalSourceFiles() const { return useExternalSourceFiles; }
/**
* \brief Return a const reference to the vector containing all the source
* files used by the game.
*/
const std::vector<std::unique_ptr<gd::SourceFile> >& GetAllSourceFiles()
const {
return externalSourceFiles;
}
/**
* \brief Return true if the source file with the specified name is used by
* the game. \param name The filename of the source file. \param language
* Optional. If specified, check that the source file that exists is in this
* language.
*/
bool HasSourceFile(gd::String name, gd::String language = "") const;
/**
* Return a reference to the external source file with the given name.
*/
SourceFile& GetSourceFile(const gd::String& name);
/**
* Return a reference to the external source file with the given name.
*/
const SourceFile& GetSourceFile(const gd::String& name) const;
/**
* Remove the specified source file.
*/
void RemoveSourceFile(const gd::String& name);
/**
* Add a new source file the specified position in the external source files
* list.
*/
gd::SourceFile& InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position = -1);
///@}
gd::WholeProjectDiagnosticReport& GetWholeProjectDiagnosticReport() {
return wholeProjectDiagnosticReport;
}
@@ -1141,10 +1090,6 @@ class GD_CORE_API Project {
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
bool useExternalSourceFiles =
false; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.

View File

@@ -97,6 +97,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<AtlasResource>();
else if (kind == "spine")
return std::make_shared<SpineResource>();
else if (kind == "javascript")
return std::make_shared<JavaScriptResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -767,6 +769,20 @@ void AtlasResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}
void JavaScriptResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void JavaScriptResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void JavaScriptResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {

View File

@@ -547,6 +547,32 @@ class GD_CORE_API AtlasResource : public Resource {
gd::String file;
};
/**
* \brief Describe a video file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API JavaScriptResource : public Resource {
public:
JavaScriptResource() : Resource() { SetKind("javascript"); };
virtual ~JavaScriptResource(){};
virtual JavaScriptResource* Clone() const override {
return new JavaScriptResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*

View File

@@ -1,36 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "GDCore/Project/SourceFile.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
SourceFile::SourceFile() : gdManaged(false) {
// ctor
}
SourceFile::~SourceFile() {
// dtor
}
void SourceFile::SerializeTo(SerializerElement& element) const {
element.SetAttribute("filename", filename);
element.SetAttribute("language", language);
element.SetAttribute("gdManaged", gdManaged);
}
void SourceFile::UnserializeFrom(const SerializerElement& element) {
filename = element.GetStringAttribute("filename");
language = element.GetStringAttribute("language", "C++");
gdManaged = element.GetBoolAttribute("gdManaged");
}
} // namespace gd
#endif

View File

@@ -1,92 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef SOURCEFILE_H
#define SOURCEFILE_H
#include <ctime>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
class BaseEvent;
namespace gd {
/**
* \brief Represents a "physical" source file.
*
* Source file can be compiled (or just integrated to the exported project)
* by platforms. Most of the time, special events are provided to use functions
* created in such files.
*/
class GD_CORE_API SourceFile {
public:
SourceFile();
virtual ~SourceFile();
/**
* \brief Return a pointer to a new SourceFile constructed from this one.
*/
SourceFile* Clone() const { return new SourceFile(*this); };
/**
* \brief Get the filename
*/
gd::String GetFileName() const { return filename; };
/**
* \brief Change the filename
*/
void SetFileName(gd::String filename_) { filename = filename_; };
/**
* \brief Serialize the source file.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the source file.
*/
void UnserializeFrom(const SerializerElement& element);
/**
* \brief Set if the file is hidden from the user point of view and is only
* managed by GDevelop
*/
void SetGDManaged(bool gdManaged_) { gdManaged = gdManaged_; };
/**
* \brief Return true if the file is hidden from the user point of view and is
* only managed by GDevelop
*/
bool IsGDManaged() const { return gdManaged; };
/**
* \brief Change the language of the source file
*/
void SetLanguage(gd::String lang) { language = lang; }
/**
* \brief Get the language of the source file
*/
const gd::String& GetLanguage() const { return language; }
private:
gd::String filename; ///< Filename
bool gdManaged; ///< True if the source file is hidden from the user point of
///< view and is managed only by GDevelop.
gd::String language; ///< String identifying the language of this source file
///< (typically "C++ or "Javascript").
std::weak_ptr<BaseEvent>
associatedGdEvent; ///< When a source file is GD-managed, it is usually
///< created for a specific event. This member is not
///< saved: It is the event responsibility to call
///< SetAssociatedEvent.
};
} // namespace gd
#endif // SOURCEFILE_H

View File

@@ -32,7 +32,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetScenesDependencies().find("Layout2") !=
analyzer.GetScenesDependencies().end());
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 0);
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a simple external events dependency") {
@@ -55,7 +54,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a transitive scene and external events dependency") {
@@ -87,7 +85,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a (nested) circular dependency with scenes") {

View File

@@ -1,30 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/CommonTools.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "catch.hpp"
TEST_CASE("SourceFile", "[common]") {
SECTION("Basics") {
gd::Project project;
project.InsertNewSourceFile("test.cpp", "C++");
project.InsertNewSourceFile("test.js", "Javascript");
REQUIRE(project.HasSourceFile("test.cpp", "C++") == true);
REQUIRE(project.HasSourceFile("test.cpp", "JS") == false);
REQUIRE(project.HasSourceFile("test.cpp") == true);
gd::SourceFile& cppSourceFile = project.GetSourceFile("test.cpp");
REQUIRE(cppSourceFile.GetFileName() == "test.cpp");
REQUIRE(cppSourceFile.GetLanguage() == "C++");
project.RemoveSourceFile("test.cpp");
REQUIRE(project.HasSourceFile("test.cpp") == false);
REQUIRE(project.HasSourceFile("test.js") == true);
}
}

View File

@@ -0,0 +1,39 @@
declare namespace bondage {
export class Runner {
yarnNodes: any;
variables: any;
functions: any;
visited: any;
load(data: any[]): void;
setVariableStorage(storage: any): void;
registerFunction(name: string, func): void;
run(startNode: string): any;
evalNodes(nodes: any[], yarnNodeData: any): any;
handleSelections(selections: any[]): any;
evaluateAssignment(node: any): any;
evaluateConditional(node: any): any;
evaluateExpressionOrLiteral(node): any;
}
export class Result {}
export class TextResult extends Result {
text: string;
data: any;
lineNum: number;
}
export class CommandResult extends Result {
text: string;
data: any;
lineNum: number;
}
export class OptionsResult extends Result {
options: string[];
lineNum: number[];
selected: number;
select(index: number): void;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -340,7 +340,8 @@ module.exports = {
_(
'The friction applied when touching other objects. The higher the value, the more friction.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('restitution')
.setValue(
@@ -352,7 +353,8 @@ module.exports = {
_(
'The "bounciness" of the object. The higher the value, the more other objects will bounce against it.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('linearDamping')
.setValue(

View File

@@ -964,7 +964,7 @@ namespace gdjs {
// It would be useless to try to recreate it as updateBodyFromObject already does it.
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (this._body !== null) {
if (this._body !== null && !this.isStatic() && this._body.IsAwake()) {
this.owner.setX(
this._body.GetPosition().get_x() * this._sharedData.worldScale -
this.owner.getWidth() / 2 +

View File

@@ -300,7 +300,8 @@ module.exports = {
_(
'The friction applied when touching other objects. The higher the value, the more friction.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('restitution')
.setValue(
@@ -315,7 +316,8 @@ module.exports = {
_(
'The "bounciness" of the object. The higher the value, the more other objects will bounce against it.'
)
);
)
.setGroup(_('Movement'));
behaviorProperties
.getOrCreate('linearDamping')
.setValue(
@@ -1533,6 +1535,15 @@ module.exports = {
return true;
}
if (propertyName === 'stairHeightMax') {
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('stairHeightMax')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shouldBindObjectAndForwardAngle') {
behaviorContent
.getChild('shouldBindObjectAndForwardAngle')
@@ -1701,6 +1712,24 @@ module.exports = {
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
if (!behaviorContent.hasChild('stairHeightMax')) {
behaviorContent.addChild('stairHeightMax').setDoubleValue(20);
}
behaviorProperties
.getOrCreate('stairHeightMax')
.setLabel('Max. stair height')
.setGroup(_('Walk'))
.setType('Number')
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
.setValue(
behaviorContent
.getChild('stairHeightMax')
.getDoubleValue()
.toString(10)
)
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
behaviorProperties
.getOrCreate('shouldBindObjectAndForwardAngle')
.setLabel('Keep object angle and forward direction the same')
@@ -1732,6 +1761,7 @@ module.exports = {
behaviorContent.addChild('sidewaysDeceleration').setDoubleValue(800);
behaviorContent.addChild('sidewaysSpeedMax').setDoubleValue(400);
behaviorContent.addChild('slopeMaxAngle').setDoubleValue(50);
behaviorContent.addChild('stairHeightMax').setDoubleValue(20);
behaviorContent
.addChild('shouldBindObjectAndForwardAngle')
.setBoolValue(true);

View File

@@ -296,6 +296,7 @@ namespace gdjs {
export class Physics3DRuntimeBehavior extends gdjs.RuntimeBehavior {
bodyUpdater: gdjs.Physics3DRuntimeBehavior.BodyUpdater;
collisionChecker: gdjs.Physics3DRuntimeBehavior.CollisionChecker;
owner3D: gdjs.RuntimeObject3D;
bodyType: string;
@@ -338,7 +339,7 @@ namespace gdjs {
* each time the methods onContactBegin and onContactEnd are called by the contact
* listener.
*/
private _currentContacts: Array<Physics3DRuntimeBehavior> = [];
_currentContacts: Array<Physics3DRuntimeBehavior> = [];
private _destroyedDuringFrameLogic: boolean;
_body: Jolt.Body | null = null;
@@ -381,6 +382,9 @@ namespace gdjs {
this.bodyUpdater = new gdjs.Physics3DRuntimeBehavior.DefaultBodyUpdater(
this
);
this.collisionChecker = new gdjs.Physics3DRuntimeBehavior.DefaultCollisionChecker(
this
);
this.owner3D = owner;
this.bodyType = behaviorData.bodyType;
this.bullet = behaviorData.bullet;
@@ -540,7 +544,7 @@ namespace gdjs {
if (this._body) {
this._sharedData.bodyInterface.SetPosition(
this._body.GetID(),
this.getVec3(
this.getRVec3(
behaviorSpecificProps.px,
behaviorSpecificProps.py,
behaviorSpecificProps.pz
@@ -804,13 +808,11 @@ namespace gdjs {
getBodyLayer(): number {
return Jolt.ObjectLayerPairFilterMask.prototype.sGetObjectLayer(
// Make sure objects don't register in the wrong layer group.
this.bodyType === 'Static'
this.isStatic()
? this.layers & gdjs.Physics3DSharedData.staticLayersMask
: this.layers & gdjs.Physics3DSharedData.dynamicLayersMask,
// Static objects accept all collisions as it's the mask of dynamic objects that matters.
this.bodyType === 'Static'
? gdjs.Physics3DSharedData.allLayersMask
: this.masks
this.isStatic() ? gdjs.Physics3DSharedData.allLayersMask : this.masks
);
}
@@ -1696,27 +1698,7 @@ namespace gdjs {
behaviorName
) as Physics3DRuntimeBehavior | null;
if (!behavior1) return false;
if (
behavior1._currentContacts.some(
(behavior) => behavior.owner === object2
)
) {
return true;
}
// If a contact has started at this frame and ended right away, it
// won't appear in current contacts but the condition should return
// true anyway.
if (
behavior1._contactsStartedThisFrame.some(
(behavior) => behavior.owner === object2
)
) {
return true;
}
// No contact found
return false;
return behavior1.collisionChecker.isColliding(object2);
}
static hasCollisionStartedBetween(
@@ -1728,10 +1710,7 @@ namespace gdjs {
behaviorName
) as Physics3DRuntimeBehavior | null;
if (!behavior1) return false;
return behavior1._contactsStartedThisFrame.some(
(behavior) => behavior.owner === object2
);
return behavior1.collisionChecker.hasCollisionStartedWith(object2);
}
static hasCollisionStoppedBetween(
@@ -1743,10 +1722,7 @@ namespace gdjs {
behaviorName
) as Physics3DRuntimeBehavior | null;
if (!behavior1) return false;
return behavior1._contactsEndedThisFrame.some(
(behavior) => behavior.owner === object2
);
return behavior1.collisionChecker.hasCollisionStoppedWith(object2);
}
}
@@ -1827,7 +1803,7 @@ namespace gdjs {
// It would be useless to try to recreate it as updateBodyFromObject already does it.
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (_body !== null) {
if (_body !== null && _body.IsActive()) {
behavior.moveObjectToPhysicsPosition(_body.GetPosition());
behavior.moveObjectToPhysicsRotation(_body.GetRotation());
}
@@ -1841,8 +1817,6 @@ namespace gdjs {
}
const body = behavior._body!;
// TODO the `if` is probably unnecessary because `SetPositionAndRotationWhenChanged` already check this.
// The object object transform has changed, update body transform:
if (
this.behavior._objectOldX !== owner3D.getX() ||
this.behavior._objectOldY !== owner3D.getY() ||
@@ -1877,5 +1851,49 @@ namespace gdjs {
);
}
}
export interface CollisionChecker {
isColliding(object: gdjs.RuntimeObject): boolean;
hasCollisionStartedWith(object: gdjs.RuntimeObject): boolean;
hasCollisionStoppedWith(object: gdjs.RuntimeObject): boolean;
}
/**
* The default collision checker uses the contacts found while
* stepping the physics simulation. For characters, another one is used
* as characters are simulated before the rest of the physics simulation.
*/
export class DefaultCollisionChecker implements CollisionChecker {
behavior: gdjs.Physics3DRuntimeBehavior;
constructor(behavior: gdjs.Physics3DRuntimeBehavior) {
this.behavior = behavior;
}
isColliding(object: gdjs.RuntimeObject): boolean {
if (
this.behavior._currentContacts.some(
(behavior) => behavior.owner === object
)
) {
return true;
}
return this.behavior._contactsStartedThisFrame.some(
(behavior) => behavior.owner === object
);
}
hasCollisionStartedWith(object: gdjs.RuntimeObject): boolean {
return this.behavior._contactsStartedThisFrame.some(
(behavior) => behavior.owner === object
);
}
hasCollisionStoppedWith(object: gdjs.RuntimeObject): boolean {
return this.behavior._contactsEndedThisFrame.some(
(behavior) => behavior.owner === object
);
}
}
}
}

View File

@@ -53,6 +53,7 @@ namespace gdjs {
* before stepping the world.
*/
_sharedData: gdjs.Physics3DSharedData;
collisionChecker: gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterCollisionChecker;
// TODO Should there be angle were the character can climb but will slip?
_slopeMaxAngle: float;
@@ -70,6 +71,7 @@ namespace gdjs {
private _maxFallingSpeed: float;
private _jumpSpeed: float;
private _jumpSustainTime: float;
private _stairHeightMax: float;
private _hasPressedForwardKey: boolean = false;
private _hasPressedBackwardKey: boolean = false;
@@ -121,6 +123,9 @@ namespace gdjs {
instanceContainer.getScene(),
behaviorData.Physics3D
);
this.collisionChecker = new gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterCollisionChecker(
this
);
this._slopeMaxAngle = 0;
this.setSlopeMaxAngle(behaviorData.slopeMaxAngle);
@@ -136,6 +141,10 @@ namespace gdjs {
this._jumpSpeed = this.getJumpSpeedToReach(behaviorData.jumpHeight);
this._shouldBindObjectAndForwardAngle =
behaviorData.shouldBindObjectAndForwardAngle;
this._stairHeightMax =
behaviorData.stairHeightMax === undefined
? 20
: behaviorData.stairHeightMax;
}
private getVec3(x: float, y: float, z: float): Jolt.Vec3 {
@@ -155,7 +164,6 @@ namespace gdjs {
const sharedData = behavior._sharedData;
const jolt = sharedData.jolt;
const extendedUpdateSettings = new Jolt.ExtendedUpdateSettings();
extendedUpdateSettings.mWalkStairsStepUp = this.getVec3(0, 0, 0.4);
const broadPhaseLayerFilter = new Jolt.DefaultBroadPhaseLayerFilter(
jolt.GetObjectVsBroadPhaseLayerFilter(),
gdjs.Physics3DSharedData.dynamicBroadPhaseLayerIndex
@@ -175,11 +183,13 @@ namespace gdjs {
bodyFilter,
shapeFilter,
};
this.setStairHeightMax(this._stairHeightMax);
sharedData.registerHook(this);
behavior.bodyUpdater = new gdjs.PhysicsCharacter3DRuntimeBehavior.CharacterBodyUpdater(
this
);
behavior.collisionChecker = this.collisionChecker;
behavior.recreateBody();
// Always begin in the direction of the object.
@@ -241,6 +251,9 @@ namespace gdjs {
newBehaviorData.shouldBindObjectAndForwardAngle
);
}
if (oldBehaviorData.stairHeightMax !== newBehaviorData.stairHeightMax) {
this.setStairHeightMax(newBehaviorData.stairHeightMax);
}
return true;
}
@@ -328,7 +341,9 @@ namespace gdjs {
);
}
onDeActivate() {}
onDeActivate() {
this.collisionChecker.clearContacts();
}
onActivate() {}
@@ -498,6 +513,7 @@ namespace gdjs {
shapeFilter,
this._sharedData.jolt.GetTempAllocator()
);
this.collisionChecker.updateContacts();
if (this.isOnFloor()) {
this._canJump = true;
@@ -775,6 +791,27 @@ namespace gdjs {
);
}
getStairHeightMax(): float {
return this._stairHeightMax;
}
setStairHeightMax(stairHeightMax: float): void {
const { extendedUpdateSettings } = this.getPhysics3D();
this._stairHeightMax = stairHeightMax;
const walkStairsStepUp = stairHeightMax * this._sharedData.worldInvScale;
extendedUpdateSettings.mWalkStairsStepUp = this.getVec3(
0,
0,
walkStairsStepUp
);
// Use default values proportionally;
// "The factors are arbitrary but works well when tested in a game."
extendedUpdateSettings.mWalkStairsMinStepForward =
(0.02 / 0.4) * walkStairsStepUp;
extendedUpdateSettings.mWalkStairsStepForwardTest =
(0.15 / 0.4) * walkStairsStepUp;
}
/**
* Get the gravity of the Character.
* @returns The current gravity.
@@ -1187,7 +1224,7 @@ namespace gdjs {
* @returns Returns true if it is falling and false if not.
*/
isFallingWithoutJumping(): boolean {
return !this.isOnFloor() && this._currentJumpSpeed === 0;
return !this.isOnFloor() && !this.isJumping();
}
/**
@@ -1204,7 +1241,8 @@ namespace gdjs {
*/
isFalling(): boolean {
return (
!this.isOnFloor() && this._currentJumpSpeed < this._currentFallSpeed
!this.isOnFloor() ||
(this.isJumping() && this._currentFallSpeed > this._currentJumpSpeed)
);
}
@@ -1426,5 +1464,87 @@ namespace gdjs {
this.updateCharacterPosition();
}
}
/**
* A character is simulated by Jolt before the rest of the physics simulation
* (see `doBeforePhysicsStep`).
* This means that contacts with the character would only rarely be recognized by
* the physics engine if using the default contact listeners.
* Instead, this class allows to properly track contacts of the character
* using Jolt `CharacterVirtual::GetActiveContacts`.
*/
export class CharacterCollisionChecker
implements gdjs.Physics3DRuntimeBehavior.CollisionChecker {
characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior;
_currentContacts: Array<Physics3DRuntimeBehavior> = [];
_previousContacts: Array<Physics3DRuntimeBehavior> = [];
constructor(characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior) {
this.characterBehavior = characterBehavior;
}
clearContacts(): void {
this._previousContacts.length = 0;
this._currentContacts.length = 0;
}
updateContacts(): void {
const swap = this._previousContacts;
this._previousContacts = this._currentContacts;
this._currentContacts = swap;
this._currentContacts.length = 0;
const { character, _sharedData } = this.characterBehavior;
if (!character) {
return;
}
const contacts = character.GetActiveContacts();
for (let index = 0; index < contacts.size(); index++) {
const contact = contacts.at(index);
const bodyLockInterface = _sharedData.physicsSystem.GetBodyLockInterface();
const body = bodyLockInterface.TryGetBody(contact.mBodyB);
const behavior = body.gdjsAssociatedBehavior;
if (behavior) {
this._currentContacts.push(behavior);
}
}
}
isColliding(object: gdjs.RuntimeObject): boolean {
const { character } = this.characterBehavior;
if (!character) {
return false;
}
return this._currentContacts.some(
(behavior) => behavior.owner === object
);
}
hasCollisionStartedWith(object: gdjs.RuntimeObject): boolean {
const { character } = this.characterBehavior;
if (!character) {
return false;
}
return (
this._currentContacts.some((behavior) => behavior.owner === object) &&
!this._previousContacts.some((behavior) => behavior.owner === object)
);
}
hasCollisionStoppedWith(object: gdjs.RuntimeObject): boolean {
const { character } = this.characterBehavior;
if (!character) {
return false;
}
return (
!this._currentContacts.some(
(behavior) => behavior.owner === object
) &&
this._previousContacts.some((behavior) => behavior.owner === object)
);
}
}
}
}

View File

@@ -55,6 +55,26 @@ declare namespace Jolt {
clear(): void;
data(): Mat44MemRef;
}
class ArrayBodyID {
empty(): boolean;
size(): number;
at(inIndex: number): BodyID;
push_back(inValue: BodyID): void;
reserve(inSize: number): void;
resize(inSize: number): void;
clear(): void;
data(): BodyIDMemRef;
}
class ArrayBodyPtr {
empty(): boolean;
size(): number;
at(inIndex: number): Body;
push_back(inValue: Body): void;
reserve(inSize: number): void;
resize(inSize: number): void;
clear(): void;
data(): BodyPtrMemRef;
}
const EBodyType_RigidBody: number;
const EBodyType_SoftBody: number;
type EBodyType = typeof EBodyType_RigidBody | typeof EBodyType_SoftBody;
@@ -414,6 +434,8 @@ declare namespace Jolt {
class Vec3MemRef {}
class QuatMemRef {}
class Mat44MemRef {}
class BodyIDMemRef {}
class BodyPtrMemRef {}
class FloatMemRef {}
class Uint8MemRef {}
class UintMemRef {}
@@ -429,6 +451,10 @@ declare namespace Jolt {
sMin(inLHS: Vec3, inRHS: Vec3): Vec3;
sMax(inLHS: Vec3, inRHS: Vec3): Vec3;
sClamp(inValue: Vec3, inMin: Vec3, inMax: Vec3): Vec3;
sFusedMultiplyAdd(inMul1: Vec3, inMul2: Vec3, inAdd: Vec3): Vec3;
sOr(inV1: Vec3, inV2: Vec3): Vec3;
sXor(inV1: Vec3, inV2: Vec3): Vec3;
sAnd(inV1: Vec3, inV2: Vec3): Vec3;
sUnitSpherical(inTheta: number, inPhi: number): Vec3;
GetComponent(inCoordinate: number): number;
Equals(inV: Vec3): boolean;
@@ -455,10 +481,21 @@ declare namespace Jolt {
Reciprocal(): Vec3;
Cross(inRHS: Vec3): Vec3;
Dot(inRHS: Vec3): number;
DotV(inRHS: Vec3): Vec3;
DotV4(inRHS: Vec3): Vec4;
Add(inV: Vec3): Vec3;
Sub(inV: Vec3): Vec3;
Mul(inV: number): Vec3;
Div(inV: number): Vec3;
MulVec3(inV: Vec3): Vec3;
MulFloat(inV: number): Vec3;
DivVec3(inV: Vec3): Vec3;
DivFloat(inV: number): Vec3;
AddVec3(inV: Vec3): Vec3;
SubVec3(inV: Vec3): Vec3;
SplatX(): Vec4;
SplatY(): Vec4;
SplatZ(): Vec4;
ReduceMin(): number;
ReduceMax(): number;
Sqrt(): Vec3;
@@ -500,6 +537,12 @@ declare namespace Jolt {
Sub(inV: Vec3): RVec3;
Mul(inV: number): RVec3;
Div(inV: number): RVec3;
MulRVec3(inV: RVec3): RVec3;
MulFloat(inV: number): RVec3;
DivRVec3(inV: RVec3): RVec3;
DivFloat(inV: number): RVec3;
AddRVec3(inV: RVec3): RVec3;
SubRVec3(inV: RVec3): RVec3;
Sqrt(): RVec3;
GetSign(): RVec3;
}
@@ -508,6 +551,14 @@ declare namespace Jolt {
constructor(inV: Vec4);
constructor(inV: Vec3, inW: number);
constructor(inX: number, inY: number, inZ: number, inW: number);
sZero(): Vec4;
sReplicate(inV: number): Vec4;
sMin(inLHS: Vec4, inRHS: Vec4): Vec4;
sMax(inLHS: Vec4, inRHS: Vec4): Vec4;
sFusedMultiplyAdd(inMul1: Vec4, inMul2: Vec4, inAdd: Vec4): Vec4;
sOr(inV1: Vec4, inV2: Vec4): Vec4;
sXor(inV1: Vec4, inV2: Vec4): Vec4;
sAnd(inV1: Vec4, inV2: Vec4): Vec4;
GetX(): number;
GetY(): number;
GetZ(): number;
@@ -518,6 +569,18 @@ declare namespace Jolt {
SetW(inW: number): void;
Set(inX: number, inY: number, inZ: number, inW: number): void;
GetComponent(inCoordinate: number): number;
IsClose(inV: Vec4, inMaxDistSq?: number): boolean;
IsNormalized(inTolerance?: number): boolean;
Add(inV: Vec4): Vec4;
Sub(inV: Vec4): Vec4;
Mul(inV: number): Vec4;
Div(inV: number): Vec4;
MulVec4(inV: Vec4): Vec4;
MulFloat(inV: number): Vec4;
DivVec4(inV: Vec4): Vec4;
DivFloat(inV: number): Vec4;
AddVec4(inV: Vec4): Vec4;
SubVec4(inV: Vec4): Vec4;
}
class Vector2 {
constructor();
@@ -531,6 +594,10 @@ declare namespace Jolt {
Sub(inV: Vector2): Vector2;
Mul(inV: number): Vector2;
Div(inV: number): Vector2;
MulFloat(inV: number): Vector2;
DivFloat(inV: number): Vector2;
AddVector2(inV: Vector2): Vector2;
SubVector2(inV: Vector2): Vector2;
Dot(inRHS: Vector2): number;
}
class Quat {
@@ -599,10 +666,17 @@ declare namespace Jolt {
sRotationY(inY: number): Mat44;
sRotationZ(inZ: number): Mat44;
sRotation(inQ: Quat): Mat44;
sRotationAxisAngle(inAxis: Vec3, inAngle: number): Mat44;
sTranslation(inTranslation: Vec3): Mat44;
sRotationTranslation(inRotation: Quat, inTranslation: Vec3): Mat44;
sInverseRotationTranslation(inRotation: Quat, inTranslation: Vec3): Mat44;
sScale(inScale: number): Mat44;
sScaleVec3(inScale: Vec3): Mat44;
sOuterProduct(inV1: Vec3, inV2: Vec3): Mat44;
sCrossProduct(inV: Vec3): Mat44;
sQuatLeftMultiply(inQ: Quat): Mat44;
sQuatRightMultiply(inQ: Quat): Mat44;
sLookAt(inPos: Vec3, inTarget: Vec3, inUp: Vec3): Mat44;
sPerspective(
inFovY: number,
inAspect: number,
@@ -612,16 +686,32 @@ declare namespace Jolt {
GetAxisX(): Vec3;
GetAxisY(): Vec3;
GetAxisZ(): Vec3;
GetDiagonal3(): Vec3;
GetDiagonal4(): Vec4;
GetRotation(): Mat44;
GetRotationSafe(): Mat44;
GetQuaternion(): Quat;
GetTranslation(): Vec3;
Equals(inV: Mat44): boolean;
NotEquals(inV: Mat44): boolean;
IsClose(inM: Mat44, inMaxDistSq?: number): boolean;
Add(inM: Mat44): Mat44;
MulFloat(inV: number): Mat44;
MulMat44(inM: Mat44): Mat44;
MulVec3(inV: Vec3): Vec3;
MulVec4(inV: Vec4): Vec4;
AddMat44(inM: Mat44): Mat44;
SubMat44(inM: Mat44): Mat44;
Multiply3x3(inV: Vec3): Vec3;
Multiply3x3Transposed(inV: Vec3): Vec3;
Multiply3x3LeftTransposed(inM: Mat44): Mat44;
Multiply3x3RightTransposed(inM: Mat44): Mat44;
Transposed(): Mat44;
Transposed3x3(): Mat44;
Inversed(): Mat44;
InversedRotationTranslation(): Mat44;
Adjointed3x3(): Mat44;
SetInversed3x3(inM: Mat44): boolean;
GetDeterminant3x3(): number;
Inversed3x3(): Mat44;
GetDirectionPreservingMatrix(): Mat44;
@@ -629,12 +719,16 @@ declare namespace Jolt {
PostTranslated(inTranslation: Vec3): Mat44;
PreScaled(inScale: Vec3): Mat44;
PostScaled(inScale: Vec3): Mat44;
Decompose(outScale: Vec3): Mat44;
SetColumn3(inCol: number, inV: Vec3): void;
SetColumn4(inCol: number, inV: Vec4): void;
SetAxisX(inV: Vec3): void;
SetAxisY(inV: Vec3): void;
SetAxisZ(inV: Vec3): void;
SetDiagonal3(inV: Vec3): void;
SetDiagonal4(inV: Vec4): void;
SetTranslation(inV: Vec3): void;
SetColumn4(inCol: number, inV: Vec4): void;
GetColumn3(inCol: number): Vec3;
GetColumn4(inCol: number): Vec4;
}
class RMat44 {
@@ -645,10 +739,18 @@ declare namespace Jolt {
sTranslation(inTranslation: RVec3): RMat44;
sRotationTranslation(inRotation: Quat, inTranslation: RVec3): RMat44;
sInverseRotationTranslation(inRotation: Quat, inTranslation: RVec3): RMat44;
ToMat44(): Mat44;
Equals(inV: RMat44): boolean;
NotEquals(inV: RMat44): boolean;
MulVec3(inV: Vec3): RVec3;
MulRVec3(inV: RVec3): RVec3;
MulMat44(inM: Mat44): RMat44;
MulRMat44(inM: RMat44): RMat44;
GetAxisX(): Vec3;
GetAxisY(): Vec3;
GetAxisZ(): Vec3;
GetRotation(): Mat44;
SetRotation(inRotation: Mat44): void;
GetQuaternion(): Quat;
GetTranslation(): RVec3;
IsClose(inM: RMat44, inMaxDistSq?: number): boolean;
@@ -661,25 +763,59 @@ declare namespace Jolt {
PostTranslated(inTranslation: Vec3): RMat44;
PreScaled(inScale: Vec3): RMat44;
PostScaled(inScale: Vec3): RMat44;
GetDirectionPreservingMatrix(): Mat44;
SetColumn3(inCol: number, inV: Vec3): void;
GetColumn3(inCol: number): Vec3;
SetAxisX(inV: Vec3): void;
SetAxisY(inV: Vec3): void;
SetAxisZ(inV: Vec3): void;
SetTranslation(inV: RVec3): void;
SetColumn4(inCol: number, inV: Vec4): void;
GetColumn4(inCol: number): Vec4;
Decompose(outScale: Vec3): RMat44;
}
class AABox {
constructor();
constructor(inMin: Vec3, inMax: Vec3);
sBiggest(): AABox;
sFromTwoPoints(inP1: Vec3, inP2: Vec3): AABox;
sFromTriangle(inVertices: VertexList, inTriangle: IndexedTriangle): AABox;
Equals(inB: AABox): boolean;
NotEquals(inB: AABox): boolean;
SetEmpty(): void;
IsValid(): boolean;
EncapsulateVec3(inV: Vec3): void;
EncapsulateAABox(inBox: AABox): void;
EncapsulateTriangle(inTriangle: Triangle): void;
EncapsulateIndexedTriangle(
inVertices: VertexList,
inTriangle: IndexedTriangle
): void;
Intersect(inOther: AABox): AABox;
EnsureMinimalEdgeLength(inMinEdgeLength: number): void;
ExpandBy(inV: Vec3): void;
GetCenter(): Vec3;
GetExtent(): Vec3;
GetSize(): Vec3;
GetSurfaceArea(): number;
GetVolume(): number;
ContainsVec3(inOther: Vec3): boolean;
ContainsRVec3(inOther: RVec3): boolean;
OverlapsAABox(inOther: AABox): boolean;
OverlapsPlane(inOther: AABox): boolean;
TranslateVec3(inOther: Vec3): void;
TranslateRVec3(inOther: RVec3): void;
TransformedMat44(inOther: Mat44): AABox;
TransformedRMat44(inOther: RMat44): AABox;
Scaled(inScale: Vec3): AABox;
GetClosestPoint(inV: Vec3): Vec3;
GetSqDistanceTo(inV: Vec3): number;
get_mMin(): Vec3;
set_mMin(mMin: Vec3): void;
mMin: Vec3;
get_mMax(): Vec3;
set_mMax(mMax: Vec3): void;
mMax: Vec3;
Overlaps(inOther: AABox): boolean;
}
class OrientedBox {
constructor();
@@ -1019,6 +1155,7 @@ declare namespace Jolt {
mDensity: number;
}
class ConvexShape extends Shape {
SetMaterial(inMaterial: PhysicsMaterial): void;
GetDensity(): number;
SetDensity(inDensity: number): void;
}
@@ -1203,7 +1340,8 @@ declare namespace Jolt {
inPosition: Vec3,
inRotation: Quat,
inShape: Shape,
inUserData: number
inUserData: number,
inIndex?: number
): number;
RemoveShape(inIndex: number): void;
ModifyShape(inIndex: number, inPosition: Vec3, inRotation: Quat): void;
@@ -1405,6 +1543,7 @@ declare namespace Jolt {
inMaterial?: PhysicsMaterial,
inHalfExtent?: number
);
SetMaterial(inMaterial: PhysicsMaterial): void;
GetPlane(): Plane;
GetHalfExtent(): number;
}
@@ -1449,6 +1588,8 @@ declare namespace Jolt {
GetUserData(): number;
SetUserData(inUserData: number): void;
ResetWarmStart(): void;
SaveState(inStream: StateRecorder): void;
RestoreState(inStream: StateRecorder): void;
}
class TwoBodyConstraintSettings extends ConstraintSettings {
Create(inBody1: Body, inBody2: Body): Constraint;
@@ -1936,6 +2077,9 @@ declare namespace Jolt {
AddRef(): void;
Release(): void;
}
class PathConstraintPathHermite extends PathConstraintPath {
AddPoint(inPosition: Vec3, inTangent: Vec3, inNormal: Vec3): void;
}
class PathConstraintPathEm extends PathConstraintPath {}
class PathConstraintPathJS extends PathConstraintPathEm {
constructor();
@@ -2088,6 +2232,7 @@ declare namespace Jolt {
GetInverseInertiaDiagonal(): Vec3;
GetInertiaRotation(): Quat;
SetInverseInertia(inInvI: Vec3, inRotation: Quat): void;
ScaleToMass(inMass: number): void;
GetLocalSpaceInverseInertia(): Mat44;
GetInverseInertiaForRotation(inRotation: Mat44): Mat44;
MultiplyWorldSpaceInverseInertiaByVector(inRotation: Quat, inV: Vec3): Vec3;
@@ -2211,22 +2356,31 @@ declare namespace Jolt {
): Vec3;
GetUserData(): number;
SetUserData(inUserData: number): void;
SaveState(inStream: StateRecorder): void;
RestoreState(inStream: StateRecorder): void;
}
class BodyInterface_AddState {}
class BodyInterface {
CreateBody(inSettings: BodyCreationSettings): Body;
CreateSoftBody(inSettings: SoftBodyCreationSettings): Body;
CreateBodyWithID(inBodyID: BodyID, inSettings: BodyCreationSettings): void;
CreateBodyWithID(inBodyID: BodyID, inSettings: BodyCreationSettings): Body;
CreateSoftBodyWithID(
inBodyID: BodyID,
inSettings: SoftBodyCreationSettings
): void;
): Body;
CreateBodyWithoutID(inSettings: BodyCreationSettings): Body;
CreateSoftBodyWithoutID(inSettings: SoftBodyCreationSettings): Body;
DestroyBodyWithoutID(inBody: Body): void;
AssignBodyID(ioBody: Body): boolean;
AssignBodyID(ioBody: Body, inBodyID: BodyID): boolean;
UnassignBodyID(inBodyID: BodyID): Body;
UnassignBodyIDs(
inBodyIDs: BodyIDMemRef,
inNumber: number,
outBodies: BodyPtrMemRef
): void;
DestroyBody(inBodyID: BodyID): void;
DestroyBodies(inBodyIDs: BodyIDMemRef, inNumber: number): void;
AddBody(inBodyID: BodyID, inActivationMode: EActivation): void;
RemoveBody(inBodyID: BodyID): void;
IsAdded(inBodyID: BodyID): boolean;
@@ -2238,6 +2392,22 @@ declare namespace Jolt {
inSettings: SoftBodyCreationSettings,
inActivationMode: EActivation
): BodyID;
AddBodiesPrepare(
ioBodies: BodyIDMemRef,
inNumber: number
): BodyInterface_AddState;
AddBodiesFinalize(
ioBodies: BodyIDMemRef,
inNumber: number,
inAddState: BodyInterface_AddState,
inActivationMode: EActivation
): void;
AddBodiesAbort(
ioBodies: BodyIDMemRef,
inNumber: number,
inAddState: BodyInterface_AddState
): void;
RemoveBodies(ioBodies: BodyIDMemRef, inNumber: number): void;
CreateConstraint(
inSettings: TwoBodyConstraintSettings,
inBodyID1: BodyID,
@@ -2290,6 +2460,16 @@ declare namespace Jolt {
GetRotation(inBodyID: BodyID): Quat;
GetWorldTransform(inBodyID: BodyID): RMat44;
GetCenterOfMassTransform(inBodyID: BodyID): RMat44;
SetLinearAndAngularVelocity(
inBodyID: BodyID,
inLinearVelocity: Vec3,
inAngularVelocity: Vec3
): void;
GetLinearAndAngularVelocity(
inBodyID: BodyID,
outLinearVelocity: Vec3,
outAngularVelocity: Vec3
): void;
SetLinearVelocity(inBodyID: BodyID, inLinearVelocity: Vec3): void;
GetLinearVelocity(inBodyID: BodyID): Vec3;
AddLinearVelocity(inBodyID: BodyID, inLinearVelocity: Vec3): void;
@@ -2315,12 +2495,14 @@ declare namespace Jolt {
inDeltaTime: number
): void;
ActivateBody(inBodyID: BodyID): void;
ActivateBodies(inBodyIDs: BodyIDMemRef, inNumber: number): void;
ActivateBodiesInAABox(
inBox: AABox,
inBroadPhaseLayerFilter: BroadPhaseLayerFilter,
inObjectLayerFilter: ObjectLayerFilter
): void;
DeactivateBody(inBodyID: BodyID): void;
DeactivateBodies(inBodyIDs: BodyIDMemRef, inNumber: number): void;
IsActive(inBodyID: BodyID): boolean;
ResetSleepTimer(inBodyID: BodyID): void;
GetBodyType(inBodyID: BodyID): EBodyType;
@@ -3083,6 +3265,18 @@ declare namespace Jolt {
inBodyFilter: BodyFilter,
inShapeFilter: ShapeFilter
): void;
CollideShapeWithInternalEdgeRemoval(
inShape: Shape,
inShapeScale: Vec3,
inCenterOfMassTransform: RMat44,
inCollideShapeSettings: CollideShapeSettings,
inBaseOffset: RVec3,
ioCollector: CollideShapeCollector,
inBroadPhaseLayerFilter: BroadPhaseLayerFilter,
inObjectLayerFilter: ObjectLayerFilter,
inBodyFilter: BodyFilter,
inShapeFilter: ShapeFilter
): void;
CastShape(
inShapeCast: RShapeCast,
inShapeCastSettings: ShapeCastSettings,
@@ -3176,6 +3370,8 @@ declare namespace Jolt {
SetBodyActivationListener(inListener: BodyActivationListener): void;
GetBodyActivationListener(): BodyActivationListener;
WereBodiesInContact(inBodyID1: BodyID, inBodyID2: BodyID): boolean;
SetSimShapeFilter(inShapeFilter: SimShapeFilter): void;
GetSimShapeFilter(): SimShapeFilter;
}
class MassProperties {
constructor();
@@ -3845,6 +4041,7 @@ declare namespace Jolt {
}
class CharacterVsCharacterCollision {}
class CharacterVsCharacterCollisionSimple extends CharacterVsCharacterCollision {
constructor();
Add(inCharacter: CharacterVirtual): void;
Remove(inCharacter: CharacterVirtual): void;
}
@@ -3871,6 +4068,62 @@ declare namespace Jolt {
set_mWalkStairsStepDownExtra(mWalkStairsStepDownExtra: Vec3): void;
mWalkStairsStepDownExtra: Vec3;
}
class CharacterVirtualContact {
IsSameBody(inOther: CharacterVirtualContact): boolean;
get_mPosition(): RVec3;
set_mPosition(mPosition: RVec3): void;
mPosition: RVec3;
get_mLinearVelocity(): Vec3;
set_mLinearVelocity(mLinearVelocity: Vec3): void;
mLinearVelocity: Vec3;
get_mContactNormal(): Vec3;
set_mContactNormal(mContactNormal: Vec3): void;
mContactNormal: Vec3;
get_mSurfaceNormal(): Vec3;
set_mSurfaceNormal(mSurfaceNormal: Vec3): void;
mSurfaceNormal: Vec3;
get_mDistance(): number;
set_mDistance(mDistance: number): void;
mDistance: number;
get_mFraction(): number;
set_mFraction(mFraction: number): void;
mFraction: number;
get_mBodyB(): BodyID;
set_mBodyB(mBodyB: BodyID): void;
mBodyB: BodyID;
get_mCharacterB(): CharacterVirtual;
set_mCharacterB(mCharacterB: CharacterVirtual): void;
mCharacterB: CharacterVirtual;
get_mSubShapeIDB(): SubShapeID;
set_mSubShapeIDB(mSubShapeIDB: SubShapeID): void;
mSubShapeIDB: SubShapeID;
get_mMotionTypeB(): EMotionType;
set_mMotionTypeB(mMotionTypeB: EMotionType): void;
mMotionTypeB: EMotionType;
get_mIsSensorB(): boolean;
set_mIsSensorB(mIsSensorB: boolean): void;
mIsSensorB: boolean;
get_mUserData(): number;
set_mUserData(mUserData: number): void;
mUserData: number;
get_mMaterial(): PhysicsMaterial;
set_mMaterial(mMaterial: PhysicsMaterial): void;
mMaterial: PhysicsMaterial;
get_mHadCollision(): boolean;
set_mHadCollision(mHadCollision: boolean): void;
mHadCollision: boolean;
get_mWasDiscarded(): boolean;
set_mWasDiscarded(mWasDiscarded: boolean): void;
mWasDiscarded: boolean;
get_mCanPushCharacter(): boolean;
set_mCanPushCharacter(mCanPushCharacter: boolean): void;
mCanPushCharacter: boolean;
}
class ArrayCharacterVirtualContact {
empty(): boolean;
size(): number;
at(inIndex: number): CharacterVirtualContact;
}
class TempAllocator {}
class BroadPhaseLayerFilter {
constructor();
@@ -3940,6 +4193,20 @@ declare namespace Jolt {
inSubShapeIDOfShape2: number
): boolean;
}
class SimShapeFilter {
constructor();
}
class SimShapeFilterJS extends SimShapeFilter {
constructor();
ShouldCollide(
inBody1: number,
inShape1: number,
inSubShapeIDOfShape1: number,
inBody2: number,
inShape2: number,
inSubShapeIDOfShape2: number
): boolean;
}
class CharacterBase {
GetRefCount(): number;
AddRef(): void;
@@ -3957,6 +4224,8 @@ declare namespace Jolt {
GetGroundVelocity(): Vec3;
GetGroundMaterial(): PhysicsMaterial;
GetGroundBodyID(): BodyID;
SaveState(inStream: StateRecorder): void;
RestoreState(inStream: StateRecorder): void;
}
class CharacterVirtual extends CharacterBase {
constructor(
@@ -4056,6 +4325,9 @@ declare namespace Jolt {
): boolean;
SetInnerBodyShape(inShape: Shape): void;
GetTransformedShape(): TransformedShape;
HasCollidedWith(inBodyID: BodyID): boolean;
HasCollidedWithCharacter(inCharacter: CharacterVirtual): boolean;
GetActiveContacts(): ArrayCharacterVirtualContact;
}
class LinearCurve {
constructor();
@@ -4550,6 +4822,7 @@ declare namespace Jolt {
GetRefCount(): number;
AddRef(): void;
Release(): void;
GetConstraint(): VehicleConstraint;
}
class WheeledVehicleController extends VehicleController {
constructor(
@@ -4665,6 +4938,8 @@ declare namespace Jolt {
}
class SkeletalAnimation {
constructor();
SetIsLooping(inLooping: boolean): void;
IsLooping(): boolean;
GetDuration(): number;
ScaleJoints(inScale: number): void;
Sample(inTime: number, ioPose: SkeletonPose): void;
@@ -4845,6 +5120,9 @@ declare namespace Jolt {
get_mMaxContactConstraints(): number;
set_mMaxContactConstraints(mMaxContactConstraints: number): void;
mMaxContactConstraints: number;
get_mMaxWorkerThreads(): number;
set_mMaxWorkerThreads(mMaxWorkerThreads: number): void;
mMaxWorkerThreads: number;
get_mBroadPhaseLayerInterface(): BroadPhaseLayerInterface;
set_mBroadPhaseLayerInterface(
mBroadPhaseLayerInterface: BroadPhaseLayerInterface

File diff suppressed because it is too large Load Diff

View File

@@ -175,7 +175,8 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
.AddExtraInfo("center")
.AddExtraInfo("bottom")
.SetLabel(_("Vertical alignment"))
.SetGroup(_("Font"));
.SetGroup(_("Font"))
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
objectProperties["isOutlineEnabled"]
.SetValue(isOutlineEnabled ? "true" : "false")

View File

@@ -10,7 +10,6 @@
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"

View File

@@ -53,6 +53,10 @@ void MetadataDeclarationHelper::DeclareExtension(
extension.SetCategory(eventsFunctionsExtension.GetCategory());
DeclareExtensionDependencies(extension, eventsFunctionsExtension);
for (const auto &sourceFile : eventsFunctionsExtension.GetAllSourceFiles()) {
extension.AddSourceFile() = sourceFile;
}
}
/**
@@ -154,6 +158,9 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
.AddDefaultBehavior("TextContainerCapability::TextContainerBehavior");
}
if (eventsBasedObject.IsPrivate())
objectMetadata.SetPrivate();
// TODO EBO Use full type to identify object to avoid collision.
// Objects are identified by their name alone.
const gd::String &objectType = eventsBasedObject.GetName();

View File

@@ -22,7 +22,6 @@
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
@@ -130,15 +129,6 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
return false;
}
// Export source files
if (!helper.ExportExternalSourceFiles(
exportedProject, codeOutputDir, includesFiles)) {
gd::LogError(
_("Error during exporting! Unable to export source files:\n") +
lastError);
return false;
}
auto projectUsedResources =
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
@@ -173,10 +163,11 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
else if (options.target == "facebookInstantGames")
source = gdjsRoot + "/Runtime/FacebookInstantGames/index.html";
if (!helper.ExportPixiIndexFile(exportedProject,
if (!helper.ExportIndexFile(exportedProject,
source,
exportDir,
includesFiles,
usedExtensionsResult.GetUsedSourceFiles(),
/*nonRuntimeScriptsCacheBurst=*/0,
"")) {
gd::LogError(_("Error during export:\n") + lastError);
@@ -204,11 +195,8 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
exportedProject, options.exportPath, usedExtensions))
return false;
if (!helper.ExportBuildResourcesElectronFiles(
// It's important to use the original project here, as the exported
// project can have its resources modified.
options.project,
options.exportPath))
if (!helper.ExportBuildResourcesElectronFiles(exportedProject,
options.exportPath))
return false;
} else if (options.target == "facebookInstantGames") {
if (!exportProject(options.exportPath)) return false;

View File

@@ -38,7 +38,6 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
@@ -71,6 +70,11 @@ static void InsertUnique(std::vector<gd::String> &container, gd::String str) {
container.push_back(str);
}
static void InsertUniqueFirst(std::vector<gd::String> &container, gd::String str) {
if (std::find(container.begin(), container.end(), str) == container.end())
container.insert(container.begin(), str);
}
static gd::String CleanProjectName(gd::String projectName) {
gd::String partiallyCleanedProjectName = projectName;
@@ -190,15 +194,6 @@ bool ExporterHelper::ExportProjectForPixiPreview(
return false;
}
// Export source files
if (!ExportExternalSourceFiles(
immutableProject, codeOutputDir, includesFiles)) {
gd::LogError(
_("Error during exporting! Unable to export source files:\n") +
lastError);
return false;
}
previousTime = LogTimeSpent("Events code export", previousTime);
}
@@ -318,10 +313,11 @@ bool ExporterHelper::ExportProjectForPixiPreview(
ExportIncludesAndLibs(resourcesFiles, options.exportPath, true);
// Create the index file
if (!ExportPixiIndexFile(exportedProject,
if (!ExportIndexFile(exportedProject,
gdjsRoot + "/Runtime/index.html",
options.exportPath,
includesFiles,
usedExtensionsResult.GetUsedSourceFiles(),
options.nonRuntimeScriptsCacheBurst,
"gdjs.runtimeGameOptions"))
return false;
@@ -383,19 +379,40 @@ void ExporterHelper::SerializeUsedResources(
}
}
bool ExporterHelper::ExportPixiIndexFile(
bool ExporterHelper::ExportIndexFile(
const gd::Project &project,
gd::String source,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
const std::vector<gd::SourceFileMetadata> &sourceFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec) {
gd::String str = fs.ReadFile(source);
// Add a reference to all files to include, as weel as the source files
// required by the project.
std::vector<gd::String> finalIncludesFiles = includesFiles;
auto addSourceFileToIncludeFiles = [&](const gd::SourceFileMetadata& sourceFile) {
const auto& resourcesManager = project.GetResourcesManager();
if (!resourcesManager.HasResource(sourceFile.GetResourceName()))
return;
const gd::String& sourceFileFilename = resourcesManager.GetResource(sourceFile.GetResourceName()).GetFile();
if (sourceFile.GetIncludePosition() == "first") {
InsertUniqueFirst(finalIncludesFiles, sourceFileFilename);
} else if (sourceFile.GetIncludePosition() == "last") {
InsertUnique(finalIncludesFiles, sourceFileFilename);
}
};
for (const auto& sourceFile : sourceFiles) {
addSourceFileToIncludeFiles(sourceFile);
}
// Generate the file
if (!CompleteIndexFile(str,
exportDir,
includesFiles,
finalIncludesFiles,
nonRuntimeScriptsCacheBurst,
additionalSpec))
return false;
@@ -730,9 +747,7 @@ bool ExporterHelper::ExportBuildResourcesElectronFiles(
.GetResource(platformSpecificAssets.Get("desktop", "icon-512"))
.GetFile();
auto projectDirectory = gd::AbstractFileSystem::NormalizeSeparator(
fs.DirNameFrom(project.GetProjectFile()));
fs.MakeAbsolute(iconFilename, projectDirectory);
fs.MakeAbsolute(iconFilename, exportDir + "/app");
fs.MkDir(exportDir + "/buildResources");
if (fs.FileExists(iconFilename)) {
fs.CopyFile(iconFilename, exportDir + "/buildResources/icon.png");
@@ -964,29 +979,6 @@ bool ExporterHelper::ExportEventsCode(
return true;
}
bool ExporterHelper::ExportExternalSourceFiles(
const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles) {
const auto &allFiles = project.GetAllSourceFiles();
for (std::size_t i = 0; i < allFiles.size(); ++i) {
if (!allFiles[i]) continue;
if (allFiles[i]->GetLanguage() != "Javascript") continue;
gd::SourceFile &file = *allFiles[i];
gd::String filename = file.GetFileName();
fs.MakeAbsolute(filename, fs.DirNameFrom(project.GetProjectFile()));
gd::String outFilename = "ext-code" + gd::String::From(i) + ".js";
if (!fs.CopyFile(filename, outputDir + outFilename))
gd::LogWarning(_("Could not copy external file") + filename);
InsertUnique(includesFiles, outputDir + outFilename);
}
return true;
}
gd::String ExporterHelper::GetExportedIncludeFilename(
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst) {
auto addSearchParameterToUrl = [](const gd::String &url,

View File

@@ -20,11 +20,11 @@ class ExternalLayout;
class SerializerElement;
class AbstractFileSystem;
class ResourcesManager;
class SourceFileMetadata;
class WholeProjectDiagnosticReport;
class CaptureOptions;
class Screenshot;
} // namespace gd
class wxProgressDialog;
namespace gdjs {
@@ -450,21 +450,6 @@ class ExporterHelper {
bool ExportEffectIncludes(gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Copy the external source files used by the game into the export
* directory, and add them into files to be included.
*
* Files are named "ext-codeX.js", X being the index of the external source
* file in the project. \param project The project with resources to be
* exported. \param outputDir The directory where the events code must be
* generated. \param includesFiles A reference to a vector that will be filled
* with JS files to be exported along with the project. (including
* "ext-codeX.js" files).
*/
bool ExportExternalSourceFiles(const gd::Project &project,
gd::String outputDir,
std::vector<gd::String> &includesFiles);
/**
* \brief Generate the standard index file and save it to the export
* directory.
@@ -482,10 +467,11 @@ class ExporterHelper {
* \param additionalSpec JSON string that will be passed to the
* gdjs.RuntimeGame object.
*/
bool ExportPixiIndexFile(const gd::Project &project,
bool ExportIndexFile(const gd::Project &project,
gd::String source,
gd::String exportDir,
const std::vector<gd::String> &includesFiles,
const std::vector<gd::SourceFileMetadata> &sourceFiles,
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec = "");

View File

@@ -340,7 +340,6 @@ declare interface ProjectPropertiesData {
antialiasingMode: 'none' | 'MSAA';
antialisingEnabledOnMobile: boolean;
sizeOnStartupMode: string;
useExternalSourceFiles: boolean;
version: string;
name: string;
author: string;

View File

@@ -20,7 +20,6 @@ gdjs.createProjectData = (settings) => {
sizeOnStartupMode: '',
antialiasingMode: 'MSAA',
antialisingEnabledOnMobile: false,
useExternalSourceFiles: true,
version: '1.0.0',
name: 'Test game',
author: '',

View File

@@ -24,7 +24,6 @@ gdjs.getPixiRuntimeGameWithAssets = () => {
sizeOnStartupMode: 'adaptWidth',
antialiasingMode: 'MSAA',
antialisingEnabledOnMobile: false,
useExternalSourceFiles: true,
version: '1.0.0',
name: 'Test game with real assets',
author: '',

View File

@@ -39,6 +39,11 @@ interface VectorDependencyMetadata {
[Const, Ref] DependencyMetadata at(unsigned long index);
};
interface VectorSourceFileMetadata {
unsigned long size();
[Const, Ref] SourceFileMetadata at(unsigned long index);
};
interface VectorInt {
unsigned long size();
long at(unsigned long index);
@@ -833,6 +838,7 @@ interface ObjectConfiguration {
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
unsigned long GetAnimationsCount();
[Const, Ref] DOMString GetAnimationName(unsigned long index);
};
interface UniquePtrObjectConfiguration {
@@ -1322,6 +1328,11 @@ interface AtlasResource {
};
AtlasResource implements Resource;
interface JavaScriptResource {
void JavaScriptResource();
};
JavaScriptResource implements Resource;
interface InitialInstance {
void InitialInstance();
@@ -1759,6 +1770,15 @@ interface DependencyMetadata {
void CopyFrom([Const, Ref] DependencyMetadata dependencyMetadata);
};
interface SourceFileMetadata {
void SourceFileMetadata();
[Const, Ref] DOMString GetResourceName();
[Ref] SourceFileMetadata SetResourceName([Const] DOMString resourceName_);
[Const, Ref] DOMString GetIncludePosition();
[Ref] SourceFileMetadata SetIncludePosition([Const] DOMString includePosition_);
};
interface ParameterMetadata {
void ParameterMetadata();
@@ -1928,6 +1948,9 @@ interface ObjectMetadata {
boolean HasDefaultBehavior([Const] DOMString behaviorType);
[Ref] ObjectMetadata AddDefaultBehavior([Const] DOMString behaviorType);
boolean IsPrivate();
[Ref] ObjectMetadata SetPrivate();
[Ref] ObjectMetadata SetHidden();
boolean IsHidden();
@@ -2245,6 +2268,7 @@ interface PlatformExtension {
[Ref] MapStringPropertyDescriptor GetAllProperties();
[Ref] VectorDependencyMetadata GetAllDependencies();
[Ref] VectorSourceFileMetadata GetAllSourceFiles();
[Const, Value] DOMString STATIC_GetNamespaceSeparator();
[Const, Value] DOMString STATIC_GetBehaviorFullType(
@@ -3006,6 +3030,11 @@ interface AbstractEventsBasedEntity {
[Ref] EventsFunctionsContainer GetEventsFunctions();
[Ref] PropertiesContainer GetPropertyDescriptors();
[Const, Ref] DOMString GetName();
[Const, Ref] DOMString GetFullName();
[Const, Ref] DOMString GetDescription();
boolean IsPrivate();
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
};
@@ -3013,16 +3042,13 @@ interface AbstractEventsBasedEntity {
interface EventsBasedBehavior {
void EventsBasedBehavior();
[Ref] EventsBasedBehavior SetDescription([Const] DOMString description);
[Const, Ref] DOMString GetDescription();
[Ref] EventsBasedBehavior SetName([Const] DOMString name);
[Const, Ref] DOMString GetName();
[Ref] EventsBasedBehavior SetFullName([Const] DOMString fullName);
[Const, Ref] DOMString GetFullName();
[Ref] EventsBasedBehavior SetDescription([Const] DOMString description);
[Ref] EventsBasedBehavior SetPrivate(boolean isPrivate);
[Ref] EventsBasedBehavior SetObjectType([Const] DOMString fullName);
[Const, Ref] DOMString GetObjectType();
[Ref] EventsBasedBehavior SetPrivate(boolean isPrivate);
boolean IsPrivate();
[Ref] EventsBasedBehavior SetQuickCustomizationVisibility(QuickCustomization_Visibility visibility);
QuickCustomization_Visibility GetQuickCustomizationVisibility();
@@ -3057,15 +3083,13 @@ interface EventsBasedBehaviorsList {
interface EventsBasedObject {
void EventsBasedObject();
[Ref] EventsBasedObject SetDescription([Const] DOMString description);
[Const, Ref] DOMString GetDescription();
[Ref] EventsBasedObject SetName([Const] DOMString name);
[Const, Ref] DOMString GetName();
[Ref] EventsBasedObject SetFullName([Const] DOMString fullName);
[Const, Ref] DOMString GetFullName();
[Ref] EventsBasedObject SetDescription([Const] DOMString description);
[Ref] EventsBasedObject SetPrivate(boolean isPrivate);
[Ref] EventsBasedObject SetDefaultName([Const] DOMString defaultName);
[Const, Ref] DOMString GetDefaultName();
[Ref] EventsBasedObject MarkAsRenderedIn3D(boolean isRenderedIn3D);
boolean IsRenderedIn3D();
[Ref] EventsBasedObject MarkAsAnimatable(boolean isAnimatable);
@@ -3168,6 +3192,10 @@ interface EventsFunctionsExtension {
void RemoveDependencyAt(unsigned long index);
[Ref] VectorDependencyMetadata GetAllDependencies();
[Ref] SourceFileMetadata AddSourceFile();
void RemoveSourceFileAt(unsigned long index);
[Ref] VectorSourceFileMetadata GetAllSourceFiles();
[Ref] VariablesContainer GetGlobalVariables();
[Ref] VariablesContainer GetSceneVariables();

View File

@@ -424,6 +424,7 @@ typedef std::vector<Polygon2d> VectorPolygon2d;
typedef std::vector<gd::Vector2f> VectorVector2f;
typedef std::vector<EventsSearchResult> VectorEventsSearchResult;
typedef std::vector<gd::DependencyMetadata> VectorDependencyMetadata;
typedef std::vector<gd::SourceFileMetadata> VectorSourceFileMetadata;
typedef std::vector<gd::EventsFunction> VectorEventsFunction;
typedef gd::Object gdObject; // To avoid clashing javascript Object in glue.js
typedef ParticleEmitterObject::RendererType ParticleEmitterObject_RendererType;

View File

@@ -1368,6 +1368,24 @@ describe('libGD.js', function () {
});
});
describe('gd.JavaScriptResource', function () {
it('should have name and file', function () {
const resource = new gd.JavaScriptResource();
resource.setName('MyJavaScriptResource');
resource.setFile('MyJavaScriptFile');
expect(resource.getName()).toBe('MyJavaScriptResource');
expect(resource.getFile()).toBe('MyJavaScriptFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.JavaScriptResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
});
describe('gd.JsonResource', function () {
it('should have name and file', function () {
const resource = new gd.JsonResource();

View File

@@ -125,6 +125,11 @@ export class VectorDependencyMetadata extends EmscriptenObject {
at(index: number): DependencyMetadata;
}
export class VectorSourceFileMetadata extends EmscriptenObject {
size(): number;
at(index: number): SourceFileMetadata;
}
export class VectorInt extends EmscriptenObject {
size(): number;
at(index: number): number;
@@ -708,6 +713,7 @@ export class ObjectConfiguration extends EmscriptenObject {
serializeTo(element: SerializerElement): void;
unserializeFrom(project: Project, element: SerializerElement): void;
getAnimationsCount(): number;
getAnimationName(index: number): string;
}
export class UniquePtrObjectConfiguration extends EmscriptenObject {
@@ -1100,6 +1106,10 @@ export class AtlasResource extends Resource {
constructor();
}
export class JavaScriptResource extends Resource {
constructor();
}
export class InitialInstance extends EmscriptenObject {
constructor();
setObjectName(name: string): void;
@@ -1448,6 +1458,14 @@ export class DependencyMetadata extends EmscriptenObject {
copyFrom(dependencyMetadata: DependencyMetadata): void;
}
export class SourceFileMetadata extends EmscriptenObject {
constructor();
getResourceName(): string;
setResourceName(resourceName_: string): SourceFileMetadata;
getIncludePosition(): string;
setIncludePosition(includePosition_: string): SourceFileMetadata;
}
export class ParameterMetadata extends EmscriptenObject {
constructor();
getType(): string;
@@ -1543,6 +1561,8 @@ export class ObjectMetadata extends EmscriptenObject {
getDefaultBehaviors(): SetString;
hasDefaultBehavior(behaviorType: string): boolean;
addDefaultBehavior(behaviorType: string): ObjectMetadata;
isPrivate(): boolean;
setPrivate(): ObjectMetadata;
setHidden(): ObjectMetadata;
isHidden(): boolean;
markAsRenderedIn3D(): ObjectMetadata;
@@ -1704,6 +1724,7 @@ export class PlatformExtension extends EmscriptenObject {
getAllStrExpressionsForBehavior(autoType: string): MapStringExpressionMetadata;
getAllProperties(): MapStringPropertyDescriptor;
getAllDependencies(): VectorDependencyMetadata;
getAllSourceFiles(): VectorSourceFileMetadata;
static getNamespaceSeparator(): string;
static getBehaviorFullType(extensionName: string, behaviorName: string): string;
static getObjectFullType(extensionName: string, objectName: string): string;
@@ -2172,22 +2193,22 @@ export class EventsFunctionsContainer extends EmscriptenObject {
export class AbstractEventsBasedEntity extends EmscriptenObject {
getEventsFunctions(): EventsFunctionsContainer;
getPropertyDescriptors(): PropertiesContainer;
getName(): string;
getFullName(): string;
getDescription(): string;
isPrivate(): boolean;
serializeTo(element: SerializerElement): void;
unserializeFrom(project: Project, element: SerializerElement): void;
}
export class EventsBasedBehavior extends AbstractEventsBasedEntity {
constructor();
setDescription(description: string): EventsBasedBehavior;
getDescription(): string;
setName(name: string): EventsBasedBehavior;
getName(): string;
setFullName(fullName: string): EventsBasedBehavior;
getFullName(): string;
setDescription(description: string): EventsBasedBehavior;
setPrivate(isPrivate: boolean): EventsBasedBehavior;
setObjectType(fullName: string): EventsBasedBehavior;
getObjectType(): string;
setPrivate(isPrivate: boolean): EventsBasedBehavior;
isPrivate(): boolean;
setQuickCustomizationVisibility(visibility: QuickCustomization_Visibility): EventsBasedBehavior;
getQuickCustomizationVisibility(): QuickCustomization_Visibility;
getSharedPropertyDescriptors(): PropertiesContainer;
@@ -2217,12 +2238,10 @@ export class EventsBasedBehaviorsList extends EmscriptenObject {
export class EventsBasedObject extends AbstractEventsBasedEntity {
constructor();
setDescription(description: string): EventsBasedObject;
getDescription(): string;
setName(name: string): EventsBasedObject;
getName(): string;
setFullName(fullName: string): EventsBasedObject;
getFullName(): string;
setDescription(description: string): EventsBasedObject;
setPrivate(isPrivate: boolean): EventsBasedObject;
setDefaultName(defaultName: string): EventsBasedObject;
getDefaultName(): string;
markAsRenderedIn3D(isRenderedIn3D: boolean): EventsBasedObject;
@@ -2317,6 +2336,9 @@ export class EventsFunctionsExtension extends EmscriptenObject {
addDependency(): DependencyMetadata;
removeDependencyAt(index: number): void;
getAllDependencies(): VectorDependencyMetadata;
addSourceFile(): SourceFileMetadata;
removeSourceFileAt(index: number): void;
getAllSourceFiles(): VectorSourceFileMetadata;
getGlobalVariables(): VariablesContainer;
getSceneVariables(): VariablesContainer;
getEventsBasedBehaviors(): EventsBasedBehaviorsList;

View File

@@ -2,6 +2,10 @@
declare class gdAbstractEventsBasedEntity {
getEventsFunctions(): gdEventsFunctionsContainer;
getPropertyDescriptors(): gdPropertiesContainer;
getName(): string;
getFullName(): string;
getDescription(): string;
isPrivate(): boolean;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
delete(): void;

View File

@@ -1,16 +1,12 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsBasedBehavior extends gdAbstractEventsBasedEntity {
constructor(): void;
setDescription(description: string): gdEventsBasedBehavior;
getDescription(): string;
setName(name: string): gdEventsBasedBehavior;
getName(): string;
setFullName(fullName: string): gdEventsBasedBehavior;
getFullName(): string;
setDescription(description: string): gdEventsBasedBehavior;
setPrivate(isPrivate: boolean): gdEventsBasedBehavior;
setObjectType(fullName: string): gdEventsBasedBehavior;
getObjectType(): string;
setPrivate(isPrivate: boolean): gdEventsBasedBehavior;
isPrivate(): boolean;
setQuickCustomizationVisibility(visibility: QuickCustomization_Visibility): gdEventsBasedBehavior;
getQuickCustomizationVisibility(): QuickCustomization_Visibility;
getSharedPropertyDescriptors(): gdPropertiesContainer;

View File

@@ -1,12 +1,10 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsBasedObject extends gdAbstractEventsBasedEntity {
constructor(): void;
setDescription(description: string): gdEventsBasedObject;
getDescription(): string;
setName(name: string): gdEventsBasedObject;
getName(): string;
setFullName(fullName: string): gdEventsBasedObject;
getFullName(): string;
setDescription(description: string): gdEventsBasedObject;
setPrivate(isPrivate: boolean): gdEventsBasedObject;
setDefaultName(defaultName: string): gdEventsBasedObject;
getDefaultName(): string;
markAsRenderedIn3D(isRenderedIn3D: boolean): gdEventsBasedObject;

View File

@@ -31,6 +31,9 @@ declare class gdEventsFunctionsExtension extends gdEventsFunctionsContainer {
addDependency(): gdDependencyMetadata;
removeDependencyAt(index: number): void;
getAllDependencies(): gdVectorDependencyMetadata;
addSourceFile(): gdSourceFileMetadata;
removeSourceFileAt(index: number): void;
getAllSourceFiles(): gdVectorSourceFileMetadata;
getGlobalVariables(): gdVariablesContainer;
getSceneVariables(): gdVariablesContainer;
getEventsBasedBehaviors(): gdEventsBasedBehaviorsList;

View File

@@ -0,0 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdJavaScriptResource extends gdResource {
constructor(): void;
delete(): void;
ptr: number;
};

View File

@@ -12,6 +12,7 @@ declare class gdObjectConfiguration {
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
getAnimationsCount(): number;
getAnimationName(index: number): string;
delete(): void;
ptr: number;
};

View File

@@ -24,6 +24,8 @@ declare class gdObjectMetadata {
getDefaultBehaviors(): gdSetString;
hasDefaultBehavior(behaviorType: string): boolean;
addDefaultBehavior(behaviorType: string): gdObjectMetadata;
isPrivate(): boolean;
setPrivate(): gdObjectMetadata;
setHidden(): gdObjectMetadata;
isHidden(): boolean;
markAsRenderedIn3D(): gdObjectMetadata;

View File

@@ -55,6 +55,7 @@ declare class gdPlatformExtension {
getAllStrExpressionsForBehavior(autoType: string): gdMapStringExpressionMetadata;
getAllProperties(): gdMapStringPropertyDescriptor;
getAllDependencies(): gdVectorDependencyMetadata;
getAllSourceFiles(): gdVectorSourceFileMetadata;
static getNamespaceSeparator(): string;
static getBehaviorFullType(extensionName: string, behaviorName: string): string;
static getObjectFullType(extensionName: string, objectName: string): string;

View File

@@ -0,0 +1,10 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdSourceFileMetadata {
constructor(): void;
getResourceName(): string;
setResourceName(resourceName_: string): gdSourceFileMetadata;
getIncludePosition(): string;
setIncludePosition(includePosition_: string): gdSourceFileMetadata;
delete(): void;
ptr: number;
};

View File

@@ -0,0 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdVectorSourceFileMetadata {
size(): number;
at(index: number): gdSourceFileMetadata;
delete(): void;
ptr: number;
};

View File

@@ -43,6 +43,7 @@ declare class libGDevelop {
VectorString: Class<gdVectorString>;
VectorPlatformExtension: Class<gdVectorPlatformExtension>;
VectorDependencyMetadata: Class<gdVectorDependencyMetadata>;
VectorSourceFileMetadata: Class<gdVectorSourceFileMetadata>;
VectorInt: Class<gdVectorInt>;
VectorVariable: Class<gdVectorVariable>;
VectorObjectFolderOrObject: Class<gdVectorObjectFolderOrObject>;
@@ -123,6 +124,7 @@ declare class libGDevelop {
TilesetResource: Class<gdTilesetResource>;
Model3DResource: Class<gdModel3DResource>;
AtlasResource: Class<gdAtlasResource>;
JavaScriptResource: Class<gdJavaScriptResource>;
InitialInstance: Class<gdInitialInstance>;
InitialInstancesContainer: Class<gdInitialInstancesContainer>;
HighestZOrderFinder: Class<gdHighestZOrderFinder>;
@@ -146,6 +148,7 @@ declare class libGDevelop {
ExpressionMetadata: Class<gdExpressionMetadata>;
MultipleInstructionMetadata: Class<gdMultipleInstructionMetadata>;
DependencyMetadata: Class<gdDependencyMetadata>;
SourceFileMetadata: Class<gdSourceFileMetadata>;
ParameterMetadata: Class<gdParameterMetadata>;
ValueTypeMetadata: Class<gdValueTypeMetadata>;
ParameterMetadataContainer: Class<gdParameterMetadataContainer>;

View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14" height="14" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m7 0-6 3.5v7l6 3.5 6-3.5v-7l-6-3.5z" fill="#2e388a"/><g transform="translate(-.0405 .026)" fill="none" stroke="#53a6da" stroke-width=".7"><path d="m8.294 9.112c-1.291 0.7556-2.601 1.165-3.664 1.229-1.085 0.0656-1.803-0.2277-2.095-0.7272-0.2924-0.4996-0.1966-1.269 0.3921-2.183 0.5768-0.8956 1.575-1.837 2.866-2.593 1.291-0.7556 2.601-1.165 3.664-1.229 1.085-0.06552 1.803 0.2277 2.095 0.7272s0.1964 1.269-0.3862 2.174-1.581 1.846-2.872 2.602z"/><path d="m5.787 9.112c1.291 0.7556 2.601 1.165 3.664 1.229 1.085 0.0656 1.803-0.2277 2.095-0.7272 0.2924-0.4996 0.1966-1.269-0.3921-2.183-0.5768-0.8956-1.575-1.837-2.866-2.593-1.291-0.7556-2.601-1.165-3.664-1.229-1.085-0.06552-1.803 0.2277-2.095 0.7272s-0.1964 1.269 0.3862 2.174 1.581 1.846 2.872 2.602z"/><path d="m7.038 1.75c-0.5788 0-1.194 0.4711-1.686 1.441-0.4817 0.9502-0.7898 2.287-0.7898 3.783s0.3081 2.833 0.7898 3.783c0.4916 0.9697 1.107 1.441 1.686 1.441s1.194-0.4712 1.686-1.441c0.4817-0.9502 0.7898-2.287 0.7898-3.783s-0.3081-2.833-0.7898-3.783c-0.4916-0.9698-1.107-1.441-1.686-1.441z"/></g><path d="m7 8c0.5524 0 1-0.4478 1-1 0-0.5524-0.4478-1-1-1s-1 0.4478-1 1c0 0.5524 0.4478 1 1 1z" fill="#fff" stroke-width=".6439"/></svg>
<svg width="14" height="14" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m7 0-6 3.5v7l6 3.5 6-3.5v-7z" fill="#55b1e0" style="paint-order:normal"/><path transform="translate(-5.299 -.3008)" d="m12.3 0.5898 5.75 3.355v6.711l-5.75 3.355-5.75-3.355v-6.711z" fill="#2e388a" style="paint-order:normal"/><g transform="translate(-.0405 .026)" fill="none" stroke="#55b1e0" stroke-width=".7"><path d="m8.294 9.112c-1.291 0.7556-2.601 1.165-3.664 1.229-1.085 0.0656-1.803-0.2277-2.095-0.7272-0.2924-0.4996-0.1966-1.269 0.3921-2.183 0.5768-0.8956 1.575-1.837 2.866-2.593 1.291-0.7556 2.601-1.165 3.664-1.229 1.085-0.06552 1.803 0.2277 2.095 0.7272s0.1964 1.269-0.3862 2.174-1.581 1.846-2.872 2.602z"/><path d="m5.787 9.112c1.291 0.7556 2.601 1.165 3.664 1.229 1.085 0.0656 1.803-0.2277 2.095-0.7272 0.2924-0.4996 0.1966-1.269-0.3921-2.183-0.5768-0.8956-1.575-1.837-2.866-2.593-1.291-0.7556-2.601-1.165-3.664-1.229-1.085-0.06552-1.803 0.2277-2.095 0.7272s-0.1964 1.269 0.3862 2.174 1.581 1.846 2.872 2.602z"/><path d="m7.038 1.75c-0.5788 0-1.194 0.4711-1.686 1.441-0.4817 0.9502-0.7898 2.287-0.7898 3.783s0.3081 2.833 0.7898 3.783c0.4916 0.9697 1.107 1.441 1.686 1.441s1.194-0.4712 1.686-1.441c0.4817-0.9502 0.7898-2.287 0.7898-3.783s-0.3081-2.833-0.7898-3.783c-0.4916-0.9698-1.107-1.441-1.686-1.441z"/></g><path d="m7 8c0.5524 0 1-0.4478 1-1 0-0.5524-0.4478-1-1-1s-1 0.4478-1 1c0 0.5524 0.4478 1 1 1z" fill="#fff" stroke-width=".6439"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14" height="14" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m7 0-6 3.5v7l6 3.5 6-3.5v-7l-6-3.5z" fill="#2e388a"/><g fill="#27aae1" stroke-width=".7"><path d="m9.1 3.801v1.398h-0.6992-2.801-2.1v1.4 1.4h1.4v-1.4h0.6992v2.801h-2.1v1.4h2.1 1.4v-1.4h1.4 0.6992v1.4h1.4v-1.4-1.4h-2.1v-1.4h2.1v-1.4-1.398h-1.4z"/><path d="m7 5.2c0.7732 0 1.4-0.6268 1.4-1.4s-0.6268-1.4-1.4-1.4-1.4 0.6268-1.4 1.4 0.6268 1.4 1.4 1.4z"/></g></svg>
<svg width="14" height="14" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g><path d="m7 0-6 3.5v7l6 3.5 6-3.5v-7l-6-3.5z" fill="#55b1e0" style="paint-order:normal"/><path transform="translate(-5.299 -.3008)" d="m12.3 0.5898 5.75 3.355v6.711l-5.75 3.355-5.75-3.355v-6.711z" fill="#2e388a" style="paint-order:normal"/><g fill="#55b1e0" stroke-width=".7"><path d="m9.1 3.801v1.398h-5.6v2.8h1.4v-1.4h0.6992v2.801h-2.1v1.4h3.5v-1.4h2.099v1.4h1.4v-2.8h-2.1v-1.4h2.1v-2.798h-1.4z"/><path d="m7 5.2c0.7732 0 1.4-0.6268 1.4-1.4s-0.6268-1.4-1.4-1.4-1.4 0.6268-1.4 1.4 0.6268 1.4 1.4 1.4z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -7,6 +7,7 @@ const shell = require('shelljs');
const path = require('path');
const copy = require('recursive-copy');
const args = require('minimist')(process.argv.slice(2));
const fs = require('fs');
const gdevelopRootPath = path.join(__dirname, '..', '..', '..');
const destinationPaths = [
@@ -53,6 +54,8 @@ if (!args['skip-sources']) {
const startTime = Date.now();
const typesDestinationPath = path.join(destinationPath, 'Runtime-sources', 'types');
const pixiDestinationPath = path.join(typesDestinationPath, 'pixi');
// TODO: Investigate the use of a smart & faster sync
// that only copy files with changed content.
return Promise.all([
@@ -66,8 +69,71 @@ if (!args['skip-sources']) {
path.join(destinationPath, 'Runtime-sources', 'Extensions'),
{ ...copyOptions, filter: ['**/*.js', '**/*.ts'] }
),
copy(
path.join(gdevelopRootPath, 'GDJS', 'node_modules', '@types', 'three'),
path.join(typesDestinationPath, 'three'),
{ ...copyOptions, filter: ['*.d.ts'] }
),
copy(
path.join(gdevelopRootPath, 'GDJS', 'node_modules', '@types', 'three', 'src'),
path.join(typesDestinationPath, 'three', 'src'),
{ ...copyOptions, filter: ['**/*.d.ts'] }
),
copy(
path.join(gdevelopRootPath, 'GDJS', 'node_modules', '@pixi'),
pixiDestinationPath,
{ ...copyOptions, filter: ['**/*.d.ts'] }
),
])
.then(function([unbundledResults, unbundledExtensionsResults]) {
// Replace import of packages by import of relative folders.
shell.sed(
'-i',
'from \'@pixi((/\\w+)+)',
'from \'../..$1/lib',
pixiDestinationPath + '/*/lib/*.d.ts'
);
fs.writeFileSync(path.join(pixiDestinationPath, 'index.d.ts'), `
import './mixin-cache-as-bitmap/lib';
import './mixin-get-child-by-name/lib';
import './mixin-get-global-position/lib';
export * from './accessibility/lib';
export * from './app/lib';
export * from './assets/lib';
export * from './color/lib';
export * from './compressed-textures/lib';
export * from './constant/lib';
export * from './core';
export * from './display/lib';
export * from './events/lib';
export * from './extensions/lib';
export * from './extract/lib';
export * from './filter-alpha/lib';
export * from './filter-blur/lib';
export * from './filter-color-matrix/lib';
export * from './filter-displacement/lib';
export * from './filter-fxaa/lib';
export * from './filter-noise/lib';
export * from './graphics/lib';
export * from './math/lib';
export * from './mesh/lib';
export * from './mesh-extras/lib';
export * from './particle-container/lib';
export * from './prepare/lib';
export * from './runner/lib';
export * from './settings/lib';
export * from './sprite/lib';
export * from './sprite-animated/lib';
export * from './sprite-tiling/lib';
export * from './spritesheet/lib';
export * from './text/lib';
export * from './text-bitmap/lib';
export * from './text-html/lib';
export * from './ticker/lib';
export * from './utils/lib';
export as namespace PIXI;
`);
const totalFilesCount =
unbundledResults.length + unbundledExtensionsResults.length;
const duration = Date.now() - startTime;

View File

@@ -403,6 +403,9 @@ const generateExtensionReference = extension => {
/** @type {Array<ObjectReference>} */
let objectReferences = objectTypes.map(objectType => {
const objectMetadata = extension.getObjectMetadata(objectType);
if (objectMetadata.isPrivate()) {
return null;
}
const actionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActionsForObject(objectType),
@@ -433,7 +436,7 @@ const generateExtensionReference = extension => {
conditionsReferenceTexts,
expressionsReferenceTexts,
};
});
}).filter(Boolean);
// Behavior expressions
/** @type {Array<BehaviorReference>} */

View File

@@ -201,11 +201,11 @@ export const AssetDetails = React.forwardRef<Props, AssetDetailsInterface>(
React.useEffect(
() => {
if (!asset) {
if (!asset || asset.id !== assetShortHeader.id) {
loadAsset();
}
},
[asset, loadAsset]
[asset, loadAsset, assetShortHeader]
);
const loadAuthorPublicProfiles = React.useCallback(

View File

@@ -29,9 +29,9 @@ import {
AssetSwappingAssetStoreSearchFilter,
} from './AssetStoreSearchFilter';
import {
type NavigationState,
type AssetStorePageState,
assetStoreHomePageState,
AssetStoreNavigatorContext,
} from './AssetStoreNavigator';
import { type ChosenCategory } from '../UI/Search/FiltersChooser';
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
@@ -41,8 +41,6 @@ import {
} from './AssetStoreUtils';
import useAlertDialog from '../UI/Alert/useAlertDialog';
const defaultSearchText = '';
export type AssetFiltersState = {|
animatedFilter: AnimatedAssetStoreSearchFilter,
setAnimatedFilter: AnimatedAssetStoreSearchFilter => void,
@@ -78,12 +76,9 @@ type AssetStoreState = {|
privateAssetPackListingDatasSearchResults: ?Array<PrivateAssetPackListingData>,
fetchAssetsAndFilters: () => void,
error: ?Error,
searchText: string,
setSearchText: string => void,
assetFiltersState: AssetFiltersState,
assetPackFiltersState: AssetPackFiltersState,
clearAllFilters: () => void,
shopNavigationState: NavigationState,
currentPage: AssetStorePageState,
useSearchItem: (
searchText: string,
@@ -108,8 +103,6 @@ export const initialAssetStoreState: AssetStoreState = {
privateAssetPackListingDatasSearchResults: null,
fetchAssetsAndFilters: () => {},
error: null,
searchText: '',
setSearchText: () => {},
assetFiltersState: {
animatedFilter: new AnimatedAssetStoreSearchFilter(),
setAnimatedFilter: filter => {},
@@ -131,30 +124,6 @@ export const initialAssetStoreState: AssetStoreState = {
setTypeFilter: filter => {},
},
clearAllFilters: () => {},
shopNavigationState: {
getCurrentPage: () => assetStoreHomePageState,
isRootPage: true,
backToPreviousPage: () => assetStoreHomePageState,
openHome: () => assetStoreHomePageState,
openAssetSwapping: () => assetStoreHomePageState,
clearHistory: () => {},
clearPreviousPageFromHistory: () => {},
openSearchResultPage: () => {},
openTagPage: tag => {},
openShopCategoryPage: category => {},
openPackPage: ({ assetPack, previousSearchText }) => {},
openAssetDetailPage: ({ assetShortHeader, previousSearchText }) => {},
openPrivateAssetPackInformationPage: ({
privateAssetPackListingData,
previousSearchText,
}) => {},
openPrivateGameTemplateInformationPage: ({
privateGameTemplateListingData,
previousSearchText,
}) => {},
navigateInsideFolder: folder => {},
goBackToFolderIndex: index => {},
},
currentPage: assetStoreHomePageState,
useSearchItem: (searchText, chosenCategory, chosenFilters, searchFilters) =>
null,
@@ -167,7 +136,6 @@ export const AssetStoreContext = React.createContext<AssetStoreState>(
);
type AssetStoreStateProviderProps = {|
shopNavigationState: NavigationState,
children: React.Node,
|};
@@ -189,9 +157,11 @@ const getPrivateAssetPackListingDataSearchTerms = (
) => privateAssetPack.name + '\n' + privateAssetPack.description;
export const AssetStoreStateProvider = ({
shopNavigationState,
children,
}: AssetStoreStateProviderProps) => {
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const { searchText } = shopNavigationState;
const [assetShortHeadersById, setAssetShortHeadersById] = React.useState<?{
[string]: AssetShortHeader,
}>(null);
@@ -229,8 +199,6 @@ export const AssetStoreStateProvider = ({
const initialPackOpened = React.useRef<boolean>(false);
const { showAlert } = useAlertDialog();
const [searchText, setSearchText] = React.useState(defaultSearchText);
const [
animatedFilter,
setAnimatedFilter,
@@ -403,7 +371,8 @@ export const AssetStoreStateProvider = ({
if (assetPack) {
shopNavigationState.openPackPage({
assetPack,
previousSearchText: searchText,
storeSearchText: true,
clearSearchText: false,
});
initialPackOpened.current = false; // Allow to open the pack again if the effect run again.
setInitialPackUserFriendlySlug(null);
@@ -421,7 +390,8 @@ export const AssetStoreStateProvider = ({
if (privateAssetPackListingData) {
shopNavigationState.openPrivateAssetPackInformationPage({
privateAssetPackListingData,
previousSearchText: searchText,
storeSearchText: true,
clearSearchText: false,
});
initialPackOpened.current = false; // Allow to open the pack again if the effect run again.
setInitialPackUserFriendlySlug(null);
@@ -441,7 +411,6 @@ export const AssetStoreStateProvider = ({
shopNavigationState,
showAlert,
initialPackUserFriendlySlug,
searchText,
]
);
@@ -606,10 +575,7 @@ export const AssetStoreStateProvider = ({
environment,
setEnvironment,
error,
shopNavigationState,
currentPage,
searchText,
setSearchText,
assetFiltersState,
assetPackFiltersState,
clearAllFilters,
@@ -643,9 +609,7 @@ export const AssetStoreStateProvider = ({
licenses,
environment,
error,
shopNavigationState,
currentPage,
searchText,
assetFiltersState,
assetPackFiltersState,
clearAllFilters,

View File

@@ -22,6 +22,7 @@ import { type RGBColor } from '../Utils/ColorTransformer';
import { HexColorField } from './HexColorField';
import Slider from '../UI/Slider';
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
import { AssetStoreNavigatorContext } from './AssetStoreNavigator';
type Choice = {|
label: React.Node,
@@ -229,8 +230,9 @@ export const AssetStoreFilterPanel = ({
assetFiltersState,
assetPackFiltersState,
clearAllFilters,
shopNavigationState,
} = React.useContext(AssetStoreContext);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const { receivedAssetPacks } = React.useContext(AuthenticatedUserContext);
const onChoiceChange = React.useCallback(
() => {

View File

@@ -26,6 +26,8 @@ export type AssetStorePageState = {|
|};
export type NavigationState = {|
searchText: string,
setSearchText: string => void,
getCurrentPage: () => AssetStorePageState,
isRootPage: boolean,
backToPreviousPage: () => AssetStorePageState,
@@ -38,19 +40,23 @@ export type NavigationState = {|
openShopCategoryPage: string => void,
openPackPage: ({|
assetPack: PublicAssetPack | PrivateAssetPack,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => void,
openPrivateAssetPackInformationPage: ({|
privateAssetPackListingData: PrivateAssetPackListingData,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => void,
openPrivateGameTemplateInformationPage: ({|
privateGameTemplateListingData: PrivateGameTemplateListingData,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => void,
openAssetDetailPage: ({|
assetShortHeader: AssetShortHeader,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => void,
navigateInsideFolder: string => void,
goBackToFolderIndex: number => void,
@@ -111,14 +117,44 @@ type AssetStorePageHistory = {|
previousPages: Array<AssetStorePageState>,
|};
export const useShopNavigation = (): NavigationState => {
export const AssetStoreNavigatorContext = React.createContext<NavigationState>({
searchText: '',
setSearchText: () => {},
getCurrentPage: () => assetStoreHomePageState,
isRootPage: true,
backToPreviousPage: () => assetStoreHomePageState,
openHome: () => assetStoreHomePageState,
openAssetSwapping: () => assetStoreHomePageState,
clearHistory: () => {},
clearPreviousPageFromHistory: () => {},
openSearchResultPage: () => {},
openTagPage: string => {},
openShopCategoryPage: string => {},
openPackPage: () => {},
openPrivateAssetPackInformationPage: () => {},
openPrivateGameTemplateInformationPage: () => {},
openAssetDetailPage: () => {},
navigateInsideFolder: string => {},
goBackToFolderIndex: number => {},
});
type AssetStoreNavigatorStateProviderProps = {|
children: React.Node,
|};
export const AssetStoreNavigatorStateProvider = (
props: AssetStoreNavigatorStateProviderProps
) => {
const [searchText, setSearchText] = React.useState<string>('');
const [history, setHistory] = React.useState<AssetStorePageHistory>({
previousPages: [assetStoreHomePageState],
});
const previousPages = history.previousPages;
return React.useMemo(
const state = React.useMemo(
() => ({
searchText,
setSearchText,
getCurrentPage: () => previousPages[previousPages.length - 1],
isRootPage: previousPages.length <= 1,
backToPreviousPage: () => {
@@ -244,10 +280,12 @@ export const useShopNavigation = (): NavigationState => {
},
openPackPage: ({
assetPack,
previousSearchText,
storeSearchText,
clearSearchText,
}: {|
assetPack: PublicAssetPack | PrivateAssetPack,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => {
setHistory(previousHistory => {
const currentPage =
@@ -256,7 +294,7 @@ export const useShopNavigation = (): NavigationState => {
];
const currentPageWithSearchText = {
...currentPage,
searchText: previousSearchText,
searchText: storeSearchText ? searchText : '',
};
const previousPagesWithoutCurrentPage = previousHistory.previousPages.slice(
0,
@@ -297,13 +335,16 @@ export const useShopNavigation = (): NavigationState => {
],
};
});
if (clearSearchText) setSearchText('');
},
openPrivateAssetPackInformationPage: ({
privateAssetPackListingData,
previousSearchText,
storeSearchText,
clearSearchText,
}: {|
privateAssetPackListingData: PrivateAssetPackListingData,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => {
setHistory(previousHistory => {
const currentPage =
@@ -312,7 +353,7 @@ export const useShopNavigation = (): NavigationState => {
];
const currentPageWithSearchText = {
...currentPage,
searchText: previousSearchText,
searchText: storeSearchText ? searchText : '',
};
const previousPagesWithoutCurrentPage = previousHistory.previousPages.slice(
0,
@@ -339,13 +380,16 @@ export const useShopNavigation = (): NavigationState => {
],
};
});
if (clearSearchText) setSearchText('');
},
openAssetDetailPage: ({
assetShortHeader,
previousSearchText,
storeSearchText,
clearSearchText,
}: {|
assetShortHeader: AssetShortHeader,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => {
setHistory(previousHistory => {
const currentPage =
@@ -354,7 +398,7 @@ export const useShopNavigation = (): NavigationState => {
];
const currentPageWithSearchText = {
...currentPage,
searchText: previousSearchText,
searchText: storeSearchText ? searchText : '',
};
const previousPagesWithoutCurrentPage = previousHistory.previousPages.slice(
0,
@@ -381,13 +425,16 @@ export const useShopNavigation = (): NavigationState => {
],
};
});
if (clearSearchText) setSearchText('');
},
openPrivateGameTemplateInformationPage: ({
privateGameTemplateListingData,
previousSearchText,
storeSearchText,
clearSearchText,
}: {|
privateGameTemplateListingData: PrivateGameTemplateListingData,
previousSearchText: string,
storeSearchText: boolean,
clearSearchText: boolean,
|}) => {
setHistory(previousHistory => {
const currentPage =
@@ -396,7 +443,7 @@ export const useShopNavigation = (): NavigationState => {
];
const currentPageWithSearchText = {
...currentPage,
searchText: previousSearchText,
searchText: storeSearchText ? searchText : '',
};
const previousPagesWithoutCurrentPage = previousHistory.previousPages.slice(
0,
@@ -423,6 +470,7 @@ export const useShopNavigation = (): NavigationState => {
],
};
});
if (clearSearchText) setSearchText('');
},
navigateInsideFolder: (folderTag: string) => {
setHistory(previousHistory => {
@@ -476,6 +524,12 @@ export const useShopNavigation = (): NavigationState => {
});
},
}),
[previousPages]
[searchText, previousPages]
);
return (
<AssetStoreNavigatorContext.Provider value={state}>
{props.children}
</AssetStoreNavigatorContext.Provider>
);
};

View File

@@ -6,7 +6,6 @@ import Dialog from '../UI/Dialog';
import FlatButton from '../UI/FlatButton';
import { AssetStore, type AssetStoreInterface } from '.';
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
import { AssetStoreContext } from './AssetStoreContext';
import ErrorBoundary from '../UI/ErrorBoundary';
import LoaderModal from '../UI/LoaderModal';
import { useInstallAsset } from './NewObjectDialog';
@@ -14,6 +13,7 @@ import { swapAsset } from './AssetSwapper';
import PixiResourcesLoader from '../ObjectsRendering/PixiResourcesLoader';
import useAlertDialog from '../UI/Alert/useAlertDialog';
import RaisedButton from '../UI/RaisedButton';
import { AssetStoreNavigatorContext } from './AssetStoreNavigator';
type Props = {|
project: gdProject,
@@ -37,7 +37,7 @@ function AssetSwappingDialog({
onClose,
minimalUI,
}: Props) {
const { shopNavigationState } = React.useContext(AssetStoreContext);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const { openedAssetShortHeader } = shopNavigationState.getCurrentPage();
const [

View File

@@ -42,6 +42,7 @@ import { getAssetShortHeadersToDisplay } from './AssetsList';
import ErrorBoundary from '../UI/ErrorBoundary';
import type { ObjectFolderOrObjectWithContext } from '../ObjectsList/EnumerateObjectFolderOrObject';
import LoaderModal from '../UI/LoaderModal';
import { AssetStoreNavigatorContext } from './AssetStoreNavigator';
const isDev = Window.isDev();
@@ -130,7 +131,7 @@ export const useInstallAsset = ({
targetObjectFolderOrObjectWithContext?: ?ObjectFolderOrObjectWithContext,
resourceManagementProps: ResourceManagementProps,
|}) => {
const { shopNavigationState } = React.useContext(AssetStoreContext);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const { openedAssetPack } = shopNavigationState.getCurrentPage();
const eventsFunctionsExtensionsState = React.useContext(
EventsFunctionsExtensionsContext
@@ -261,10 +262,10 @@ function NewObjectDialog({
const {
assetShortHeadersSearchResults,
shopNavigationState,
environment,
setEnvironment,
} = React.useContext(AssetStoreContext);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const {
openedAssetPack,
openedAssetShortHeader,

View File

@@ -12,7 +12,7 @@ import {
type PrivateGameTemplateListingData,
} from '../../Utils/GDevelopServices/Shop';
import { capitalize } from 'lodash';
import { type NavigationState } from '../AssetStoreNavigator';
import { AssetStoreNavigatorContext } from '../AssetStoreNavigator';
import { getPrivateGameTemplateListingDataFromUserFriendlySlug } from '../AssetStoreUtils';
import useAlertDialog from '../../UI/Alert/useAlertDialog';
import { t } from '@lingui/macro';
@@ -40,8 +40,6 @@ type PrivateGameTemplateStoreState = {|
error: ?Error,
shop: {
privateGameTemplateListingDatasSearchResults: ?Array<PrivateGameTemplateListingData>,
searchText: string,
setSearchText: string => void,
filtersState: FiltersState,
setInitialGameTemplateUserFriendlySlug: string => void,
},
@@ -63,8 +61,6 @@ export const initialPrivateGameTemplateStoreState: PrivateGameTemplateStoreState
error: null,
shop: {
privateGameTemplateListingDatasSearchResults: null,
searchText: '',
setSearchText: () => {},
filtersState: {
chosenFilters: new Set(),
addFilter: () => {},
@@ -95,14 +91,17 @@ export const PrivateGameTemplateStoreContext = React.createContext<PrivateGameTe
);
type PrivateGameTemplateStoreStateProviderProps = {|
shopNavigationState: NavigationState,
children: React.Node,
|};
export const PrivateGameTemplateStoreStateProvider = ({
shopNavigationState,
children,
}: PrivateGameTemplateStoreStateProviderProps) => {
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const {
searchText: shopSearchText,
setSearchText: setShopSearchText,
} = shopNavigationState;
const { limits } = React.useContext(AuthenticatedUserContext);
const [
@@ -123,7 +122,6 @@ export const PrivateGameTemplateStoreStateProvider = ({
const isLoading = React.useRef<boolean>(false);
const { showAlert } = useAlertDialog();
const [shopSearchText, setShopSearchText] = React.useState(defaultSearchText);
const [exampleStoreSearchText, setExampleStoreSearchText] = React.useState(
defaultSearchText
);
@@ -224,7 +222,8 @@ export const PrivateGameTemplateStoreStateProvider = ({
});
shopNavigationState.openPrivateGameTemplateInformationPage({
privateGameTemplateListingData,
previousSearchText: shopSearchText,
storeSearchText: true,
clearSearchText: false,
});
initialGameTemplateOpened.current = false; // Allow to open the game template again if the effect run again.
setInitialGameTemplateUserFriendlySlug(null);
@@ -242,7 +241,6 @@ export const PrivateGameTemplateStoreStateProvider = ({
shopNavigationState,
showAlert,
initialGameTemplateUserFriendlySlug,
shopSearchText,
]
);
@@ -341,6 +339,7 @@ export const PrivateGameTemplateStoreStateProvider = ({
privateGameTemplateListingDatasSearchResultsForExampleStore,
privateGameTemplateListingDatasSearchResultsForShop,
shopSearchText,
setShopSearchText,
exampleStoreSearchText,
currentPage.filtersState,
filtersStateForExampleStore,

View File

@@ -47,6 +47,7 @@ import AlertMessage from '../UI/AlertMessage';
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
import { LineStackLayout } from '../UI/Layout';
import {
AssetStoreNavigatorContext,
isHomePage,
isSearchResultPage,
type AssetStorePageState,
@@ -121,14 +122,17 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
privateAssetPackListingDatas,
error: assetStoreError,
fetchAssetsAndFilters,
shopNavigationState,
searchText,
setSearchText: setAssetStoreSearchText,
clearAllFilters: clearAllAssetStoreFilters,
assetFiltersState,
getAssetShortHeaderFromId,
} = React.useContext(AssetStoreContext);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const {
searchText,
setSearchText: setAssetStoreSearchText,
} = shopNavigationState;
const assetSwappedObjectPtr = React.useRef<number | null>(null);
React.useEffect(
() => {
@@ -169,10 +173,7 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
privateGameTemplateListingDatas,
error: privateGameTemplateStoreError,
fetchGameTemplates,
shop: {
privateGameTemplateListingDatasSearchResults,
setSearchText: setPrivateGameTemplateSearchText,
},
shop: { privateGameTemplateListingDatasSearchResults },
} = React.useContext(PrivateGameTemplateStoreContext);
const currentPage = shopNavigationState.getCurrentPage();
const {
@@ -226,9 +227,8 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
const setSearchText = React.useCallback(
(newSearchText: string) => {
setAssetStoreSearchText(newSearchText);
setPrivateGameTemplateSearchText(newSearchText);
},
[setAssetStoreSearchText, setPrivateGameTemplateSearchText]
[setAssetStoreSearchText]
);
const fetchAssetsAndGameTemplates = React.useCallback(
@@ -344,12 +344,10 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
assetPackKind,
});
saveScrollPosition();
const previousSearchText = searchText;
// Don't reset search text when opening an asset as the search bar is not active.
// This helps speeding up the navigation when going back to the results page.
shopNavigationState.openAssetDetailPage({
assetShortHeader,
previousSearchText,
storeSearchText: true,
clearSearchText: false,
});
},
[
@@ -358,7 +356,6 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
privateAssetPackListingDatas,
saveScrollPosition,
shopNavigationState,
searchText,
]
);
@@ -378,19 +375,15 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
Window.openExternalURL(assetPack.externalWebLink);
} else {
saveScrollPosition();
const previousSearchText = searchText;
setSearchText(''); // Reset search text when opening a pack.
shopNavigationState.openPackPage({ assetPack, previousSearchText });
shopNavigationState.openPackPage({
assetPack,
storeSearchText: true,
clearSearchText: true,
});
openFiltersPanelIfAppropriate();
}
},
[
shopNavigationState,
searchText,
saveScrollPosition,
setSearchText,
openFiltersPanelIfAppropriate,
]
[shopNavigationState, saveScrollPosition, openFiltersPanelIfAppropriate]
);
// When a private pack is selected from the home page,
@@ -417,11 +410,10 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
assetPackKind: 'private',
});
saveScrollPosition();
const previousSearchText = searchText;
setSearchText(''); // Reset search text when opening a pack.
shopNavigationState.openPrivateAssetPackInformationPage({
privateAssetPackListingData,
previousSearchText,
storeSearchText: true,
clearSearchText: true,
});
return;
}
@@ -435,11 +427,10 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
source: 'store-home',
});
saveScrollPosition();
const previousSearchText = searchText;
setSearchText(''); // Reset search text when opening a pack.
shopNavigationState.openPackPage({
assetPack: receivedAssetPack,
previousSearchText,
storeSearchText: true,
clearSearchText: true,
});
openFiltersPanelIfAppropriate();
},
@@ -447,9 +438,7 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
receivedAssetPacks,
saveScrollPosition,
shopNavigationState,
setSearchText,
openFiltersPanelIfAppropriate,
searchText,
]
);
@@ -475,14 +464,13 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
source: 'store',
});
saveScrollPosition();
const previousSearchText = searchText;
setSearchText(''); // Reset search text when opening a template.
shopNavigationState.openPrivateGameTemplateInformationPage({
privateGameTemplateListingData,
previousSearchText,
storeSearchText: true,
clearSearchText: true,
});
},
[saveScrollPosition, setSearchText, searchText, shopNavigationState]
[saveScrollPosition, shopNavigationState]
);
const selectShopCategory = React.useCallback(
@@ -505,16 +493,17 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
publicAssetPacks &&
publicAssetPacks.starterPacks.find(pack => pack.tag === tag);
saveScrollPosition();
setSearchText('');
if (privateAssetPack) {
shopNavigationState.openPackPage({
assetPack: privateAssetPack,
previousSearchText: '', // We were on an asset page.
storeSearchText: false,
clearSearchText: true,
});
} else if (publicAssetPack) {
shopNavigationState.openPackPage({
assetPack: publicAssetPack,
previousSearchText: '', // We were on an asset page.
storeSearchText: false,
clearSearchText: true,
});
} else {
shopNavigationState.openTagPage(tag);
@@ -523,7 +512,6 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
openFiltersPanelIfAppropriate();
},
[
setSearchText,
receivedAssetPacks,
publicAssetPacks,
saveScrollPosition,

View File

@@ -27,10 +27,11 @@ import Button from '@material-ui/core/Button';
import ButtonGroup from '@material-ui/core/ButtonGroup';
import InputAdornment from '@material-ui/core/InputAdornment';
import Tooltip from '@material-ui/core/Tooltip';
import CircledInfo from '../../../UI/CustomSvgIcons/SmallCircledInfo';
type Props = BehaviorEditorProps;
const NumericProperty = (props: {|
export const NumericProperty = (props: {|
id?: string,
properties: gdMapStringPropertyDescriptor,
propertyName: string,
@@ -55,9 +56,24 @@ const NumericProperty = (props: {|
);
};
const UnitAdornment = (props: {| property: gdPropertyDescriptor |}) => {
export const UnitAdornment = (props: {| property: gdPropertyDescriptor |}) => {
const { property } = props;
const measurementUnit = property.getMeasurementUnit();
if (measurementUnit.isUndefined() && property.getDescription()) {
return (
<Tooltip
title={
<MeasurementUnitDocumentation
label={property.getLabel()}
description={property.getDescription()}
elementsWithWords={''}
/>
}
>
<InputAdornment position="end">{<CircledInfo />}</InputAdornment>
</Tooltip>
);
}
return (
<Tooltip
title={
@@ -220,11 +236,13 @@ const Physics2Editor = (props: Props) => {
value={properties.get('shapeDimensionA').getValue()}
key={'shapeDimensionA'}
floatingLabelText={
shape === 'Circle'
? 'Radius'
: shape === 'Edge'
? 'Length'
: 'Width'
shape === 'Circle' ? (
<Trans>Radius</Trans>
) : shape === 'Edge' ? (
<Trans>Length</Trans>
) : (
<Trans>Width</Trans>
)
}
min={0}
onChange={newValue =>
@@ -241,7 +259,9 @@ const Physics2Editor = (props: Props) => {
fullWidth
value={properties.get('shapeDimensionB').getValue()}
key={'shapeDimensionB'}
floatingLabelText={shape === 'Edge' ? 'Angle' : 'Height'}
floatingLabelText={
shape === 'Edge' ? <Trans>Angle</Trans> : <Trans>Height</Trans>
}
min={shape === 'Edge' ? undefined : 0}
onChange={newValue =>
updateBehaviorProperty('shapeDimensionB', newValue)

View File

@@ -8,8 +8,6 @@ import Checkbox from '../../../UI/Checkbox';
import SelectField from '../../../UI/SelectField';
import SelectOption from '../../../UI/SelectOption';
import SemiControlledTextField from '../../../UI/SemiControlledTextField';
import { getMeasurementUnitShortLabel } from '../../../PropertiesEditor/PropertiesMapToSchema';
import MeasurementUnitDocumentation from '../../../PropertiesEditor/MeasurementUnitDocumentation';
import { type BehaviorEditorProps } from '../BehaviorEditorProps.flow';
import Text from '../../../UI/Text';
import DismissableAlertMessage from '../../../UI/DismissableAlertMessage';
@@ -17,56 +15,10 @@ import { ResponsiveLineStackLayout } from '../../../UI/Layout';
import useForceUpdate from '../../../Utils/UseForceUpdate';
import Button from '@material-ui/core/Button';
import ButtonGroup from '@material-ui/core/ButtonGroup';
import InputAdornment from '@material-ui/core/InputAdornment';
import Tooltip from '@material-ui/core/Tooltip';
import { NumericProperty, UnitAdornment } from '../Physics2Editor';
type Props = BehaviorEditorProps;
const NumericProperty = (props: {|
id?: string,
properties: gdMapStringPropertyDescriptor,
propertyName: string,
step: number,
onUpdate: (newValue: string) => void,
|}) => {
const { properties, propertyName, step, onUpdate, id } = props;
const property = properties.get(propertyName);
return (
<SemiControlledTextField
id={id}
fullWidth
value={property.getValue()}
key={propertyName}
floatingLabelText={property.getLabel()}
step={step}
onChange={onUpdate}
type="number"
endAdornment={<UnitAdornment property={property} />}
/>
);
};
const UnitAdornment = (props: {| property: gdPropertyDescriptor |}) => {
const { property } = props;
const measurementUnit = property.getMeasurementUnit();
return (
<Tooltip
title={
<MeasurementUnitDocumentation
label={measurementUnit.getLabel()}
description={measurementUnit.getDescription()}
elementsWithWords={measurementUnit.getElementsWithWords()}
/>
}
>
<InputAdornment position="end">
{getMeasurementUnitShortLabel(measurementUnit)}
</InputAdornment>
</Tooltip>
);
};
const BitGroupEditor = (props: {|
bits: Array<boolean>,
onChange: (index: number, value: boolean) => void,
@@ -227,7 +179,9 @@ const Physics3DEditor = (props: Props) => {
fullWidth
value={properties.get('shapeDimensionA').getValue()}
key={'shapeDimensionA'}
floatingLabelText={shape === 'Box' ? 'Width' : 'Radius'}
floatingLabelText={
shape === 'Box' ? <Trans>Width</Trans> : <Trans>Radius</Trans>
}
min={0}
onChange={newValue =>
updateBehaviorProperty('shapeDimensionA', newValue)
@@ -242,7 +196,9 @@ const Physics3DEditor = (props: Props) => {
fullWidth
value={properties.get('shapeDimensionB').getValue()}
key={'shapeDimensionB'}
floatingLabelText={shape === 'Box' ? 'Width' : 'Depth'}
floatingLabelText={
shape === 'Box' ? <Trans>Height</Trans> : <Trans>Depth</Trans>
}
min={0}
onChange={newValue =>
updateBehaviorProperty('shapeDimensionB', newValue)
@@ -258,7 +214,7 @@ const Physics3DEditor = (props: Props) => {
fullWidth
value={properties.get('shapeDimensionC').getValue()}
key={'shapeDimensionC'}
floatingLabelText={'Depth'}
floatingLabelText={<Trans>Depth</Trans>}
min={0}
onChange={newValue =>
updateBehaviorProperty('shapeDimensionC', newValue)

View File

@@ -4,6 +4,16 @@ import optionalRequire from '../Utils/OptionalRequire';
const fs = optionalRequire('fs');
const path = optionalRequire('path');
// Avoid conflicts in declaration of PIXI and THREE namespaces.
const excludedFiles = [
'global-three.d.ts',
'global-pixi.d.ts',
'pixi-particles-pixi-renderer.d.ts',
'pixi-tilemap.d.ts',
'pixi.js',
'three.js',
];
export const setupAutocompletions = (monaco: any) => {
const importAllJsFilesFromFolder = (folderPath: string) =>
fs.readdir(folderPath, (error: ?Error, filenames: Array<string>) => {
@@ -21,6 +31,7 @@ export const setupAutocompletions = (monaco: any) => {
.isDirectory();
if (
(filename.endsWith('.ts') || filename.endsWith('.js')) &&
!excludedFiles.includes(filename) &&
// Dialogue tree uses a folder called `bondage.js` that should not be read as a file.
!isDirectory
) {
@@ -43,6 +54,40 @@ export const setupAutocompletions = (monaco: any) => {
});
});
const importAllJsFilesFromFolderRecursively = (folderPath: string) =>
fs.readdir(folderPath, (error: ?Error, filenames: Array<string>) => {
if (error) {
console.error(
'Unable to read GDJS files for setting up autocompletions:',
error
);
return;
}
filenames.forEach(filename => {
const fullPath = path.join(folderPath, filename);
const isDirectory = fs.lstatSync(fullPath).isDirectory();
if (isDirectory) {
importAllJsFilesFromFolderRecursively(fullPath);
} else if (filename.endsWith('.ts') || filename.endsWith('.js')) {
fs.readFile(fullPath, 'utf8', (fileError, content) => {
if (fileError) {
console.error(
`Unable to read ${fullPath} for setting up autocompletions:`,
fileError
);
return;
}
monaco.languages.typescript.javascriptDefaults.addExtraLib(
content,
fullPath
);
});
}
});
});
findGDJS().then(({ gdjsRoot }) => {
// Autocompletions are generated by reading the sources of the game engine
// (much like how autocompletions work in Visual Studio Code) - *not* the built files.
@@ -66,6 +111,8 @@ export const setupAutocompletions = (monaco: any) => {
);
const extensionsPath = path.join(runtimePath, 'Extensions');
const eventToolsPath = path.join(runtimePath, 'events-tools');
const threeTypesPath = path.join(runtimeTypesPath, 'three');
const pixiTypesPath = path.join(runtimeTypesPath, 'pixi');
importAllJsFilesFromFolder(runtimePath);
importAllJsFilesFromFolder(runtimeTypesPath);
@@ -74,6 +121,9 @@ export const setupAutocompletions = (monaco: any) => {
importAllJsFilesFromFolder(runtimeHowlerSoundManagerPath);
importAllJsFilesFromFolder(runtimeFontfaceobserverFontManagerPath);
importAllJsFilesFromFolder(eventToolsPath);
importAllJsFilesFromFolderRecursively(threeTypesPath);
importAllJsFilesFromFolderRecursively(pixiTypesPath);
fs.readdir(extensionsPath, (error: ?Error, folderNames: Array<string>) => {
if (error) {
console.error(

View File

@@ -72,7 +72,8 @@ export class CodeEditor extends React.Component<Props, State> {
// Enable type checking of JavaScript files
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
...monaco.languages.typescript.javascriptDefaults.getCompilerOptions(),
// TODO: Uncomment to activate type checking of JS files, once autocompletions are all handled properly.
// ...monaco.languages.typescript.javascriptDefaults.getCompilerOptions()
target: monaco.languages.typescript.ScriptTarget.ES6,
allowNonTsExtensions: true,
allowJs: true,
@@ -82,12 +83,15 @@ export class CodeEditor extends React.Component<Props, State> {
// Activate checks - though this won't work for .d.ts classes/funcions for
// some reason. See:
// https://microsoft.github.io/monaco-editor/playground.html?source=v0.52.0#XQAAAALxBQAAAAAAAABBqQkHQ5NjdMjwa-jY7SIQ9S7DNlzs5W-mwj0fe1ZCDRFc9ws9XQE0SJE1jc2VKxhaLFIw9vEWSxW3yscw90T9lfa8wm3bKzPN7npWhYaA4x4rxLXXBbB2_OnRSToJ21hOUR_hS9pG2m6stSYmFVtipRHVTrIB91DOr8dFz_2f6wKJhg_ghHcsBt-hKnlu-qt2H4NuwdaqfbMd6vqkrLY5fzdHCB8FZIGGUDanilP6QXPTpV0rme8fvINUj2BklEotLylw-HHch53nHQMCabAMh3iNqg8S_rHB5OZg2IxLB-8POziUzSwFrTcfwxftX7gi3ZIWNPyf4vJOFtRKVr3hJLIKUGJew4RN0hYb1SnPUm1x6B8hb8I5-9WqAgbYz6E0ntlpOU4jvw6zty4iIJ4GQFuyQ1ys6LtVNKef1bnBs7fDr3xFb3LOW7E4RMonCTxapW3_DQvpQ_x4psM1GYFKMZZXr0lCRvrFs4PBf0uXMwEeTfC1PurLOeoNNEEZFLutUE1ojQVjkTgxZC_HCheas9sKTtZRsJ0i6qDWJh3PbNocPFIbvPQ5la2NXuQIph1oaGrDjqgoirRoexyKumFnXVLkvKVoTdglfgXLwEaoheNdItwJfWKdPqmHh18GTsk8Df_mr8zt3r-pLUtNvB6220ZAKaswAMPR0yDrfHfz_7vWaBfDy6yykp-ROV0Ckt2qV83DVNrHW9HNm4e0WC1ByFoDOB8AdJzjQye8YD3aCwy8ft4sSOGvSeNo2FOPoqd2TU-Sr58fhP09rWes4Vgrv1MhMlUtZ5zBNSL_Mpb1R32H8hrNShgxxaT0r048_u6-nFbkNKq-VCjTlFuUoTW-3y8b1UrYxSmTkjmp-KCc9ObqKgzbRV5tPIX_sqYqVCJFOZN0Nndp4s6xm7qmVPi8SMcDfCD4zfPWBpanAohwTcIIbXPJqggntHxMUFvH7h85mh3AZoJ5FPz2v387LTrxdqOZZW7kTGqSl-Y8NHsa_znjlaNDOU-qywr5DypPbH3_wOm2XMeQNg0jX1bBtIoh_PG5BWMQNlJmQRlDnmDtueZf_nTzQw
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
...monaco.languages.typescript.javascriptDefaults.getDiagnosticsOptions(),
noSemanticValidation: false,
noSuggestionDiagnostics: false,
noSyntaxValidation: false,
});
//
// TODO: Uncomment to activate type checking of JS files, once autocompletions are all handled properly.
//
// monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
// ...monaco.languages.typescript.javascriptDefaults.getDiagnosticsOptions(),
// noSemanticValidation: false,
// noSuggestionDiagnostics: false,
// noSyntaxValidation: false,
// });
setupAutocompletions(monaco);
}

View File

@@ -234,6 +234,42 @@ const createField = (
getDescription,
hasImpactOnAllOtherFields: property.hasImpactOnOtherProperties(),
};
} else if (valueType === 'animationname') {
return {
getChoices: () => {
if (!object) {
return [];
}
const animationArray = mapFor(
0,
object.getConfiguration().getAnimationsCount(),
i => {
const animationName = object.getConfiguration().getAnimationName(i);
if (animationName === '') {
return null;
}
return {
value: animationName,
label: animationName,
};
}
).filter(Boolean);
animationArray.push({ value: '', label: '(no animation)' });
return animationArray;
},
name,
valueType: 'string',
getValue: (instance: Instance): string => {
return getProperties(instance)
.get(name)
.getValue();
},
setValue: (instance: Instance, newValue: string) => {
onUpdateProperty(instance, name, newValue);
},
getLabel,
getDescription,
};
} else {
console.error(
`A property with type=${valueType} could not be mapped to a field. Ensure that this type is correct and understood by the IDE.`

View File

@@ -729,6 +729,11 @@ export default function EventsBasedBehaviorPropertiesEditor({
value="Boolean"
label={t`Boolean (checkbox)`}
/>
<SelectOption
key="property-type-animationname"
value="AnimationName"
label={t`Animation name (text)`}
/>
<SelectOption
key="property-type-choice"
value="Choice"
@@ -802,7 +807,9 @@ export default function EventsBasedBehaviorPropertiesEditor({
</SelectField>
)}
{(property.getType() === 'String' ||
property.getType() === 'Number') && (
property.getType() === 'Number' ||
property.getType() ===
'AnimationName') && (
<SemiControlledTextField
commitOnBlur
floatingLabelText={

View File

@@ -178,6 +178,18 @@ export default function EventsBasedBehaviorEditor({
if (onConfigurationUpdated) onConfigurationUpdated('isPrivate');
onChange();
}}
tooltipOrHelperText={
eventsBasedBehavior.isPrivate() ? (
<Trans>
This behavior won't be visible in the scene and events
editors.
</Trans>
) : (
<Trans>
This behavior will be visible in the scene and events editors.
</Trans>
)
}
/>
{eventsBasedBehavior
.getEventsFunctions()

View File

@@ -141,6 +141,26 @@ export default function EventsBasedObjectEditor({
}}
/>
)}
<Checkbox
label={<Trans>Private</Trans>}
checked={eventsBasedObject.isPrivate()}
onCheck={(e, checked) => {
eventsBasedObject.setPrivate(checked);
onChange();
onEventsBasedObjectChildrenEdited();
}}
tooltipOrHelperText={
eventsBasedObject.isPrivate() ? (
<Trans>
This object won't be visible in the scene and events editors.
</Trans>
) : (
<Trans>
This object will be visible in the scene and events editors.
</Trans>
)
}
/>
<Line noMargin justifyContent="center">
<RaisedButton
label={<Trans>Open visual editor for the object</Trans>}

View File

@@ -529,6 +529,17 @@ export const EventsFunctionPropertiesEditor = ({
onConfigurationUpdated('isPrivate');
forceUpdate();
}}
tooltipOrHelperText={
eventsFunction.isPrivate() ? (
<Trans>
This function won't be visible in the events editor.
</Trans>
) : (
<Trans>
This function will be visible in the events editor.
</Trans>
)
}
/>
<Checkbox
label={<Trans>Asynchronous</Trans>}

View File

@@ -12,8 +12,7 @@ import {
} from '../../UI/Table';
import RaisedButton from '../../UI/RaisedButton';
import IconButton from '../../UI/IconButton';
import SemiControlledTextField from '../../UI/SemiControlledTextField';
import SelectField from '../../UI/SelectField';
import CompactSemiControlledTextField from '../../UI/CompactSemiControlledTextField';
import SelectOption from '../../UI/SelectOption';
import { Line, Column } from '../../UI/Grid';
@@ -25,6 +24,11 @@ import { showWarningBox } from '../../UI/Messages/MessageBox';
import Paper from '../../UI/Paper';
import Trash from '../../UI/CustomSvgIcons/Trash';
import Add from '../../UI/CustomSvgIcons/Add';
import { ColumnStackLayout } from '../../UI/Layout';
import Text from '../../UI/Text';
import { CompactResourceSelectorWithThumbnail } from '../../ResourcesList/CompactResourceSelectorWithThumbnail';
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
import CompactSelectField from '../../UI/CompactSelectField';
const styles = {
paper: { minWidth: '100%' },
@@ -39,10 +43,18 @@ const checkNameExists = (
return false;
};
type Props = {| eventsFunctionsExtension: gdEventsFunctionsExtension |};
type Props = {|
eventsFunctionsExtension: gdEventsFunctionsExtension,
// For source files:
project: gdProject,
resourceManagementProps: ResourceManagementProps,
|};
export const ExtensionDependenciesEditor = ({
eventsFunctionsExtension,
project,
resourceManagementProps,
}: Props) => {
const deps = eventsFunctionsExtension.getAllDependencies();
const forceUpdate = useForceUpdate();
@@ -62,146 +74,242 @@ export const ExtensionDependenciesEditor = ({
};
return (
<Column noMargin>
<Line expand>
<TableContainer
component={({ children }) => (
<Paper style={styles.paper} background="medium">
{children}
</Paper>
)}
>
<Table>
<TableHeader>
<TableRow>
<TableHeaderColumn>
<Trans>Name</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Export name</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Version</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Dependency type</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Action</Trans>
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody>
{// $FlowFixMe - unsure why Flow complains about TableRow.
mapVector<gdDependencyMetadata, TableRow>(
eventsFunctionsExtension.getAllDependencies(),
(dependency, index) => (
// $FlowFixMe - unsure why Flow complains about TableRow.
<TableRow key={dependency.getName()}>
<TableRowColumn>
<SemiControlledTextField
commitOnBlur
value={dependency.getName()}
onChange={newName => {
if (newName === dependency.getName()) return;
<ColumnStackLayout noMargin noOverflowParent>
<Text size="block-title">
<Trans>Dependencies</Trans>
</Text>
<TableContainer
component={({ children }) => (
<Paper style={styles.paper} background="medium">
{children}
</Paper>
)}
>
<Table>
<TableHeader>
<TableRow>
<TableHeaderColumn>
<Trans>Name</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Export name</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Version</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Dependency type</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Action</Trans>
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody>
{// $FlowFixMe - unsure why Flow complains about TableRow.
mapVector<gdDependencyMetadata, TableRow>(
eventsFunctionsExtension.getAllDependencies(),
(dependency, index) => (
// $FlowFixMe - unsure why Flow complains about TableRow.
<TableRow key={dependency.getName()}>
<TableRowColumn>
<CompactSemiControlledTextField
commitOnBlur
value={dependency.getName()}
onChange={newName => {
if (newName === dependency.getName()) return;
if (checkNameExists(newName, deps)) {
showWarningBox(
`This name is already in use! Please use a unique name.`,
{ delayToNextTick: true }
);
} else {
dependency.setName(newName);
forceUpdate();
}
}}
margin="none"
if (checkNameExists(newName, deps)) {
showWarningBox(
`This name is already in use! Please use a unique name.`,
{ delayToNextTick: true }
);
} else {
dependency.setName(newName);
forceUpdate();
}
}}
/>
</TableRowColumn>
<TableRowColumn>
<CompactSemiControlledTextField
commitOnBlur
value={dependency.getExportName()}
onChange={newExportName => {
if (newExportName === dependency.getExportName())
return;
dependency.setExportName(newExportName);
forceUpdate();
}}
/>
</TableRowColumn>
<TableRowColumn>
<CompactSemiControlledTextField
commitOnBlur
value={dependency.getVersion()}
onChange={newVersion => {
if (newVersion === dependency.getVersion()) return;
dependency.setVersion(newVersion);
forceUpdate();
}}
/>
</TableRowColumn>
<TableRowColumn>
<CompactSelectField
value={dependency.getDependencyType()}
onChange={newType => {
if (newType === dependency.getDependencyType()) return;
dependency.setDependencyType(newType);
forceUpdate();
}}
>
<SelectOption value="npm" label={t`NPM`} />
<SelectOption value="cordova" label={t`Cordova`} />
</CompactSelectField>
</TableRowColumn>
<TableRowColumn>
<IconButton
tooltip={t`Delete`}
onClick={() => {
eventsFunctionsExtension.removeDependencyAt(index);
forceUpdate();
}}
size="small"
>
<Trash />
</IconButton>
</TableRowColumn>
</TableRow>
)
)}
</TableBody>
</Table>
<Column expand>
<Line justifyContent="flex-end">
<RaisedButton
primary
icon={<Add />}
label={<Trans>Add</Trans>}
onClick={addDependency}
/>
</Line>
</Column>
</TableContainer>
<BackgroundText>
<Trans>
Dependencies allow to add additional libraries in the exported games.
NPM dependencies will be included for Electron builds (Windows, macOS,
Linux) and Cordova dependencies will be included for Cordova builds
(Android, iOS). Note that this is intended for usage in JavaScript
events only. If you are only using standard events, you should not
worry about this.
</Trans>
</BackgroundText>
<Text size="block-title">
<Trans>Extra source files (experimental)</Trans>
</Text>
<TableContainer
component={({ children }) => (
<Paper style={styles.paper} background="medium">
{children}
</Paper>
)}
>
<Table>
<TableHeader>
<TableRow>
<TableHeaderColumn>
<Trans>Source file</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Loading Position</Trans>
</TableHeaderColumn>
<TableHeaderColumn>
<Trans>Action</Trans>
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody>
{// $FlowFixMe - unsure why Flow complains about TableRow.
mapVector<gdSourceFileMetadata, TableRow>(
eventsFunctionsExtension.getAllSourceFiles(),
(sourceFile, index) => (
// $FlowFixMe - unsure why Flow complains about TableRow.
<TableRow key={sourceFile.getResourceName()}>
<TableRowColumn>
<CompactResourceSelectorWithThumbnail
project={project}
resourceManagementProps={resourceManagementProps}
resourceKind="javascript"
resourceName={sourceFile.getResourceName()}
onChange={newResourceName => {
sourceFile.setResourceName(newResourceName);
forceUpdate();
}}
/>
</TableRowColumn>
<TableRowColumn>
<CompactSelectField
value={sourceFile.getIncludePosition()}
onChange={newType => {
if (newType === sourceFile.getIncludePosition()) return;
sourceFile.setIncludePosition(newType);
forceUpdate();
}}
>
<SelectOption
value="first"
label={t`First (before other files)`}
/>
</TableRowColumn>
<TableRowColumn>
<SemiControlledTextField
commitOnBlur
value={dependency.getExportName()}
onChange={newExportName => {
if (newExportName === dependency.getExportName())
return;
dependency.setExportName(newExportName);
forceUpdate();
}}
margin="none"
<SelectOption
value="last"
label={t`Last (after other files)`}
/>
</TableRowColumn>
<TableRowColumn>
<SemiControlledTextField
commitOnBlur
value={dependency.getVersion()}
onChange={newVersion => {
if (newVersion === dependency.getVersion()) return;
dependency.setVersion(newVersion);
forceUpdate();
}}
margin="none"
/>
</TableRowColumn>
<TableRowColumn>
<SelectField
value={dependency.getDependencyType()}
onChange={(_, __, newType) => {
if (newType === dependency.getDependencyType())
return;
dependency.setDependencyType(newType);
forceUpdate();
}}
margin="none"
>
<SelectOption value="npm" label={t`NPM`} />
<SelectOption value="cordova" label={t`Cordova`} />
</SelectField>
</TableRowColumn>
<TableRowColumn>
<IconButton
tooltip={t`Delete`}
onClick={() => {
eventsFunctionsExtension.removeDependencyAt(index);
forceUpdate();
}}
size="small"
>
<Trash />
</IconButton>
</TableRowColumn>
</TableRow>
)
)}
</TableBody>
</Table>
<Column expand>
<Line justifyContent="flex-end">
<RaisedButton
primary
icon={<Add />}
label={<Trans>Add</Trans>}
onClick={addDependency}
/>
</Line>
</Column>
</TableContainer>
</Line>
<Line>
<BackgroundText>
<Trans>
Dependencies allow to add additional libraries in the exported
games. NPM dependencies will be included for Electron builds
(Windows, macOS, Linux) and Cordova dependencies will be included
for Cordova builds (Android, iOS). Note that this is intended for
usage in JavaScript events only. If you are only using standard
events, you should not worry about this.
</Trans>
</BackgroundText>
</Line>
</Column>
</CompactSelectField>
</TableRowColumn>
<TableRowColumn>
<IconButton
tooltip={t`Delete`}
onClick={() => {
eventsFunctionsExtension.removeSourceFileAt(index);
forceUpdate();
}}
size="small"
>
<Trash />
</IconButton>
</TableRowColumn>
</TableRow>
)
)}
</TableBody>
</Table>
<Column expand>
<Line justifyContent="flex-end">
<RaisedButton
primary
icon={<Add />}
label={<Trans>Add</Trans>}
onClick={() => {
eventsFunctionsExtension.addSourceFile();
forceUpdate();
}}
/>
</Line>
</Column>
</TableContainer>
<BackgroundText>
<Trans>
JavaScript files are imported as is (no compilation and not available
in JavaScript code block autocompletions). Make sure your extension is
used by the game (at least one action/condition used in a scene),
otherwise the files won't be imported.
</Trans>
</BackgroundText>
</ColumnStackLayout>
);
};

View File

@@ -11,6 +11,7 @@ import { ExtensionDependenciesEditor } from './ExtensionDependenciesEditor';
import ExtensionExporterDialog from './ExtensionExporterDialog';
import { Line } from '../../UI/Grid';
import Upload from '../../UI/CustomSvgIcons/Upload';
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
type TabName = 'options' | 'dependencies';
@@ -19,10 +20,14 @@ type Props = {|
eventsFunctionsExtension: gdEventsFunctionsExtension,
onClose: () => void,
open: boolean,
// For source files:
resourceManagementProps: ResourceManagementProps,
|};
export default function OptionsEditorDialog({
project,
resourceManagementProps,
eventsFunctionsExtension,
onClose,
open,
@@ -95,6 +100,8 @@ export default function OptionsEditorDialog({
{currentTab === 'dependencies' && (
<Line>
<ExtensionDependenciesEditor
project={project}
resourceManagementProps={resourceManagementProps}
eventsFunctionsExtension={eventsFunctionsExtension}
/>
</Line>

View File

@@ -1630,6 +1630,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
{editOptionsDialogOpen && (
<OptionsEditorDialog
project={project}
resourceManagementProps={this.props.resourceManagementProps}
eventsFunctionsExtension={eventsFunctionsExtension}
open
onClose={() => this._editOptions(false)}

View File

@@ -198,7 +198,9 @@ export class EventsBasedBehaviorTreeViewItemContent
return this.eventsBasedBehavior.isPrivate() ? (
<Tooltip
title={
<Trans>This behavior won't be visible in the events editor.</Trans>
<Trans>
This behavior won't be visible in the scene and events editors.
</Trans>
}
>
<VisibilityOff

View File

@@ -1,6 +1,7 @@
// @flow
import { type I18n as I18nType } from '@lingui/core';
import { t } from '@lingui/macro';
import { Trans } from '@lingui/macro';
import * as React from 'react';
import newNameGenerator from '../Utils/NewNameGenerator';
@@ -15,10 +16,16 @@ import {
type TreeItemProps,
extensionObjectsRootFolderId,
} from '.';
import Tooltip from '@material-ui/core/Tooltip';
import VisibilityOff from '../UI/CustomSvgIcons/VisibilityOff';
import Add from '../UI/CustomSvgIcons/Add';
const EVENTS_BASED_OBJECT_CLIPBOARD_KIND = 'Events Based Object';
const styles = {
tooltip: { marginRight: 5, verticalAlign: 'bottom' },
};
export type EventsBasedObjectCreationParameters = {|
isRenderedIn3D: boolean,
|};
@@ -170,6 +177,12 @@ export class EventsBasedObjectTreeViewItemContent
click: () => this.delete(),
accelerator: 'Backspace',
},
{
label: this.eventsBasedObject.isPrivate()
? i18n._(t`Make public`)
: i18n._(t`Make private`),
click: () => this._togglePrivate(),
},
{
type: 'separator',
},
@@ -193,7 +206,23 @@ export class EventsBasedObjectTreeViewItemContent
}
renderRightComponent(i18n: I18nType): ?React.Node {
return null;
return this.eventsBasedObject.isPrivate() ? (
<Tooltip
title={
<Trans>
This object won't be visible in the scene and events editors.
</Trans>
}
>
<VisibilityOff
fontSize="small"
style={{
...styles.tooltip,
color: this.props.gdevelopTheme.text.color.disabled,
}}
/>
</Tooltip>
) : null;
}
delete(): void {
@@ -225,6 +254,11 @@ export class EventsBasedObjectTreeViewItemContent
});
}
_togglePrivate(): void {
this.eventsBasedObject.setPrivate(!this.eventsBasedObject.isPrivate());
this.props.forceUpdateEditor();
}
getIndex(): number {
return this.props.eventsBasedObjectsList.getPosition(
this.eventsBasedObject

View File

@@ -50,6 +50,25 @@ type Props = {|
|}) => React.Node,
|};
const deduplicateEventSearchResults = (
eventsSearchResults: gdVectorEventsSearchResult
) => {
const resultEventsWithDuplicates = mapFor(
0,
eventsSearchResults.size(),
eventIndex => {
const eventsSearchResult = eventsSearchResults.at(eventIndex);
return eventsSearchResult.isEventValid()
? eventsSearchResult.getEvent()
: null;
}
).filter(Boolean);
// Store a list of unique events, because browsing for results in the events
// tree is made event by event.
return uniqBy<gdBaseEvent>(resultEventsWithDuplicates, event => event.ptr);
};
/**
* Computes the positions of the first selected event and the search results
* in the flatten event tree and looks for the search result just after the
@@ -116,25 +135,6 @@ export default class EventsSearcher extends React.Component<Props, State> {
});
};
_deduplicateEventSearchResults = (
eventsSearchResults: gdVectorEventsSearchResult
) => {
const resultEventsWithDuplicates = mapFor(
0,
eventsSearchResults.size(),
eventIndex => {
const eventsSearchResult = eventsSearchResults.at(eventIndex);
return eventsSearchResult.isEventValid()
? eventsSearchResult.getEvent()
: null;
}
).filter(Boolean);
// Store a list of unique events, because browsing for results in the events
// tree is made event by event.
return uniqBy<gdBaseEvent>(resultEventsWithDuplicates, event => event.ptr);
};
_doReplaceInEvents = (
{
searchInSelection,
@@ -181,7 +181,7 @@ export default class EventsSearcher extends React.Component<Props, State> {
cb();
}
);
return this._deduplicateEventSearchResults(modifiedEvents);
return deduplicateEventSearchResults(modifiedEvents);
};
_doSearchInEvents = (
@@ -238,9 +238,7 @@ export default class EventsSearcher extends React.Component<Props, State> {
return;
}
this._resultEvents = this._deduplicateEventSearchResults(
eventsSearchResults
);
this._resultEvents = deduplicateEventSearchResults(eventsSearchResults);
};
_goToSearchResults = (step: number): ?gdBaseEvent => {

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