mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
98 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
451d525b36 | ||
![]() |
c755946d42 | ||
![]() |
7a6b6fbf7f | ||
![]() |
730c8283e5 | ||
![]() |
7ea250706c | ||
![]() |
db7a108354 | ||
![]() |
2e15d68bce | ||
![]() |
f6c9e1408c | ||
![]() |
0b3d4d048a | ||
![]() |
5d625dd497 | ||
![]() |
079eca829a | ||
![]() |
35b5f92c59 | ||
![]() |
70eb95b132 | ||
![]() |
7c7ee8b7fc | ||
![]() |
3556dd2e3c | ||
![]() |
f74f77f66a | ||
![]() |
712eb4b647 | ||
![]() |
c4474c766d | ||
![]() |
ad17a21973 | ||
![]() |
5001411ccb | ||
![]() |
05939f5c3e | ||
![]() |
91978d4c6e | ||
![]() |
d6433d89f0 | ||
![]() |
e652ab9f5a | ||
![]() |
e7decc7b92 | ||
![]() |
33dcc04112 | ||
![]() |
58ba2668c2 | ||
![]() |
b34e802dcb | ||
![]() |
31dac9cc93 | ||
![]() |
ab97258832 | ||
![]() |
2ece223737 | ||
![]() |
8342873b6e | ||
![]() |
0a85fd3814 | ||
![]() |
fe15b6d30b | ||
![]() |
f3c3559518 | ||
![]() |
a403b1343b | ||
![]() |
83dba6c21e | ||
![]() |
7a4aea6557 | ||
![]() |
82a6abacb4 | ||
![]() |
f60613cc64 | ||
![]() |
274aedb3d9 | ||
![]() |
efb31c3caf | ||
![]() |
6781c0fd6e | ||
![]() |
e9781133e1 | ||
![]() |
8972e0e3a6 | ||
![]() |
4c76a5979b | ||
![]() |
484a0daec4 | ||
![]() |
de432c1bf2 | ||
![]() |
8c7dcc1c23 | ||
![]() |
39648af248 | ||
![]() |
5c7bbf5293 | ||
![]() |
9d97b9d0eb | ||
![]() |
b39b12864f | ||
![]() |
2c3dbbbbde | ||
![]() |
72cc60bae9 | ||
![]() |
c03e94849b | ||
![]() |
80daaf5e5b | ||
![]() |
cb457cfd04 | ||
![]() |
0a55aa631b | ||
![]() |
8e104f9ae4 | ||
![]() |
a90b9a27e9 | ||
![]() |
6321e82f63 | ||
![]() |
05fc63ab1b | ||
![]() |
85a6a21934 | ||
![]() |
a9741e7b42 | ||
![]() |
23afe7b71c | ||
![]() |
85757e6d98 | ||
![]() |
524ca4dbb3 | ||
![]() |
55cf710ef5 | ||
![]() |
eee7e7f04c | ||
![]() |
ecd984e08b | ||
![]() |
d0a7fbbd02 | ||
![]() |
2a2c930b74 | ||
![]() |
f6cb203029 | ||
![]() |
fe743bbe57 | ||
![]() |
78c31408d4 | ||
![]() |
75a038344a | ||
![]() |
f0567b674c | ||
![]() |
9b85f35856 | ||
![]() |
f4568febf2 | ||
![]() |
de7f60b693 | ||
![]() |
3a595b200e | ||
![]() |
431b5929e8 | ||
![]() |
ec2e82cee5 | ||
![]() |
76517f1a2a | ||
![]() |
43f5bd1c0e | ||
![]() |
7198a22ae2 | ||
![]() |
46c52905f6 | ||
![]() |
40954bf497 | ||
![]() |
02c06ac6e7 | ||
![]() |
1fa3f59a77 | ||
![]() |
cc371273ce | ||
![]() |
364ec2ecfb | ||
![]() |
0d36a27b87 | ||
![]() |
6d597a430b | ||
![]() |
27b71b08e5 | ||
![]() |
82158f7073 | ||
![]() |
8ba352d4ba |
@@ -176,6 +176,7 @@ jobs:
|
||||
|
||||
# Build the WebAssembly library only (so that it's cached on a S3 and easy to re-use).
|
||||
build-gdevelop_js-wasm-only:
|
||||
resource_class: medium+ # Compilation time decrease linearly with the number of CPUs, but not linking (so "large" does not speedup total build time).
|
||||
docker:
|
||||
- image: cimg/node:16.13
|
||||
|
||||
@@ -232,10 +233,83 @@ jobs:
|
||||
name: Deploy to S3 (latest)
|
||||
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/latest/
|
||||
|
||||
# Build the WebAssembly library with clang-tidy and memory sanitizers.
|
||||
build-gdevelop_js-debug-sanitizers-and-extra-checks:
|
||||
resource_class: xlarge # Total time decrease linearly with the number of CPUs.
|
||||
docker:
|
||||
- image: cimg/node:16.13
|
||||
|
||||
working_directory: ~/GDevelop
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- aws-cli/setup
|
||||
|
||||
# System dependencies (for Emscripten)
|
||||
- run:
|
||||
name: Install dependencies for Emscripten
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install dependencies for clang-tidy v19
|
||||
command: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 19 && sudo apt install clang-tidy-19
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- gdevelop.js-linux-nodejs-dependencies-{{ checksum "GDevelop.js/package-lock.json" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- gdevelop.js-linux-nodejs-dependencies-
|
||||
|
||||
- run:
|
||||
name: Install GDevelop.js dependencies and build it
|
||||
command: cd GDevelop.js && npm install && cd ..
|
||||
|
||||
# Build GDevelop.js
|
||||
- run:
|
||||
name: Build GDevelop.js ('debug-sanitizers' variant)
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build -- --variant=debug-sanitizers
|
||||
|
||||
- run:
|
||||
name: Run clang-tidy
|
||||
command: cd GDevelop.js && npm run lint
|
||||
|
||||
- run:
|
||||
name: Run tests
|
||||
command: cd GDevelop.js && npm run test -- --maxWorkers=4
|
||||
|
||||
# Upload artifacts (CircleCI)
|
||||
- store_artifacts:
|
||||
path: Binaries/embuild/GDevelop.js
|
||||
|
||||
# Upload artifacts (AWS)
|
||||
- run:
|
||||
name: Deploy to S3 (specific commit)
|
||||
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/variant/debug-sanitizers/commit/$(git rev-parse HEAD)/
|
||||
|
||||
workflows:
|
||||
builds:
|
||||
gdevelop_js-wasm:
|
||||
jobs:
|
||||
- build-gdevelop_js-wasm-only
|
||||
gdevelop_js-wasm-extra-checks:
|
||||
jobs:
|
||||
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
|
||||
# Extra checks are resource intensive so don't all run them.
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /experimental-build.*/
|
||||
builds:
|
||||
jobs:
|
||||
- build-macos:
|
||||
filters:
|
||||
branches:
|
||||
|
4
.clang-tidy
Normal file
4
.clang-tidy
Normal file
@@ -0,0 +1,4 @@
|
||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,-cppcoreguidelines-explicit-virtual-functions,-cppcoreguidelines-avoid-const-or-ref-data-members,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-owning-memory,-cppcoreguidelines-virtual-class-destructor,-clang-analyzer-optin.performance.Padding,-cppcoreguidelines-narrowing-conversions'
|
||||
WarningsAsErrors: 'cppcoreguidelines-pro-type-member-init, clang-analyzer-optin.cplusplus.UninitializedObject'
|
||||
HeaderFilterRegex: '.*'
|
||||
FormatStyle: none
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -114,7 +114,8 @@
|
||||
"__bits": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"variant": "cpp",
|
||||
"charconv": "cpp"
|
||||
"charconv": "cpp",
|
||||
"execution": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -69,12 +69,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# uninitialized variables or other hard to debug bugs.
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wuninitialized
|
||||
-Wconditional-uninitialized
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-reorder-ctor
|
||||
-Wno-reorder
|
||||
-Wno-unused-parameter
|
||||
-Wno-pessimizing-move
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-variable # Not a good style, but not a risk
|
||||
-Wno-unused-private-field
|
||||
-Wno-ignored-qualifiers # Not a risk
|
||||
-Wno-sign-compare # Not a big risk
|
||||
|
||||
# Make as much warnings considered as errors as possible (only one for now).
|
||||
-Werror=return-stack-address
|
||||
|
@@ -72,8 +72,6 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
|
||||
///< de/activate infinite loop warning when the
|
||||
///< user create the event
|
||||
|
||||
mutable unsigned int whileConditionsHeight;
|
||||
|
||||
int GetConditionsHeight() const;
|
||||
int GetActionsHeight() const;
|
||||
int GetWhileConditionsHeight() const;
|
||||
|
@@ -14,10 +14,10 @@
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief
|
||||
* \brief
|
||||
*/
|
||||
class GD_CORE_API ProjectDiagnostic {
|
||||
public:
|
||||
public:
|
||||
enum ErrorType {
|
||||
UndeclaredVariable,
|
||||
MissingBehavior,
|
||||
@@ -25,12 +25,17 @@ public:
|
||||
MismatchedObjectType,
|
||||
};
|
||||
|
||||
ProjectDiagnostic(ErrorType type_, const gd::String &message_,
|
||||
ProjectDiagnostic(ErrorType type_,
|
||||
const gd::String &message_,
|
||||
const gd::String &actualValue_,
|
||||
const gd::String &expectedValue_, const gd::String &objectName_ = "")
|
||||
: type(type_), message(message_), actualValue(actualValue_), expectedValue(expectedValue_),
|
||||
objectName(objectName_){};
|
||||
virtual ~ProjectDiagnostic(){};
|
||||
const gd::String &expectedValue_,
|
||||
const gd::String &objectName_ = "")
|
||||
: type(type_),
|
||||
message(message_),
|
||||
actualValue(actualValue_),
|
||||
expectedValue(expectedValue_),
|
||||
objectName(objectName_) {};
|
||||
virtual ~ProjectDiagnostic() {};
|
||||
|
||||
ErrorType GetType() const { return type; };
|
||||
const gd::String &GetMessage() const { return message; }
|
||||
@@ -38,7 +43,7 @@ public:
|
||||
const gd::String &GetActualValue() const { return actualValue; }
|
||||
const gd::String &GetExpectedValue() const { return expectedValue; }
|
||||
|
||||
private:
|
||||
private:
|
||||
ErrorType type;
|
||||
gd::String message;
|
||||
gd::String objectName;
|
||||
@@ -47,12 +52,12 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief
|
||||
* \brief
|
||||
*/
|
||||
class GD_CORE_API DiagnosticReport {
|
||||
public:
|
||||
DiagnosticReport(){};
|
||||
virtual ~DiagnosticReport(){};
|
||||
public:
|
||||
DiagnosticReport() {};
|
||||
virtual ~DiagnosticReport() {};
|
||||
|
||||
void Add(const gd::ProjectDiagnostic &projectDiagnostic) {
|
||||
projectDiagnostics.push_back(
|
||||
@@ -67,32 +72,39 @@ public:
|
||||
|
||||
const gd::String &GetSceneName() const { return sceneName; }
|
||||
|
||||
void SetSceneName(const gd::String &sceneName_) {
|
||||
sceneName = sceneName_;
|
||||
void SetSceneName(const gd::String &sceneName_) { sceneName = sceneName_; }
|
||||
|
||||
void LogAllDiagnostics() {
|
||||
for (auto &diagnostic : projectDiagnostics) {
|
||||
std::cout << diagnostic->GetMessage()
|
||||
<< "(object: " << diagnostic->GetObjectName()
|
||||
<< ", actual value: " << diagnostic->GetActualValue()
|
||||
<< ", expected value: " << diagnostic->GetExpectedValue() << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<std::unique_ptr<gd::ProjectDiagnostic>> projectDiagnostics;
|
||||
gd::String sceneName;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief
|
||||
* \brief
|
||||
*/
|
||||
class GD_CORE_API WholeProjectDiagnosticReport {
|
||||
public:
|
||||
WholeProjectDiagnosticReport(){};
|
||||
virtual ~WholeProjectDiagnosticReport(){};
|
||||
public:
|
||||
WholeProjectDiagnosticReport() {};
|
||||
virtual ~WholeProjectDiagnosticReport() {};
|
||||
|
||||
const DiagnosticReport &Get(std::size_t index) const {
|
||||
return *diagnosticReports[index].get();
|
||||
};
|
||||
|
||||
void Clear() {
|
||||
diagnosticReports.clear();
|
||||
};
|
||||
void Clear() { diagnosticReports.clear(); };
|
||||
|
||||
DiagnosticReport& AddNewDiagnosticReportForScene(const gd::String &sceneName) {
|
||||
DiagnosticReport &AddNewDiagnosticReportForScene(
|
||||
const gd::String &sceneName) {
|
||||
auto diagnosticReport = gd::make_unique<gd::DiagnosticReport>();
|
||||
diagnosticReport->SetSceneName(sceneName);
|
||||
diagnosticReports.push_back(std::move(diagnosticReport));
|
||||
@@ -102,7 +114,7 @@ public:
|
||||
std::size_t Count() const { return diagnosticReports.size(); };
|
||||
|
||||
bool HasAnyIssue() {
|
||||
for (auto& diagnosticReport : diagnosticReports) {
|
||||
for (auto &diagnosticReport : diagnosticReports) {
|
||||
if (diagnosticReport->Count() > 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -110,8 +122,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<std::unique_ptr<gd::DiagnosticReport>> diagnosticReports;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
} // namespace gd
|
||||
|
@@ -345,14 +345,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,7 @@ void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
|
||||
actualBehaviorType, expectedBehaviorType, lastObjectName);
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -567,14 +567,14 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
|
||||
objectInParameter, "");
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!expectedObjectType.empty() &&
|
||||
actualObjectType != expectedObjectType) {
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
|
||||
actualObjectType, expectedObjectType, objectInParameter);
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
}
|
||||
|
@@ -36,8 +36,8 @@ struct GD_CORE_API ExpressionParserLocation {
|
||||
|
||||
private:
|
||||
bool isValid;
|
||||
size_t startPosition;
|
||||
size_t endPosition;
|
||||
size_t startPosition = 0;
|
||||
size_t endPosition = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1768,6 +1768,22 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("expression",
|
||||
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.SetHidden()
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("IsTurnedTowardObject",
|
||||
_("An object is turned toward another"),
|
||||
_("Check if an object is turned toward another"),
|
||||
_("_PARAM0_ is turned toward _PARAM1_ ± _PARAM2_°"),
|
||||
_("Angle"),
|
||||
"res/conditions/estTourne24.png",
|
||||
"res/conditions/estTourne.png")
|
||||
.AddParameter("objectList", _("Name of the object"))
|
||||
.AddParameter("objectList", _("Name of the second object"))
|
||||
.AddParameter("expression",
|
||||
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
|
@@ -338,6 +338,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
|
||||
.SetDefaultValue("0");
|
||||
|
||||
// Deprecated
|
||||
extension
|
||||
.AddCondition(
|
||||
"PopStartedTouch",
|
||||
@@ -354,6 +355,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetHidden();
|
||||
|
||||
// Deprecated
|
||||
extension
|
||||
.AddCondition(
|
||||
"PopEndedTouch",
|
||||
@@ -370,6 +372,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetHidden();
|
||||
|
||||
// Deprecated
|
||||
extension
|
||||
.AddCondition(
|
||||
"HasAnyTouchStarted",
|
||||
|
@@ -33,6 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
"most elements of a game."),
|
||||
"CppPlatform/Extensions/spriteicon.png")
|
||||
.SetCategoryFullName(_("General"))
|
||||
.SetOpenFullEditorLabel(_("Edit animations"))
|
||||
.AddDefaultBehavior("EffectCapability::EffectBehavior")
|
||||
.AddDefaultBehavior("ResizableCapability::ResizableBehavior")
|
||||
.AddDefaultBehavior("ScalableCapability::ScalableBehavior")
|
||||
|
@@ -47,19 +47,11 @@ void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties()
|
||||
const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Animate even if hidden or far from the screen")]
|
||||
.SetValue(updateIfNotVisible ? "true" : "false")
|
||||
.SetType("Boolean");
|
||||
properties["PLEASE_ALSO_SHOW_EDIT_BUTTON_THANKS"].SetValue("");
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool SpriteObject::UpdateProperty(const gd::String& name,
|
||||
const gd::String& value) {
|
||||
if (name == _("Animate even if hidden or far from the screen"))
|
||||
updateIfNotVisible = value == "1";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -37,8 +37,7 @@ BehaviorMetadata::BehaviorMetadata(
|
||||
className(className_),
|
||||
iconFilename(icon24x24),
|
||||
instance(instance_),
|
||||
sharedDatasInstance(sharedDatasInstance_),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {
|
||||
sharedDatasInstance(sharedDatasInstance_) {
|
||||
SetFullName(gd::String(fullname_));
|
||||
SetDescription(gd::String(description_));
|
||||
SetDefaultName(gd::String(defaultName_));
|
||||
|
@@ -307,6 +307,15 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
return *this;
|
||||
}
|
||||
|
||||
BehaviorMetadata &SetOpenFullEditorLabel(const gd::String& label) {
|
||||
openFullEditorLabel = label;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetOpenFullEditorLabel() const {
|
||||
return openFullEditorLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the associated gd::Behavior, handling behavior contents.
|
||||
*
|
||||
@@ -384,7 +393,8 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
mutable std::vector<gd::String> requiredBehaviors;
|
||||
bool isPrivate = false;
|
||||
bool isHidden = false;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
gd::String openFullEditorLabel;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
|
||||
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
|
||||
std::shared_ptr<gd::Behavior> instance;
|
||||
|
@@ -185,10 +185,10 @@ class GD_CORE_API EffectMetadata {
|
||||
gd::String fullname;
|
||||
gd::String description;
|
||||
std::vector<gd::String> includeFiles;
|
||||
bool isMarkedAsNotWorkingForObjects;
|
||||
bool isMarkedAsOnlyWorkingFor2D;
|
||||
bool isMarkedAsOnlyWorkingFor3D;
|
||||
bool isMarkedAsUnique;
|
||||
bool isMarkedAsNotWorkingForObjects = false;
|
||||
bool isMarkedAsOnlyWorkingFor2D = false;
|
||||
bool isMarkedAsOnlyWorkingFor3D = false;
|
||||
bool isMarkedAsUnique = false;
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
};
|
||||
|
||||
|
@@ -19,7 +19,8 @@ EventMetadata::EventMetadata(const gd::String &name_,
|
||||
: fullname(fullname_),
|
||||
description(description_),
|
||||
group(group_),
|
||||
instance(instance_) {
|
||||
instance(instance_),
|
||||
hasCustomCodeGenerator(false) {
|
||||
ClearCodeGenerationAndPreprocessing();
|
||||
if (instance) instance->SetType(name_);
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ class GD_CORE_API EventMetadata {
|
||||
gd::String group;
|
||||
|
||||
std::shared_ptr<gd::BaseEvent> instance;
|
||||
bool hasCustomCodeGenerator;
|
||||
bool hasCustomCodeGenerator = false;
|
||||
std::function<gd::String(gd::BaseEvent& event,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context)>
|
||||
|
@@ -288,8 +288,8 @@ class GD_CORE_API MetadataProvider {
|
||||
static EffectMetadata badEffectMetadata;
|
||||
static gd::InstructionMetadata badInstructionMetadata;
|
||||
static gd::ExpressionMetadata badExpressionMetadata;
|
||||
int useless; // Useless member to avoid emscripten "must have a positive
|
||||
// integer typeid pointer" runtime error.
|
||||
int useless = 0; // Useless member to avoid emscripten "must have a positive
|
||||
// integer typeid pointer" runtime error.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -323,6 +323,15 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
*/
|
||||
bool IsRenderedIn3D() const { return isRenderedIn3D; }
|
||||
|
||||
ObjectMetadata &SetOpenFullEditorLabel(const gd::String& label) {
|
||||
openFullEditorLabel = label;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gd::String& GetOpenFullEditorLabel() const {
|
||||
return openFullEditorLabel;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
|
||||
@@ -344,6 +353,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
std::set<gd::String> defaultBehaviorTypes;
|
||||
bool hidden = false;
|
||||
bool isRenderedIn3D = false;
|
||||
gd::String openFullEditorLabel;
|
||||
|
||||
std::shared_ptr<gd::ObjectConfiguration>
|
||||
blueprintObject; ///< The "blueprint" object to be copied when a new
|
||||
|
@@ -53,20 +53,23 @@ const gd::String &ValueTypeMetadata::GetExpressionPrimitiveValueType(
|
||||
|
||||
const gd::String &
|
||||
ValueTypeMetadata::GetPrimitiveValueType(const gd::String ¶meterType) {
|
||||
if (parameterType == "variable" ||
|
||||
gd::ValueTypeMetadata::IsTypeExpression("variable", parameterType)) {
|
||||
return ValueTypeMetadata::variableType;
|
||||
if (parameterType == "number" ||
|
||||
gd::ValueTypeMetadata::IsTypeValue("number", parameterType)) {
|
||||
return ValueTypeMetadata::numberType;
|
||||
}
|
||||
if (parameterType == "boolean" || parameterType == "yesorno" ||
|
||||
parameterType == "trueorfalse") {
|
||||
return ValueTypeMetadata::booleanType;
|
||||
}
|
||||
// These 2 types are not strings from the code generator point of view,
|
||||
// but it is for event-based extensions.
|
||||
if (parameterType == "key" || parameterType == "mouse") {
|
||||
if (parameterType == "string" ||
|
||||
gd::ValueTypeMetadata::IsTypeValue("string", parameterType)) {
|
||||
return ValueTypeMetadata::stringType;
|
||||
}
|
||||
return GetExpressionPrimitiveValueType(parameterType);
|
||||
if (parameterType == "variable" ||
|
||||
gd::ValueTypeMetadata::IsTypeValue("variable", parameterType)) {
|
||||
return ValueTypeMetadata::variableType;
|
||||
}
|
||||
if (parameterType == "boolean" ||
|
||||
gd::ValueTypeMetadata::IsTypeValue("boolean", parameterType)) {
|
||||
return ValueTypeMetadata::booleanType;
|
||||
}
|
||||
return parameterType;
|
||||
}
|
||||
|
||||
const gd::String ValueTypeMetadata::numberValueType = "number";
|
||||
|
@@ -111,21 +111,21 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
* given type.
|
||||
*/
|
||||
bool IsNumber() const {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("number", name);
|
||||
return gd::ValueTypeMetadata::IsTypeValue("number", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is a string.
|
||||
*/
|
||||
bool IsString() const {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
|
||||
return gd::ValueTypeMetadata::IsTypeValue("string", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is a boolean.
|
||||
*/
|
||||
bool IsBoolean() const {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("boolean", name);
|
||||
return gd::ValueTypeMetadata::IsTypeValue("boolean", name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +135,7 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
|
||||
*/
|
||||
bool IsVariable() const {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
|
||||
return gd::ValueTypeMetadata::GetPrimitiveValueType(name) == "variable";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +175,9 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is an expression of the given type.
|
||||
* \brief Return true if the type is an expression of the given type from the
|
||||
* caller point of view.
|
||||
*
|
||||
* \note If you are adding a new type of parameter, also add it in the IDE (
|
||||
* see EventsFunctionParametersEditor, ParameterRenderingService
|
||||
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
|
||||
@@ -186,6 +188,7 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
return parameterType == "number" || parameterType == "expression" ||
|
||||
parameterType == "camera" || parameterType == "forceMultiplier";
|
||||
} else if (type == "string") {
|
||||
// "key" and "mouse" are not mapped her, see GetPrimitiveValueType.
|
||||
return parameterType == "string" || parameterType == "layer" ||
|
||||
parameterType == "color" || parameterType == "file" ||
|
||||
parameterType == "stringWithSelector" ||
|
||||
@@ -227,6 +230,26 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is a value of the given primitive type from
|
||||
* the function events point of view
|
||||
*/
|
||||
static bool IsTypeValue(const gd::String &type,
|
||||
const gd::String ¶meterType) {
|
||||
if (gd::ValueTypeMetadata::IsTypeExpression(type, parameterType)) {
|
||||
return true;
|
||||
}
|
||||
// These 2 parameter types are not strings from the outside of a function as
|
||||
// the generator add quote around a text, but from the events inside of the
|
||||
// function the parameter is a string.
|
||||
//
|
||||
// See EventsCodeGenerator::GenerateParameterCodes
|
||||
if (type == "string") {
|
||||
return parameterType == "key" || parameterType == "mouse";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the expression type from the parameter type.
|
||||
* Declinations of "number" and "string" types (like "forceMultiplier" or
|
||||
|
@@ -808,6 +808,24 @@ gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
|
||||
return extensionName + separator + objectName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetExtensionFromFullObjectType(const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
type.find(PlatformExtension::GetNamespaceSeparator());
|
||||
if (separatorIndex == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return type.substr(0, separatorIndex);
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetObjectNameFromFullObjectType(const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
type.find(PlatformExtension::GetNamespaceSeparator());
|
||||
if (separatorIndex == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return type.substr(separatorIndex + 2);
|
||||
}
|
||||
|
||||
PlatformExtension::PlatformExtension()
|
||||
: deprecated(false), category(_("General")) {}
|
||||
|
||||
|
@@ -40,8 +40,7 @@ class Object;
|
||||
class ObjectConfiguration;
|
||||
} // namespace gd
|
||||
|
||||
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
|
||||
CreateFunPtr;
|
||||
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()> CreateFunPtr;
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -51,25 +50,25 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API CompilationInfo {
|
||||
public:
|
||||
CompilationInfo() : informationCompleted(false){};
|
||||
virtual ~CompilationInfo(){};
|
||||
CompilationInfo() {};
|
||||
virtual ~CompilationInfo() {};
|
||||
|
||||
bool informationCompleted;
|
||||
bool informationCompleted = false;
|
||||
|
||||
bool runtimeOnly; ///< True if the extension was compiled for a runtime use
|
||||
///< only
|
||||
bool runtimeOnly = false; ///< True if the extension was compiled for a
|
||||
///< runtime use only
|
||||
|
||||
#if defined(__GNUC__)
|
||||
int gccMajorVersion;
|
||||
int gccMinorVersion;
|
||||
int gccPatchLevel;
|
||||
int gccMajorVersion = 0;
|
||||
int gccMinorVersion = 0;
|
||||
int gccPatchLevel = 0;
|
||||
#endif
|
||||
|
||||
int sfmlMajorVersion;
|
||||
int sfmlMinorVersion;
|
||||
int sfmlMajorVersion = 0;
|
||||
int sfmlMinorVersion = 0;
|
||||
|
||||
gd::String gdCoreVersion;
|
||||
int sizeOfpInt;
|
||||
int sizeOfpInt = 0;
|
||||
};
|
||||
|
||||
struct GD_CORE_API DuplicatedInstructionOptions {
|
||||
@@ -239,11 +238,12 @@ class GD_CORE_API PlatformExtension {
|
||||
* \param instance The "blueprint" object to be copied when a new object is
|
||||
asked for.
|
||||
*/
|
||||
gd::ObjectMetadata& AddObject(const gd::String& name_,
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& icon_,
|
||||
std::shared_ptr<gd::ObjectConfiguration> instance);
|
||||
gd::ObjectMetadata& AddObject(
|
||||
const gd::String& name_,
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& icon_,
|
||||
std::shared_ptr<gd::ObjectConfiguration> instance);
|
||||
|
||||
/**
|
||||
* \brief Declare a new events based object as being part of the extension.
|
||||
@@ -253,11 +253,10 @@ class GD_CORE_API PlatformExtension {
|
||||
* \param description The user friendly description of the object
|
||||
* \param icon The icon of the object.
|
||||
*/
|
||||
gd::ObjectMetadata& AddEventsBasedObject(
|
||||
const gd::String& name_,
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& icon_);
|
||||
gd::ObjectMetadata& AddEventsBasedObject(const gd::String& name_,
|
||||
const gd::String& fullname_,
|
||||
const gd::String& description_,
|
||||
const gd::String& icon_);
|
||||
|
||||
/**
|
||||
* \brief Declare a new behavior as being part of the extension.
|
||||
@@ -420,8 +419,7 @@ class GD_CORE_API PlatformExtension {
|
||||
PlatformExtension& SetTags(const gd::String& csvTags) {
|
||||
tags.clear();
|
||||
tags = csvTags.Split(',');
|
||||
for (size_t i = 0; i < tags.size(); i++)
|
||||
{
|
||||
for (size_t i = 0; i < tags.size(); i++) {
|
||||
tags[i] = tags[i].Trim().LowerCase();
|
||||
}
|
||||
return *this;
|
||||
@@ -634,26 +632,30 @@ class GD_CORE_API PlatformExtension {
|
||||
*/
|
||||
static gd::String GetNamespaceSeparator() { return "::"; }
|
||||
|
||||
static gd::String GetEventsFunctionFullType(const gd::String &extensionName,
|
||||
const gd::String &functionName);
|
||||
static gd::String GetEventsFunctionFullType(const gd::String& extensionName,
|
||||
const gd::String& functionName);
|
||||
|
||||
static gd::String
|
||||
GetBehaviorEventsFunctionFullType(const gd::String &extensionName,
|
||||
const gd::String &behaviorName,
|
||||
const gd::String &functionName);
|
||||
static gd::String GetBehaviorEventsFunctionFullType(
|
||||
const gd::String& extensionName,
|
||||
const gd::String& behaviorName,
|
||||
const gd::String& functionName);
|
||||
|
||||
static gd::String GetBehaviorFullType(const gd::String &extensionName,
|
||||
const gd::String &behaviorName);
|
||||
static gd::String GetBehaviorFullType(const gd::String& extensionName,
|
||||
const gd::String& behaviorName);
|
||||
|
||||
static gd::String
|
||||
GetObjectEventsFunctionFullType(const gd::String &extensionName,
|
||||
const gd::String &objectName,
|
||||
const gd::String &functionName);
|
||||
static gd::String GetObjectEventsFunctionFullType(
|
||||
const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
const gd::String& functionName);
|
||||
|
||||
static gd::String GetObjectFullType(const gd::String &extensionName,
|
||||
const gd::String &objectName);
|
||||
static gd::String GetObjectFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName);
|
||||
|
||||
private:
|
||||
static gd::String GetExtensionFromFullObjectType(const gd::String& type);
|
||||
|
||||
static gd::String GetObjectNameFromFullObjectType(const gd::String& type);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Set the namespace (the string all actions/conditions/expressions start
|
||||
* with).
|
||||
@@ -668,10 +670,10 @@ private:
|
||||
gd::String fullname; ///< Name displayed to users in the editor.
|
||||
gd::String informations; ///< Description displayed to users in the editor.
|
||||
gd::String category;
|
||||
gd::String author; ///< Author displayed to users in the editor.
|
||||
gd::String license; ///< License name displayed to users in the editor.
|
||||
bool deprecated; ///< true if the extension is deprecated and shouldn't be
|
||||
///< shown in IDE.
|
||||
gd::String author; ///< Author displayed to users in the editor.
|
||||
gd::String license; ///< License name displayed to users in the editor.
|
||||
bool deprecated; ///< true if the extension is deprecated and shouldn't be
|
||||
///< shown in IDE.
|
||||
gd::String helpPath; ///< The relative path to the help for this extension in
|
||||
///< the documentation.
|
||||
gd::String iconUrl; ///< The URL to the icon to be shown for this extension.
|
||||
|
@@ -5,6 +5,8 @@
|
||||
* project is released under the MIT License.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
||||
#ifndef GDCORE_PLATFORMEXTENSION_INL
|
||||
#define GDCORE_PLATFORMEXTENSION_INL
|
||||
|
||||
@@ -36,3 +38,5 @@ gd::ObjectMetadata& PlatformExtension::AddObject(const gd::String& name,
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
||||
|
||||
// NOLINTEND
|
@@ -29,9 +29,6 @@ void EventBasedBehaviorBrowser::ExposeEvents(
|
||||
project, eventsFunctionsExtension, eventsBasedBehavior, worker);
|
||||
}
|
||||
|
||||
void EventBasedBehaviorBrowser::ExposeObjects(
|
||||
gd::Project &project, gd::ArbitraryObjectsWorker &worker) const {}
|
||||
|
||||
void EventBasedBehaviorBrowser::ExposeFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
|
||||
worker.Launch(eventsBasedBehavior.GetEventsFunctions());
|
||||
@@ -43,7 +40,4 @@ void EventBasedBehaviorBrowser::ExposeEventBasedBehaviors(
|
||||
worker.Launch(eventsBasedBehavior);
|
||||
}
|
||||
|
||||
void EventBasedBehaviorBrowser::ExposeBehaviorSharedDatas(
|
||||
gd::Project &project, gd::ArbitraryBehaviorSharedDataWorker &worker) const {}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -67,7 +67,7 @@ public:
|
||||
* \brief Do nothing.
|
||||
*/
|
||||
void ExposeObjects(gd::Project &project,
|
||||
gd::ArbitraryObjectsWorker &worker) const override;
|
||||
gd::ArbitraryObjectsWorker &worker) const override {};
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on the event-based behavior.
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
* \brief Do nothing.
|
||||
*/
|
||||
void ExposeBehaviorSharedDatas(gd::Project &project,
|
||||
gd::ArbitraryBehaviorSharedDataWorker &worker) const override;
|
||||
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
|
||||
|
||||
private:
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension;
|
||||
|
37
Core/GDCore/IDE/EventBasedObjectBrowser.cpp
Normal file
37
Core/GDCore/IDE/EventBasedObjectBrowser.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "EventBasedObjectBrowser.h"
|
||||
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
|
||||
#include "GDCore/IDE/ProjectBrowserHelper.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void EventBasedObjectBrowser::ExposeEvents(
|
||||
gd::Project &project, gd::ArbitraryEventsWorker &worker) const {
|
||||
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
project, eventsBasedObject, worker);
|
||||
}
|
||||
|
||||
void EventBasedObjectBrowser::ExposeEvents(
|
||||
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) const {
|
||||
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
project, eventsFunctionsExtension, eventsBasedObject, worker);
|
||||
}
|
||||
|
||||
void EventBasedObjectBrowser::ExposeFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
|
||||
worker.Launch(eventsBasedObject.GetEventsFunctions());
|
||||
}
|
||||
|
||||
} // namespace gd
|
90
Core/GDCore/IDE/EventBasedObjectBrowser.h
Normal file
90
Core/GDCore/IDE/EventBasedObjectBrowser.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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/IDE/ProjectBrowser.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class String;
|
||||
class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
class EventsBasedBehavior;
|
||||
class EventsBasedObject;
|
||||
class ArbitraryEventsWorker;
|
||||
class ArbitraryEventsWorkerWithContext;
|
||||
class ArbitraryEventsFunctionsWorker;
|
||||
class ArbitraryObjectsWorker;
|
||||
class ArbitraryEventBasedBehaviorsWorker;
|
||||
class ArbitraryBehaviorSharedDataWorker;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Expose event-based object contents to workers.
|
||||
*/
|
||||
class GD_CORE_API EventBasedObjectBrowser : public ProjectBrowser {
|
||||
public:
|
||||
EventBasedObjectBrowser(
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension_,
|
||||
gd::EventsBasedObject &eventsBasedObject_)
|
||||
: eventsFunctionsExtension(eventsFunctionsExtension_),
|
||||
eventsBasedObject(eventsBasedObject_) {}
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an event-based object.
|
||||
*/
|
||||
void ExposeEvents(gd::Project &project,
|
||||
gd::ArbitraryEventsWorker &worker) const override;
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an event-based object.
|
||||
*/
|
||||
void
|
||||
ExposeEvents(gd::Project &project,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) const override;
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all functions of the event-based object
|
||||
*
|
||||
* This should be the preferred way to traverse all the function signatures
|
||||
* of an event-based object.
|
||||
*/
|
||||
void ExposeFunctions(gd::Project &project,
|
||||
gd::ArbitraryEventsFunctionsWorker &worker) const override;
|
||||
|
||||
/**
|
||||
* \brief Do nothing.
|
||||
*/
|
||||
void ExposeObjects(gd::Project &project,
|
||||
gd::ArbitraryObjectsWorker &worker) const override {};
|
||||
|
||||
/**
|
||||
* @brief Do nothing.
|
||||
*/
|
||||
void ExposeEventBasedBehaviors(
|
||||
gd::Project &project,
|
||||
gd::ArbitraryEventBasedBehaviorsWorker &worker) const override {};
|
||||
|
||||
/**
|
||||
* \brief Do nothing.
|
||||
*/
|
||||
void ExposeBehaviorSharedDatas(gd::Project &project,
|
||||
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
|
||||
|
||||
private:
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension;
|
||||
gd::EventsBasedObject &eventsBasedObject;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -337,19 +337,19 @@ struct GD_CORE_API ExpressionCompletionDescription {
|
||||
|
||||
private:
|
||||
CompletionKind completionKind;
|
||||
gd::Variable::Type variableType;
|
||||
gd::VariablesContainer::SourceType variableScope;
|
||||
gd::Variable::Type variableType = gd::Variable::Unknown;
|
||||
gd::VariablesContainer::SourceType variableScope = gd::VariablesContainer::Unknown;
|
||||
gd::String type;
|
||||
gd::String prefix;
|
||||
gd::String completion;
|
||||
size_t replacementStartPosition;
|
||||
size_t replacementEndPosition;
|
||||
size_t replacementStartPosition = 0;
|
||||
size_t replacementEndPosition = 0;
|
||||
gd::String objectName;
|
||||
gd::String behaviorName;
|
||||
bool isExact;
|
||||
bool isLastParameter;
|
||||
const gd::ParameterMetadata* parameterMetadata;
|
||||
const gd::ObjectConfiguration* objectConfiguration;
|
||||
bool isExact = false;
|
||||
bool isLastParameter = false;
|
||||
const gd::ParameterMetadata* parameterMetadata = &badParameterMetadata;
|
||||
const gd::ObjectConfiguration* objectConfiguration = &badObjectConfiguration;
|
||||
|
||||
static const gd::ParameterMetadata badParameterMetadata;
|
||||
static const gd::ObjectConfiguration badObjectConfiguration;
|
||||
|
@@ -33,8 +33,10 @@ class GD_CORE_API ExpressionNodeLocationFinder
|
||||
* \brief Initialize the finder to search at the specified position.
|
||||
*/
|
||||
ExpressionNodeLocationFinder(size_t searchedPosition_)
|
||||
: searchedPosition(searchedPosition_), foundNode(nullptr){};
|
||||
virtual ~ExpressionNodeLocationFinder(){};
|
||||
: searchedPosition(searchedPosition_),
|
||||
foundNode(nullptr),
|
||||
parentNode(nullptr) {};
|
||||
virtual ~ExpressionNodeLocationFinder() {};
|
||||
|
||||
/**
|
||||
* \brief Helper function to find the deepest node at the search position, if
|
||||
|
@@ -72,7 +72,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
child(nullptr) {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
return gd::ParameterMetadata::GetExpressionValueType(type);
|
||||
return gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(type);
|
||||
};
|
||||
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
|
@@ -42,7 +42,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
const gd::String &extraInfo_ = "")
|
||||
: platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
|
||||
parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
|
||||
childType(Type::Unknown),
|
||||
forbidsUsageOfBracketsBecauseParentIsObject(false),
|
||||
currentParameterExtraInfo(&extraInfo_) {};
|
||||
|
@@ -74,8 +74,8 @@ class GD_CORE_API ExpressionsParameterMover
|
||||
|
||||
const gd::Platform &platform;
|
||||
gd::String functionName;
|
||||
std::size_t oldIndex;
|
||||
std::size_t newIndex;
|
||||
std::size_t oldIndex = 0;
|
||||
std::size_t newIndex = 0;
|
||||
gd::String behaviorType;
|
||||
gd::String objectType;
|
||||
};
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
|
@@ -6,17 +6,9 @@
|
||||
#include "ResourceExposer.h"
|
||||
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/IDE/EventsFunctionTools.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/IDE/ProjectBrowserHelper.h"
|
||||
#include "GDCore/Project/EventsBasedBehavior.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/Effect.h"
|
||||
@@ -24,7 +16,6 @@
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "GDCore/IDE/DependenciesAnalyzer.h"
|
||||
#include "GDCore/IDE/GroupVariableHelper.h"
|
||||
#include "GDCore/IDE/EventBasedBehaviorBrowser.h"
|
||||
#include "GDCore/IDE/EventBasedObjectBrowser.h"
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/IDE/Events/BehaviorParametersFiller.h"
|
||||
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
|
||||
@@ -28,11 +29,6 @@
|
||||
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
|
||||
#include "GDCore/IDE/Events/LinkEventTargetRenamer.h"
|
||||
#include "GDCore/IDE/Events/ProjectElementRenamer.h"
|
||||
#include "GDCore/IDE/EventsFunctionTools.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/IDE/Project/BehaviorObjectTypeRenamer.h"
|
||||
#include "GDCore/IDE/Project/BehaviorsSharedDataBehaviorTypeRenamer.h"
|
||||
#include "GDCore/IDE/Project/FunctionParameterBehaviorTypeRenamer.h"
|
||||
@@ -415,20 +411,32 @@ void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &sourceExtensionName) {
|
||||
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
|
||||
const EventBasedBehaviorBrowser eventBasedBehaviorBrowser(
|
||||
eventsFunctionsExtension, eventsBasedBehavior);
|
||||
WholeProjectRefactorer::RenameEventsFunctionsExtension(
|
||||
project, eventsFunctionsExtension, sourceExtensionName,
|
||||
eventsFunctionsExtension.GetName(), eventBasedBehaviorExposer);
|
||||
eventsFunctionsExtension.GetName(), eventBasedBehaviorBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &sourceExtensionName) {
|
||||
const EventBasedObjectBrowser eventBasedObjectBrowser(
|
||||
eventsFunctionsExtension, eventsBasedObject);
|
||||
WholeProjectRefactorer::RenameEventsFunctionsExtension(
|
||||
project, eventsFunctionsExtension, sourceExtensionName,
|
||||
eventsFunctionsExtension.GetName(), eventBasedObjectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::String &oldName, const gd::String &newName) {
|
||||
const WholeProjectBrowser wholeProjectExposer;
|
||||
const WholeProjectBrowser wholeProjectBrowser;
|
||||
RenameEventsFunctionsExtension(project, eventsFunctionsExtension, oldName,
|
||||
newName, wholeProjectExposer);
|
||||
newName, wholeProjectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
|
||||
@@ -1312,6 +1320,19 @@ bool WholeProjectRefactorer::FixInvalidRequiredBehaviorProperties(
|
||||
return !invalidRequiredBehaviorProblems.empty();
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::UpdateBehaviorNameInEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &sourceBehaviorName) {
|
||||
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
|
||||
eventsFunctionsExtension, eventsBasedBehavior);
|
||||
WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
project, eventsFunctionsExtension, eventsBasedBehavior,
|
||||
sourceBehaviorName, eventsBasedBehavior.GetName(),
|
||||
eventBasedBehaviorExposer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
@@ -1324,10 +1345,22 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
return;
|
||||
}
|
||||
auto &eventsBasedBehavior = eventsBasedBehaviors.Get(oldBehaviorName);
|
||||
const WholeProjectBrowser projectBrowser;
|
||||
WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
project, eventsFunctionsExtension, eventsBasedBehavior, oldBehaviorName,
|
||||
newBehaviorName, projectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &oldBehaviorName,
|
||||
const gd::String &newBehaviorName,
|
||||
const gd::ProjectBrowser &projectBrowser) {
|
||||
auto renameBehaviorEventsFunction =
|
||||
[&project, &eventsFunctionsExtension, &oldBehaviorName,
|
||||
&newBehaviorName](const gd::EventsFunction &eventsFunction) {
|
||||
&newBehaviorName, &projectBrowser](const gd::EventsFunction &eventsFunction) {
|
||||
if (eventsFunction.IsExpression()) {
|
||||
// Nothing to do, expressions are not including the name of the
|
||||
// behavior
|
||||
@@ -1341,12 +1374,12 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName,
|
||||
eventsFunction.GetName()));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
|
||||
projectBrowser.ExposeEvents(project, renamer);
|
||||
}
|
||||
};
|
||||
|
||||
auto renameBehaviorProperty = [&project, &eventsFunctionsExtension,
|
||||
&oldBehaviorName, &newBehaviorName](
|
||||
&oldBehaviorName, &newBehaviorName, &projectBrowser](
|
||||
const gd::NamedPropertyDescriptor
|
||||
&property) {
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
@@ -1357,7 +1390,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName,
|
||||
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
|
||||
projectBrowser.ExposeEvents(project, actionRenamer);
|
||||
|
||||
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -1367,7 +1400,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName,
|
||||
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
|
||||
projectBrowser.ExposeEvents(project, conditionRenamer);
|
||||
|
||||
// Nothing to do for expression, expressions are not including the name of
|
||||
// the behavior
|
||||
@@ -1375,7 +1408,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
|
||||
auto renameBehaviorSharedProperty =
|
||||
[&project, &eventsFunctionsExtension, &oldBehaviorName,
|
||||
&newBehaviorName](const gd::NamedPropertyDescriptor &property) {
|
||||
&newBehaviorName, &projectBrowser](const gd::NamedPropertyDescriptor &property) {
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
@@ -1386,7 +1419,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName,
|
||||
EventsBasedBehavior::GetSharedPropertyActionName(
|
||||
property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
|
||||
projectBrowser.ExposeEvents(project, actionRenamer);
|
||||
|
||||
gd::InstructionsTypeRenamer conditionRenamer =
|
||||
gd::InstructionsTypeRenamer(
|
||||
@@ -1399,8 +1432,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName,
|
||||
EventsBasedBehavior::GetSharedPropertyConditionName(
|
||||
property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
conditionRenamer);
|
||||
projectBrowser.ExposeEvents(project, conditionRenamer);
|
||||
|
||||
// Nothing to do for expression, expressions are not including the name
|
||||
// of the behavior
|
||||
@@ -1435,13 +1467,25 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
|
||||
renameBehaviorSharedProperty(*property);
|
||||
}
|
||||
|
||||
const WholeProjectBrowser wholeProjectExposer;
|
||||
DoRenameBehavior(project,
|
||||
gd::PlatformExtension::GetBehaviorFullType(
|
||||
eventsFunctionsExtension.GetName(), oldBehaviorName),
|
||||
gd::PlatformExtension::GetBehaviorFullType(
|
||||
eventsFunctionsExtension.GetName(), newBehaviorName),
|
||||
wholeProjectExposer);
|
||||
projectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::UpdateObjectNameInEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &sourceObjectName) {
|
||||
const EventBasedObjectBrowser eventBasedObjectBrowser(
|
||||
eventsFunctionsExtension, eventsBasedObject);
|
||||
WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
project, eventsFunctionsExtension, eventsBasedObject,
|
||||
sourceObjectName, eventsBasedObject.GetName(),
|
||||
eventBasedObjectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
@@ -1455,10 +1499,21 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
return;
|
||||
}
|
||||
auto &eventsBasedObject = eventsBasedObjects.Get(oldObjectName);
|
||||
const WholeProjectBrowser projectBrowser;
|
||||
WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
project, eventsFunctionsExtension, eventsBasedObject, oldObjectName,
|
||||
newObjectName, projectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &oldObjectName, const gd::String &newObjectName,
|
||||
const gd::ProjectBrowser &projectBrowser) {
|
||||
auto renameObjectEventsFunction =
|
||||
[&project, &eventsFunctionsExtension, &oldObjectName,
|
||||
&newObjectName](const gd::EventsFunction &eventsFunction) {
|
||||
[&project, &eventsFunctionsExtension, &oldObjectName, &newObjectName,
|
||||
&projectBrowser](const gd::EventsFunction &eventsFunction) {
|
||||
if (eventsFunction.IsExpression()) {
|
||||
// Nothing to do, expressions are not including the name of the
|
||||
// object
|
||||
@@ -1472,12 +1527,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
gd::PlatformExtension::GetObjectEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newObjectName,
|
||||
eventsFunction.GetName()));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
|
||||
projectBrowser.ExposeEvents(project, renamer);
|
||||
}
|
||||
};
|
||||
|
||||
auto renameObjectProperty = [&project, &eventsFunctionsExtension,
|
||||
&oldObjectName, &newObjectName](
|
||||
&oldObjectName, &newObjectName, &projectBrowser](
|
||||
const gd::NamedPropertyDescriptor &property) {
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -1487,7 +1542,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
gd::PlatformExtension::GetObjectEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newObjectName,
|
||||
EventsBasedObject::GetPropertyActionName(property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
|
||||
projectBrowser.ExposeEvents(project, actionRenamer);
|
||||
|
||||
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -1497,7 +1552,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
gd::PlatformExtension::GetObjectEventsFunctionFullType(
|
||||
eventsFunctionsExtension.GetName(), newObjectName,
|
||||
EventsBasedObject::GetPropertyConditionName(property.GetName())));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
|
||||
projectBrowser.ExposeEvents(project, conditionRenamer);
|
||||
|
||||
// Nothing to do for expression, expressions are not including the name of
|
||||
// the object
|
||||
@@ -1528,13 +1583,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
|
||||
renameObjectProperty(*property);
|
||||
}
|
||||
|
||||
const WholeProjectBrowser wholeProjectExposer;
|
||||
DoRenameObject(project,
|
||||
gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(), oldObjectName),
|
||||
gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(), newObjectName),
|
||||
wholeProjectExposer);
|
||||
projectBrowser);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::DoRenameEventsFunction(
|
||||
|
@@ -27,14 +27,7 @@ class ObjectsContainer;
|
||||
class VariablesContainer;
|
||||
class EventsBasedBehavior;
|
||||
class EventsBasedObject;
|
||||
class ArbitraryEventsWorker;
|
||||
class ArbitraryObjectsWorker;
|
||||
class ArbitraryEventsFunctionsWorker;
|
||||
class ArbitraryEventsWorkerWithContext;
|
||||
class ArbitraryEventBasedBehaviorsWorker;
|
||||
class ArbitraryBehaviorSharedDataWorker;
|
||||
class Behavior;
|
||||
class BehaviorMetadata;
|
||||
class UnfilledRequiredBehaviorPropertyProblem;
|
||||
class ProjectBrowser;
|
||||
class SerializerElement;
|
||||
@@ -121,14 +114,24 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newName);
|
||||
|
||||
/**
|
||||
* \brief Refactor behavior events after the extension was placed in a new
|
||||
* \brief Refactor behavior events after the behavior has been placed in a new
|
||||
* extension.
|
||||
*/
|
||||
static void UpdateExtensionNameInEventsBasedBehavior(
|
||||
gd::Project& project,
|
||||
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
|
||||
gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::String& sourceExtensionName);
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &sourceExtensionName);
|
||||
|
||||
/**
|
||||
* \brief Refactor object events after the object has been placed in a new
|
||||
* extension.
|
||||
*/
|
||||
static void UpdateExtensionNameInEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &sourceExtensionName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project **before** an events function is renamed.
|
||||
@@ -324,6 +327,16 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& oldBehaviorName,
|
||||
const gd::String& newBehaviorName);
|
||||
|
||||
/**
|
||||
* \brief Refactor events-based behavior events after the events-based
|
||||
* behavior has been duplicated.
|
||||
*/
|
||||
static void UpdateBehaviorNameInEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &sourceBehaviorName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project **before** an object is renamed.
|
||||
*
|
||||
@@ -337,6 +350,16 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& oldObjectName,
|
||||
const gd::String& newObjectName);
|
||||
|
||||
/**
|
||||
* \brief Refactor events-based object events after the events-based object
|
||||
* has been duplicated.
|
||||
*/
|
||||
static void UpdateObjectNameInEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &sourceObjectName);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after a layout is renamed.
|
||||
*/
|
||||
@@ -654,6 +677,35 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
const gd::String& newName,
|
||||
const gd::ProjectBrowser& projectBrowser);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project **before** a behavior is renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified behavior after calling this.
|
||||
* This is because the behavior is expected to have its old name for the
|
||||
* refactoring.
|
||||
*/
|
||||
static void RenameEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &oldBehaviorName,
|
||||
const gd::String &newBehaviorName,
|
||||
const gd::ProjectBrowser &projectBrowser);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project **before** an object is renamed.
|
||||
*
|
||||
* \warning Do the renaming of the specified object after calling this.
|
||||
* This is because the object is expected to have its old name for the
|
||||
* refactoring.
|
||||
*/
|
||||
static void RenameEventsBasedObject(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::EventsBasedObject &eventsBasedObject,
|
||||
const gd::String &oldObjectName, const gd::String &newObjectName,
|
||||
const gd::ProjectBrowser &projectBrowser);
|
||||
|
||||
static void FindDependentBehaviorNames(
|
||||
const gd::Project& project,
|
||||
const gd::Object& object,
|
||||
|
@@ -8,8 +8,10 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -32,12 +34,21 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API BehaviorConfigurationContainer {
|
||||
public:
|
||||
BehaviorConfigurationContainer() : folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
BehaviorConfigurationContainer()
|
||||
: folded(false),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
|
||||
propertiesQuickCustomizationVisibilities() {};
|
||||
BehaviorConfigurationContainer(const gd::String& name_,
|
||||
const gd::String& type_)
|
||||
: name(name_), type(type_), folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
|
||||
: name(name_),
|
||||
type(type_),
|
||||
folded(false),
|
||||
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
|
||||
propertiesQuickCustomizationVisibilities() {};
|
||||
virtual ~BehaviorConfigurationContainer();
|
||||
virtual BehaviorConfigurationContainer* Clone() const { return new BehaviorConfigurationContainer(*this); }
|
||||
virtual BehaviorConfigurationContainer* Clone() const {
|
||||
return new BehaviorConfigurationContainer(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the name identifying the behavior
|
||||
@@ -68,7 +79,6 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
*/
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a custom property of the
|
||||
* behavior
|
||||
@@ -84,9 +94,7 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
* \brief Called to initialize the content with the default properties
|
||||
* for the behavior.
|
||||
*/
|
||||
virtual void InitializeContent() {
|
||||
InitializeContent(content);
|
||||
};
|
||||
virtual void InitializeContent() { InitializeContent(content); };
|
||||
|
||||
/**
|
||||
* \brief Serialize the behavior content.
|
||||
@@ -115,15 +123,42 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
void SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
|
||||
/**
|
||||
* @brief Set if the whole behavior should be visible or not in the Quick
|
||||
* Customization.
|
||||
*/
|
||||
void SetQuickCustomizationVisibility(
|
||||
QuickCustomization::Visibility visibility) {
|
||||
quickCustomizationVisibility = visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if the whole behavior should be visible or not in the Quick
|
||||
* Customization.
|
||||
*/
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Get the map of properties and their visibility in the Quick
|
||||
* Customization.
|
||||
*/
|
||||
QuickCustomizationVisibilitiesContainer&
|
||||
GetPropertiesQuickCustomizationVisibilities() {
|
||||
return propertiesQuickCustomizationVisibilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the map of properties and their visibility in the Quick
|
||||
* Customization.
|
||||
*/
|
||||
const QuickCustomizationVisibilitiesContainer&
|
||||
GetPropertiesQuickCustomizationVisibilities() const {
|
||||
return propertiesQuickCustomizationVisibilities;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Called when the IDE wants to know about the custom properties of the
|
||||
* behavior.
|
||||
@@ -159,7 +194,7 @@ protected:
|
||||
* \brief Called to initialize the content with the default properties
|
||||
* for the behavior.
|
||||
*/
|
||||
virtual void InitializeContent(gd::SerializerElement& behaviorContent){};
|
||||
virtual void InitializeContent(gd::SerializerElement& behaviorContent) {};
|
||||
|
||||
private:
|
||||
gd::String name; ///< Name of the behavior
|
||||
@@ -169,6 +204,8 @@ protected:
|
||||
gd::SerializerElement content; // Storage for the behavior properties
|
||||
bool folded;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
QuickCustomizationVisibilitiesContainer
|
||||
propertiesQuickCustomizationVisibilities;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -72,7 +72,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
|
||||
}
|
||||
|
||||
if (!eventsBasedObject->GetObjects().HasObjectNamed(objectName)) {
|
||||
gd::LogError("Tried to get the configuration of a child-object:" + objectName
|
||||
gd::LogError("Tried to get the configuration of a child-object: " + objectName
|
||||
+ " that doesn't exist in the event-based object: " + GetType());
|
||||
return badObjectConfiguration;
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
@@ -109,6 +110,27 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
|
||||
static const gd::CustomObjectConfiguration::EdgeAnchor
|
||||
GetEdgeAnchorFromString(const gd::String &value);
|
||||
|
||||
/**
|
||||
* Check if a child object properties must be displayed as folded in the editor.
|
||||
* This is only useful when the object can override its children configuration (which
|
||||
* is something being deprecated).
|
||||
*/
|
||||
bool IsChildObjectFolded(const gd::String& childName) const {
|
||||
return unfoldedChildren.find(childName) == unfoldedChildren.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if a child object properties must be displayed as folded in the editor.
|
||||
* This is only useful when the object can override its children configuration (which
|
||||
* is something being deprecated).
|
||||
*/
|
||||
void SetChildObjectFolded(const gd::String& childName, bool folded) {
|
||||
if (!folded)
|
||||
unfoldedChildren.insert(childName);
|
||||
else
|
||||
unfoldedChildren.erase(childName);
|
||||
}
|
||||
|
||||
protected:
|
||||
void DoSerializeTo(SerializerElement& element) const override;
|
||||
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
|
||||
@@ -118,11 +140,12 @@ protected:
|
||||
|
||||
bool IsOverridingEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
const Project* project; ///< The project is used to get the
|
||||
///< EventBasedObject from the fullType.
|
||||
const Project* project = nullptr; ///< The project is used to get the
|
||||
///< EventBasedObject from the fullType.
|
||||
gd::SerializerElement objectContent;
|
||||
|
||||
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
|
||||
std::unordered_set<gd::String> unfoldedChildren;
|
||||
|
||||
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration = false;
|
||||
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
|
||||
|
||||
static gd::ObjectConfiguration badObjectConfiguration;
|
||||
|
@@ -12,6 +12,7 @@ namespace gd {
|
||||
void Effect::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("effectType", GetEffectType());
|
||||
if (IsFolded()) element.SetBoolAttribute("folded", true);
|
||||
|
||||
SerializerElement& doubleParametersElement =
|
||||
element.AddChild("doubleParameters");
|
||||
@@ -41,6 +42,7 @@ void Effect::UnserializeFrom(const SerializerElement& element) {
|
||||
"effectName"
|
||||
// end of compatibility code
|
||||
));
|
||||
SetFolded(element.GetBoolAttribute("folded", false));
|
||||
|
||||
doubleParameters.clear();
|
||||
const SerializerElement& doubleParametersElement =
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "EventsFunction.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -98,8 +99,11 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
void EventsFunctionsExtension::UnserializeFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
UnserializeExtensionDeclarationFrom(project, element);
|
||||
UnserializeExtensionImplementationFrom(project, element);
|
||||
// Unserialize first the "declaration" (everything but objects content)
|
||||
// so that objects can be then unserialized in proper order (they can depend
|
||||
// on each others)
|
||||
UnserializeExtensionDeclarationFrom(project, element);
|
||||
UnserializeExtensionImplementationFrom(project, element);
|
||||
}
|
||||
|
||||
void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
|
||||
@@ -185,11 +189,93 @@ void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
|
||||
eventsBasedBehaviors.UnserializeElementsFrom(
|
||||
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
|
||||
|
||||
// It's important to load the objects without erasing them first as each object
|
||||
// might reference other objects, and so need to know if a custom object exists
|
||||
// in the project or not.
|
||||
eventsBasedObjects.ProgressivelyUnserializeElementsFrom(
|
||||
"eventsBasedObject", project, element.GetChild("eventsBasedObjects"));
|
||||
auto &eventsBasedObjectsElement = element.GetChild("eventsBasedObjects");
|
||||
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
|
||||
for (gd::String &eventsBasedObjectName :
|
||||
GetUnserializingOrderEventsBasedObjectNames(eventsBasedObjectsElement)) {
|
||||
size_t extensionIndex = eventsBasedObjects.GetPosition(
|
||||
eventsBasedObjects.Get(eventsBasedObjectName));
|
||||
const SerializerElement &eventsBasedObjectElement =
|
||||
eventsBasedObjectsElement.GetChild(extensionIndex);
|
||||
|
||||
eventsBasedObjects.at(extensionIndex)
|
||||
.UnserializeFrom(project, eventsBasedObjectElement);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<gd::String>
|
||||
EventsFunctionsExtension::GetUnserializingOrderEventsBasedObjectNames(
|
||||
const gd::SerializerElement &eventsBasedObjectsElement) {
|
||||
|
||||
// Child-objects need the event-based objects they use to be loaded completely
|
||||
// before they are unserialized.
|
||||
|
||||
// At the beginning, everything is yet to be loaded.
|
||||
std::vector<gd::String> remainingEventsBasedObjectNames(
|
||||
eventsBasedObjects.size());
|
||||
for (std::size_t i = 0; i < eventsBasedObjects.size(); ++i) {
|
||||
remainingEventsBasedObjectNames[i] = eventsBasedObjects.at(i).GetName();
|
||||
}
|
||||
|
||||
// Helper allowing to find if an object depends on at least one other object from
|
||||
// the extension that is not loaded yet.
|
||||
auto &extensionName = name;
|
||||
auto isDependentFromRemainingEventsBasedObjects =
|
||||
[&remainingEventsBasedObjectNames,
|
||||
&extensionName](const gd::SerializerElement &eventsBasedObjectElement) {
|
||||
auto &objectsElement = eventsBasedObjectElement.GetChild("objects");
|
||||
objectsElement.ConsiderAsArrayOf("object");
|
||||
|
||||
for (std::size_t objectIndex = 0;
|
||||
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
|
||||
const gd::String &objectType =
|
||||
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
|
||||
|
||||
gd::String usedExtensionName =
|
||||
PlatformExtension::GetExtensionFromFullObjectType(objectType);
|
||||
if (usedExtensionName != extensionName) {
|
||||
// The object comes from another extension: the project is already responsible
|
||||
// for loading extensions in the proper order.
|
||||
continue;
|
||||
}
|
||||
gd::String eventsBasedObjectName =
|
||||
gd::PlatformExtension::GetObjectNameFromFullObjectType(
|
||||
objectType);
|
||||
|
||||
if (std::find(remainingEventsBasedObjectNames.begin(),
|
||||
remainingEventsBasedObjectNames.end(),
|
||||
eventsBasedObjectName) !=
|
||||
remainingEventsBasedObjectNames.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Find the order of loading so that the objects are loaded when all the objects
|
||||
// they depend on are already loaded.
|
||||
std::vector<gd::String> loadOrderEventsBasedObjectNames;
|
||||
bool foundAnyEventsBasedObject = true;
|
||||
while (foundAnyEventsBasedObject) {
|
||||
foundAnyEventsBasedObject = false;
|
||||
for (std::size_t i = 0; i < remainingEventsBasedObjectNames.size(); ++i) {
|
||||
auto eventsBasedObjectName = remainingEventsBasedObjectNames[i];
|
||||
size_t extensionIndex = eventsBasedObjects.GetPosition(
|
||||
eventsBasedObjects.Get(eventsBasedObjectName));
|
||||
const SerializerElement &eventsBasedObjectElement =
|
||||
eventsBasedObjectsElement.GetChild(extensionIndex);
|
||||
|
||||
if (!isDependentFromRemainingEventsBasedObjects(
|
||||
eventsBasedObjectElement)) {
|
||||
loadOrderEventsBasedObjectNames.push_back(eventsBasedObjectName);
|
||||
remainingEventsBasedObjectNames.erase(
|
||||
remainingEventsBasedObjectNames.begin() + i);
|
||||
i--;
|
||||
foundAnyEventsBasedObject = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadOrderEventsBasedObjectNames;
|
||||
}
|
||||
|
||||
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(
|
||||
|
@@ -314,6 +314,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
|
||||
return dependency;
|
||||
}
|
||||
|
||||
std::vector<gd::String> GetUnserializingOrderEventsBasedObjectNames(
|
||||
const gd::SerializerElement &eventsBasedObjectsElement);
|
||||
|
||||
gd::String version;
|
||||
gd::String extensionNamespace;
|
||||
gd::String shortDescription;
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace gd {
|
||||
|
||||
ExternalEvents::ExternalEvents() : lastChangeTimeStamp(0) {
|
||||
ExternalEvents::ExternalEvents() {
|
||||
// ctor
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ ExternalEvents& ExternalEvents::operator=(const ExternalEvents& rhs) {
|
||||
void ExternalEvents::Init(const ExternalEvents& externalEvents) {
|
||||
name = externalEvents.GetName();
|
||||
associatedScene = externalEvents.GetAssociatedLayout();
|
||||
lastChangeTimeStamp = externalEvents.GetLastChangeTimeStamp();
|
||||
events = externalEvents.events;
|
||||
}
|
||||
|
||||
void ExternalEvents::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", name);
|
||||
element.SetAttribute("associatedLayout", associatedScene);
|
||||
element.SetAttribute("lastChangeTimeStamp", (int)lastChangeTimeStamp);
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
}
|
||||
@@ -41,8 +39,6 @@ void ExternalEvents::UnserializeFrom(gd::Project& project,
|
||||
name = element.GetStringAttribute("name", "", "Name");
|
||||
associatedScene =
|
||||
element.GetStringAttribute("associatedLayout", "", "AssociatedScene");
|
||||
lastChangeTimeStamp =
|
||||
element.GetIntAttribute("lastChangeTimeStamp", 0, "LastChangeTimeStamp");
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
@@ -67,24 +67,6 @@ class GD_CORE_API ExternalEvents {
|
||||
associatedScene = name_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the latest time of the build.
|
||||
* Used when the IDE found that the external events can be compiled separately
|
||||
* from scene's events.
|
||||
*
|
||||
* \todo This is specific to GD C++ Platform
|
||||
*/
|
||||
time_t GetLastChangeTimeStamp() const { return lastChangeTimeStamp; };
|
||||
|
||||
/**
|
||||
* Change the latest time of the build of the external events.
|
||||
*
|
||||
* \todo This is specific to GD C++ Platform
|
||||
*/
|
||||
void SetLastChangeTimeStamp(time_t newTimeStamp) {
|
||||
lastChangeTimeStamp = newTimeStamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the events.
|
||||
*/
|
||||
@@ -109,7 +91,6 @@ class GD_CORE_API ExternalEvents {
|
||||
private:
|
||||
gd::String name;
|
||||
gd::String associatedScene;
|
||||
time_t lastChangeTimeStamp; ///< Time of the last build
|
||||
gd::EventsList events; ///< List of events
|
||||
|
||||
/**
|
||||
@@ -119,19 +100,6 @@ class GD_CORE_API ExternalEvents {
|
||||
void Init(const ExternalEvents& externalEvents);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Functor testing ExternalEvents' name
|
||||
*/
|
||||
struct ExternalEventsHasName
|
||||
: public std::binary_function<std::unique_ptr<gd::ExternalEvents>,
|
||||
gd::String,
|
||||
bool> {
|
||||
bool operator()(const std::unique_ptr<gd::ExternalEvents>& externalEvents,
|
||||
gd::String name) const {
|
||||
return externalEvents->GetName() == name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EXTERNALEVENTS_H
|
||||
|
@@ -163,6 +163,20 @@ bool InitialInstancesContainer::HasInstancesOfObject(
|
||||
});
|
||||
}
|
||||
|
||||
bool InitialInstancesContainer::IsInstancesCountOfObjectGreaterThan(
|
||||
const gd::String &objectName, const std::size_t minInstanceCount) const {
|
||||
std::size_t count = 0;
|
||||
for (const gd::InitialInstance &instance : initialInstances) {
|
||||
if (instance.GetObjectName() == objectName) {
|
||||
count++;
|
||||
if (count > minInstanceCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InitialInstancesContainer::Create(
|
||||
const InitialInstancesContainer& source) {
|
||||
try {
|
||||
|
@@ -153,6 +153,13 @@ class GD_CORE_API InitialInstancesContainer {
|
||||
*/
|
||||
bool HasInstancesOfObject(const gd::String &objectName) const;
|
||||
|
||||
/**
|
||||
* \brief Return true if there is at least N instances of the given object.
|
||||
*/
|
||||
bool
|
||||
IsInstancesCountOfObjectGreaterThan(const gd::String &objectName,
|
||||
const std::size_t minInstanceCount) const;
|
||||
|
||||
/**
|
||||
* \brief Remove all instances
|
||||
*/
|
||||
|
@@ -24,10 +24,11 @@
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/PolymorphicClone.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/Tools/PolymorphicClone.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -43,7 +44,7 @@ Layout& Layout::operator=(const Layout& other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Layout::~Layout(){};
|
||||
Layout::~Layout() {};
|
||||
|
||||
Layout::Layout()
|
||||
: backgroundColorR(209),
|
||||
@@ -52,9 +53,7 @@ Layout::Layout()
|
||||
stopSoundsOnStartup(true),
|
||||
standardSortMethod(true),
|
||||
disableInputWhenNotFocused(true),
|
||||
variables(gd::VariablesContainer::SourceType::Scene)
|
||||
{
|
||||
}
|
||||
variables(gd::VariablesContainer::SourceType::Scene) {}
|
||||
|
||||
void Layout::SetName(const gd::String& name_) {
|
||||
name = name_;
|
||||
@@ -102,7 +101,9 @@ const gd::Layer& Layout::GetLayer(const gd::String& name) const {
|
||||
return layers.GetLayer(name);
|
||||
}
|
||||
|
||||
gd::Layer& Layout::GetLayer(std::size_t index) { return layers.GetLayer(index); }
|
||||
gd::Layer& Layout::GetLayer(std::size_t index) {
|
||||
return layers.GetLayer(index);
|
||||
}
|
||||
|
||||
const gd::Layer& Layout::GetLayer(std::size_t index) const {
|
||||
return layers.GetLayer(index);
|
||||
@@ -125,9 +126,7 @@ void Layout::InsertLayer(const gd::Layer& layer, std::size_t position) {
|
||||
layers.InsertLayer(layer, position);
|
||||
}
|
||||
|
||||
void Layout::RemoveLayer(const gd::String& name) {
|
||||
layers.RemoveLayer(name);
|
||||
}
|
||||
void Layout::RemoveLayer(const gd::String& name) { layers.RemoveLayer(name); }
|
||||
|
||||
void Layout::SwapLayers(std::size_t firstLayerIndex,
|
||||
std::size_t secondLayerIndex) {
|
||||
@@ -153,7 +152,7 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
|
||||
allBehaviorsNames.push_back(behavior.GetName());
|
||||
}
|
||||
}
|
||||
auto &globalObjects = project.GetObjects();
|
||||
auto& globalObjects = project.GetObjects();
|
||||
for (std::size_t i = 0; i < globalObjects.GetObjectsCount(); ++i) {
|
||||
std::vector<gd::String> objectBehaviors =
|
||||
globalObjects.GetObject(i).GetAllBehaviorNames();
|
||||
@@ -173,7 +172,8 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
|
||||
|
||||
if (behaviorsSharedData.find(name) != behaviorsSharedData.end()) continue;
|
||||
|
||||
auto sharedData = CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
|
||||
auto sharedData =
|
||||
CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
|
||||
if (sharedData) {
|
||||
behaviorsSharedData[name] = std::move(sharedData);
|
||||
}
|
||||
@@ -196,37 +196,39 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
|
||||
}
|
||||
|
||||
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
|
||||
gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
|
||||
if (project.HasEventsBasedBehavior(behaviorsType)) {
|
||||
auto sharedData =
|
||||
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
|
||||
sharedData->InitializeContent();
|
||||
return std::move(sharedData);
|
||||
}
|
||||
const gd::BehaviorMetadata& behaviorMetadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(
|
||||
project.GetCurrentPlatform(),
|
||||
behaviorsType);
|
||||
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
|
||||
gd::LogWarning("Tried to create a behavior shared data with an unknown type: " +
|
||||
behaviorsType + " on object " + GetName() + "!");
|
||||
gd::Project& project,
|
||||
const gd::String& name,
|
||||
const gd::String& behaviorsType) {
|
||||
if (project.HasEventsBasedBehavior(behaviorsType)) {
|
||||
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
|
||||
name, project, behaviorsType);
|
||||
sharedData->InitializeContent();
|
||||
return std::move(sharedData);
|
||||
}
|
||||
const gd::BehaviorMetadata& behaviorMetadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
|
||||
behaviorsType);
|
||||
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
|
||||
gd::LogWarning(
|
||||
"Tried to create a behavior shared data with an unknown type: " +
|
||||
behaviorsType + " on object " + GetName() + "!");
|
||||
// It's probably an events-based behavior that was removed.
|
||||
// Create a custom behavior shared data to preserve the properties values.
|
||||
auto sharedData =
|
||||
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
|
||||
sharedData->InitializeContent();
|
||||
return std::move(sharedData);
|
||||
}
|
||||
|
||||
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
|
||||
behaviorMetadata.GetSharedDataInstance();
|
||||
if (!behaviorsSharedDataBluePrint) return nullptr;
|
||||
|
||||
auto sharedData = behaviorsSharedDataBluePrint->Clone();
|
||||
sharedData->SetName(name);
|
||||
sharedData->SetTypeName(behaviorsType);
|
||||
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
|
||||
name, project, behaviorsType);
|
||||
sharedData->InitializeContent();
|
||||
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
|
||||
return std::move(sharedData);
|
||||
}
|
||||
|
||||
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
|
||||
behaviorMetadata.GetSharedDataInstance();
|
||||
if (!behaviorsSharedDataBluePrint) return nullptr;
|
||||
|
||||
auto sharedData = behaviorsSharedDataBluePrint->Clone();
|
||||
sharedData->SetName(name);
|
||||
sharedData->SetTypeName(behaviorsType);
|
||||
sharedData->InitializeContent();
|
||||
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
|
||||
}
|
||||
|
||||
void Layout::SerializeTo(SerializerElement& element) const {
|
||||
@@ -243,11 +245,13 @@ void Layout::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
editorSettings.SerializeTo(element.AddChild("uiSettings"));
|
||||
|
||||
objectsContainer.GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
|
||||
objectsContainer.GetObjectGroups().SerializeTo(
|
||||
element.AddChild("objectsGroups"));
|
||||
GetVariables().SerializeTo(element.AddChild("variables"));
|
||||
GetInitialInstances().SerializeTo(element.AddChild("instances"));
|
||||
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
|
||||
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
objectsContainer.SerializeFoldersTo(
|
||||
element.AddChild("objectsFolderStructure"));
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
|
||||
@@ -257,15 +261,33 @@ void Layout::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("behaviorsSharedData");
|
||||
behaviorDatasElement.ConsiderAsArrayOf("behaviorSharedData");
|
||||
for (const auto& it : behaviorsSharedData) {
|
||||
const gd::BehaviorsSharedData& sharedData = *it.second;
|
||||
SerializerElement& dataElement =
|
||||
behaviorDatasElement.AddChild("behaviorSharedData");
|
||||
|
||||
it.second->SerializeTo(dataElement);
|
||||
sharedData.SerializeTo(dataElement);
|
||||
dataElement.RemoveChild("type"); // The content can contain type or name
|
||||
// properties, remove them.
|
||||
dataElement.RemoveChild("name");
|
||||
dataElement.SetAttribute("type", it.second->GetTypeName());
|
||||
dataElement.SetAttribute("name", it.second->GetName());
|
||||
dataElement.SetAttribute("type", sharedData.GetTypeName());
|
||||
dataElement.SetAttribute("name", sharedData.GetName());
|
||||
|
||||
// Handle Quick Customization info.
|
||||
dataElement.RemoveChild("propertiesQuickCustomizationVisibilities");
|
||||
const QuickCustomizationVisibilitiesContainer&
|
||||
propertiesQuickCustomizationVisibilities =
|
||||
sharedData.GetPropertiesQuickCustomizationVisibilities();
|
||||
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
|
||||
propertiesQuickCustomizationVisibilities.SerializeTo(
|
||||
dataElement.AddChild("propertiesQuickCustomizationVisibilities"));
|
||||
}
|
||||
const QuickCustomization::Visibility visibility =
|
||||
sharedData.GetQuickCustomizationVisibility();
|
||||
if (visibility != QuickCustomization::Visibility::Default) {
|
||||
dataElement.SetAttribute(
|
||||
"quickCustomizationVisibility",
|
||||
QuickCustomization::VisibilityAsString(visibility));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,9 +311,11 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, GetEvents(), element.GetChild("events", 0, "Events"));
|
||||
|
||||
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
|
||||
objectsContainer.UnserializeObjectsFrom(
|
||||
project, element.GetChild("objects", 0, "Objets"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
objectsContainer.UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
objectsContainer.UnserializeFoldersFrom(
|
||||
project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
objectsContainer.AddMissingObjectsInRootFolder();
|
||||
|
||||
@@ -321,7 +345,6 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
"Behavior"); // Compatibility with GD <= 4
|
||||
gd::String name = sharedDataElement.GetStringAttribute("name", "", "Name");
|
||||
|
||||
|
||||
auto sharedData = CreateBehaviorsSharedData(project, name, type);
|
||||
if (sharedData) {
|
||||
// Compatibility with GD <= 4.0.98
|
||||
@@ -336,6 +359,21 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
else {
|
||||
sharedData->UnserializeFrom(sharedDataElement);
|
||||
}
|
||||
|
||||
// Handle Quick Customization info.
|
||||
if (sharedDataElement.HasChild(
|
||||
"propertiesQuickCustomizationVisibilities")) {
|
||||
sharedData->GetPropertiesQuickCustomizationVisibilities()
|
||||
.UnserializeFrom(sharedDataElement.GetChild(
|
||||
"propertiesQuickCustomizationVisibilities"));
|
||||
}
|
||||
if (sharedDataElement.HasChild("quickCustomizationVisibility")) {
|
||||
sharedData->SetQuickCustomizationVisibility(
|
||||
QuickCustomization::StringAsVisibility(
|
||||
sharedDataElement.GetStringAttribute(
|
||||
"quickCustomizationVisibility")));
|
||||
}
|
||||
|
||||
behaviorsSharedData[name] = std::move(sharedData);
|
||||
}
|
||||
}
|
||||
@@ -390,8 +428,9 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
|
||||
type = project.GetObject(name).GetType();
|
||||
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
else if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
@@ -448,11 +487,12 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
|
||||
return type;
|
||||
}
|
||||
|
||||
void GD_CORE_API FilterBehaviorNamesFromObject(
|
||||
const gd::Object &object, const gd::String &behaviorType,
|
||||
std::vector<gd::String> &behaviorNames) {
|
||||
void GD_CORE_API
|
||||
FilterBehaviorNamesFromObject(const gd::Object& object,
|
||||
const gd::String& behaviorType,
|
||||
std::vector<gd::String>& behaviorNames) {
|
||||
for (size_t i = 0; i < behaviorNames.size();) {
|
||||
auto &behaviorName = behaviorNames[i];
|
||||
auto& behaviorName = behaviorNames[i];
|
||||
if (!object.HasBehaviorNamed(behaviorName) ||
|
||||
object.GetBehavior(behaviorName).GetTypeName() != behaviorType) {
|
||||
behaviorNames.erase(behaviorNames.begin() + i);
|
||||
@@ -462,19 +502,21 @@ void GD_CORE_API FilterBehaviorNamesFromObject(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
const gd::ObjectsContainer &project, const gd::ObjectsContainer &layout,
|
||||
const gd::String &objectOrGroupName, const gd::String &behaviorType,
|
||||
bool searchInGroups) {
|
||||
std::vector<gd::String> GD_CORE_API
|
||||
GetBehaviorNamesInObjectOrGroup(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorType,
|
||||
bool searchInGroups) {
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
auto& object = layout.GetObject(objectOrGroupName);
|
||||
auto behaviorNames = object.GetAllBehaviorNames();
|
||||
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
|
||||
return behaviorNames;
|
||||
}
|
||||
if (project.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = project.GetObject(objectOrGroupName);
|
||||
auto& object = project.GetObject(objectOrGroupName);
|
||||
auto behaviorNames = object.GetAllBehaviorNames();
|
||||
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
|
||||
return behaviorNames;
|
||||
@@ -486,9 +528,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
}
|
||||
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
const gd::ObjectsContainer* container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
@@ -497,7 +540,7 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
std::vector<gd::String> behaviorNames;
|
||||
return behaviorNames;
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
const vector<gd::String>& groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
@@ -510,15 +553,15 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
auto behaviorNames = GetBehaviorNamesInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorType, false);
|
||||
for (size_t i = 1; i < groupsObjects.size(); i++) {
|
||||
auto &objectName = groupsObjects[i];
|
||||
auto& objectName = groupsObjects[i];
|
||||
|
||||
if (layout.HasObjectNamed(objectName)) {
|
||||
auto &object = layout.GetObject(objectName);
|
||||
auto& object = layout.GetObject(objectName);
|
||||
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
|
||||
return behaviorNames;
|
||||
}
|
||||
if (project.HasObjectNamed(objectName)) {
|
||||
auto &object = project.GetObject(objectName);
|
||||
auto& object = project.GetObject(objectName);
|
||||
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
|
||||
return behaviorNames;
|
||||
}
|
||||
@@ -529,10 +572,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
return behaviorNames;
|
||||
}
|
||||
|
||||
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorName,
|
||||
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
@@ -547,9 +590,10 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
}
|
||||
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
const gd::ObjectsContainer* container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
@@ -557,7 +601,7 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
const vector<gd::String>& groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
@@ -566,9 +610,9 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
}
|
||||
|
||||
// Check that all objects have the behavior.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
|
||||
false)) {
|
||||
for (auto&& object : groupsObjects) {
|
||||
if (!HasBehaviorInObjectOrGroup(
|
||||
project, layout, object, behaviorName, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -576,18 +620,18 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
}
|
||||
|
||||
bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::String objectOrGroupName,
|
||||
gd::String behaviorName,
|
||||
bool searchInGroups) {
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::String objectOrGroupName,
|
||||
gd::String behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
auto& object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) &&
|
||||
object.GetBehavior(behaviorName).IsDefaultBehavior();
|
||||
}
|
||||
if (project.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = project.GetObject(objectOrGroupName);
|
||||
auto& object = project.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) &&
|
||||
object.GetBehavior(behaviorName).IsDefaultBehavior();
|
||||
}
|
||||
@@ -597,9 +641,10 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
}
|
||||
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
const gd::ObjectsContainer* container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
@@ -607,7 +652,7 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
const vector<gd::String>& groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
@@ -616,30 +661,32 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
}
|
||||
|
||||
// Check that all objects have the same type.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!IsDefaultBehavior(project, layout, object, behaviorName,
|
||||
false)) {
|
||||
for (auto&& object : groupsObjects) {
|
||||
if (!IsDefaultBehavior(project, layout, object, behaviorName, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) {
|
||||
gd::String GD_CORE_API
|
||||
GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) ?
|
||||
object.GetBehavior(behaviorName).GetTypeName() : "";
|
||||
auto& object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName)
|
||||
? object.GetBehavior(behaviorName).GetTypeName()
|
||||
: "";
|
||||
}
|
||||
if (project.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = project.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) ?
|
||||
object.GetBehavior(behaviorName).GetTypeName() : "";
|
||||
auto& object = project.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName)
|
||||
? object.GetBehavior(behaviorName).GetTypeName()
|
||||
: "";
|
||||
}
|
||||
|
||||
if (!searchInGroups) {
|
||||
@@ -647,9 +694,10 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
}
|
||||
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
const gd::ObjectsContainer* container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
@@ -657,7 +705,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
const vector<gd::String>& groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
@@ -668,9 +716,9 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
// Check that all objects have the behavior with the same type.
|
||||
auto behaviorType = GetTypeOfBehaviorInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorName, false);
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (GetTypeOfBehaviorInObjectOrGroup(project, layout, object, behaviorName,
|
||||
false) != behaviorType) {
|
||||
for (auto&& object : groupsObjects) {
|
||||
if (GetTypeOfBehaviorInObjectOrGroup(
|
||||
project, layout, object, behaviorName, false) != behaviorType) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -682,14 +730,14 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
|
||||
gd::String name,
|
||||
bool searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectsCount(); ++i) {
|
||||
const auto &object = layout.GetObject(i);
|
||||
const auto& object = layout.GetObject(i);
|
||||
if (object.HasBehaviorNamed(name)) {
|
||||
return object.GetBehavior(name).GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
|
||||
const auto &object = project.GetObject(i);
|
||||
const auto& object = project.GetObject(i);
|
||||
if (object.HasBehaviorNamed(name)) {
|
||||
return object.GetBehavior(name).GetTypeName();
|
||||
}
|
||||
@@ -726,8 +774,9 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase.
|
||||
if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
|
@@ -369,9 +369,9 @@ class GD_CORE_API Layout {
|
||||
private:
|
||||
gd::String name; ///< Scene name
|
||||
gd::String mangledName; ///< The scene name mangled by SceneNameMangler
|
||||
unsigned int backgroundColorR; ///< Background color Red component
|
||||
unsigned int backgroundColorG; ///< Background color Green component
|
||||
unsigned int backgroundColorB; ///< Background color Blue component
|
||||
unsigned int backgroundColorR = 0; ///< Background color Red component
|
||||
unsigned int backgroundColorG = 0; ///< Background color Green component
|
||||
unsigned int backgroundColorB = 0; ///< Background color Blue component
|
||||
gd::String title; ///< Title displayed in the window
|
||||
gd::VariablesContainer variables; ///< Variables list
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
@@ -379,12 +379,12 @@ class GD_CORE_API Layout {
|
||||
gd::LayersContainer layers;
|
||||
std::map<gd::String, std::unique_ptr<gd::BehaviorsSharedData>>
|
||||
behaviorsSharedData; ///< Initial shared datas of behaviors
|
||||
bool stopSoundsOnStartup; ///< True to make the scene stop all sounds at
|
||||
///< startup.
|
||||
bool standardSortMethod; ///< True to sort objects using standard sort.
|
||||
bool disableInputWhenNotFocused; /// If set to true, the input must be
|
||||
/// disabled when the window do not have the
|
||||
/// focus.
|
||||
bool stopSoundsOnStartup = true; ///< True to make the scene stop all sounds at
|
||||
///< startup.
|
||||
bool standardSortMethod = true; ///< True to sort objects using standard sort.
|
||||
bool disableInputWhenNotFocused = true; /// If set to true, the input must be
|
||||
/// disabled when the window do not have the
|
||||
/// focus.
|
||||
static gd::BehaviorsSharedData
|
||||
badBehaviorSharedData; ///< Null object, returned when
|
||||
///< GetBehaviorSharedData can not find the
|
||||
|
@@ -12,8 +12,9 @@
|
||||
#include "GDCore/Project/CustomBehavior.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
|
||||
@@ -27,8 +28,8 @@ Object::Object(const gd::String& name_,
|
||||
: name(name_),
|
||||
configuration(std::move(configuration_)),
|
||||
objectVariables(gd::VariablesContainer::SourceType::Object) {
|
||||
SetType(type_);
|
||||
}
|
||||
SetType(type_);
|
||||
}
|
||||
|
||||
Object::Object(const gd::String& name_,
|
||||
const gd::String& type_,
|
||||
@@ -36,8 +37,8 @@ Object::Object(const gd::String& name_,
|
||||
: name(name_),
|
||||
configuration(configuration_),
|
||||
objectVariables(gd::VariablesContainer::SourceType::Object) {
|
||||
SetType(type_);
|
||||
}
|
||||
SetType(type_);
|
||||
}
|
||||
|
||||
void Object::Init(const gd::Object& object) {
|
||||
persistentUuid = object.persistentUuid;
|
||||
@@ -54,9 +55,7 @@ void Object::Init(const gd::Object& object) {
|
||||
configuration = object.configuration->Clone();
|
||||
}
|
||||
|
||||
gd::ObjectConfiguration& Object::GetConfiguration() {
|
||||
return *configuration;
|
||||
}
|
||||
gd::ObjectConfiguration& Object::GetConfiguration() { return *configuration; }
|
||||
|
||||
const gd::ObjectConfiguration& Object::GetConfiguration() const {
|
||||
return *configuration;
|
||||
@@ -77,8 +76,7 @@ bool Object::RenameBehavior(const gd::String& name, const gd::String& newName) {
|
||||
behaviors.find(newName) != behaviors.end())
|
||||
return false;
|
||||
|
||||
std::unique_ptr<Behavior> aut =
|
||||
std::move(behaviors.find(name)->second);
|
||||
std::unique_ptr<Behavior> aut = std::move(behaviors.find(name)->second);
|
||||
behaviors.erase(name);
|
||||
behaviors[newName] = std::move(aut);
|
||||
behaviors[newName]->SetName(newName);
|
||||
@@ -99,10 +97,10 @@ bool Object::HasBehaviorNamed(const gd::String& name) const {
|
||||
}
|
||||
|
||||
gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
|
||||
const gd::String& type,
|
||||
const gd::String& name) {
|
||||
auto initializeAndAdd =
|
||||
[this, &name](std::unique_ptr<gd::Behavior> behavior) {
|
||||
const gd::String& type,
|
||||
const gd::String& name) {
|
||||
auto initializeAndAdd = [this,
|
||||
&name](std::unique_ptr<gd::Behavior> behavior) {
|
||||
behavior->InitializeContent();
|
||||
this->behaviors[name] = std::move(behavior);
|
||||
return this->behaviors[name].get();
|
||||
@@ -111,18 +109,17 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
|
||||
if (project.HasEventsBasedBehavior(type)) {
|
||||
return initializeAndAdd(
|
||||
gd::make_unique<CustomBehavior>(name, project, type));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const gd::BehaviorMetadata& behaviorMetadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
|
||||
type);
|
||||
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
|
||||
gd::LogWarning("Tried to create a behavior with an unknown type: " + type
|
||||
+ " on object " + GetName() + "!");
|
||||
// It's probably an events-based behavior that was removed.
|
||||
// Create a custom behavior to preserve the properties values.
|
||||
return initializeAndAdd(
|
||||
gd::make_unique<CustomBehavior>(name, project, type));
|
||||
gd::LogWarning("Tried to create a behavior with an unknown type: " +
|
||||
type + " on object " + GetName() + "!");
|
||||
// It's probably an events-based behavior that was removed.
|
||||
// Create a custom behavior to preserve the properties values.
|
||||
return initializeAndAdd(
|
||||
gd::make_unique<CustomBehavior>(name, project, type));
|
||||
}
|
||||
std::unique_ptr<gd::Behavior> behavior(behaviorMetadata.Get().Clone());
|
||||
behavior->SetName(name);
|
||||
@@ -196,6 +193,23 @@ void Object::UnserializeFrom(gd::Project& project,
|
||||
else {
|
||||
behavior->UnserializeFrom(behaviorElement);
|
||||
}
|
||||
|
||||
bool isFolded = behaviorElement.GetBoolAttribute("isFolded", false);
|
||||
behavior->SetFolded(isFolded);
|
||||
|
||||
// Handle Quick Customization info.
|
||||
if (behaviorElement.HasChild(
|
||||
"propertiesQuickCustomizationVisibilities")) {
|
||||
behavior->GetPropertiesQuickCustomizationVisibilities().UnserializeFrom(
|
||||
behaviorElement.GetChild(
|
||||
"propertiesQuickCustomizationVisibilities"));
|
||||
}
|
||||
if (behaviorElement.HasChild("quickCustomizationVisibility")) {
|
||||
behavior->SetQuickCustomizationVisibility(
|
||||
QuickCustomization::StringAsVisibility(
|
||||
behaviorElement.GetStringAttribute(
|
||||
"quickCustomizationVisibility")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +231,8 @@ void Object::SerializeTo(SerializerElement& element) const {
|
||||
std::vector<gd::String> allBehaviors = GetAllBehaviorNames();
|
||||
for (std::size_t i = 0; i < allBehaviors.size(); ++i) {
|
||||
const gd::Behavior& behavior = GetBehavior(allBehaviors[i]);
|
||||
// Default behaviors are added at the object creation according to metadata.
|
||||
// They don't need to be serialized.
|
||||
// Default behaviors are added at the object creation according to
|
||||
// metadata. They don't need to be serialized.
|
||||
if (behavior.IsDefaultBehavior()) {
|
||||
continue;
|
||||
}
|
||||
@@ -228,8 +242,27 @@ void Object::SerializeTo(SerializerElement& element) const {
|
||||
behaviorElement.RemoveChild("type"); // The content can contain type or
|
||||
// name properties, remove them.
|
||||
behaviorElement.RemoveChild("name");
|
||||
behaviorElement.RemoveChild("isFolded");
|
||||
behaviorElement.SetAttribute("type", behavior.GetTypeName());
|
||||
behaviorElement.SetAttribute("name", behavior.GetName());
|
||||
if (behavior.IsFolded()) behaviorElement.SetAttribute("isFolded", true);
|
||||
|
||||
// Handle Quick Customization info.
|
||||
behaviorElement.RemoveChild("propertiesQuickCustomizationVisibilities");
|
||||
const QuickCustomizationVisibilitiesContainer&
|
||||
propertiesQuickCustomizationVisibilities =
|
||||
behavior.GetPropertiesQuickCustomizationVisibilities();
|
||||
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
|
||||
propertiesQuickCustomizationVisibilities.SerializeTo(
|
||||
behaviorElement.AddChild("propertiesQuickCustomizationVisibilities"));
|
||||
}
|
||||
const QuickCustomization::Visibility visibility =
|
||||
behavior.GetQuickCustomizationVisibility();
|
||||
if (visibility != QuickCustomization::Visibility::Default) {
|
||||
behaviorElement.SetAttribute(
|
||||
"quickCustomizationVisibility",
|
||||
QuickCustomization::VisibilityAsString(visibility));
|
||||
}
|
||||
}
|
||||
|
||||
configuration->SerializeTo(element);
|
||||
|
@@ -193,7 +193,7 @@ class GD_CORE_API ObjectFolderOrObject {
|
||||
static gd::ObjectFolderOrObject badObjectFolderOrObject;
|
||||
|
||||
gd::ObjectFolderOrObject*
|
||||
parent; // nullptr if root folder, points to the parent folder otherwise.
|
||||
parent = nullptr; // nullptr if root folder, points to the parent folder otherwise.
|
||||
QuickCustomization::Visibility quickCustomizationVisibility;
|
||||
|
||||
// Representing an object:
|
||||
|
@@ -530,6 +530,32 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
|
||||
*objectsContainers[0], *objectsContainers[1], objectName, searchInGroups);
|
||||
}
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::GetBehaviorNamesInObjectOrGroup(
|
||||
const gd::String &objectOrGroupName, const gd::String &behaviorType, bool searchInGroups) const {
|
||||
if (objectsContainers.size() > 2) {
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
if (objectsContainers.size() == 0) {
|
||||
gd::LogWarning("ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called without any "
|
||||
"objectsContainer");
|
||||
std::vector<gd::String> behaviors;
|
||||
return behaviors;
|
||||
}
|
||||
if (objectsContainers.size() == 1) {
|
||||
gd::ObjectsContainer emptyObjectsContainer;
|
||||
return gd::GetBehaviorNamesInObjectOrGroup(emptyObjectsContainer,
|
||||
*objectsContainers[0], objectOrGroupName, behaviorType,
|
||||
searchInGroups);
|
||||
}
|
||||
return gd::GetBehaviorNamesInObjectOrGroup(
|
||||
*objectsContainers[0], *objectsContainers[1], objectOrGroupName, behaviorType, searchInGroups);
|
||||
}
|
||||
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
|
||||
const gd::String &objectOrGroupName) const {
|
||||
std::vector<gd::String> animationNames;
|
||||
|
@@ -50,6 +50,11 @@ class GD_CORE_API ObjectsContainersList {
|
||||
*/
|
||||
bool HasObjectOrGroupNamed(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object exists ignoring groups.
|
||||
*/
|
||||
bool HasObjectNamed(const gd::String& name) const;
|
||||
|
||||
enum VariableExistence {
|
||||
DoesNotExist,
|
||||
Exists,
|
||||
@@ -128,6 +133,18 @@ class GD_CORE_API ObjectsContainersList {
|
||||
std::vector<gd::String> GetBehaviorsOfObject(
|
||||
const gd::String& objectName, bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get behaviors of an object/group of a given behavior type.
|
||||
* \note The behaviors of a group are the behaviors which are found in common
|
||||
* when looking all the objects of the group.
|
||||
*
|
||||
* @return Vector containing names of behaviors
|
||||
*/
|
||||
std::vector<gd::String>
|
||||
GetBehaviorNamesInObjectOrGroup(const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorType,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get the animation names of an object/group.
|
||||
* \note The animation names of a group are the animation names common to
|
||||
@@ -188,8 +205,6 @@ class GD_CORE_API ObjectsContainersList {
|
||||
ObjectsContainersList(){};
|
||||
|
||||
private:
|
||||
bool HasObjectNamed(const gd::String& name) const;
|
||||
|
||||
const gd::Object* GetObject(const gd::String& name) const;
|
||||
|
||||
bool HasObjectWithVariableNamed(const gd::String& objectName,
|
||||
|
@@ -71,7 +71,7 @@ Project::Project()
|
||||
isPlayableWithKeyboard(false),
|
||||
isPlayableWithGamepad(false),
|
||||
isPlayableWithMobile(false),
|
||||
currentPlatform(NULL),
|
||||
currentPlatform(nullptr),
|
||||
gdMajorVersion(gd::VersionWrapper::Major()),
|
||||
gdMinorVersion(gd::VersionWrapper::Minor()),
|
||||
gdBuildVersion(gd::VersionWrapper::Build()),
|
||||
@@ -830,7 +830,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
|
||||
"eventsFunctionsExtension");
|
||||
// First, only unserialize behaviors and objects names.
|
||||
// As event based objects can contains CustomObject and Custom Object,
|
||||
// As event based objects can contains custom behaviors and custom objects,
|
||||
// this allows them to reference EventBasedBehavior and EventBasedObject
|
||||
// respectively.
|
||||
for (std::size_t i = 0;
|
||||
@@ -845,15 +845,17 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
newEventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
}
|
||||
// Then unserialize functions, behaviors and objects content.
|
||||
for (std::size_t i = 0;
|
||||
i < eventsFunctionsExtensionsElement.GetChildrenCount();
|
||||
++i) {
|
||||
const SerializerElement& eventsFunctionsExtensionElement =
|
||||
eventsFunctionsExtensionsElement.GetChild(i);
|
||||
|
||||
eventsFunctionsExtensions.at(i)->UnserializeExtensionImplementationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
// Then unserialize functions, behaviors and objects content.
|
||||
for (gd::String &extensionName :
|
||||
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
|
||||
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
|
||||
const SerializerElement &eventsFunctionsExtensionElement =
|
||||
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
|
||||
|
||||
eventsFunctionsExtensions.at(extensionIndex)
|
||||
->UnserializeExtensionImplementationFrom(
|
||||
*this, eventsFunctionsExtensionElement);
|
||||
}
|
||||
|
||||
objectsContainer.GetObjectGroups().UnserializeFrom(
|
||||
@@ -922,6 +924,83 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
|
||||
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
|
||||
|
||||
// Some extension have custom objects, which have child objects coming from other extension.
|
||||
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
|
||||
// This implies: an order on the extension unserialization (and no cycles).
|
||||
|
||||
// At the beginning, everything is yet to be loaded.
|
||||
std::vector<gd::String> remainingExtensionNames(
|
||||
eventsFunctionsExtensions.size());
|
||||
for (std::size_t i = 0; i < eventsFunctionsExtensions.size(); ++i) {
|
||||
remainingExtensionNames[i] = eventsFunctionsExtensions.at(i)->GetName();
|
||||
}
|
||||
|
||||
// Helper allowing to find if an extension has an object that depends on
|
||||
// at least one other object from another extension that is not loaded yet.
|
||||
auto isDependentFromRemainingExtensions =
|
||||
[&remainingExtensionNames](
|
||||
const gd::SerializerElement &eventsFunctionsExtensionElement) {
|
||||
auto &eventsBasedObjectsElement =
|
||||
eventsFunctionsExtensionElement.GetChild("eventsBasedObjects");
|
||||
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
|
||||
for (std::size_t eventsBasedObjectsIndex = 0;
|
||||
eventsBasedObjectsIndex <
|
||||
eventsBasedObjectsElement.GetChildrenCount();
|
||||
++eventsBasedObjectsIndex) {
|
||||
auto &objectsElement =
|
||||
eventsBasedObjectsElement.GetChild(eventsBasedObjectsIndex)
|
||||
.GetChild("objects");
|
||||
objectsElement.ConsiderAsArrayOf("object");
|
||||
|
||||
for (std::size_t objectIndex = 0;
|
||||
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
|
||||
const gd::String &objectType =
|
||||
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
|
||||
|
||||
gd::String extensionName =
|
||||
eventsFunctionsExtensionElement.GetStringAttribute("name");
|
||||
gd::String usedExtensionName =
|
||||
gd::PlatformExtension::GetExtensionFromFullObjectType(objectType);
|
||||
|
||||
if (usedExtensionName != extensionName &&
|
||||
std::find(remainingExtensionNames.begin(),
|
||||
remainingExtensionNames.end(),
|
||||
usedExtensionName) != remainingExtensionNames.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Find the order of loading so that the extensions are loaded when all the other
|
||||
// extensions they depend on are already loaded.
|
||||
std::vector<gd::String> loadOrderExtensionNames;
|
||||
bool foundAnyExtension = true;
|
||||
while (foundAnyExtension) {
|
||||
foundAnyExtension = false;
|
||||
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
|
||||
auto extensionName = remainingExtensionNames[i];
|
||||
size_t extensionIndex =
|
||||
GetEventsFunctionsExtensionPosition(extensionName);
|
||||
const SerializerElement &eventsFunctionsExtensionElement =
|
||||
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
|
||||
|
||||
if (!isDependentFromRemainingExtensions(
|
||||
eventsFunctionsExtensionElement)) {
|
||||
loadOrderExtensionNames.push_back(extensionName);
|
||||
remainingExtensionNames.erase(remainingExtensionNames.begin() + i);
|
||||
i--;
|
||||
foundAnyExtension = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadOrderExtensionNames;
|
||||
}
|
||||
|
||||
void Project::SerializeTo(SerializerElement& element) const {
|
||||
SerializerElement& versionElement = element.AddChild("gdVersion");
|
||||
versionElement.SetAttribute("major", gd::VersionWrapper::Major());
|
||||
|
@@ -990,16 +990,12 @@ class GD_CORE_API Project {
|
||||
/**
|
||||
* \brief return the objects of the project.
|
||||
*/
|
||||
gd::ObjectsContainer& GetObjects() {
|
||||
return objectsContainer;
|
||||
}
|
||||
gd::ObjectsContainer& GetObjects() { return objectsContainer; }
|
||||
|
||||
/**
|
||||
* \brief Return the objects of the project.
|
||||
*/
|
||||
const gd::ObjectsContainer& GetObjects() const {
|
||||
return objectsContainer;
|
||||
}
|
||||
const gd::ObjectsContainer& GetObjects() const { return objectsContainer; }
|
||||
///@}
|
||||
|
||||
/** \name Identifier names
|
||||
@@ -1080,32 +1076,43 @@ class GD_CORE_API Project {
|
||||
*/
|
||||
void Init(const gd::Project& project);
|
||||
|
||||
gd::String name; ///< Game name
|
||||
gd::String description; ///< Game description
|
||||
gd::String version; ///< Game version number (used for some exports)
|
||||
unsigned int windowWidth; ///< Window default width
|
||||
unsigned int windowHeight; ///< Window default height
|
||||
int maxFPS; ///< Maximum Frame Per Seconds, -1 for unlimited
|
||||
unsigned int minFPS; ///< Minimum Frame Per Seconds ( slow down game if FPS
|
||||
///< are below this number )
|
||||
bool verticalSync; ///< If true, must activate vertical synchronization.
|
||||
/**
|
||||
* @brief Get the project extensions names in the order they have to be unserialized.
|
||||
*
|
||||
* Child-objects need the event-based objects they use to be loaded completely
|
||||
* before they are unserialized.
|
||||
*/
|
||||
std::vector<gd::String> GetUnserializingOrderExtensionNames(const gd::SerializerElement &eventsFunctionsExtensionsElement);
|
||||
|
||||
gd::String name; ///< Game name
|
||||
gd::String description; ///< Game description
|
||||
gd::String version; ///< Game version number (used for some exports)
|
||||
unsigned int windowWidth = 0; ///< Window default width
|
||||
unsigned int windowHeight = 0; ///< Window default height
|
||||
int maxFPS = 0; ///< Maximum Frame Per Seconds, -1 for unlimited
|
||||
unsigned int minFPS = 0; ///< Minimum Frame Per Seconds ( slow down game if
|
||||
///< FPS are below this number )
|
||||
bool verticalSync =
|
||||
false; ///< If true, must activate vertical synchronization.
|
||||
gd::String scaleMode;
|
||||
bool pixelsRounding; ///< If true, the rendering should stop pixel
|
||||
///< interpolation of rendered objects.
|
||||
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
|
||||
///< to the window size at runtime
|
||||
bool pixelsRounding = false; ///< If true, the rendering should stop pixel
|
||||
///< interpolation of rendered objects.
|
||||
bool adaptGameResolutionAtRuntime =
|
||||
true; ///< Should the game resolution be adapted
|
||||
///< to the window size at runtime
|
||||
gd::String
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
gd::String antialiasingMode;
|
||||
bool isAntialisingEnabledOnMobile;
|
||||
bool isAntialisingEnabledOnMobile = false;
|
||||
gd::String projectUuid; ///< UUID useful to identify the game in online
|
||||
///< services or database that would require it.
|
||||
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from
|
||||
///< events will have 0 as Z order,
|
||||
///< instead of the highest Z order
|
||||
///< found on the layer at the scene
|
||||
///< startup.
|
||||
bool useDeprecatedZeroAsDefaultZOrder =
|
||||
false; ///< If true, objects created from
|
||||
///< events will have 0 as Z order,
|
||||
///< instead of the highest Z order
|
||||
///< found on the layer at the scene
|
||||
///< startup.
|
||||
std::vector<std::unique_ptr<gd::Layout> > scenes; ///< List of all scenes
|
||||
gd::VariablesContainer variables; ///< Initial global variables
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
@@ -1118,7 +1125,8 @@ class GD_CORE_API Project {
|
||||
std::vector<gd::Platform*>
|
||||
platforms; ///< Pointers to the platforms this project supports.
|
||||
gd::String firstLayout;
|
||||
bool useExternalSourceFiles; ///< True if game used external source files.
|
||||
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.
|
||||
@@ -1127,35 +1135,40 @@ class GD_CORE_API Project {
|
||||
std::vector<gd::String>
|
||||
authorUsernames; ///< Game author usernames, from GDevelop users DB.
|
||||
std::vector<gd::String> categories; ///< Game categories
|
||||
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
bool isPlayableWithKeyboard =
|
||||
false; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad =
|
||||
false; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile = false; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String templateSlug; ///< The slug of the template from which the game is
|
||||
///< created.
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
bool
|
||||
folderProject; ///< True if folder project, false if single file project.
|
||||
bool folderProject =
|
||||
false; ///< True if folder project, false if single file project.
|
||||
gd::String
|
||||
projectFile; ///< Path to the project file - when editing a local file.
|
||||
gd::String latestCompilationDirectory;
|
||||
gd::Platform*
|
||||
currentPlatform; ///< The platform being used to edit the project.
|
||||
gd::Platform* currentPlatform =
|
||||
nullptr; ///< The platform being used to edit the project.
|
||||
gd::PlatformSpecificAssets platformSpecificAssets;
|
||||
gd::LoadingScreen loadingScreen;
|
||||
gd::Watermark watermark;
|
||||
std::vector<std::unique_ptr<gd::ExternalEvents> >
|
||||
externalEvents; ///< List of all externals events
|
||||
ExtensionProperties
|
||||
extensionProperties; ///< The properties of the extensions.
|
||||
extensionProperties; ///< The properties of the extensions.
|
||||
gd::WholeProjectDiagnosticReport wholeProjectDiagnosticReport;
|
||||
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdMajorVersion =
|
||||
0; ///< The GD major version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdMinorVersion =
|
||||
0; ///< The GD minor version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdBuildVersion =
|
||||
0; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -1,16 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class QuickCustomization {
|
||||
public:
|
||||
enum Visibility {
|
||||
/** Visibility based on the parent or editor heuristics (probably visible). */
|
||||
/** Visibility based on the parent or editor heuristics (probably visible).
|
||||
*/
|
||||
Default,
|
||||
/** Visible in the quick customization editor. */
|
||||
Visible,
|
||||
/** Not visible in the quick customization editor. */
|
||||
Hidden
|
||||
};
|
||||
|
||||
static Visibility StringAsVisibility(const gd::String& str) {
|
||||
if (str == "visible")
|
||||
return Visibility::Visible;
|
||||
else if (str == "hidden")
|
||||
return Visibility::Hidden;
|
||||
|
||||
return Visibility::Default;
|
||||
}
|
||||
|
||||
static gd::String VisibilityAsString(Visibility visibility) {
|
||||
if (visibility == Visibility::Visible)
|
||||
return "visible";
|
||||
else if (visibility == Visibility::Hidden)
|
||||
return "hidden";
|
||||
|
||||
return "default";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
QuickCustomizationVisibilitiesContainer::
|
||||
QuickCustomizationVisibilitiesContainer() {}
|
||||
|
||||
bool QuickCustomizationVisibilitiesContainer::IsEmpty() const {
|
||||
return visibilities.empty();
|
||||
}
|
||||
|
||||
void QuickCustomizationVisibilitiesContainer::Set(
|
||||
const gd::String& name, QuickCustomization::Visibility visibility) {
|
||||
visibilities[name] = visibility;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility QuickCustomizationVisibilitiesContainer::Get(
|
||||
const gd::String& name) const {
|
||||
auto it = visibilities.find(name);
|
||||
if (it != visibilities.end()) return it->second;
|
||||
|
||||
return QuickCustomization::Visibility::Default;
|
||||
}
|
||||
|
||||
void QuickCustomizationVisibilitiesContainer::SerializeTo(
|
||||
SerializerElement& element) const {
|
||||
for (auto& visibility : visibilities) {
|
||||
element.SetStringAttribute(
|
||||
visibility.first,
|
||||
QuickCustomization::VisibilityAsString(visibility.second));
|
||||
}
|
||||
}
|
||||
|
||||
void QuickCustomizationVisibilitiesContainer::UnserializeFrom(
|
||||
const SerializerElement& element) {
|
||||
visibilities.clear();
|
||||
for (auto& child : element.GetAllChildren()) {
|
||||
visibilities[child.first] =
|
||||
QuickCustomization::StringAsVisibility(child.second->GetStringValue());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/QuickCustomization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
class QuickCustomizationVisibilitiesContainer {
|
||||
public:
|
||||
QuickCustomizationVisibilitiesContainer();
|
||||
|
||||
void Set(const gd::String& name, QuickCustomization::Visibility visibility);
|
||||
|
||||
QuickCustomization::Visibility Get(const gd::String& name) const;
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
private:
|
||||
std::map<gd::String, QuickCustomization::Visibility> visibilities;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -152,8 +152,8 @@ class GD_CORE_API Resource {
|
||||
gd::String metadata;
|
||||
gd::String originName;
|
||||
gd::String originIdentifier;
|
||||
bool userAdded; ///< True if the resource was added by the user, and not
|
||||
///< automatically by GDevelop.
|
||||
bool userAdded = false; ///< True if the resource was added by the user, and not
|
||||
///< automatically by GDevelop.
|
||||
|
||||
static gd::String badStr;
|
||||
};
|
||||
|
@@ -33,7 +33,7 @@ class GD_CORE_API Variable {
|
||||
Unknown,
|
||||
/** Used when objects of a group have different types for a variable. */
|
||||
MixedTypes,
|
||||
|
||||
|
||||
// Primitive types
|
||||
String,
|
||||
Number,
|
||||
@@ -393,11 +393,11 @@ class GD_CORE_API Variable {
|
||||
*/
|
||||
static Type StringAsType(const gd::String& str);
|
||||
|
||||
bool folded;
|
||||
bool folded = false;
|
||||
mutable Type type;
|
||||
mutable gd::String str;
|
||||
mutable double value;
|
||||
mutable bool boolVal;
|
||||
mutable bool boolVal = false;
|
||||
mutable bool hasMixedValues;
|
||||
mutable std::map<gd::String, std::shared_ptr<Variable>>
|
||||
children; ///< Children, when the variable is considered as a structure.
|
||||
|
@@ -187,7 +187,7 @@ class GD_CORE_API VariablesContainer {
|
||||
///@}
|
||||
|
||||
private:
|
||||
SourceType sourceType;
|
||||
SourceType sourceType = Unknown;
|
||||
std::vector<std::pair<gd::String, std::shared_ptr<gd::Variable>>> variables;
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
///< useful for computing changesets.
|
||||
|
@@ -456,13 +456,13 @@ class GD_CORE_API SerializerElement {
|
||||
*/
|
||||
void Init(const gd::SerializerElement &other);
|
||||
|
||||
bool valueUndefined; ///< If true, the element does not have a value.
|
||||
bool valueUndefined = true; ///< If true, the element does not have a value.
|
||||
SerializerValue elementValue;
|
||||
|
||||
std::map<gd::String, SerializerValue> attributes;
|
||||
std::vector<std::pair<gd::String, std::shared_ptr<SerializerElement> > >
|
||||
children;
|
||||
mutable bool isArray; ///< true if element is considered as an array
|
||||
mutable bool isArray = false; ///< true if element is considered as an array
|
||||
mutable gd::String arrayOf; ///< The name of the children (was useful for XML
|
||||
///< parsed elements).
|
||||
mutable gd::String deprecatedArrayOf; ///< Alternate name for children
|
||||
|
2
Core/GDCore/Serialization/rapidjson/.clang-tidy
Normal file
2
Core/GDCore/Serialization/rapidjson/.clang-tidy
Normal file
@@ -0,0 +1,2 @@
|
||||
# Disable all checks in this folder.
|
||||
Checks: '-*'
|
@@ -4,6 +4,8 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -825,3 +827,5 @@ bool GD_CORE_API CaseInsensitiveEquiv( const String &lhs, const String &rhs, boo
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NOLINTEND
|
||||
|
@@ -4,6 +4,8 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
||||
#ifndef GDCORE_UTF8_STRING_H
|
||||
#define GDCORE_UTF8_STRING_H
|
||||
|
||||
@@ -898,3 +900,5 @@ namespace std
|
||||
* In Unicode, uppercasing/lowercasing strings to compare them in a case-insensitive way is not recommended.
|
||||
* That's why the function gd::CaseInsensitiveEquiv exists to compare two strings in a case-insensitive way.
|
||||
*/
|
||||
|
||||
// NOLINTEND
|
@@ -2,6 +2,7 @@
|
||||
#define GD_CORE_POLYMORPHICCLONE_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace gd {
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
|
@@ -3,6 +3,9 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
||||
#include "SystemStats.h"
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
@@ -49,3 +52,5 @@ size_t SystemStats::GetUsedVirtualMemory() {
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
// NOLINTEND
|
2
Core/GDCore/Tools/UUID/.clang-tidy
Normal file
2
Core/GDCore/Tools/UUID/.clang-tidy
Normal file
@@ -0,0 +1,2 @@
|
||||
# Disable all checks in this folder.
|
||||
Checks: '-*'
|
2
Core/GDCore/Utf8/.clang-tidy
Normal file
2
Core/GDCore/Utf8/.clang-tidy
Normal file
@@ -0,0 +1,2 @@
|
||||
# Disable all checks in this folder.
|
||||
Checks: '-*'
|
@@ -262,4 +262,134 @@ TEST_CASE("ObjectSerialization", "[common]") {
|
||||
auto &behaviorElement = behaviorsElement.GetChild(0);
|
||||
REQUIRE(behaviorElement.GetStringAttribute("name") == "MyBehavior");
|
||||
}
|
||||
}
|
||||
|
||||
// Event-based object dependency cycles are not tested because they are forbidden by the editor.
|
||||
SECTION("Save and load a project with custom object dependencies from different extensions") {
|
||||
gd::Platform platform;
|
||||
gd::Project writtenProject;
|
||||
SetupProjectWithDummyPlatform(writtenProject, platform);
|
||||
|
||||
{
|
||||
// The extension with the dependency is added first to make the
|
||||
// implementation change the order in which extensions are loaded.
|
||||
auto &eventsExtensionWithDependency =
|
||||
writtenProject.InsertNewEventsFunctionsExtension(
|
||||
"MyEventsExtensionWithDependency", 0);
|
||||
auto &eventsExtension = writtenProject.InsertNewEventsFunctionsExtension(
|
||||
"MyEventsExtension", 1);
|
||||
{
|
||||
auto &eventsBasedObject =
|
||||
eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
writtenProject, "MyExtension::Sprite", "MyChildSprite", 0);
|
||||
}
|
||||
// An event-based object with a custom object child that overrides its
|
||||
// configuration.
|
||||
{
|
||||
auto &eventsBasedObject =
|
||||
eventsExtensionWithDependency.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObjectWithDependency", 0);
|
||||
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
writtenProject, "MyEventsExtension::MyEventsBasedObject",
|
||||
"MyChildCustomObject", 0);
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(
|
||||
&childObject.GetConfiguration());
|
||||
auto &spriteConfiguration =
|
||||
customObjectConfiguration->GetChildObjectConfiguration(
|
||||
"MyChildSprite");
|
||||
SetupSpriteConfiguration(spriteConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
SerializerElement projectElement;
|
||||
writtenProject.SerializeTo(projectElement);
|
||||
|
||||
gd::Project readProject;
|
||||
readProject.AddPlatform(platform);
|
||||
readProject.UnserializeFrom(projectElement);
|
||||
|
||||
REQUIRE(readProject.GetEventsFunctionsExtensionsCount() == 2);
|
||||
auto &eventsExtensionWithDependency =
|
||||
readProject.GetEventsFunctionsExtension(0);
|
||||
REQUIRE(eventsExtensionWithDependency.GetEventsBasedObjects().GetCount() ==
|
||||
1);
|
||||
auto &eventsBasedObject =
|
||||
eventsExtensionWithDependency.GetEventsBasedObjects().Get(0);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectsCount() == 1);
|
||||
auto &childObject = eventsBasedObject.GetObjects().GetObject(0);
|
||||
REQUIRE(childObject.GetName() == "MyChildCustomObject");
|
||||
REQUIRE(childObject.GetType() == "MyEventsExtension::MyEventsBasedObject");
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(
|
||||
&childObject.GetConfiguration());
|
||||
REQUIRE(customObjectConfiguration != nullptr);
|
||||
auto &spriteConfiguration =
|
||||
customObjectConfiguration->GetChildObjectConfiguration("MyChildSprite");
|
||||
CheckSpriteConfiguration(spriteConfiguration);
|
||||
}
|
||||
|
||||
SECTION("Save and load a project with custom object dependencies inside an extension") {
|
||||
gd::Platform platform;
|
||||
gd::Project writtenProject;
|
||||
SetupProjectWithDummyPlatform(writtenProject, platform);
|
||||
|
||||
{
|
||||
auto &eventsExtension = writtenProject.InsertNewEventsFunctionsExtension(
|
||||
"MyEventsExtension", 0);
|
||||
{
|
||||
auto &eventsBasedObject =
|
||||
eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObject", 0);
|
||||
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
writtenProject, "MyExtension::Sprite", "MyChildSprite", 0);
|
||||
}
|
||||
// An event-based object with a custom object child that overrides its
|
||||
// configuration.
|
||||
// The extension with the dependency is added first to make the
|
||||
// implementation change the order in which extensions are loaded.
|
||||
{
|
||||
auto &eventsBasedObject =
|
||||
eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyEventsBasedObjectWithDependency", 0);
|
||||
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
|
||||
writtenProject, "MyEventsExtension::MyEventsBasedObject",
|
||||
"MyChildCustomObject", 0);
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(
|
||||
&childObject.GetConfiguration());
|
||||
auto &spriteConfiguration =
|
||||
customObjectConfiguration->GetChildObjectConfiguration(
|
||||
"MyChildSprite");
|
||||
SetupSpriteConfiguration(spriteConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
SerializerElement projectElement;
|
||||
writtenProject.SerializeTo(projectElement);
|
||||
|
||||
gd::Project readProject;
|
||||
readProject.AddPlatform(platform);
|
||||
readProject.UnserializeFrom(projectElement);
|
||||
|
||||
REQUIRE(readProject.GetEventsFunctionsExtensionsCount() == 1);
|
||||
auto &eventsExtension =
|
||||
readProject.GetEventsFunctionsExtension(0);
|
||||
REQUIRE(eventsExtension.GetEventsBasedObjects().GetCount() ==
|
||||
2);
|
||||
auto &eventsBasedObject =
|
||||
eventsExtension.GetEventsBasedObjects().Get(0);
|
||||
REQUIRE(eventsBasedObject.GetObjects().GetObjectsCount() == 1);
|
||||
auto &childObject = eventsBasedObject.GetObjects().GetObject(0);
|
||||
REQUIRE(childObject.GetName() == "MyChildCustomObject");
|
||||
REQUIRE(childObject.GetType() == "MyEventsExtension::MyEventsBasedObject");
|
||||
auto *customObjectConfiguration =
|
||||
dynamic_cast<gd::CustomObjectConfiguration *>(
|
||||
&childObject.GetConfiguration());
|
||||
REQUIRE(customObjectConfiguration != nullptr);
|
||||
auto &spriteConfiguration =
|
||||
customObjectConfiguration->GetChildObjectConfiguration("MyChildSprite");
|
||||
CheckSpriteConfiguration(spriteConfiguration);
|
||||
}
|
||||
}
|
||||
|
@@ -1992,7 +1992,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
|
||||
|
||||
// A behavior is copied from one extension to another.
|
||||
// An events-based behavior is copied from one extension to another.
|
||||
|
||||
auto &destinationExtension =
|
||||
project.InsertNewEventsFunctionsExtension("DestinationExtension", 0);
|
||||
@@ -2001,21 +2001,19 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
// instruction keeps pointing to the old extension.
|
||||
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
|
||||
|
||||
auto &copiedBehavior =
|
||||
auto &duplicatedBehavior =
|
||||
destinationExtension.GetEventsBasedBehaviors().InsertNew(
|
||||
"MyOtherEventsBasedBehavior", 0);
|
||||
copiedBehavior.SetFullName("My events based behavior");
|
||||
copiedBehavior.SetDescription("An events based behavior for test");
|
||||
copiedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
|
||||
duplicatedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
|
||||
|
||||
// Add the copied events.
|
||||
auto &behaviorEventsFunctions = copiedBehavior.GetEventsFunctions();
|
||||
auto &behaviorAction = behaviorEventsFunctions.InsertNewEventsFunction(
|
||||
"MyBehaviorEventsFunction", 0);
|
||||
auto &eventsFunctions = duplicatedBehavior.GetEventsFunctions();
|
||||
auto &behaviorAction = eventsFunctions.InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
SetupEvents(behaviorAction.GetEvents());
|
||||
|
||||
gd::WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
|
||||
project, destinationExtension, copiedBehavior, "MyEventsExtension");
|
||||
project, destinationExtension, duplicatedBehavior, "MyEventsExtension");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(GetEventFirstActionType(behaviorAction.GetEvents().GetEvent(
|
||||
@@ -2023,13 +2021,123 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
|
||||
for (auto *eventsList : GetEventsLists(project)) {
|
||||
// Check that events function calls in instructions have NOT been renamed
|
||||
// outside of the copied behavior.
|
||||
// outside of the copied events-based behavior.
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(eventsList->GetEvent(FreeFunctionAction)) ==
|
||||
"MyEventsExtension::MyEventsFunction");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Events extension renamed in instructions scoped to one object") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
|
||||
|
||||
// An events-based object is copied from one extension to another.
|
||||
|
||||
auto &destinationExtension =
|
||||
project.InsertNewEventsFunctionsExtension("DestinationExtension", 0);
|
||||
// Add the function used by the instruction that is checked in this test.
|
||||
// When the function doesn't exist the destination extension, the
|
||||
// instruction keeps pointing to the old extension.
|
||||
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
|
||||
|
||||
auto &duplicatedObject =
|
||||
destinationExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyDuplicatedEventsBasedObject", 0);
|
||||
|
||||
// Add the copied events.
|
||||
auto &eventsFunctions = duplicatedObject.GetEventsFunctions();
|
||||
auto &objectAction = eventsFunctions.InsertNewEventsFunction(
|
||||
"MyBehaviorEventsFunction", 0);
|
||||
SetupEvents(objectAction.GetEvents());
|
||||
|
||||
gd::WholeProjectRefactorer::UpdateExtensionNameInEventsBasedObject(
|
||||
project, destinationExtension, duplicatedObject, "MyEventsExtension");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(GetEventFirstActionType(objectAction.GetEvents().GetEvent(
|
||||
FreeFunctionAction)) == "DestinationExtension::MyEventsFunction");
|
||||
|
||||
for (auto *eventsList : GetEventsLists(project)) {
|
||||
// Check that events function calls in instructions have NOT been renamed
|
||||
// outside of the copied events-based object.
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(eventsList->GetEvent(FreeFunctionAction)) ==
|
||||
"MyEventsExtension::MyEventsFunction");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Events-based behavior renamed in instructions scoped to one behavior") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
|
||||
|
||||
// An events-based behavior is duplicated in the same extension.
|
||||
auto &duplicatedBehavior =
|
||||
eventsExtension.GetEventsBasedBehaviors().InsertNew(
|
||||
"MyDuplicatedEventsBasedBehavior", 0);
|
||||
duplicatedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
|
||||
|
||||
// Add the copied events.
|
||||
auto &eventsFunctions = duplicatedBehavior.GetEventsFunctions();
|
||||
auto &behaviorAction = eventsFunctions.InsertNewEventsFunction(
|
||||
"MyBehaviorEventsFunction", 0);
|
||||
SetupEvents(behaviorAction.GetEvents());
|
||||
|
||||
gd::WholeProjectRefactorer::UpdateBehaviorNameInEventsBasedBehavior(
|
||||
project, eventsExtension, duplicatedBehavior, "MyEventsBasedBehavior");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
behaviorAction.GetEvents().GetEvent(BehaviorAction)) ==
|
||||
"MyEventsExtension::MyDuplicatedEventsBasedBehavior::MyBehaviorEventsFunction");
|
||||
|
||||
for (auto *eventsList : GetEventsLists(project)) {
|
||||
// Check that events function calls in instructions have NOT been renamed
|
||||
// outside of the duplicated events-based behavior.
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(eventsList->GetEvent(BehaviorAction)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunction");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Events-based object renamed in instructions scoped to one object") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
|
||||
|
||||
// An events-based object is duplicated in the same extension.
|
||||
auto &duplicatedObject =
|
||||
eventsExtension.GetEventsBasedObjects().InsertNew(
|
||||
"MyDuplicatedEventsBasedObject", 0);
|
||||
|
||||
// Add the copied events.
|
||||
auto &eventsFunctions = duplicatedObject.GetEventsFunctions();
|
||||
auto &objectAction = eventsFunctions.InsertNewEventsFunction(
|
||||
"MyObjectEventsFunction", 0);
|
||||
SetupEvents(objectAction.GetEvents());
|
||||
|
||||
gd::WholeProjectRefactorer::UpdateObjectNameInEventsBasedObject(
|
||||
project, eventsExtension, duplicatedObject, "MyEventsBasedObject");
|
||||
|
||||
// Check that events function calls in instructions have been renamed
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
objectAction.GetEvents().GetEvent(ObjectAction)) ==
|
||||
"MyEventsExtension::MyDuplicatedEventsBasedObject::MyObjectEventsFunction");
|
||||
|
||||
for (auto *eventsList : GetEventsLists(project)) {
|
||||
// Check that events function calls in instructions have NOT been renamed
|
||||
// outside of the duplicated events-based object.
|
||||
REQUIRE(
|
||||
GetEventFirstActionType(eventsList->GetEvent(ObjectAction)) ==
|
||||
"MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunction");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Events extension renamed in parameters") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
|
@@ -73,9 +73,7 @@ namespace gdjs {
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'color') {
|
||||
this.light.color.setHex(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
);
|
||||
this.light.color.setHex(gdjs.rgbOrHexStringToNumber(value));
|
||||
}
|
||||
}
|
||||
updateColorParameter(parameterName: string, value: number): void {
|
||||
|
@@ -94,7 +94,7 @@ namespace gdjs {
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'color') {
|
||||
this.light.color = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'top') {
|
||||
|
@@ -71,7 +71,7 @@ namespace gdjs {
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'color') {
|
||||
this.fog.color = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -95,12 +95,12 @@ namespace gdjs {
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'skyColor') {
|
||||
this.light.color = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'groundColor') {
|
||||
this.light.groundColor = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
if (parameterName === 'top') {
|
||||
|
@@ -803,11 +803,8 @@ module.exports = {
|
||||
}
|
||||
|
||||
const Cube3DObject = new gd.ObjectJsImplementation();
|
||||
Cube3DObject.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
Cube3DObject.updateProperty = function (propertyName, newValue) {
|
||||
const objectContent = this.content;
|
||||
if (
|
||||
propertyName === 'width' ||
|
||||
propertyName === 'height' ||
|
||||
@@ -851,8 +848,9 @@ module.exports = {
|
||||
|
||||
return false;
|
||||
};
|
||||
Cube3DObject.getProperties = function (objectContent) {
|
||||
Cube3DObject.getProperties = function () {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
const objectContent = this.content;
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('enableTextureTransparency')
|
||||
@@ -878,7 +876,8 @@ module.exports = {
|
||||
'The top of each image can touch the **top face** (Y) or the **front face** (Z).'
|
||||
)
|
||||
)
|
||||
.setGroup(_('Face orientation'));
|
||||
.setGroup(_('Face orientation'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('width')
|
||||
@@ -909,7 +908,7 @@ module.exports = {
|
||||
.setValue(objectContent.frontFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Front face image'))
|
||||
.setLabel(_('Front face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
@@ -917,7 +916,7 @@ module.exports = {
|
||||
.setValue(objectContent.backFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Back face image'))
|
||||
.setLabel(_('Back face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
@@ -932,14 +931,15 @@ module.exports = {
|
||||
'The top of the image can touch the **top face** (Y) or the **bottom face** (X).'
|
||||
)
|
||||
)
|
||||
.setGroup(_('Textures'));
|
||||
.setGroup(_('Face orientation'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('leftFaceResourceName')
|
||||
.setValue(objectContent.leftFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Left face image'))
|
||||
.setLabel(_('Left face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
@@ -947,7 +947,7 @@ module.exports = {
|
||||
.setValue(objectContent.rightFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Right face image'))
|
||||
.setLabel(_('Right face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
@@ -955,7 +955,7 @@ module.exports = {
|
||||
.setValue(objectContent.topFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Top face image'))
|
||||
.setLabel(_('Top face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
@@ -963,92 +963,98 @@ module.exports = {
|
||||
.setValue(objectContent.bottomFaceResourceName || '')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Bottom face image'))
|
||||
.setLabel(_('Bottom face'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('frontFaceResourceRepeat')
|
||||
.setValue(objectContent.frontFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile front face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('backFaceResourceRepeat')
|
||||
.setValue(objectContent.backFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile back face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('leftFaceResourceRepeat')
|
||||
.setValue(objectContent.leftFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile left face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('rightFaceResourceRepeat')
|
||||
.setValue(objectContent.rightFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile right face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('topFaceResourceRepeat')
|
||||
.setValue(objectContent.topFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile top face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('bottomFaceResourceRepeat')
|
||||
.setValue(objectContent.bottomFaceResourceRepeat ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Tile bottom face image'))
|
||||
.setLabel(_('Tile'))
|
||||
.setGroup(_('Textures'));
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('frontFaceVisible')
|
||||
.setValue(objectContent.frontFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show front face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Front face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('backFaceVisible')
|
||||
.setValue(objectContent.backFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show back face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Back face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('leftFaceVisible')
|
||||
.setValue(objectContent.leftFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show left face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Left face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('rightFaceVisible')
|
||||
.setValue(objectContent.rightFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show right face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Right face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('topFaceVisible')
|
||||
.setValue(objectContent.topFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show top face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Top face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('bottomFaceVisible')
|
||||
.setValue(objectContent.bottomFaceVisible ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Show bottom face'))
|
||||
.setGroup(_('Face visibility'));
|
||||
.setLabel(_('Bottom face'))
|
||||
.setGroup(_('Face visibility'))
|
||||
.setAdvanced(true);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('materialType')
|
||||
@@ -1060,38 +1066,35 @@ module.exports = {
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
Cube3DObject.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
width: 100,
|
||||
height: 100,
|
||||
depth: 100,
|
||||
enableTextureTransparency: false,
|
||||
facesOrientation: 'Y',
|
||||
frontFaceResourceName: '',
|
||||
backFaceResourceName: '',
|
||||
backFaceUpThroughWhichAxisRotation: 'X',
|
||||
leftFaceResourceName: '',
|
||||
rightFaceResourceName: '',
|
||||
topFaceResourceName: '',
|
||||
bottomFaceResourceName: '',
|
||||
frontFaceVisible: true,
|
||||
backFaceVisible: false,
|
||||
leftFaceVisible: true,
|
||||
rightFaceVisible: true,
|
||||
topFaceVisible: true,
|
||||
bottomFaceVisible: true,
|
||||
frontFaceResourceRepeat: false,
|
||||
backFaceResourceRepeat: false,
|
||||
leftFaceResourceRepeat: false,
|
||||
rightFaceResourceRepeat: false,
|
||||
topFaceResourceRepeat: false,
|
||||
bottomFaceResourceRepeat: false,
|
||||
materialType: 'Basic',
|
||||
})
|
||||
);
|
||||
Cube3DObject.content = {
|
||||
width: 100,
|
||||
height: 100,
|
||||
depth: 100,
|
||||
enableTextureTransparency: false,
|
||||
facesOrientation: 'Y',
|
||||
frontFaceResourceName: '',
|
||||
backFaceResourceName: '',
|
||||
backFaceUpThroughWhichAxisRotation: 'X',
|
||||
leftFaceResourceName: '',
|
||||
rightFaceResourceName: '',
|
||||
topFaceResourceName: '',
|
||||
bottomFaceResourceName: '',
|
||||
frontFaceVisible: true,
|
||||
backFaceVisible: false,
|
||||
leftFaceVisible: true,
|
||||
rightFaceVisible: true,
|
||||
topFaceVisible: true,
|
||||
bottomFaceVisible: true,
|
||||
frontFaceResourceRepeat: false,
|
||||
backFaceResourceRepeat: false,
|
||||
leftFaceResourceRepeat: false,
|
||||
rightFaceResourceRepeat: false,
|
||||
topFaceResourceRepeat: false,
|
||||
bottomFaceResourceRepeat: false,
|
||||
materialType: 'Basic',
|
||||
};
|
||||
|
||||
Cube3DObject.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue
|
||||
@@ -1099,7 +1102,7 @@ module.exports = {
|
||||
return false;
|
||||
};
|
||||
|
||||
Cube3DObject.getInitialInstanceProperties = function (content, instance) {
|
||||
Cube3DObject.getInitialInstanceProperties = function (instance) {
|
||||
const instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
return instanceProperties;
|
||||
};
|
||||
@@ -2060,7 +2063,10 @@ module.exports = {
|
||||
};
|
||||
|
||||
const getFirstVisibleFaceResourceName = (objectConfiguration) => {
|
||||
const properties = objectConfiguration.getProperties();
|
||||
const object = gd.castObject(
|
||||
objectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
const orderedFaces = [
|
||||
['frontFaceVisible', 'frontFaceResourceName'],
|
||||
@@ -2075,10 +2081,8 @@ module.exports = {
|
||||
faceVisibleProperty,
|
||||
faceResourceNameProperty,
|
||||
] of orderedFaces) {
|
||||
if (properties.get(faceVisibleProperty).getValue() === 'true') {
|
||||
const textureResource = properties
|
||||
.get(faceResourceNameProperty)
|
||||
.getValue();
|
||||
if (object.content[faceVisibleProperty]) {
|
||||
const textureResource = object.content[faceResourceNameProperty];
|
||||
if (textureResource) return textureResource;
|
||||
}
|
||||
}
|
||||
@@ -2124,10 +2128,14 @@ module.exports = {
|
||||
// Name of the resource that is rendered.
|
||||
// If no face is visible, this will be null.
|
||||
this._renderedResourceName = undefined;
|
||||
const properties = associatedObjectConfiguration.getProperties();
|
||||
this._defaultWidth = parseFloat(properties.get('width').getValue());
|
||||
this._defaultHeight = parseFloat(properties.get('height').getValue());
|
||||
this._defaultDepth = parseFloat(properties.get('depth').getValue());
|
||||
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
this._defaultWidth = object.content.width;
|
||||
this._defaultHeight = object.content.height;
|
||||
this._defaultDepth = object.content.depth;
|
||||
|
||||
this._pixiObject = new PIXI.Container();
|
||||
this._pixiFallbackObject = new PIXI.Graphics();
|
||||
@@ -2195,6 +2203,8 @@ module.exports = {
|
||||
if (!texture.baseTexture.valid) {
|
||||
// Post pone texture update if texture is not loaded.
|
||||
texture.once('update', () => {
|
||||
if (this._wasDestroyed) return;
|
||||
|
||||
this.updateTexture();
|
||||
this.updatePIXISprite();
|
||||
});
|
||||
@@ -2315,70 +2325,68 @@ module.exports = {
|
||||
pixiResourcesLoader
|
||||
);
|
||||
|
||||
const properties = associatedObjectConfiguration.getProperties();
|
||||
this._defaultWidth = parseFloat(properties.get('width').getValue());
|
||||
this._defaultHeight = parseFloat(properties.get('height').getValue());
|
||||
this._defaultDepth = parseFloat(properties.get('depth').getValue());
|
||||
this._defaultWidth = 1;
|
||||
this._defaultHeight = 1;
|
||||
this._defaultDepth = 1;
|
||||
|
||||
this._pixiObject = new PIXI.Graphics();
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
|
||||
this._faceResourceNames = [
|
||||
properties.get('frontFaceResourceName').getValue(),
|
||||
properties.get('backFaceResourceName').getValue(),
|
||||
properties.get('leftFaceResourceName').getValue(),
|
||||
properties.get('rightFaceResourceName').getValue(),
|
||||
properties.get('topFaceResourceName').getValue(),
|
||||
properties.get('bottomFaceResourceName').getValue(),
|
||||
];
|
||||
this._faceVisibilities = [
|
||||
properties.get('frontFaceVisible').getValue() === 'true',
|
||||
properties.get('backFaceVisible').getValue() === 'true',
|
||||
properties.get('leftFaceVisible').getValue() === 'true',
|
||||
properties.get('rightFaceVisible').getValue() === 'true',
|
||||
properties.get('topFaceVisible').getValue() === 'true',
|
||||
properties.get('bottomFaceVisible').getValue() === 'true',
|
||||
];
|
||||
this._shouldRepeatTextureOnFace = [
|
||||
properties.get('frontFaceResourceRepeat').getValue() === 'true',
|
||||
properties.get('backFaceResourceRepeat').getValue() === 'true',
|
||||
properties.get('leftFaceResourceRepeat').getValue() === 'true',
|
||||
properties.get('rightFaceResourceRepeat').getValue() === 'true',
|
||||
properties.get('topFaceResourceRepeat').getValue() === 'true',
|
||||
properties.get('bottomFaceResourceRepeat').getValue() === 'true',
|
||||
];
|
||||
this._facesOrientation = properties.get('facesOrientation').getValue();
|
||||
this._backFaceUpThroughWhichAxisRotation = properties
|
||||
.get('backFaceUpThroughWhichAxisRotation')
|
||||
.getValue();
|
||||
this._shouldUseTransparentTexture =
|
||||
properties.get('enableTextureTransparency').getValue() === 'true';
|
||||
this._faceResourceNames = ['', '', '', '', '', ''];
|
||||
this._faceVisibilities = [true, true, true, true, true, true];
|
||||
this._shouldRepeatTextureOnFace = [true, true, true, true, true, true];
|
||||
this._facesOrientation = 'Y';
|
||||
this._backFaceUpThroughWhichAxisRotation = 'X';
|
||||
this._shouldUseTransparentTexture = false;
|
||||
|
||||
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
||||
const materials = [
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[0]),
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[1]),
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[2]),
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[3]),
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[4]),
|
||||
this._getFaceMaterial(project, materialIndexToFaceIndex[5]),
|
||||
getTransparentMaterial(),
|
||||
getTransparentMaterial(),
|
||||
getTransparentMaterial(),
|
||||
getTransparentMaterial(),
|
||||
getTransparentMaterial(),
|
||||
getTransparentMaterial(),
|
||||
];
|
||||
this._threeObject = new THREE.Mesh(geometry, materials);
|
||||
this._threeObject.rotation.order = 'ZYX';
|
||||
|
||||
this._threeGroup.add(this._threeObject);
|
||||
|
||||
this.updateThreeObject();
|
||||
}
|
||||
|
||||
_getFaceMaterial(project, faceIndex) {
|
||||
if (!this._faceVisibilities[faceIndex]) return getTransparentMaterial();
|
||||
async _updateThreeObjectMaterials() {
|
||||
const getFaceMaterial = async (project, faceIndex) => {
|
||||
if (!this._faceVisibilities[faceIndex])
|
||||
return getTransparentMaterial();
|
||||
|
||||
return this._pixiResourcesLoader.getThreeMaterial(
|
||||
project,
|
||||
this._faceResourceNames[faceIndex],
|
||||
{
|
||||
useTransparentTexture: this._shouldUseTransparentTexture,
|
||||
}
|
||||
);
|
||||
return await this._pixiResourcesLoader.getThreeMaterial(
|
||||
project,
|
||||
this._faceResourceNames[faceIndex],
|
||||
{
|
||||
useTransparentTexture: this._shouldUseTransparentTexture,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const materials = await Promise.all([
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[0]),
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[1]),
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[2]),
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[3]),
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[4]),
|
||||
getFaceMaterial(this._project, materialIndexToFaceIndex[5]),
|
||||
]);
|
||||
if (this._wasDestroyed) return;
|
||||
|
||||
this._threeObject.material[0] = materials[0];
|
||||
this._threeObject.material[1] = materials[1];
|
||||
this._threeObject.material[2] = materials[2];
|
||||
this._threeObject.material[3] = materials[3];
|
||||
this._threeObject.material[4] = materials[4];
|
||||
this._threeObject.material[5] = materials[5];
|
||||
|
||||
this._updateTextureUvMapping();
|
||||
}
|
||||
|
||||
static _getResourceNameToDisplay(objectConfiguration) {
|
||||
@@ -2386,6 +2394,15 @@ module.exports = {
|
||||
}
|
||||
|
||||
updateThreeObject() {
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
this._defaultWidth = object.content.width;
|
||||
this._defaultHeight = object.content.height;
|
||||
this._defaultDepth = object.content.depth;
|
||||
|
||||
const width = this.getWidth();
|
||||
const height = this.getHeight();
|
||||
const depth = this.getDepth();
|
||||
@@ -2402,18 +2419,107 @@ module.exports = {
|
||||
RenderedInstance.toRad(this._instance.getAngle())
|
||||
);
|
||||
|
||||
let materialsDirty = false;
|
||||
let uvMappingDirty = false;
|
||||
|
||||
const shouldUseTransparentTexture =
|
||||
object.content.enableTextureTransparency;
|
||||
if (this._shouldUseTransparentTexture !== shouldUseTransparentTexture) {
|
||||
this._shouldUseTransparentTexture = shouldUseTransparentTexture;
|
||||
materialsDirty = true;
|
||||
}
|
||||
|
||||
const faceResourceNames = [
|
||||
object.content.frontFaceResourceName,
|
||||
object.content.backFaceResourceName,
|
||||
object.content.leftFaceResourceName,
|
||||
object.content.rightFaceResourceName,
|
||||
object.content.topFaceResourceName,
|
||||
object.content.bottomFaceResourceName,
|
||||
];
|
||||
if (
|
||||
this._faceResourceNames[0] !== faceResourceNames[0] ||
|
||||
this._faceResourceNames[1] !== faceResourceNames[1] ||
|
||||
this._faceResourceNames[2] !== faceResourceNames[2] ||
|
||||
this._faceResourceNames[3] !== faceResourceNames[3] ||
|
||||
this._faceResourceNames[4] !== faceResourceNames[4] ||
|
||||
this._faceResourceNames[5] !== faceResourceNames[5]
|
||||
) {
|
||||
this._faceResourceNames = faceResourceNames;
|
||||
materialsDirty = true;
|
||||
}
|
||||
|
||||
const faceVisibilities = [
|
||||
object.content.frontFaceVisible,
|
||||
object.content.backFaceVisible,
|
||||
object.content.leftFaceVisible,
|
||||
object.content.rightFaceVisible,
|
||||
object.content.topFaceVisible,
|
||||
object.content.bottomFaceVisible,
|
||||
];
|
||||
if (
|
||||
this._faceVisibilities[0] !== faceVisibilities[0] ||
|
||||
this._faceVisibilities[1] !== faceVisibilities[1] ||
|
||||
this._faceVisibilities[2] !== faceVisibilities[2] ||
|
||||
this._faceVisibilities[3] !== faceVisibilities[3] ||
|
||||
this._faceVisibilities[4] !== faceVisibilities[4] ||
|
||||
this._faceVisibilities[5] !== faceVisibilities[5]
|
||||
) {
|
||||
this._faceVisibilities = faceVisibilities;
|
||||
materialsDirty = true;
|
||||
uvMappingDirty = true;
|
||||
}
|
||||
|
||||
const shouldRepeatTextureOnFace = [
|
||||
object.content.frontFaceResourceRepeat,
|
||||
object.content.backFaceResourceRepeat,
|
||||
object.content.leftFaceResourceRepeat,
|
||||
object.content.rightFaceResourceRepeat,
|
||||
object.content.topFaceResourceRepeat,
|
||||
object.content.bottomFaceResourceRepeat,
|
||||
];
|
||||
if (
|
||||
this._shouldRepeatTextureOnFace[0] !== shouldRepeatTextureOnFace[0] ||
|
||||
this._shouldRepeatTextureOnFace[1] !== shouldRepeatTextureOnFace[1] ||
|
||||
this._shouldRepeatTextureOnFace[2] !== shouldRepeatTextureOnFace[2] ||
|
||||
this._shouldRepeatTextureOnFace[3] !== shouldRepeatTextureOnFace[3] ||
|
||||
this._shouldRepeatTextureOnFace[4] !== shouldRepeatTextureOnFace[4] ||
|
||||
this._shouldRepeatTextureOnFace[5] !== shouldRepeatTextureOnFace[5]
|
||||
) {
|
||||
this._shouldRepeatTextureOnFace = shouldRepeatTextureOnFace;
|
||||
uvMappingDirty = true;
|
||||
}
|
||||
|
||||
const backFaceUpThroughWhichAxisRotation =
|
||||
object.content.backFaceUpThroughWhichAxisRotation;
|
||||
if (
|
||||
backFaceUpThroughWhichAxisRotation !==
|
||||
this._backFaceUpThroughWhichAxisRotation
|
||||
) {
|
||||
this._backFaceUpThroughWhichAxisRotation = backFaceUpThroughWhichAxisRotation;
|
||||
uvMappingDirty = true;
|
||||
}
|
||||
|
||||
const facesOrientation = object.content.facesOrientation;
|
||||
if (facesOrientation !== this._facesOrientation) {
|
||||
this._facesOrientation = facesOrientation;
|
||||
uvMappingDirty = true;
|
||||
}
|
||||
|
||||
const scaleX = width * (this._instance.isFlippedX() ? -1 : 1);
|
||||
const scaleY = height * (this._instance.isFlippedY() ? -1 : 1);
|
||||
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
|
||||
|
||||
if (
|
||||
scaleX !== this._threeObject.scale.width ||
|
||||
scaleY !== this._threeObject.scale.height ||
|
||||
scaleZ !== this._threeObject.scale.depth
|
||||
scaleX !== this._threeObject.scale.x ||
|
||||
scaleY !== this._threeObject.scale.y ||
|
||||
scaleZ !== this._threeObject.scale.z
|
||||
) {
|
||||
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
|
||||
this.updateTextureUvMapping();
|
||||
uvMappingDirty = true;
|
||||
}
|
||||
|
||||
if (materialsDirty) this._updateThreeObjectMaterials();
|
||||
if (uvMappingDirty) this._updateTextureUvMapping();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2422,7 +2528,7 @@ module.exports = {
|
||||
* The mesh must be configured with a list of materials in order
|
||||
* for the method to work.
|
||||
*/
|
||||
updateTextureUvMapping() {
|
||||
_updateTextureUvMapping() {
|
||||
// @ts-ignore - position is stored as a Float32BufferAttribute
|
||||
/** @type {THREE.BufferAttribute} */
|
||||
const pos = this._threeObject.geometry.getAttribute('position');
|
||||
@@ -2696,25 +2802,23 @@ module.exports = {
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
);
|
||||
const properties = associatedObjectConfiguration.getProperties();
|
||||
this._defaultWidth = parseFloat(properties.get('width').getValue());
|
||||
this._defaultHeight = parseFloat(properties.get('height').getValue());
|
||||
this._defaultDepth = parseFloat(properties.get('depth').getValue());
|
||||
const rotationX = parseFloat(properties.get('rotationX').getValue());
|
||||
const rotationY = parseFloat(properties.get('rotationY').getValue());
|
||||
const rotationZ = parseFloat(properties.get('rotationZ').getValue());
|
||||
const keepAspectRatio =
|
||||
properties.get('keepAspectRatio').getValue() === 'true';
|
||||
const modelResourceName = properties
|
||||
.get('modelResourceName')
|
||||
.getValue();
|
||||
this._originPoint = getPointForLocation(
|
||||
properties.get('originLocation').getValue()
|
||||
);
|
||||
this._centerPoint = getPointForLocation(
|
||||
properties.get('centerLocation').getValue()
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.Model3DObjectConfiguration
|
||||
);
|
||||
|
||||
this._defaultWidth = object.getWidth();
|
||||
this._defaultHeight = object.getHeight();
|
||||
this._defaultDepth = object.getDepth();
|
||||
const rotationX = object.getRotationX();
|
||||
const rotationY = object.getRotationY();
|
||||
const rotationZ = object.getRotationZ();
|
||||
const keepAspectRatio = object.shouldKeepAspectRatio();
|
||||
const modelResourceName = object.getModelResourceName();
|
||||
|
||||
this._originPoint = getPointForLocation(object.getOriginLocation());
|
||||
this._centerPoint = getPointForLocation(object.getCenterLocation());
|
||||
|
||||
// This renderer shows a placeholder for the object:
|
||||
this._pixiObject = new PIXI.Graphics();
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
@@ -2722,6 +2826,7 @@ module.exports = {
|
||||
this._pixiResourcesLoader
|
||||
.get3DModel(project, modelResourceName)
|
||||
.then((model3d) => {
|
||||
if (this._wasDestroyed) return;
|
||||
const clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
|
||||
model3d.scene
|
||||
);
|
||||
@@ -2923,6 +3028,17 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
const isSamePoint = (point1, point2) => {
|
||||
if (!point1 && !point2) return true;
|
||||
if (point1 && !point2) return false;
|
||||
if (!point1 && point2) return false;
|
||||
return (
|
||||
point1[0] === point2[0] &&
|
||||
point1[1] === point2[1] &&
|
||||
point1[2] === point2[2]
|
||||
);
|
||||
};
|
||||
|
||||
const getPointForLocation = (location) => {
|
||||
switch (location) {
|
||||
case 'ModelOrigin':
|
||||
@@ -2959,24 +3075,20 @@ module.exports = {
|
||||
threeGroup,
|
||||
pixiResourcesLoader
|
||||
);
|
||||
const properties = associatedObjectConfiguration.getProperties();
|
||||
this._defaultWidth = parseFloat(properties.get('width').getValue());
|
||||
this._defaultHeight = parseFloat(properties.get('height').getValue());
|
||||
this._defaultDepth = parseFloat(properties.get('depth').getValue());
|
||||
const rotationX = parseFloat(properties.get('rotationX').getValue());
|
||||
const rotationY = parseFloat(properties.get('rotationY').getValue());
|
||||
const rotationZ = parseFloat(properties.get('rotationZ').getValue());
|
||||
const keepAspectRatio =
|
||||
properties.get('keepAspectRatio').getValue() === 'true';
|
||||
const modelResourceName = properties
|
||||
.get('modelResourceName')
|
||||
.getValue();
|
||||
this._originPoint = getPointForLocation(
|
||||
properties.get('originLocation').getValue()
|
||||
);
|
||||
this._centerPoint = getPointForLocation(
|
||||
properties.get('centerLocation').getValue()
|
||||
);
|
||||
|
||||
this._defaultWidth = 1;
|
||||
this._defaultHeight = 1;
|
||||
this._defaultDepth = 1;
|
||||
this._originalWidth = 1;
|
||||
this._originalHeight = 1;
|
||||
this._originalDepth = 1;
|
||||
this._rotationX = 0;
|
||||
this._rotationY = 0;
|
||||
this._rotationZ = 0;
|
||||
this._keepAspectRatio = false;
|
||||
|
||||
this._originPoint = null;
|
||||
this._centerPoint = null;
|
||||
|
||||
this._pixiObject = new PIXI.Graphics();
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
@@ -2985,28 +3097,8 @@ module.exports = {
|
||||
this._threeObject.rotation.order = 'ZYX';
|
||||
this._threeGroup.add(this._threeObject);
|
||||
|
||||
this._pixiResourcesLoader
|
||||
.get3DModel(project, modelResourceName)
|
||||
.then((model3d) => {
|
||||
const clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
|
||||
model3d.scene
|
||||
);
|
||||
// This group hold the rotation defined by properties.
|
||||
const threeObject = new THREE.Group();
|
||||
threeObject.rotation.order = 'ZYX';
|
||||
threeObject.add(clonedModel3D);
|
||||
this._updateDefaultTransformation(
|
||||
threeObject,
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
this._defaultWidth,
|
||||
this._defaultHeight,
|
||||
this._defaultDepth,
|
||||
keepAspectRatio
|
||||
);
|
||||
this._threeObject.add(threeObject);
|
||||
});
|
||||
this._threeModelGroup = null;
|
||||
this._clonedModel3D = null;
|
||||
}
|
||||
|
||||
getOriginX() {
|
||||
@@ -3047,23 +3139,28 @@ module.exports = {
|
||||
return this._centerPoint || this._modelOriginPoint;
|
||||
}
|
||||
|
||||
_updateDefaultTransformation(
|
||||
threeObject,
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
originalDepth,
|
||||
keepAspectRatio
|
||||
) {
|
||||
threeObject.rotation.set(
|
||||
(rotationX * Math.PI) / 180,
|
||||
(rotationY * Math.PI) / 180,
|
||||
(rotationZ * Math.PI) / 180
|
||||
_updateDefaultTransformation() {
|
||||
if (!this._clonedModel3D) return; // Model is not ready - nothing to do.
|
||||
|
||||
if (this._threeModelGroup) {
|
||||
// Remove any previous container as we will recreate it just below
|
||||
this._threeObject.clear();
|
||||
}
|
||||
// This group hold the rotation defined by properties.
|
||||
// Always restart from a new group to avoid miscomputing bounding boxes/sizes.
|
||||
this._threeModelGroup = new THREE.Group();
|
||||
this._threeModelGroup.rotation.order = 'ZYX';
|
||||
this._threeModelGroup.add(this._clonedModel3D);
|
||||
|
||||
this._threeModelGroup.rotation.set(
|
||||
(this._rotationX * Math.PI) / 180,
|
||||
(this._rotationY * Math.PI) / 180,
|
||||
(this._rotationZ * Math.PI) / 180
|
||||
);
|
||||
this._threeModelGroup.updateMatrixWorld(true);
|
||||
const boundingBox = new THREE.Box3().setFromObject(
|
||||
this._threeModelGroup
|
||||
);
|
||||
threeObject.updateMatrixWorld(true);
|
||||
const boundingBox = new THREE.Box3().setFromObject(threeObject);
|
||||
|
||||
const shouldKeepModelOrigin = !this._originPoint;
|
||||
if (shouldKeepModelOrigin) {
|
||||
@@ -3090,7 +3187,7 @@ module.exports = {
|
||||
// Center the model.
|
||||
const centerPoint = this._centerPoint;
|
||||
if (centerPoint) {
|
||||
threeObject.position.set(
|
||||
this._threeModelGroup.position.set(
|
||||
-(boundingBox.min.x + modelWidth * centerPoint[0]),
|
||||
// The model is flipped on Y axis.
|
||||
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
|
||||
@@ -3099,11 +3196,11 @@ module.exports = {
|
||||
}
|
||||
|
||||
// Rotate the model.
|
||||
threeObject.scale.set(1, 1, 1);
|
||||
threeObject.rotation.set(
|
||||
(rotationX * Math.PI) / 180,
|
||||
(rotationY * Math.PI) / 180,
|
||||
(rotationZ * Math.PI) / 180
|
||||
this._threeModelGroup.scale.set(1, 1, 1);
|
||||
this._threeModelGroup.rotation.set(
|
||||
(this._rotationX * Math.PI) / 180,
|
||||
(this._rotationY * Math.PI) / 180,
|
||||
(this._rotationZ * Math.PI) / 180
|
||||
);
|
||||
|
||||
// Stretch the model in a 1x1x1 cube.
|
||||
@@ -3115,23 +3212,23 @@ module.exports = {
|
||||
// Flip on Y because the Y axis is on the opposite side of direct basis.
|
||||
// It avoids models to be like a mirror refection.
|
||||
scaleMatrix.makeScale(scaleX, -scaleY, scaleZ);
|
||||
threeObject.updateMatrix();
|
||||
threeObject.applyMatrix4(scaleMatrix);
|
||||
this._threeModelGroup.updateMatrix();
|
||||
this._threeModelGroup.applyMatrix4(scaleMatrix);
|
||||
|
||||
if (keepAspectRatio) {
|
||||
if (this._keepAspectRatio) {
|
||||
// Reduce the object dimensions to keep aspect ratio.
|
||||
const widthRatio =
|
||||
modelWidth < epsilon
|
||||
? Number.POSITIVE_INFINITY
|
||||
: originalWidth / modelWidth;
|
||||
: this._originalWidth / modelWidth;
|
||||
const heightRatio =
|
||||
modelHeight < epsilon
|
||||
? Number.POSITIVE_INFINITY
|
||||
: originalHeight / modelHeight;
|
||||
: this._originalHeight / modelHeight;
|
||||
const depthRatio =
|
||||
modelDepth < epsilon
|
||||
? Number.POSITIVE_INFINITY
|
||||
: originalDepth / modelDepth;
|
||||
: this._originalDepth / modelDepth;
|
||||
const minScaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
|
||||
if (!Number.isFinite(minScaleRatio)) {
|
||||
this._defaultWidth = modelWidth;
|
||||
@@ -3139,48 +3236,125 @@ module.exports = {
|
||||
this._defaultDepth = modelDepth;
|
||||
} else {
|
||||
if (widthRatio === minScaleRatio) {
|
||||
this._defaultWidth = originalWidth;
|
||||
this._defaultWidth = this._originalWidth;
|
||||
this._defaultHeight = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelWidth,
|
||||
newReferenceValue: originalWidth,
|
||||
newReferenceValue: this._originalWidth,
|
||||
valueToApplyTo: modelHeight,
|
||||
});
|
||||
this._defaultDepth = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelWidth,
|
||||
newReferenceValue: originalWidth,
|
||||
newReferenceValue: this._originalWidth,
|
||||
valueToApplyTo: modelDepth,
|
||||
});
|
||||
} else if (heightRatio === minScaleRatio) {
|
||||
this._defaultWidth = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelHeight,
|
||||
newReferenceValue: originalHeight,
|
||||
newReferenceValue: this._originalHeight,
|
||||
valueToApplyTo: modelWidth,
|
||||
});
|
||||
|
||||
this._defaultHeight = originalHeight;
|
||||
this._defaultHeight = this._originalHeight;
|
||||
this._defaultDepth = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelHeight,
|
||||
newReferenceValue: originalHeight,
|
||||
newReferenceValue: this._originalHeight,
|
||||
valueToApplyTo: modelDepth,
|
||||
});
|
||||
} else {
|
||||
this._defaultWidth = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelDepth,
|
||||
newReferenceValue: originalDepth,
|
||||
newReferenceValue: this._originalDepth,
|
||||
valueToApplyTo: modelWidth,
|
||||
});
|
||||
this._defaultHeight = Rendered3DInstance.applyRatio({
|
||||
oldReferenceValue: modelDepth,
|
||||
newReferenceValue: originalDepth,
|
||||
newReferenceValue: this._originalDepth,
|
||||
valueToApplyTo: modelHeight,
|
||||
});
|
||||
this._defaultDepth = originalDepth;
|
||||
this._defaultDepth = this._originalDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._threeObject.add(this._threeModelGroup);
|
||||
}
|
||||
|
||||
updateThreeObject() {
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.Model3DObjectConfiguration
|
||||
);
|
||||
|
||||
let defaultTransformationDirty = false;
|
||||
|
||||
const originalWidth = object.getWidth();
|
||||
const originalHeight = object.getHeight();
|
||||
const originalDepth = object.getDepth();
|
||||
if (
|
||||
this._originalWidth !== originalWidth ||
|
||||
this._originalHeight !== originalHeight ||
|
||||
this._originalDepth !== originalDepth
|
||||
) {
|
||||
this._originalWidth = originalWidth;
|
||||
this._originalHeight = originalHeight;
|
||||
this._originalDepth = originalDepth;
|
||||
defaultTransformationDirty = true;
|
||||
}
|
||||
|
||||
const rotationX = object.getRotationX();
|
||||
const rotationY = object.getRotationY();
|
||||
const rotationZ = object.getRotationZ();
|
||||
if (
|
||||
this._rotationX !== rotationX ||
|
||||
this._rotationY !== rotationY ||
|
||||
this._rotationZ !== rotationZ
|
||||
) {
|
||||
this._rotationX = rotationX;
|
||||
this._rotationY = rotationY;
|
||||
this._rotationZ = rotationZ;
|
||||
defaultTransformationDirty = true;
|
||||
}
|
||||
|
||||
const keepAspectRatio = object.shouldKeepAspectRatio();
|
||||
if (this._keepAspectRatio !== keepAspectRatio) {
|
||||
this._keepAspectRatio = keepAspectRatio;
|
||||
defaultTransformationDirty = true;
|
||||
}
|
||||
|
||||
const originPoint = getPointForLocation(object.getOriginLocation());
|
||||
if (!isSamePoint(originPoint, this._originPoint)) {
|
||||
this._originPoint = originPoint;
|
||||
defaultTransformationDirty = true;
|
||||
}
|
||||
|
||||
const centerPoint = getPointForLocation(object.getCenterLocation());
|
||||
if (!isSamePoint(centerPoint, this._centerPoint)) {
|
||||
this._centerPoint = centerPoint;
|
||||
defaultTransformationDirty = true;
|
||||
}
|
||||
|
||||
if (defaultTransformationDirty) this._updateDefaultTransformation();
|
||||
|
||||
const modelResourceName = object.getModelResourceName();
|
||||
if (this._modelResourceName !== modelResourceName) {
|
||||
this._modelResourceName = modelResourceName;
|
||||
|
||||
this._pixiResourcesLoader
|
||||
.get3DModel(this._project, modelResourceName)
|
||||
.then((model3d) => {
|
||||
if (this._wasDestroyed) return;
|
||||
this._clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
|
||||
model3d.scene
|
||||
);
|
||||
|
||||
this._updateDefaultTransformation();
|
||||
});
|
||||
}
|
||||
|
||||
this._updateThreeObjectPosition();
|
||||
}
|
||||
|
||||
_updateThreeObjectPosition() {
|
||||
const width = this.getWidth();
|
||||
const height = this.getHeight();
|
||||
const depth = this.getDepth();
|
||||
@@ -3204,9 +3378,9 @@ module.exports = {
|
||||
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
|
||||
|
||||
if (
|
||||
scaleX !== this._threeObject.scale.width ||
|
||||
scaleY !== this._threeObject.scale.height ||
|
||||
scaleZ !== this._threeObject.scale.depth
|
||||
scaleX !== this._threeObject.scale.x ||
|
||||
scaleY !== this._threeObject.scale.y ||
|
||||
scaleZ !== this._threeObject.scale.z
|
||||
) {
|
||||
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ namespace gdjs {
|
||||
updateStringParameter(parameterName: string, value: string): void {
|
||||
if (parameterName === 'color') {
|
||||
this.fog.color = new THREE.Color(
|
||||
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
|
||||
gdjs.rgbOrHexStringToNumber(value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -109,23 +109,26 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
objectProperties["rotationX"]
|
||||
.SetValue(gd::String::From(rotationX))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around X axis"))
|
||||
.SetLabel(_("X"))
|
||||
.SetDescription(_("Rotation around X axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
.SetGroup(_("Default rotation"));
|
||||
|
||||
objectProperties["rotationY"]
|
||||
.SetValue(gd::String::From(rotationY))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around Y axis"))
|
||||
.SetLabel(_("Y"))
|
||||
.SetDescription(_("Rotation around Y axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
.SetGroup(_("Default rotation"));
|
||||
|
||||
objectProperties["rotationZ"]
|
||||
.SetValue(gd::String::From(rotationZ))
|
||||
.SetType("number")
|
||||
.SetLabel(_("Rotation around Z axis"))
|
||||
.SetLabel(_("Z"))
|
||||
.SetDescription(_("Rotation around Z axis"))
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
|
||||
.SetGroup(_("Default orientation"));
|
||||
.SetGroup(_("Default rotation"));
|
||||
|
||||
objectProperties["modelResourceName"]
|
||||
.SetValue(modelResourceName)
|
||||
@@ -139,7 +142,7 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
.AddExtraInfo("Basic")
|
||||
.AddExtraInfo("StandardWithoutMetalness")
|
||||
.AddExtraInfo("KeepOriginal")
|
||||
.SetLabel(_("Material modifier"));
|
||||
.SetLabel(_("Material"));
|
||||
|
||||
objectProperties["originLocation"]
|
||||
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
|
||||
@@ -149,7 +152,9 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.SetLabel(_("Origin point"));
|
||||
.SetLabel(_("Origin point"))
|
||||
.SetGroup(_("Points"))
|
||||
.SetAdvanced(true);
|
||||
|
||||
objectProperties["centerLocation"]
|
||||
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
|
||||
@@ -158,7 +163,9 @@ Model3DObjectConfiguration::GetProperties() const {
|
||||
.AddExtraInfo("ObjectCenter")
|
||||
.AddExtraInfo("BottomCenterZ")
|
||||
.AddExtraInfo("BottomCenterY")
|
||||
.SetLabel(_("Center point"));
|
||||
.SetLabel(_("Center point"))
|
||||
.SetGroup(_("Points"))
|
||||
.SetAdvanced(true);
|
||||
|
||||
return objectProperties;
|
||||
}
|
||||
|
@@ -140,7 +140,25 @@ public:
|
||||
const std::vector<Model3DAnimation> &GetAllAnimations() const {
|
||||
return animations;
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Getters
|
||||
* Fast access for rendering instances.
|
||||
*/
|
||||
///@{
|
||||
double GetWidth() const { return width; };
|
||||
double GetHeight() const { return height; };
|
||||
double GetDepth() const { return depth; };
|
||||
double GetRotationX() const { return rotationX; };
|
||||
double GetRotationY() const { return rotationY; };
|
||||
double GetRotationZ() const { return rotationZ; };
|
||||
|
||||
const gd::String& GetModelResourceName() const { return modelResourceName; };
|
||||
const gd::String& GetMaterialType() const { return materialType; };
|
||||
const gd::String& GetOriginLocation() const { return originLocation; };
|
||||
const gd::String& GetCenterLocation() const { return centerLocation; };
|
||||
|
||||
bool shouldKeepAspectRatio() const { return keepAspectRatio; };
|
||||
///@}
|
||||
|
||||
protected:
|
||||
|
@@ -376,6 +376,9 @@ namespace gdjs {
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
this._renderer.setAnimationElapsedTime(time);
|
||||
if (!this._animationPaused) {
|
||||
this._renderer.resumeAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
getAnimationDuration(): float {
|
||||
|
@@ -7,15 +7,18 @@ namespace gdjs {
|
||||
export namespace evtTools {
|
||||
export namespace advancedWindow {
|
||||
const getElectronBrowserWindow = (runtimeScene: gdjs.RuntimeScene) => {
|
||||
const electronRemote = runtimeScene
|
||||
.getGame()
|
||||
.getRenderer()
|
||||
.getElectronRemote();
|
||||
if (electronRemote) {
|
||||
return electronRemote.getCurrentWindow();
|
||||
try {
|
||||
const electronRemote = runtimeScene
|
||||
.getGame()
|
||||
.getRenderer()
|
||||
.getElectronRemote();
|
||||
if (electronRemote) {
|
||||
return electronRemote.getCurrentWindow();
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const focus = function (
|
||||
|
@@ -75,7 +75,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window right"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Left edge anchor"))
|
||||
.SetLabel(_("Left edge"))
|
||||
.SetDescription(_("Anchor the left edge of the object on X axis."));
|
||||
|
||||
properties["rightEdgeAnchor"]
|
||||
@@ -87,7 +87,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window right"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Right edge anchor"))
|
||||
.SetLabel(_("Right edge"))
|
||||
.SetDescription(_("Anchor the right edge of the object on X axis."));
|
||||
|
||||
properties["topEdgeAnchor"]
|
||||
@@ -99,7 +99,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window bottom"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Top edge anchor"))
|
||||
.SetLabel(_("Top edge"))
|
||||
.SetDescription(_("Anchor the top edge of the object on Y axis."));
|
||||
|
||||
properties["bottomEdgeAnchor"]
|
||||
@@ -111,7 +111,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
.AddExtraInfo(_("Window center"))
|
||||
.AddExtraInfo(_("Window bottom"))
|
||||
.AddExtraInfo(_("Proportional"))
|
||||
.SetLabel(_("Bottom edge anchor"))
|
||||
.SetLabel(_("Bottom edge"))
|
||||
.SetDescription(_("Anchor the bottom edge of the object on Y axis."));
|
||||
|
||||
properties["useLegacyBottomAndRightAnchors"]
|
||||
@@ -119,12 +119,12 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
|
||||
"Stretch object when anchoring right or bottom edge (deprecated, "
|
||||
"it's recommended to leave this unchecked and anchor both sides if "
|
||||
"you want Sprite to stretch instead.)"))
|
||||
.SetGroup(_("Deprecated options (advanced)"))
|
||||
.SetValue(behaviorContent.GetBoolAttribute(
|
||||
"useLegacyBottomAndRightAnchors", true)
|
||||
? "true"
|
||||
: "false")
|
||||
.SetType("Boolean");
|
||||
.SetType("Boolean")
|
||||
.SetDeprecated(true);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
@@ -88,7 +88,6 @@ namespace gdjs {
|
||||
const workingPoint: FloatPoint = gdjs.staticArray(
|
||||
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
|
||||
) as FloatPoint;
|
||||
// TODO EBO Make it work with event based objects or hide this behavior for them.
|
||||
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
|
||||
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
|
||||
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
|
||||
@@ -111,10 +110,11 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
//Calculate the distances from the window's bounds.
|
||||
const topLeftPixel = layer.convertCoords(
|
||||
const topLeftPixel = this._convertCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX(),
|
||||
this.owner.getDrawableY(),
|
||||
0,
|
||||
workingPoint
|
||||
);
|
||||
|
||||
@@ -141,10 +141,11 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
|
||||
const bottomRightPixel = layer.convertCoords(
|
||||
const bottomRightPixel = this._convertCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
this.owner.getDrawableX() + this.owner.getWidth(),
|
||||
this.owner.getDrawableY() + this.owner.getHeight(),
|
||||
0,
|
||||
workingPoint
|
||||
);
|
||||
|
||||
@@ -225,19 +226,21 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
|
||||
const topLeftCoord = layer.convertInverseCoords(
|
||||
const topLeftCoord = this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
leftPixel,
|
||||
topPixel,
|
||||
0,
|
||||
workingPoint
|
||||
);
|
||||
const left = topLeftCoord[0];
|
||||
const top = topLeftCoord[1];
|
||||
|
||||
const bottomRightCoord = layer.convertInverseCoords(
|
||||
const bottomRightCoord = this._convertInverseCoords(
|
||||
instanceContainer,
|
||||
layer,
|
||||
rightPixel,
|
||||
bottomPixel,
|
||||
0,
|
||||
workingPoint
|
||||
);
|
||||
const right = bottomRightCoord[0];
|
||||
@@ -270,8 +273,20 @@ namespace gdjs {
|
||||
this._rightEdgeAnchor !== HorizontalAnchor.None &&
|
||||
this._leftEdgeAnchor !== HorizontalAnchor.None
|
||||
) {
|
||||
this.owner.setWidth(right - left);
|
||||
this.owner.setX(left);
|
||||
const width = right - left;
|
||||
this.owner.setX(
|
||||
this.owner.getX() === this.owner.getDrawableX()
|
||||
? left
|
||||
: // It uses the position of the origin relatively to the object
|
||||
// size to apply it with the new size.
|
||||
// This is the same as doing:
|
||||
// lerp(left, right, (this.owner.getX() - this.owner.getDrawableX() / this.owner.getWidth())
|
||||
// But, the division is done at the end to avoid rounding errors.
|
||||
left +
|
||||
((this.owner.getX() - this.owner.getDrawableX()) * width) /
|
||||
this.owner.getWidth()
|
||||
);
|
||||
this.owner.setWidth(width);
|
||||
} else {
|
||||
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
|
||||
this.owner.setX(
|
||||
@@ -292,8 +307,15 @@ namespace gdjs {
|
||||
this._bottomEdgeAnchor !== VerticalAnchor.None &&
|
||||
this._topEdgeAnchor !== VerticalAnchor.None
|
||||
) {
|
||||
this.owner.setHeight(bottom - top);
|
||||
this.owner.setY(top);
|
||||
const height = bottom - top;
|
||||
this.owner.setY(
|
||||
this.owner.getY() === this.owner.getDrawableY()
|
||||
? top
|
||||
: top +
|
||||
((this.owner.getY() - this.owner.getDrawableY()) * height) /
|
||||
this.owner.getHeight()
|
||||
);
|
||||
this.owner.setHeight(height);
|
||||
} else {
|
||||
if (this._topEdgeAnchor !== VerticalAnchor.None) {
|
||||
this.owner.setY(
|
||||
@@ -314,6 +336,40 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
||||
private _convertCoords(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
layer: gdjs.RuntimeLayer,
|
||||
x: float,
|
||||
y: float,
|
||||
result: FloatPoint
|
||||
) {
|
||||
const isParentACustomObject =
|
||||
instanceContainer !== instanceContainer.getScene();
|
||||
if (isParentACustomObject) {
|
||||
result[0] = x;
|
||||
result[1] = y;
|
||||
return result;
|
||||
}
|
||||
return layer.convertCoords(x, y, 0, result);
|
||||
}
|
||||
|
||||
private _convertInverseCoords(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
layer: gdjs.RuntimeLayer,
|
||||
x: float,
|
||||
y: float,
|
||||
result: FloatPoint
|
||||
) {
|
||||
const isParentACustomObject =
|
||||
instanceContainer !== instanceContainer.getScene();
|
||||
if (isParentACustomObject) {
|
||||
result[0] = x;
|
||||
result[1] = y;
|
||||
return result;
|
||||
}
|
||||
return layer.convertInverseCoords(x, y, 0, result);
|
||||
}
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'AnchorBehavior::AnchorBehavior',
|
||||
|
@@ -70,6 +70,50 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
return object;
|
||||
}
|
||||
|
||||
const createSpriteWithOriginAtCenter = (behaviorProperties) => {
|
||||
const object = new gdjs.TestSpriteRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [
|
||||
{
|
||||
name: anchorBehaviorName,
|
||||
type: 'AnchorBehavior::AnchorBehavior',
|
||||
// @ts-ignore - properties are not typed
|
||||
rightEdgeAnchor: 0,
|
||||
leftEdgeAnchor: 0,
|
||||
topEdgeAnchor: 0,
|
||||
bottomEdgeAnchor: 0,
|
||||
relativeToOriginalWindowSize: false,
|
||||
useLegacyBottomAndRightAnchors: false,
|
||||
...behaviorProperties,
|
||||
},
|
||||
],
|
||||
effects: [],
|
||||
animations: [
|
||||
{
|
||||
name: 'animation',
|
||||
directions: [
|
||||
{
|
||||
sprites: [
|
||||
{
|
||||
originPoint: { x: 50, y: 50 },
|
||||
centerPoint: { x: 50, y: 50 },
|
||||
points: [],
|
||||
hasCustomCollisionMask: false,
|
||||
customCollisionMask: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(10, 10);
|
||||
runtimeScene.addObject(object);
|
||||
return object;
|
||||
};
|
||||
|
||||
describe('(anchor horizontal edge)', function () {
|
||||
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
|
||||
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {
|
||||
@@ -200,5 +244,47 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
expect(object.getY()).to.equal(1000);
|
||||
expect(object.getWidth()).to.equal(10);
|
||||
});
|
||||
|
||||
it('can fill the screen with an object (with custom origin)', function () {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 1,
|
||||
topEdgeAnchor: 1,
|
||||
rightEdgeAnchor: 2,
|
||||
bottomEdgeAnchor: 2,
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
});
|
||||
|
||||
it('can fill the screen with an object using proportional anchors (with custom origin)', () => {
|
||||
setGameResolutionSizeAndStep(1000, 500);
|
||||
|
||||
const object = createSpriteWithOriginAtCenter({
|
||||
leftEdgeAnchor: 3,
|
||||
topEdgeAnchor: 3,
|
||||
rightEdgeAnchor: 3,
|
||||
bottomEdgeAnchor: 3,
|
||||
});
|
||||
object.setCustomWidthAndHeight(1000, 500);
|
||||
object.setPosition(500, 250);
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
setGameResolutionSizeAndStep(2000, 3000);
|
||||
|
||||
expect(object.getX()).to.equal(1000);
|
||||
expect(object.getY()).to.equal(1500);
|
||||
expect(object.getWidth()).to.equal(2000);
|
||||
expect(object.getHeight()).to.equal(3000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -34,11 +34,8 @@ module.exports = {
|
||||
.setIcon('JsPlatform/Extensions/bbcode32.png');
|
||||
|
||||
var objectBBText = new gd.ObjectJsImplementation();
|
||||
objectBBText.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
objectBBText.updateProperty = function (propertyName, newValue) {
|
||||
const objectContent = this.content;
|
||||
if (propertyName in objectContent) {
|
||||
if (typeof objectContent[propertyName] === 'boolean')
|
||||
objectContent[propertyName] = newValue === '1';
|
||||
@@ -50,8 +47,9 @@ module.exports = {
|
||||
|
||||
return false;
|
||||
};
|
||||
objectBBText.getProperties = function (objectContent) {
|
||||
objectBBText.getProperties = function () {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
const objectContent = this.content;
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('text')
|
||||
@@ -107,29 +105,26 @@ module.exports = {
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
objectBBText.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
text:
|
||||
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
|
||||
opacity: 255,
|
||||
fontSize: 20,
|
||||
visible: true,
|
||||
color: '0;0;0',
|
||||
fontFamily: 'Arial',
|
||||
align: 'left',
|
||||
wordWrap: true,
|
||||
})
|
||||
);
|
||||
objectBBText.content = {
|
||||
text:
|
||||
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
|
||||
opacity: 255,
|
||||
fontSize: 20,
|
||||
visible: true,
|
||||
color: '0;0;0',
|
||||
fontFamily: 'Arial',
|
||||
align: 'left',
|
||||
wordWrap: true,
|
||||
};
|
||||
|
||||
objectBBText.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
objectBBText.getInitialInstanceProperties = function (content, instance) {
|
||||
objectBBText.getInitialInstanceProperties = function (instance) {
|
||||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
return instanceProperties;
|
||||
};
|
||||
@@ -531,22 +526,33 @@ module.exports = {
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
update() {
|
||||
const properties = this._associatedObjectConfiguration.getProperties();
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
const rawText = properties.get('text').getValue();
|
||||
const rawText = object.content.text;
|
||||
if (rawText !== this._pixiObject.text) {
|
||||
this._pixiObject.text = rawText;
|
||||
}
|
||||
|
||||
const color = properties.get('color').getValue();
|
||||
this._pixiObject.textStyles.default.fill = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
color
|
||||
);
|
||||
const color = object.content.color;
|
||||
const newColor = objectsRenderingService.rgbOrHexToHexNumber(color);
|
||||
if (newColor !== this._pixiObject.textStyles.default.fill) {
|
||||
this._pixiObject.textStyles.default.fill = newColor;
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
const fontSize = properties.get('fontSize').getValue();
|
||||
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
|
||||
const fontSize = object.content.fontSize;
|
||||
const newDefaultFontsize = `${fontSize}px`;
|
||||
if (
|
||||
newDefaultFontsize !== this._pixiObject.textStyles.default.fontSize
|
||||
) {
|
||||
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
const fontResourceName = properties.get('fontFamily').getValue();
|
||||
const fontResourceName = object.content.fontFamily;
|
||||
|
||||
if (this._fontResourceName !== fontResourceName) {
|
||||
this._fontResourceName = fontResourceName;
|
||||
@@ -567,13 +573,13 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
const wordWrap = properties.get('wordWrap').getValue() === 'true';
|
||||
const wordWrap = object.content.wordWrap;
|
||||
if (wordWrap !== this._pixiObject._style.wordWrap) {
|
||||
this._pixiObject._style.wordWrap = wordWrap;
|
||||
this._pixiObject.dirty = true;
|
||||
}
|
||||
|
||||
const align = properties.get('align').getValue();
|
||||
const align = object.content.align;
|
||||
if (align !== this._pixiObject._style.align) {
|
||||
this._pixiObject._style.align = align;
|
||||
this._pixiObject.dirty = true;
|
||||
|
@@ -34,11 +34,8 @@ module.exports = {
|
||||
.setIcon('JsPlatform/Extensions/bitmapfont32.png');
|
||||
|
||||
const bitmapTextObject = new gd.ObjectJsImplementation();
|
||||
bitmapTextObject.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
bitmapTextObject.updateProperty = function (propertyName, newValue) {
|
||||
const objectContent = this.content;
|
||||
if (propertyName in objectContent) {
|
||||
if (typeof objectContent[propertyName] === 'boolean')
|
||||
objectContent[propertyName] = newValue === '1';
|
||||
@@ -50,8 +47,9 @@ module.exports = {
|
||||
|
||||
return false;
|
||||
};
|
||||
bitmapTextObject.getProperties = function (objectContent) {
|
||||
bitmapTextObject.getProperties = function () {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
const objectContent = this.content;
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('text')
|
||||
@@ -66,7 +64,7 @@ module.exports = {
|
||||
.addExtraInfo('left')
|
||||
.addExtraInfo('center')
|
||||
.addExtraInfo('right')
|
||||
.setLabel(_('Alignment, when multiple lines are displayed'))
|
||||
.setLabel(_('Alignment'))
|
||||
.setGroup(_('Appearance'));
|
||||
|
||||
objectProperties
|
||||
@@ -82,7 +80,7 @@ module.exports = {
|
||||
.setValue(objectContent.textureAtlasResourceName)
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Bitmap atlas image'))
|
||||
.setLabel(_('Bitmap Atlas'))
|
||||
.setGroup(_('Font'));
|
||||
|
||||
objectProperties
|
||||
@@ -108,33 +106,27 @@ module.exports = {
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
bitmapTextObject.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
text:
|
||||
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
|
||||
opacity: 255,
|
||||
scale: 1,
|
||||
fontSize: 20,
|
||||
tint: '255;255;255',
|
||||
bitmapFontResourceName: '',
|
||||
textureAtlasResourceName: '',
|
||||
align: 'left',
|
||||
wordWrap: true,
|
||||
})
|
||||
);
|
||||
bitmapTextObject.content = {
|
||||
text:
|
||||
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
|
||||
opacity: 255,
|
||||
scale: 1,
|
||||
fontSize: 20,
|
||||
tint: '255;255;255',
|
||||
bitmapFontResourceName: '',
|
||||
textureAtlasResourceName: '',
|
||||
align: 'left',
|
||||
wordWrap: true,
|
||||
};
|
||||
|
||||
bitmapTextObject.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
bitmapTextObject.getInitialInstanceProperties = function (
|
||||
content,
|
||||
instance
|
||||
) {
|
||||
bitmapTextObject.getInitialInstanceProperties = function (instance) {
|
||||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
return instanceProperties;
|
||||
};
|
||||
@@ -659,31 +651,31 @@ module.exports = {
|
||||
}
|
||||
|
||||
update() {
|
||||
const properties = this._associatedObjectConfiguration.getProperties();
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
// Update the rendered text properties (note: Pixi is only
|
||||
// applying changes if there were changed).
|
||||
const rawText = properties.get('text').getValue();
|
||||
const rawText = object.content.text;
|
||||
this._pixiObject.text = rawText;
|
||||
|
||||
const align = properties.get('align').getValue();
|
||||
const align = object.content.align;
|
||||
this._pixiObject.align = align;
|
||||
|
||||
const color = properties.get('tint').getValue();
|
||||
const color = object.content.tint;
|
||||
this._pixiObject.tint = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
color
|
||||
);
|
||||
|
||||
const scale = +(properties.get('scale').getValue() || 1);
|
||||
const scale = object.content.scale;
|
||||
this._pixiObject.scale.set(scale);
|
||||
|
||||
// Track the changes in font to load the new requested font.
|
||||
const bitmapFontResourceName = properties
|
||||
.get('bitmapFontResourceName')
|
||||
.getValue();
|
||||
const textureAtlasResourceName = properties
|
||||
.get('textureAtlasResourceName')
|
||||
.getValue();
|
||||
const bitmapFontResourceName = object.content.bitmapFontResourceName;
|
||||
const textureAtlasResourceName =
|
||||
object.content.textureAtlasResourceName;
|
||||
|
||||
if (
|
||||
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
|
||||
@@ -705,6 +697,8 @@ module.exports = {
|
||||
this._currentBitmapFontResourceName,
|
||||
this._currentTextureAtlasResourceName
|
||||
).then((bitmapFont) => {
|
||||
if (this._wasDestroyed) return;
|
||||
|
||||
this._pixiObject.fontName = bitmapFont.font;
|
||||
this._pixiObject.fontSize = bitmapFont.size;
|
||||
this._pixiObject.dirty = true;
|
||||
@@ -712,7 +706,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
// Set up the wrapping width if enabled.
|
||||
const wordWrap = properties.get('wordWrap').getValue() === 'true';
|
||||
const wordWrap = object.content.wordWrap;
|
||||
if (wordWrap && this._instance.hasCustomSize()) {
|
||||
this._pixiObject.maxWidth =
|
||||
this.getCustomWidth() / this._pixiObject.scale.x;
|
||||
|
@@ -26,7 +26,8 @@ DestroyOutsideBehavior::GetProperties(
|
||||
behaviorContent.GetDoubleAttribute("extraBorder", 0)))
|
||||
.SetType("Number")
|
||||
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
|
||||
.SetLabel(_("Margin before deleting the object, in pixels"));
|
||||
.SetLabel(_("Deletion margin"))
|
||||
.SetDescription(_("Margin before deleting the object, in pixels"));
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ std::map<gd::String, gd::PropertyDescriptor> DraggableBehavior::GetProperties(
|
||||
? "true"
|
||||
: "false")
|
||||
.SetType("Boolean")
|
||||
.SetLabel(_("Do a precision check against the object's collision mask"))
|
||||
.SetLabel(_("Precise check"))
|
||||
.SetDescription(
|
||||
_("Use the object (custom) collision mask instead of the bounding "
|
||||
"box, making the behavior more precise at the cost of "
|
||||
|
@@ -67,14 +67,10 @@ namespace gdjs {
|
||||
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
|
||||
BevelFilterExtra;
|
||||
if (parameterName === 'lightColor') {
|
||||
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
bevelFilter.lightColor = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
if (parameterName === 'shadowColor') {
|
||||
bevelFilter.shadowColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
bevelFilter.shadowColor = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
}
|
||||
updateColorParameter(
|
||||
|
@@ -45,13 +45,9 @@ namespace gdjs {
|
||||
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
|
||||
ColorReplaceFilterExtra;
|
||||
if (parameterName === 'originalColor') {
|
||||
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
colorReplaceFilter.originalColor = gdjs.rgbOrHexStringToNumber(value);
|
||||
} else if (parameterName === 'newColor') {
|
||||
colorReplaceFilter.newColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
colorReplaceFilter.newColor = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
}
|
||||
updateColorParameter(
|
||||
|
@@ -66,9 +66,7 @@ namespace gdjs {
|
||||
) {
|
||||
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
|
||||
if (parameterName === 'color') {
|
||||
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
dropShadowFilter.color = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
}
|
||||
updateColorParameter(
|
||||
|
@@ -53,7 +53,7 @@ namespace gdjs {
|
||||
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
|
||||
GlowFilterExtra;
|
||||
if (parameterName === 'color') {
|
||||
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
|
||||
glowFilter.color = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
}
|
||||
updateColorParameter(
|
||||
|
@@ -41,9 +41,7 @@ namespace gdjs {
|
||||
) {
|
||||
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
|
||||
if (parameterName === 'color') {
|
||||
outlineFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
|
||||
value
|
||||
);
|
||||
outlineFilter.color = gdjs.rgbOrHexStringToNumber(value);
|
||||
}
|
||||
}
|
||||
updateColorParameter(
|
||||
|
@@ -287,11 +287,9 @@ module.exports = {
|
||||
// Everything that is stored inside the object is in "content" and is automatically
|
||||
// saved/loaded to JSON.
|
||||
var dummyObject = new gd.ObjectJsImplementation();
|
||||
dummyObject.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
dummyObject.updateProperty = function (propertyName, newValue) {
|
||||
const objectContent = this.content;
|
||||
|
||||
if (propertyName === 'My first property') {
|
||||
objectContent.property1 = newValue;
|
||||
return true;
|
||||
@@ -311,8 +309,9 @@ module.exports = {
|
||||
|
||||
return false;
|
||||
};
|
||||
dummyObject.getProperties = function (objectContent) {
|
||||
dummyObject.getProperties = function () {
|
||||
var objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
const objectContent = this.content;
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('My first property')
|
||||
@@ -336,17 +335,14 @@ module.exports = {
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
dummyObject.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
property1: 'Hello world',
|
||||
property2: true,
|
||||
property3: 123,
|
||||
myImage: '',
|
||||
})
|
||||
);
|
||||
dummyObject.content = {
|
||||
property1: 'Hello world',
|
||||
property2: true,
|
||||
property3: 123,
|
||||
myImage: '',
|
||||
};
|
||||
|
||||
dummyObject.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue
|
||||
@@ -362,7 +358,7 @@ module.exports = {
|
||||
|
||||
return false;
|
||||
};
|
||||
dummyObject.getInitialInstanceProperties = function (content, instance) {
|
||||
dummyObject.getInitialInstanceProperties = function (instance) {
|
||||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
instanceProperties
|
||||
@@ -507,12 +503,13 @@ module.exports = {
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
update() {
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
// Read a property from the object
|
||||
const property1Value = this._associatedObjectConfiguration
|
||||
.getProperties()
|
||||
.get('My first property')
|
||||
.getValue();
|
||||
this._pixiObject.text = property1Value;
|
||||
this._pixiObject.text = object.content.property1;
|
||||
|
||||
// Read position and angle from the instance
|
||||
this._pixiObject.position.x =
|
||||
|
10
Extensions/JsExtensionTypes.d.ts
vendored
10
Extensions/JsExtensionTypes.d.ts
vendored
@@ -14,9 +14,12 @@ class RenderedInstance {
|
||||
_associatedObjectConfiguration: gd.ObjectConfiguration;
|
||||
_pixiContainer: PIXI.Container;
|
||||
_pixiResourcesLoader: Class<PixiResourcesLoader>;
|
||||
_pixiObject: PIXI.DisplayObject;
|
||||
_pixiObject: PIXI.DisplayObject | null;
|
||||
wasUsed: boolean;
|
||||
|
||||
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
|
||||
_wasDestroyed: boolean;
|
||||
|
||||
constructor(
|
||||
project: gdProject,
|
||||
instance: gdInitialInstance,
|
||||
@@ -89,10 +92,13 @@ class Rendered3DInstance {
|
||||
_pixiContainer: PIXI.Container;
|
||||
_threeGroup: THREE.Group;
|
||||
_pixiResourcesLoader: Class<PixiResourcesLoader>;
|
||||
_pixiObject: PIXI.DisplayObject;
|
||||
_pixiObject: PIXI.DisplayObject | null;
|
||||
_threeObject: THREE.Object3D | null;
|
||||
wasUsed: boolean;
|
||||
|
||||
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
|
||||
_wasDestroyed: boolean;
|
||||
|
||||
constructor(
|
||||
project: gdProject,
|
||||
instance: gdInitialInstance,
|
||||
|
@@ -588,7 +588,7 @@ namespace gdjs {
|
||||
break;
|
||||
case 'openPlayerAuthentication':
|
||||
gdjs.playerAuthentication
|
||||
.openAuthenticationWindow(runtimeScene)
|
||||
.openAuthenticationWindow(runtimeScene, event.data.options)
|
||||
.promise.then(({ status }) => {
|
||||
if (
|
||||
!_leaderboardViewIframe ||
|
||||
|
@@ -68,11 +68,8 @@ module.exports = {
|
||||
|
||||
const lightObject = new gd.ObjectJsImplementation();
|
||||
|
||||
lightObject.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
lightObject.updateProperty = function (propertyName, newValue) {
|
||||
const objectContent = this.content;
|
||||
if (propertyName === 'radius') {
|
||||
objectContent.radius = parseFloat(newValue);
|
||||
return true;
|
||||
@@ -96,8 +93,9 @@ module.exports = {
|
||||
return false;
|
||||
};
|
||||
|
||||
lightObject.getProperties = function (objectContent) {
|
||||
lightObject.getProperties = function () {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
const objectContent = this.content;
|
||||
|
||||
objectProperties.set(
|
||||
'radius',
|
||||
@@ -140,17 +138,14 @@ module.exports = {
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
lightObject.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
radius: 50,
|
||||
color: '255;255;255',
|
||||
debugMode: false,
|
||||
texture: '',
|
||||
})
|
||||
);
|
||||
lightObject.content = {
|
||||
radius: 50,
|
||||
color: '255;255;255',
|
||||
debugMode: false,
|
||||
texture: '',
|
||||
};
|
||||
|
||||
lightObject.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue
|
||||
@@ -158,7 +153,7 @@ module.exports = {
|
||||
return false;
|
||||
};
|
||||
|
||||
lightObject.getInitialInstanceProperties = function (content, instance) {
|
||||
lightObject.getInitialInstanceProperties = function (instance) {
|
||||
const instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
return instanceProperties;
|
||||
@@ -238,6 +233,10 @@ module.exports = {
|
||||
* Renderer for instances of LightObject inside the IDE.
|
||||
*/
|
||||
class RenderedLightObjectInstance extends RenderedInstance {
|
||||
_radius = 0;
|
||||
_color = 0;
|
||||
_radiusGraphics = null;
|
||||
|
||||
constructor(
|
||||
project,
|
||||
instance,
|
||||
@@ -252,19 +251,6 @@ module.exports = {
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
);
|
||||
this._radius = parseFloat(
|
||||
this._associatedObjectConfiguration
|
||||
.getProperties()
|
||||
.get('radius')
|
||||
.getValue()
|
||||
);
|
||||
if (this._radius <= 0) this._radius = 1;
|
||||
const color = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
this._associatedObjectConfiguration
|
||||
.getProperties()
|
||||
.get('color')
|
||||
.getValue()
|
||||
);
|
||||
|
||||
// The icon in the middle.
|
||||
const lightIconSprite = new PIXI.Sprite(
|
||||
@@ -274,18 +260,11 @@ module.exports = {
|
||||
lightIconSprite.anchor.y = 0.5;
|
||||
|
||||
// The circle to show the radius of the light.
|
||||
const radiusBorderWidth = 2;
|
||||
const radiusGraphics = new PIXI.Graphics();
|
||||
radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
|
||||
radiusGraphics.drawCircle(
|
||||
0,
|
||||
0,
|
||||
Math.max(1, this._radius - radiusBorderWidth)
|
||||
);
|
||||
this._radiusGraphics = new PIXI.Graphics();
|
||||
|
||||
this._pixiObject = new PIXI.Container();
|
||||
this._pixiObject.addChild(lightIconSprite);
|
||||
this._pixiObject.addChild(radiusGraphics);
|
||||
this._pixiObject.addChild(this._radiusGraphics);
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
this.update();
|
||||
}
|
||||
@@ -307,8 +286,41 @@ module.exports = {
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
update() {
|
||||
const object = gd.castObject(
|
||||
this._associatedObjectConfiguration,
|
||||
gd.ObjectJsImplementation
|
||||
);
|
||||
|
||||
this._pixiObject.position.x = this._instance.getX();
|
||||
this._pixiObject.position.y = this._instance.getY();
|
||||
|
||||
let radiusGraphicsDirty = false;
|
||||
|
||||
let radius = object.content.radius;
|
||||
if (radius <= 0) radius = 1;
|
||||
if (radius !== this._radius) {
|
||||
this._radius = radius;
|
||||
radiusGraphicsDirty = true;
|
||||
}
|
||||
|
||||
const color = objectsRenderingService.rgbOrHexToHexNumber(
|
||||
object.content.color
|
||||
);
|
||||
if (color !== this._color) {
|
||||
this._color = color;
|
||||
radiusGraphicsDirty = true;
|
||||
}
|
||||
|
||||
if (radiusGraphicsDirty) {
|
||||
const radiusBorderWidth = 2;
|
||||
this._radiusGraphics.clear();
|
||||
this._radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
|
||||
this._radiusGraphics.drawCircle(
|
||||
0,
|
||||
0,
|
||||
Math.max(1, this._radius - radiusBorderWidth)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -505,8 +505,9 @@ namespace gdjs {
|
||||
closestVertices.sort(
|
||||
LightRuntimeObjectPixiRenderer._verticesWithAngleComparator
|
||||
);
|
||||
const filteredVerticesResult = [closestVertices[0].vertex];
|
||||
const closestVerticesCount = closestVertices.length;
|
||||
if (closestVerticesCount === 0) return [];
|
||||
const filteredVerticesResult = [closestVertices[0].vertex];
|
||||
for (let i = 1; i < closestVerticesCount; i++) {
|
||||
if (closestVertices[i].angle !== closestVertices[i - 1].angle) {
|
||||
filteredVerticesResult.push(closestVertices[i].vertex);
|
||||
|
@@ -120,10 +120,11 @@ namespace gdjs {
|
||||
|
||||
private _isOwnerAsPlayerOrHost() {
|
||||
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
|
||||
const isHost = gdjs.multiplayer.isCurrentPlayerHost();
|
||||
|
||||
const isOwnerOfObject =
|
||||
currentPlayerNumber === this.playerNumber || // Player as owner.
|
||||
(currentPlayerNumber === 1 && this.playerNumber === 0); // Host as owner.
|
||||
(isHost && this.playerNumber === 0); // Host as owner.
|
||||
|
||||
return isOwnerOfObject;
|
||||
}
|
||||
@@ -455,8 +456,8 @@ namespace gdjs {
|
||||
|
||||
// Before sending the destroy message, we set up the object representing the peers
|
||||
// that we need an acknowledgment from.
|
||||
// If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.
|
||||
// If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.
|
||||
// If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.
|
||||
// If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.
|
||||
// In both cases, this represents the list of peers the current user is connected to.
|
||||
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
const {
|
||||
@@ -508,7 +509,7 @@ namespace gdjs {
|
||||
|
||||
// Update the ownership locally, so the object can be used immediately.
|
||||
// This is a prediction to allow snappy interactions.
|
||||
// If we are player 1 or host, we will have the ownership immediately anyway.
|
||||
// If we are host, we will have the ownership immediately anyway.
|
||||
// If we are another player, we will have the ownership as soon as the host acknowledges the change.
|
||||
// If the host does not send an acknowledgment, we will revert the ownership.
|
||||
const previousObjectPlayerNumber = this.playerNumber;
|
||||
@@ -564,8 +565,8 @@ namespace gdjs {
|
||||
});
|
||||
// Before sending the changeOwner message, if we are becoming the new owner,
|
||||
// we want to ensure this message is acknowledged, by everyone we're connected to.
|
||||
// If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.
|
||||
// If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.
|
||||
// If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.
|
||||
// If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.
|
||||
// In both cases, this represents the list of peers the current user is connected to.
|
||||
if (newObjectPlayerNumber === currentPlayerNumber) {
|
||||
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
|
||||
@@ -581,7 +582,7 @@ namespace gdjs {
|
||||
expectedMessageName: changeOwnerAcknowledgedMessageName,
|
||||
otherPeerIds,
|
||||
// If we are not the host, we should revert the ownership if the host does not acknowledge the change.
|
||||
shouldCancelMessageIfTimesOut: currentPlayerNumber !== 1,
|
||||
shouldCancelMessageIfTimesOut: !gdjs.multiplayer.isCurrentPlayerHost(),
|
||||
});
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user